diff --git a/.env.example b/.env.example index 0a7ccb0..e8b9d64 100644 --- a/.env.example +++ b/.env.example @@ -6,7 +6,4 @@ GH_APP_CLIENT_ID= GH_APP_CLIENT_SECRET= REDIS_URL= DATABASE_URL= -MQTT_URL= -MQTT_USERNAME= -MQTT_PASSWORD= -MQTT_ROOT_TOPIC= \ No newline at end of file +UPDATER_MANIFEST_URL= \ No newline at end of file diff --git a/buf.yaml b/buf.yaml deleted file mode 100644 index 8e0e29c..0000000 --- a/buf.yaml +++ /dev/null @@ -1,11 +0,0 @@ -version: v1 -name: buf.build/meshtastic/api -deps: [] -build: - excludes: [] -breaking: - use: - - FILE -lint: - use: - - MINIMAL \ No newline at end of file diff --git a/package.json b/package.json index 62531d9..4ca32e7 100644 --- a/package.json +++ b/package.json @@ -11,12 +11,6 @@ "build": "prisma generate && tsc" }, "dependencies": { - "@buf/meshtastic_api.bufbuild_es": "1.7.2-20240110092216-3a147af14302.1", - "@buf/meshtastic_api.connectrpc_es": "1.3.0-20240110092216-3a147af14302.1", - "@bufbuild/protobuf": "^1.7.2", - "@connectrpc/connect": "^1.3.0", - "@connectrpc/connect-express": "^1.3.0", - "@meshtastic/js": "2.2.23-0", "@octokit/auth-app": "^6.0.3", "@prisma/client": "5.22.0", "@tinyhttp/app": "2.2.3", @@ -25,11 +19,9 @@ "@tinyhttp/favicon": "^3.0.0", "@tinyhttp/logger": "^2.0.0", "add": "^2.0.6", - "mqtt": "^5.3.5", "octokit": "^3.1.2", "pnpm": "^8.15.3", "redis": "^4.6.13", - "sub-events": "^1.9.0", "ts-pattern": "^5.0.8", "typescript": "^5.3.3" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 28bc99e..410e15d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,24 +8,6 @@ importers: .: dependencies: - '@buf/meshtastic_api.bufbuild_es': - specifier: 1.7.2-20240110092216-3a147af14302.1 - version: 1.7.2-20240110092216-3a147af14302.1(@bufbuild/protobuf@1.7.2) - '@buf/meshtastic_api.connectrpc_es': - specifier: 1.3.0-20240110092216-3a147af14302.1 - version: 1.3.0-20240110092216-3a147af14302.1(@bufbuild/protobuf@1.7.2)(@connectrpc/connect@1.3.0(@bufbuild/protobuf@1.7.2)) - '@bufbuild/protobuf': - specifier: ^1.7.2 - version: 1.7.2 - '@connectrpc/connect': - specifier: ^1.3.0 - version: 1.3.0(@bufbuild/protobuf@1.7.2) - '@connectrpc/connect-express': - specifier: ^1.3.0 - version: 1.3.0(@bufbuild/protobuf@1.7.2)(@connectrpc/connect-node@1.3.0(@bufbuild/protobuf@1.7.2)(@connectrpc/connect@1.3.0(@bufbuild/protobuf@1.7.2)))(@connectrpc/connect@1.3.0(@bufbuild/protobuf@1.7.2)) - '@meshtastic/js': - specifier: 2.2.23-0 - version: 2.2.23-0(buffer@6.0.3) '@octokit/auth-app': specifier: ^6.0.3 version: 6.0.3 @@ -50,9 +32,6 @@ importers: add: specifier: ^2.0.6 version: 2.0.6 - mqtt: - specifier: ^5.3.5 - version: 5.3.5 octokit: specifier: ^3.1.2 version: 3.1.2 @@ -62,9 +41,6 @@ importers: redis: specifier: ^4.6.13 version: 4.6.13 - sub-events: - specifier: ^1.9.0 - version: 1.9.0 ts-pattern: specifier: ^5.0.8 version: 5.0.8 @@ -90,10 +66,6 @@ importers: packages: - '@babel/runtime@7.23.9': - resolution: {integrity: sha512-0CX6F+BI2s9dkUqr08KFrAIZgNFj75rdBU/DjCyYLIaV/quFjkk6T+EJ2LkZHyZTbEV4L5p97mNkUsHl2wLFAw==} - engines: {node: '>=6.9.0'} - '@biomejs/biome@2.1.3': resolution: {integrity: sha512-KE/tegvJIxTkl7gJbGWSgun7G6X/n2M6C35COT6ctYrAy7SiPyNvi6JtoQERVK/VRbttZfgGq96j2bFmhmnH4w==} engines: {node: '>=14.21.3'} @@ -147,21 +119,6 @@ packages: cpu: [x64] os: [win32] - '@buf/meshtastic_api.bufbuild_es@1.6.0-20240110092216-3a147af14302.1': - resolution: {tarball: https://buf.build/gen/npm/v1/@buf/meshtastic_api.bufbuild_es/-/meshtastic_api.bufbuild_es-1.6.0-20240110092216-3a147af14302.1.tgz} - peerDependencies: - '@bufbuild/protobuf': ^1.6.0 - - '@buf/meshtastic_api.bufbuild_es@1.7.2-20240110092216-3a147af14302.1': - resolution: {tarball: https://buf.build/gen/npm/v1/@buf/meshtastic_api.bufbuild_es/-/meshtastic_api.bufbuild_es-1.7.2-20240110092216-3a147af14302.1.tgz} - peerDependencies: - '@bufbuild/protobuf': ^1.7.2 - - '@buf/meshtastic_api.connectrpc_es@1.3.0-20240110092216-3a147af14302.1': - resolution: {tarball: https://buf.build/gen/npm/v1/@buf/meshtastic_api.connectrpc_es/-/meshtastic_api.connectrpc_es-1.3.0-20240110092216-3a147af14302.1.tgz} - peerDependencies: - '@connectrpc/connect': ^1.3.0 - '@buf/meshtastic_protobufs.bufbuild_es@1.7.2-20240216123215-6b07c41c68c9.1': resolution: {tarball: https://buf.build/gen/npm/v1/@buf/meshtastic_protobufs.bufbuild_es/-/meshtastic_protobufs.bufbuild_es-1.7.2-20240216123215-6b07c41c68c9.1.tgz} peerDependencies: @@ -170,26 +127,6 @@ packages: '@bufbuild/protobuf@1.7.2': resolution: {integrity: sha512-i5GE2Dk5ekdlK1TR7SugY4LWRrKSfb5T1Qn4unpIMbfxoeGKERKQ59HG3iYewacGD10SR7UzevfPnh6my4tNmQ==} - '@connectrpc/connect-express@1.3.0': - resolution: {integrity: sha512-6wbaQheD9cb4DnU1PvgVQdB1XPfA0bhlA0V0ZKx6oJJnTgGEYBzPrQztmqs5XW36/r+qJRfMgaVzZfX8MLafgA==} - engines: {node: '>=16.0.0'} - peerDependencies: - '@bufbuild/protobuf': ^1.4.2 - '@connectrpc/connect': 1.3.0 - '@connectrpc/connect-node': 1.3.0 - - '@connectrpc/connect-node@1.3.0': - resolution: {integrity: sha512-2fV/z/8MUFOkTn2Gbm7T/qvRfkpt/D/w7ykYqACZRH6VNG/faY4lH2wUZiNkwv9tzTrECKOJFyPsaGs3nRYK3w==} - engines: {node: '>=16.0.0'} - peerDependencies: - '@bufbuild/protobuf': ^1.4.2 - '@connectrpc/connect': 1.3.0 - - '@connectrpc/connect@1.3.0': - resolution: {integrity: sha512-kTeWxJnLLtxKc2ZSDN0rIBgwfP8RwcLknthX4AKlIAmN9ZC4gGnCbwp+3BKcP/WH5c8zGBAWqSY3zeqCM+ah7w==} - peerDependencies: - '@bufbuild/protobuf': ^1.4.2 - '@esbuild/aix-ppc64@0.19.12': resolution: {integrity: sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==} engines: {node: '>=12'} @@ -328,14 +265,6 @@ packages: cpu: [x64] os: [win32] - '@fastify/busboy@2.1.1': - resolution: {integrity: sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==} - engines: {node: '>=14'} - - '@meshtastic/js@2.2.23-0': - resolution: {integrity: sha512-67L2xcgqIa+INuZBuMNikCNBLWvtOmGJIMDOsZx84pw6MLYAKAoX93SG0qKOvWHfsDyThBxjHzm2lAje8xC9/g==} - deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. - '@octokit/app@14.0.2': resolution: {integrity: sha512-NCSCktSx+XmjuSUVn2dLfqQ9WIYePGP95SDJs4I9cn/0ZkeXcPkaoCLl64Us3dRKL2ozC7hArwze5Eu+/qt1tg==} engines: {node: '>= 18'} @@ -583,58 +512,15 @@ packages: '@types/aws-lambda@8.10.130': resolution: {integrity: sha512-HxTfLeGvD1wTJqIGwcBCpNmHKenja+We1e0cuzeIDFfbEj3ixnlTInyPR/81zAe0Ss/Ip12rFK6XNeMLVucOSg==} - '@types/body-parser@1.19.5': - resolution: {integrity: sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==} - '@types/btoa-lite@1.0.2': resolution: {integrity: sha512-ZYbcE2x7yrvNFJiU7xJGrpF/ihpkM7zKgw8bha3LNJSesvTtUNxbpzaT7WXBIryf6jovisrxTBvymxMeLLj1Mg==} - '@types/connect@3.4.38': - resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} - - '@types/express-serve-static-core@4.17.41': - resolution: {integrity: sha512-OaJ7XLaelTgrvlZD8/aa0vvvxZdUmlCn6MtWeB7TkiKW70BQLc9XEPpDLPdbo52ZhXUCrznlWdCHWxJWtdyajA==} - - '@types/express@4.17.21': - resolution: {integrity: sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==} - - '@types/http-errors@2.0.4': - resolution: {integrity: sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==} - '@types/jsonwebtoken@9.0.5': resolution: {integrity: sha512-VRLSGzik+Unrup6BsouBeHsf4d1hOEgYWTm/7Nmw1sXoN1+tRly/Gy/po3yeahnP4jfnQWWAhQAqcNfH7ngOkA==} - '@types/mime@1.3.5': - resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} - - '@types/mime@3.0.4': - resolution: {integrity: sha512-iJt33IQnVRkqeqC7PzBHPTC6fDlRNRW8vjrgqtScAhrmMwe8c4Eo7+fUGTa+XdWrpEgpyKWMYmi2dIwMAYRzPw==} - '@types/node@20.11.19': resolution: {integrity: sha512-7xMnVEcZFu0DikYjWOlRq7NTPETrm7teqUT2WkQjrTIkEgUyyGdWsj/Zg8bEJt5TNklzbPD1X3fqfsHw3SpapQ==} - '@types/qs@6.9.11': - resolution: {integrity: sha512-oGk0gmhnEJK4Yyk+oI7EfXsLayXatCWPHary1MtcmbAifkobT9cM9yutG/hZKIseOU0MqbIwQ/u2nn/Gb+ltuQ==} - - '@types/range-parser@1.2.7': - resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==} - - '@types/readable-stream@4.0.10': - resolution: {integrity: sha512-AbUKBjcC8SHmImNi4yK2bbjogQlkFSg7shZCcicxPQapniOlajG8GCc39lvXzCWX4lLRRs7DM3VAeSlqmEVZUA==} - - '@types/send@0.17.4': - resolution: {integrity: sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==} - - '@types/serve-static@1.15.5': - resolution: {integrity: sha512-PDRk21MnK70hja/YF8AHfC7yIsiQHn1rcXx7ijCFBX/k+XQJhQT/gw3xekXKJvx+5SXaMMS8oqQy09Mzvz2TuQ==} - - '@types/ws@8.5.10': - resolution: {integrity: sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==} - - abort-controller@3.0.0: - resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} - engines: {node: '>=6.5'} - add@2.0.6: resolution: {integrity: sha512-j5QzrmsokwWWp6kUcJQySpbG+xfOBqqKnup3OIk1pz+kB/80SLorZ9V8zHFLO92Lcd+hbvq8bT+zOGoPkmBV0Q==} @@ -642,15 +528,9 @@ packages: resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==} engines: {node: '>=8'} - base64-js@1.5.1: - resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - before-after-hook@2.2.3: resolution: {integrity: sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==} - bl@6.0.10: - resolution: {integrity: sha512-F14DFhDZfxtVm2FY0k9kG2lWAwzZkO9+jX3Ytuoy/V0E1/5LBuBzzQHXAjqpxXEDIpmTPZZf5GVIGPQcLxFpaA==} - bottleneck@2.19.5: resolution: {integrity: sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw==} @@ -660,12 +540,6 @@ packages: buffer-equal-constant-time@1.0.1: resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==} - buffer-from@1.1.2: - resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} - - buffer@6.0.3: - resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} - clean-stack@2.2.0: resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==} engines: {node: '>=6'} @@ -677,34 +551,9 @@ packages: colorette@2.0.20: resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} - commist@3.2.0: - resolution: {integrity: sha512-4PIMoPniho+LqXmpS5d3NuGYncG6XWlkBSVGiWycL22dd42OYdUGil2CWuzklaJoNxyxUSpO4MKIBU94viWNAw==} - - concat-stream@2.0.0: - resolution: {integrity: sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==} - engines: {'0': node >= 6.0} - - crc@4.3.2: - resolution: {integrity: sha512-uGDHf4KLLh2zsHa8D8hIQ1H/HtFQhyHrc0uhHBcoKGol/Xnb+MPYfUMw7cvON6ze/GUESTudKayDcJC5HnJv1A==} - engines: {node: '>=12'} - peerDependencies: - buffer: '>=6.0.3' - peerDependenciesMeta: - buffer: - optional: true - dayjs@1.11.10: resolution: {integrity: sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==} - debug@4.3.4: - resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} - engines: {node: '>=6.0'} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true - deprecation@2.3.1: resolution: {integrity: sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==} @@ -728,18 +577,6 @@ packages: engines: {node: '>=12'} hasBin: true - event-target-shim@5.0.1: - resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} - engines: {node: '>=6'} - - events@3.3.0: - resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} - engines: {node: '>=0.8.x'} - - fast-unique-numbers@8.0.13: - resolution: {integrity: sha512-7OnTFAVPefgw2eBJ1xj2PGGR9FwYzSUso9decayHgCDX4sJkHLdcsYTytTg+tYv+wKF3U8gJuSBz2jJpQV4u/g==} - engines: {node: '>=16.1.0'} - fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -756,29 +593,17 @@ packages: resolution: {integrity: sha512-B9zCFt3jH8g09LR1vHL4pcAn8yMEtlSlOUdQemzHMRKMImNIhhszdeosYFfNW0WXKQtXIlWB+O4owHJKvEJYaA==} engines: {node: '>=12.22.0'} - help-me@5.0.0: - resolution: {integrity: sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==} - http-status-emojis@2.2.0: resolution: {integrity: sha512-ompKtgwpx8ff0hsbpIB7oE4ax1LXoHmftsHHStMELX56ivG3GhofTX8ZHWlUaFKfGjcGjw6G3rPk7dJRXMmbbg==} - ieee754@1.2.1: - resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} - indent-string@4.0.0: resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} engines: {node: '>=8'} - inherits@2.0.4: - resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} - ipaddr.js@2.1.0: resolution: {integrity: sha512-LlbxQ7xKzfBusov6UMi4MFpEg0m+mAm9xyNGEduwXMEDuf4WfzB/RZwMVYEd7IKGvh4IUkEXYxtAVu9T3OelJQ==} engines: {node: '>= 10'} - js-sdsl@4.3.0: - resolution: {integrity: sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==} - jsonwebtoken@9.0.2: resolution: {integrity: sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==} engines: {node: '>=12', npm: '>=6'} @@ -814,10 +639,6 @@ packages: resolution: {integrity: sha512-/1clY/ui8CzjKFyjdvwPWJUYKiFVXG2I2cY0ssG7h4+hwk+XOIX7ZSG9Q7TW8TW3Kp3BUSqgFWBLgL4PJ+Blag==} engines: {node: 14 || >=16.14} - lru-cache@10.2.0: - resolution: {integrity: sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==} - engines: {node: 14 || >=16.14} - lru-cache@6.0.0: resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} engines: {node: '>=10'} @@ -827,20 +648,6 @@ packages: engines: {node: '>=16'} hasBin: true - minimist@1.2.8: - resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} - - mqtt-packet@9.0.0: - resolution: {integrity: sha512-8v+HkX+fwbodsWAZIZTI074XIoxVBOmPeggQuDFCGg1SqNcC+uoRMWu7J6QlJPqIUIJXmjNYYHxBBLr1Y/Df4w==} - - mqtt@5.3.5: - resolution: {integrity: sha512-xd7qt/LEM721U6yHQcqjlaAKXL1Fsqf/MXq6C2WPi/6OXK2jdSzL1eZ7ZUgjea7IY2yFLRWD5LNdT1mL0arPoA==} - engines: {node: '>=16.0.0'} - hasBin: true - - ms@2.1.2: - resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} - ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} @@ -848,9 +655,6 @@ packages: resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} engines: {node: '>= 0.6'} - number-allocator@1.0.14: - resolution: {integrity: sha512-OrL44UTVAvkKdOdRQZIJpLkAdjXGTRda052sN4sO77bKEzYYqWKMBjQvrJFzqygI99gL6Z4u2xctPW1tB8ErvA==} - octokit@3.1.2: resolution: {integrity: sha512-MG5qmrTL5y8KYwFgE1A4JWmgfQBaIETE/lOlfwNYx1QOtCQHGVxkRJmdUJltFc1HVn73d61TlMhMyNTOtMl+ng==} engines: {node: '>= 18'} @@ -868,43 +672,16 @@ packages: engines: {node: '>=16.13'} hasBin: true - process-nextick-args@2.0.1: - resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} - - process@0.11.10: - resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} - engines: {node: '>= 0.6.0'} - - readable-stream@3.6.2: - resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} - engines: {node: '>= 6'} - - readable-stream@4.5.2: - resolution: {integrity: sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - redis@4.6.13: resolution: {integrity: sha512-MHgkS4B+sPjCXpf+HfdetBwbRz6vCtsceTmw1pHNYJAsYxrfpOP6dz+piJWGos8wqG7qb3vj/Rrc5qOlmInUuA==} - regenerator-runtime@0.14.1: - resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} - regexparam@2.0.2: resolution: {integrity: sha512-A1PeDEYMrkLrfyOwv2jwihXbo9qxdGD3atBYQA9JJgreAx8/7rC6IUkWOw2NQlOxLp2wL0ifQbh1HuidDfYA6w==} engines: {node: '>=8'} - reinterval@1.1.0: - resolution: {integrity: sha512-QIRet3SYrGp0HUHO88jVskiG6seqUGC5iAG7AwI/BV4ypGcuqk9Du6YQBUOUqm9c8pw1eyLoIaONifRua1lsEQ==} - resolve-pkg-maps@1.0.0: resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} - rfdc@1.3.1: - resolution: {integrity: sha512-r5a3l5HzYlIC68TpmYKlxWjmOP6wiPJ1vWv2HeLhNsRZMrCkxeqxiHlQ21oXmQ4F3SiryXBHhAD7JZqvOJjFmg==} - - safe-buffer@5.1.2: - resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} - safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} @@ -913,35 +690,14 @@ packages: engines: {node: '>=10'} hasBin: true - split2@4.2.0: - resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} - engines: {node: '>= 10.x'} - - string_decoder@1.3.0: - resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} - - sub-events@1.9.0: - resolution: {integrity: sha512-dnFBayilG9Ku0k/lNs1Y7WV4kv91+ovCoeBV3uIYrY49DylvBb6z9d9ED2ctcrvX2YlReFalpCgJNtSgmrOaJg==} - engines: {node: '>=10.0.0'} - ts-pattern@5.0.8: resolution: {integrity: sha512-aafbuAQOTEeWmA7wtcL94w6I89EgLD7F+IlWkr596wYxeb0oveWDO5dQpv85YP0CGbxXT/qXBIeV6IYLcoZ2uA==} - tslib@2.6.2: - resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} - - tslog@4.9.2: - resolution: {integrity: sha512-wBM+LRJoNl34Bdu8mYEFxpvmOUedpNUwMNQB/NcuPIZKwdDde6xLHUev3bBjXQU7gdurX++X/YE7gLH8eXYsiQ==} - engines: {node: '>=16'} - tsx@4.7.1: resolution: {integrity: sha512-8d6VuibXHtlN5E3zFkgY8u4DX7Y3Z27zvvPKVmLon/D4AjuKzarkUBTLDBgj9iTQ0hg5xM7c/mYiRVM+HETf0g==} engines: {node: '>=18.0.0'} hasBin: true - typedarray@0.0.6: - resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==} - typescript@5.3.3: resolution: {integrity: sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==} engines: {node: '>=14.17'} @@ -950,52 +706,20 @@ packages: undici-types@5.26.5: resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} - undici@5.29.0: - resolution: {integrity: sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg==} - engines: {node: '>=14.0'} - universal-github-app-jwt@1.1.2: resolution: {integrity: sha512-t1iB2FmLFE+yyJY9+3wMx0ejB+MQpEVkH0gQv7dR6FZyltyq+ZZO0uDpbopxhrZ3SLEO4dCEkIujOMldEQ2iOA==} universal-user-agent@6.0.1: resolution: {integrity: sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ==} - util-deprecate@1.0.2: - resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - - worker-timers-broker@6.1.1: - resolution: {integrity: sha512-CTlDnkXAewtYvw5gOwVIc6UuIPcNHJrqWxBMhZbCWOmadvl20nPs9beAsXlaTEwW3G2KBpuKiSgkhBkhl3mxDA==} - - worker-timers-worker@7.0.65: - resolution: {integrity: sha512-Dl4nGONr8A8Fr+vQnH7Ee+o2iB480S1fBcyJYqnMyMwGRVyQZLZU+o91vbMvU1vHqiryRQmjXzzMYlh86wx+YQ==} - - worker-timers@7.1.1: - resolution: {integrity: sha512-axtq83GwPqYwkQmQmei2abQ9cT7oSwmLw4lQCZ9VmMH9g4t4kuEF1Gw+tdnIJGHCiZ2QPDnr/+307bYx6tynLA==} - wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - ws@8.20.1: - resolution: {integrity: sha512-It4dO0K5v//JtTXuPkfEOaI3uUN87iYPnqo/ZzqCoG3g8uhA66QUMs/SrM0YK7/NAu+r4LMh/9dq2A7k+rHs+w==} - engines: {node: '>=10.0.0'} - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: '>=5.0.2' - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - yallist@4.0.0: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} snapshots: - '@babel/runtime@7.23.9': - dependencies: - regenerator-runtime: 0.14.1 - '@biomejs/biome@2.1.3': optionalDependencies: '@biomejs/cli-darwin-arm64': 2.1.3 @@ -1031,44 +755,12 @@ snapshots: '@biomejs/cli-win32-x64@2.1.3': optional: true - '@buf/meshtastic_api.bufbuild_es@1.6.0-20240110092216-3a147af14302.1(@bufbuild/protobuf@1.7.2)': - dependencies: - '@bufbuild/protobuf': 1.7.2 - - '@buf/meshtastic_api.bufbuild_es@1.7.2-20240110092216-3a147af14302.1(@bufbuild/protobuf@1.7.2)': - dependencies: - '@bufbuild/protobuf': 1.7.2 - - '@buf/meshtastic_api.connectrpc_es@1.3.0-20240110092216-3a147af14302.1(@bufbuild/protobuf@1.7.2)(@connectrpc/connect@1.3.0(@bufbuild/protobuf@1.7.2))': - dependencies: - '@buf/meshtastic_api.bufbuild_es': 1.6.0-20240110092216-3a147af14302.1(@bufbuild/protobuf@1.7.2) - '@connectrpc/connect': 1.3.0(@bufbuild/protobuf@1.7.2) - transitivePeerDependencies: - - '@bufbuild/protobuf' - '@buf/meshtastic_protobufs.bufbuild_es@1.7.2-20240216123215-6b07c41c68c9.1(@bufbuild/protobuf@1.7.2)': dependencies: '@bufbuild/protobuf': 1.7.2 '@bufbuild/protobuf@1.7.2': {} - '@connectrpc/connect-express@1.3.0(@bufbuild/protobuf@1.7.2)(@connectrpc/connect-node@1.3.0(@bufbuild/protobuf@1.7.2)(@connectrpc/connect@1.3.0(@bufbuild/protobuf@1.7.2)))(@connectrpc/connect@1.3.0(@bufbuild/protobuf@1.7.2))': - dependencies: - '@bufbuild/protobuf': 1.7.2 - '@connectrpc/connect': 1.3.0(@bufbuild/protobuf@1.7.2) - '@connectrpc/connect-node': 1.3.0(@bufbuild/protobuf@1.7.2)(@connectrpc/connect@1.3.0(@bufbuild/protobuf@1.7.2)) - '@types/express': 4.17.21 - - '@connectrpc/connect-node@1.3.0(@bufbuild/protobuf@1.7.2)(@connectrpc/connect@1.3.0(@bufbuild/protobuf@1.7.2))': - dependencies: - '@bufbuild/protobuf': 1.7.2 - '@connectrpc/connect': 1.3.0(@bufbuild/protobuf@1.7.2) - undici: 5.29.0 - - '@connectrpc/connect@1.3.0(@bufbuild/protobuf@1.7.2)': - dependencies: - '@bufbuild/protobuf': 1.7.2 - '@esbuild/aix-ppc64@0.19.12': optional: true @@ -1138,16 +830,6 @@ snapshots: '@esbuild/win32-x64@0.19.12': optional: true - '@fastify/busboy@2.1.1': {} - - '@meshtastic/js@2.2.23-0(buffer@6.0.3)': - dependencies: - crc: 4.3.2(buffer@6.0.3) - sub-events: 1.9.0 - tslog: 4.9.2 - transitivePeerDependencies: - - buffer - '@octokit/app@14.0.2': dependencies: '@octokit/auth-app': 6.0.3 @@ -1443,73 +1125,16 @@ snapshots: '@types/aws-lambda@8.10.130': {} - '@types/body-parser@1.19.5': - dependencies: - '@types/connect': 3.4.38 - '@types/node': 20.11.19 - '@types/btoa-lite@1.0.2': {} - '@types/connect@3.4.38': - dependencies: - '@types/node': 20.11.19 - - '@types/express-serve-static-core@4.17.41': - dependencies: - '@types/node': 20.11.19 - '@types/qs': 6.9.11 - '@types/range-parser': 1.2.7 - '@types/send': 0.17.4 - - '@types/express@4.17.21': - dependencies: - '@types/body-parser': 1.19.5 - '@types/express-serve-static-core': 4.17.41 - '@types/qs': 6.9.11 - '@types/serve-static': 1.15.5 - - '@types/http-errors@2.0.4': {} - '@types/jsonwebtoken@9.0.5': dependencies: '@types/node': 20.11.19 - '@types/mime@1.3.5': {} - - '@types/mime@3.0.4': {} - '@types/node@20.11.19': dependencies: undici-types: 5.26.5 - '@types/qs@6.9.11': {} - - '@types/range-parser@1.2.7': {} - - '@types/readable-stream@4.0.10': - dependencies: - '@types/node': 20.11.19 - safe-buffer: 5.1.2 - - '@types/send@0.17.4': - dependencies: - '@types/mime': 1.3.5 - '@types/node': 20.11.19 - - '@types/serve-static@1.15.5': - dependencies: - '@types/http-errors': 2.0.4 - '@types/mime': 3.0.4 - '@types/node': 20.11.19 - - '@types/ws@8.5.10': - dependencies: - '@types/node': 20.11.19 - - abort-controller@3.0.0: - dependencies: - event-target-shim: 5.0.1 - add@2.0.6: {} aggregate-error@3.1.0: @@ -1517,54 +1142,22 @@ snapshots: clean-stack: 2.2.0 indent-string: 4.0.0 - base64-js@1.5.1: {} - before-after-hook@2.2.3: {} - bl@6.0.10: - dependencies: - buffer: 6.0.3 - inherits: 2.0.4 - readable-stream: 4.5.2 - bottleneck@2.19.5: {} btoa-lite@1.0.0: {} buffer-equal-constant-time@1.0.1: {} - buffer-from@1.1.2: {} - - buffer@6.0.3: - dependencies: - base64-js: 1.5.1 - ieee754: 1.2.1 - clean-stack@2.2.0: {} cluster-key-slot@1.1.2: {} colorette@2.0.20: {} - commist@3.2.0: {} - - concat-stream@2.0.0: - dependencies: - buffer-from: 1.1.2 - inherits: 2.0.4 - readable-stream: 3.6.2 - typedarray: 0.0.6 - - crc@4.3.2(buffer@6.0.3): - optionalDependencies: - buffer: 6.0.3 - dayjs@1.11.10: {} - debug@4.3.4: - dependencies: - ms: 2.1.2 - deprecation@2.3.1: {} ecdsa-sig-formatter@1.0.11: @@ -1603,15 +1196,6 @@ snapshots: '@esbuild/win32-ia32': 0.19.12 '@esbuild/win32-x64': 0.19.12 - event-target-shim@5.0.1: {} - - events@3.3.0: {} - - fast-unique-numbers@8.0.13: - dependencies: - '@babel/runtime': 7.23.9 - tslib: 2.6.2 - fsevents@2.3.3: optional: true @@ -1623,20 +1207,12 @@ snapshots: header-range-parser@1.1.3: {} - help-me@5.0.0: {} - http-status-emojis@2.2.0: {} - ieee754@1.2.1: {} - indent-string@4.0.0: {} - inherits@2.0.4: {} - ipaddr.js@2.1.0: {} - js-sdsl@4.3.0: {} - jsonwebtoken@9.0.2: dependencies: jws: 3.2.2 @@ -1677,60 +1253,16 @@ snapshots: lru-cache@10.1.0: {} - lru-cache@10.2.0: {} - lru-cache@6.0.0: dependencies: yallist: 4.0.0 mime@4.0.1: {} - minimist@1.2.8: {} - - mqtt-packet@9.0.0: - dependencies: - bl: 6.0.10 - debug: 4.3.4 - process-nextick-args: 2.0.1 - transitivePeerDependencies: - - supports-color - - mqtt@5.3.5: - dependencies: - '@types/readable-stream': 4.0.10 - '@types/ws': 8.5.10 - commist: 3.2.0 - concat-stream: 2.0.0 - debug: 4.3.4 - help-me: 5.0.0 - lru-cache: 10.2.0 - minimist: 1.2.8 - mqtt-packet: 9.0.0 - number-allocator: 1.0.14 - readable-stream: 4.5.2 - reinterval: 1.1.0 - rfdc: 1.3.1 - split2: 4.2.0 - worker-timers: 7.1.1 - ws: 8.20.1 - transitivePeerDependencies: - - bufferutil - - supports-color - - utf-8-validate - - ms@2.1.2: {} - ms@2.1.3: {} negotiator@0.6.3: {} - number-allocator@1.0.14: - dependencies: - debug: 4.3.4 - js-sdsl: 4.3.0 - transitivePeerDependencies: - - supports-color - octokit@3.1.2: dependencies: '@octokit/app': 14.0.2 @@ -1756,24 +1288,6 @@ snapshots: optionalDependencies: fsevents: 2.3.3 - process-nextick-args@2.0.1: {} - - process@0.11.10: {} - - readable-stream@3.6.2: - dependencies: - inherits: 2.0.4 - string_decoder: 1.3.0 - util-deprecate: 1.0.2 - - readable-stream@4.5.2: - dependencies: - abort-controller: 3.0.0 - buffer: 6.0.3 - events: 3.3.0 - process: 0.11.10 - string_decoder: 1.3.0 - redis@4.6.13: dependencies: '@redis/bloom': 1.2.0(@redis/client@1.5.14) @@ -1783,38 +1297,18 @@ snapshots: '@redis/search': 1.1.6(@redis/client@1.5.14) '@redis/time-series': 1.0.5(@redis/client@1.5.14) - regenerator-runtime@0.14.1: {} - regexparam@2.0.2: {} - reinterval@1.1.0: {} - resolve-pkg-maps@1.0.0: {} - rfdc@1.3.1: {} - - safe-buffer@5.1.2: {} - safe-buffer@5.2.1: {} semver@7.5.4: dependencies: lru-cache: 6.0.0 - split2@4.2.0: {} - - string_decoder@1.3.0: - dependencies: - safe-buffer: 5.2.1 - - sub-events@1.9.0: {} - ts-pattern@5.0.8: {} - tslib@2.6.2: {} - - tslog@4.9.2: {} - tsx@4.7.1: dependencies: esbuild: 0.19.12 @@ -1822,16 +1316,10 @@ snapshots: optionalDependencies: fsevents: 2.3.3 - typedarray@0.0.6: {} - typescript@5.3.3: {} undici-types@5.26.5: {} - undici@5.29.0: - dependencies: - '@fastify/busboy': 2.1.1 - universal-github-app-jwt@1.1.2: dependencies: '@types/jsonwebtoken': 9.0.5 @@ -1839,29 +1327,6 @@ snapshots: universal-user-agent@6.0.1: {} - util-deprecate@1.0.2: {} - - worker-timers-broker@6.1.1: - dependencies: - '@babel/runtime': 7.23.9 - fast-unique-numbers: 8.0.13 - tslib: 2.6.2 - worker-timers-worker: 7.0.65 - - worker-timers-worker@7.0.65: - dependencies: - '@babel/runtime': 7.23.9 - tslib: 2.6.2 - - worker-timers@7.1.1: - dependencies: - '@babel/runtime': 7.23.9 - tslib: 2.6.2 - worker-timers-broker: 6.1.1 - worker-timers-worker: 7.0.65 - wrappy@1.0.2: {} - ws@8.20.1: {} - yallist@4.0.0: {} diff --git a/prisma/migrations/20260811000000_purge_gateway_coordinates/migration.sql b/prisma/migrations/20260811000000_purge_gateway_coordinates/migration.sql new file mode 100644 index 0000000..94e4da0 --- /dev/null +++ b/prisma/migrations/20260811000000_purge_gateway_coordinates/migration.sql @@ -0,0 +1,14 @@ +-- Purge stored gateway coordinates. +-- +-- These were never the gateway's own position. The row was keyed on +-- packet.gatewayId, but the coordinates came from the POSITION_APP payload of +-- the packet being relayed, whose origin is packet.packet.from -- so each row +-- held the last known position of some *other* node that the gateway happened +-- to forward, at full precision (degrees * 1e7, ~1cm). Every row was served +-- publicly and unauthenticated by GET /mqtt and the gatewayStream RPC. +-- +-- The following migration drops these tables outright, which makes this +-- statement redundant on a successful deploy. It is kept deliberately: Prisma +-- applies migrations in order and halts on the first failure, so if the DROP +-- fails for any reason the coordinates have already been cleared. +UPDATE "Gateway" SET "latitude" = NULL, "longitude" = NULL; diff --git a/prisma/migrations/20260811000100_drop_mqtt_tables/migration.sql b/prisma/migrations/20260811000100_drop_mqtt_tables/migration.sql new file mode 100644 index 0000000..14af201 --- /dev/null +++ b/prisma/migrations/20260811000100_drop_mqtt_tables/migration.sql @@ -0,0 +1,13 @@ +-- Drop the MQTT-derived tables. +-- +-- The MQTT ingest, the GET /mqtt endpoint and the gatewayStream RPC have all +-- been removed, so nothing writes or reads these tables. They were the only two +-- models in the schema. +-- +-- This is irreversible. It destroys the stored coordinates for good -- which is +-- the point, see the preceding migration -- and takes the per-channel message +-- counters with them. +-- +-- Channel is dropped first: it carries the foreign key to Gateway. +DROP TABLE IF EXISTS "Channel"; +DROP TABLE IF EXISTS "Gateway"; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index ccf58d6..8b6fa2e 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -1,6 +1,24 @@ // This is your Prisma schema file, // learn more about it in the docs: https://pris.ly/d/prisma-schema +// The MQTT subsystem that read and wrote these models is gone, and the +// migrations in this directory drop both tables on deploy. The models are kept +// here for one release regardless, because `prisma generate` exits 1 on a schema +// with no models and this project's build runs it. Emptying the schema in the +// same change would break the build, and the drop would then never reach +// production. +// +// So between this deploy and the follow-up, the schema deliberately describes +// two tables that no longer exist. Nothing reads them -- no code imports the +// generated client. Do not run `prisma migrate dev` against a deployed database +// in that window: it would read the missing tables as drift and try to recreate +// them. +// +// FOLLOW-UP, once the drop migration has been applied: delete this file and the +// migrations directory, drop the `prisma` and `@prisma/client` dependencies and +// the `prisma generate` / `prisma migrate deploy` steps from package.json, and +// remove DATABASE_URL from the environment. + generator client { provider = "prisma-client-js" } diff --git a/protobufs/gateway/v1/gateway.proto b/protobufs/gateway/v1/gateway.proto deleted file mode 100644 index 767dc1d..0000000 --- a/protobufs/gateway/v1/gateway.proto +++ /dev/null @@ -1,34 +0,0 @@ -syntax = "proto3"; - -package meshtastic.api.gateway.v1; - -import "google/protobuf/timestamp.proto"; - -message Transaction { - enum Type { - CREATE = 0; - UPDATE = 1; - DELETE = 2; - } - - Type type = 1; -} - -message Gateway { - string id = 1; - sfixed32 latitude = 2; - sfixed32 longitude = 3; - google.protobuf.Timestamp created_at = 4; - google.protobuf.Timestamp updated_at = 5; - - repeated Channel channels = 6; -} - -message Channel { - string id = 1; - string name = 2; - bool encrypted = 3; - int32 messages_count = 4; - google.protobuf.Timestamp created_at = 5; - google.protobuf.Timestamp updated_at = 6; -} diff --git a/protobufs/gateway/v1/gateway_service.proto b/protobufs/gateway/v1/gateway_service.proto deleted file mode 100644 index b8fe7bb..0000000 --- a/protobufs/gateway/v1/gateway_service.proto +++ /dev/null @@ -1,14 +0,0 @@ -syntax = "proto3"; - -package meshtastic.api.gateway.v1; - -import "google/protobuf/empty.proto"; -import "protobufs/gateway/v1/gateway.proto"; - -service GatewayService { - rpc GatewayStream(google.protobuf.Empty) returns (stream GatewayStreamResponse) {} -} - -message GatewayStreamResponse { - Gateway gateway = 1; -} diff --git a/src/index.ts b/src/index.ts index e486fc6..fed438a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,26 +1,20 @@ -import { GatewayService } from "@buf/meshtastic_api.connectrpc_es/protobufs/gateway/v1/gateway_service_connect.js"; -import { expressConnectMiddleware } from "@connectrpc/connect-express"; import { App } from "@tinyhttp/app"; import { cors } from "@tinyhttp/cors"; import { config } from "@tinyhttp/dotenv"; import { favicon } from "@tinyhttp/favicon"; import { logger } from "@tinyhttp/logger"; -import { RegisterMqttClient } from "./lib/index.js"; import { DeviceLinksRoutes, EventFirmwareIconRoutes, EventFirmwareRoutes, FirmwareRoutes, GithubRoutes, - MqttRoutes, ResourceRoutes, UpdaterRoutes, } from "./routes/index.js"; -import { Gateway } from "./services/index.js"; export const app = new App(); config(); -RegisterMqttClient(); app .use(logger()) .use(favicon("static/favicon.ico")) @@ -67,15 +61,6 @@ app res.setHeader("content-type", "application/octet-stream"); res.send(data.body); }); - }) - .use(async (req, res, next) => { - return expressConnectMiddleware({ - routes: (router) => { - router.service(GatewayService, new Gateway()); - }, - connect: true, - // @ts-ignore - })(req, res, next); }); /** @@ -88,6 +73,5 @@ DeviceLinksRoutes(); EventFirmwareRoutes(); EventFirmwareIconRoutes(); UpdaterRoutes(); -MqttRoutes(); app.listen(Number.parseInt(process.env.PORT ?? "4000")); diff --git a/src/lib/index.ts b/src/lib/index.ts index 66d2a79..09bc526 100644 --- a/src/lib/index.ts +++ b/src/lib/index.ts @@ -1,5 +1,3 @@ export * as GitHub from "./github.js"; -export { RegisterMqttClient } from "./mqtt.js"; -export { prisma } from "./prisma.js"; export { redis } from "./redis.js"; export * as Hardware from "./resource.js"; diff --git a/src/lib/mqtt.ts b/src/lib/mqtt.ts deleted file mode 100644 index 5569573..0000000 --- a/src/lib/mqtt.ts +++ /dev/null @@ -1,191 +0,0 @@ -import { Protobuf } from "@meshtastic/js"; -import MQTT from "mqtt"; -import { prisma, redis } from "./index.js"; - -const mqtt = MQTT.connect(process.env.MQTT_URL as string, { - username: process.env.MQTT_USERNAME, - password: process.env.MQTT_PASSWORD, -}); - -export const RegisterMqttClient = () => { - const queue = new MqttQueue(); - // Subscribe to all topics - mqtt.subscribe(process.env.MQTT_ROOT_TOPIC as string); - - mqtt.on("message", (topic, payload) => { - // Split topic into parts - const topicParts = topic.substring(8).split("/"); - - if (topicParts.length === 2) { - // Standard channel message - try { - const decoded = Protobuf.Mqtt.ServiceEnvelope.fromBinary(payload); - queue.push(decoded); - } catch (error) { - console.error(error, topic, payload); - } - } else { - // Likely stat message - console.log("Unknown topic", topic); - } - }); -}; - -interface QueueItem { - id: string; - latitude?: number; - longitude?: number; - channels: { - name: string; - encrypted: boolean; - messages: number; - }[]; -} - -class MqttQueue { - private queue: QueueItem[]; - private isWorking: boolean; - private lastWorked: Date; - - constructor() { - this.queue = []; - this.isWorking = false; - this.lastWorked = new Date(); - } - - private textDecoder = new TextDecoder(); - - public push(packet: Protobuf.Mqtt.ServiceEnvelope) { - if (packet.packet?.payloadVariant.case === "decoded") { - const data = packet.packet?.payloadVariant.value; - if (data.portnum === Protobuf.Portnums.PortNum.POSITION_APP) { - const position = Protobuf.Mesh.Position.fromBinary(data.payload); - if (position.latitudeI !== 0 && position.longitudeI !== 0) { - this.queue.push({ - id: packet.gatewayId, - latitude: position.latitudeI, - longitude: position.longitudeI, - channels: [], - }); - } - } else if (data.portnum === Protobuf.Portnums.PortNum.TEXT_MESSAGE_APP) { - //cache in redis - redis.set( - `mqttMessage-${packet.gatewayId}-${ - packet.channelId - }-${new Date().getTime()}`, - JSON.stringify({ - from: packet.packet.from, - to: packet.packet.to, - message: this.textDecoder.decode(data.payload), - }), - { - EX: 60 * 60 * 12, // 12 hours - }, - ); - } else { - //update counter - } - } - - // Push to queue, batch updates - const gatewayExists = this.queue.find( - (item: QueueItem) => item.id === packet.gatewayId, - ); - - if (gatewayExists) { - const channelExists = gatewayExists.channels.find( - (channel) => channel.name === packet.channelId, - ); - - if (channelExists) { - channelExists.messages += 1; - } else { - gatewayExists.channels.push({ - name: packet.channelId, - encrypted: packet.packet?.payloadVariant.case === "encrypted", - messages: 1, - }); - } - } else { - this.queue.push({ - id: packet.gatewayId, - channels: [ - { - name: packet.channelId, - encrypted: packet.packet?.payloadVariant.case === "encrypted", - messages: 1, - }, - ], - }); - } - - // Check if we should process (every 10 seconds) - if (!this.isWorking && this.lastWorked.getTime() + 10000 < Date.now()) { - this.process(); - } - } - - public async process() { - this.isWorking = true; - this.lastWorked = new Date(Date.now()); - //iterate over current queue size, shift off items and process - - const toProcess = this.queue.splice(0, Math.min(this.queue.length, 10)); - - for (const gateway of toProcess) { - for (const channel of gateway.channels) { - console.log( - "Queue:", - this.queue.length, - "Processing", - gateway.id, - channel.name, - ); - - await prisma.channel.upsert({ - create: { - name: channel.name, - encrypted: channel.encrypted, - messages: channel.messages, - gateway: { - connectOrCreate: { - create: { - id: gateway.id, - }, - where: { - id: gateway.id, - }, - }, - }, - }, - update: { - messages: { - increment: channel.messages, - }, - }, - where: { - channelIdentifier: { - gatewayId: gateway.id, - name: channel.name, - }, - }, - }); - } - - // Update gateway position - if (gateway.latitude && gateway.longitude) { - await prisma.gateway.update({ - where: { - id: gateway.id, - }, - data: { - latitude: gateway.latitude, - longitude: gateway.longitude, - }, - }); - } - } - this.isWorking = false; - } -} diff --git a/src/lib/prisma.ts b/src/lib/prisma.ts deleted file mode 100644 index e19b369..0000000 --- a/src/lib/prisma.ts +++ /dev/null @@ -1,5 +0,0 @@ -import Prisma from "@prisma/client"; - -const { PrismaClient } = Prisma; - -export const prisma = new PrismaClient(); diff --git a/src/routes/index.ts b/src/routes/index.ts index 482c8a4..eba3e3b 100644 --- a/src/routes/index.ts +++ b/src/routes/index.ts @@ -3,6 +3,5 @@ export { EventFirmwareRoutes } from "./eventFirmware.js"; export { EventFirmwareIconRoutes } from "./eventFirmwareIcon.js"; export { FirmwareRoutes } from "./firmware.js"; export { GithubRoutes } from "./github.js"; -export { MqttRoutes } from "./mqtt.js"; export { ResourceRoutes } from "./resource.js"; export { UpdaterRoutes } from "./updater.js"; diff --git a/src/routes/mqtt.ts b/src/routes/mqtt.ts deleted file mode 100644 index 094b1db..0000000 --- a/src/routes/mqtt.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { app } from "../index.js"; -import { prisma, redis } from "../lib/index.js"; - -export const MqttRoutes = () => { - return app.get("/mqtt", async (_req, res) => { - const mqttCache = await redis.get("mqttCache"); - - if (mqttCache) { - res.send(JSON.parse(mqttCache)); - } else { - const data = await prisma.gateway.findMany({ - include: { - channels: true, - }, - }); - - redis.set("mqttCache", JSON.stringify(data), { - EX: 1, - }); - res.send(data); - } - }); -}; diff --git a/src/routes/updater.ts b/src/routes/updater.ts index b733afe..a7ab5b9 100644 --- a/src/routes/updater.ts +++ b/src/routes/updater.ts @@ -2,6 +2,26 @@ import { app } from "../index.js"; export type SupportedApps = "meshtastic-desktop-flasher"; +// A production endpoint should not depend on one contributor's personal gist. +// The gist remains the default so existing clients keep working, but the source +// is overridable via UPDATER_MANIFEST_URL and should be pointed at infrastructure +// the project controls. +const DEFAULT_MANIFEST_URL = + "https://gist.githubusercontent.com/ajmcquilkin/4bdf1a679f070e74da61c64132aa431d/raw/manifests.json"; + +const manifestUrl = process.env.UPDATER_MANIFEST_URL || DEFAULT_MANIFEST_URL; + +// A manifest source that accepts the connection and then stalls would otherwise +// leave the updater request pending indefinitely. The abort rejects the fetch, +// which the catch below maps to 502 like any other upstream failure. +const MANIFEST_FETCH_TIMEOUT_MS = 10_000; + +// The payload is whatever the manifest source chose to serve, so it is checked +// at runtime rather than asserted. `typeof null === "object"`, and arrays are +// objects too, so both are excluded explicitly. +const isManifest = (value: unknown): value is object => + typeof value === "object" && value !== null && !Array.isArray(value); + export const UpdaterRoutes = () => { return app .get("/updater", (_req, res) => { @@ -42,15 +62,52 @@ export const UpdaterRoutes = () => { // } // const gistContent = await fetch(rawUrl); - const gistContent = await fetch( - "https://gist.githubusercontent.com/ajmcquilkin/4bdf1a679f070e74da61c64132aa431d/raw/manifests.json", - ); - const parsedGistContent = (await gistContent.json()) as object[]; - const mostRecentManifest = parsedGistContent[0]; + let parsedManifests: unknown; + + try { + const manifestResponse = await fetch(manifestUrl, { + signal: AbortSignal.timeout(MANIFEST_FETCH_TIMEOUT_MS), + }); + + if (!manifestResponse.ok) { + console.error( + "[updater] manifest fetch returned", + manifestResponse.status, + manifestUrl, + ); + // Release the connection: an unread body is not returned to the pool + // until it is consumed or cancelled. + await manifestResponse.body?.cancel().catch(() => {}); + return res + .status(502) + .send("Error fetching manifests, please contact a developer."); + } + + parsedManifests = await manifestResponse.json(); + } catch (error) { + // An unreachable or non-JSON manifest source previously threw out of the + // handler rather than returning a response. + console.error("[updater] manifest fetch failed", manifestUrl, error); + return res + .status(502) + .send("Error fetching manifests, please contact a developer."); + } + + const mostRecentManifest = Array.isArray(parsedManifests) + ? parsedManifests[0] + : undefined; - if (!mostRecentManifest) { + // Non-array JSON, an empty array and a first element that is not a + // manifest object are all the manifest source serving something this + // endpoint cannot use, so they are reported as upstream failures rather + // than as a fault in this service. + if (!isManifest(mostRecentManifest)) { + console.error( + "[updater] manifest source returned an unusable payload", + manifestUrl, + ); return res - .status(500) + .status(502) .send( "Error finding most recent manifest, please contact a developer.", ); diff --git a/src/services/gateway.ts b/src/services/gateway.ts deleted file mode 100644 index 9bf95be..0000000 --- a/src/services/gateway.ts +++ /dev/null @@ -1,56 +0,0 @@ -import {} from "@buf/meshtastic_api.bufbuild_es/protobufs/gateway/v1/gateway_pb.js"; -import { GatewayStreamResponse } from "@buf/meshtastic_api.bufbuild_es/protobufs/gateway/v1/gateway_service_pb.js"; -import type { GatewayService } from "@buf/meshtastic_api.connectrpc_es/protobufs/gateway/v1/gateway_service_connect.js"; -import { Timestamp } from "@bufbuild/protobuf"; -import type { ServiceImpl } from "@connectrpc/connect"; -import type { Channel } from "@prisma/client"; -import { prisma } from "../lib/index.js"; - -export class Gateway implements ServiceImpl { - public async *gatewayStream(): AsyncGenerator { - const gateways = await prisma.gateway.findMany({ - include: { - channels: true, - }, - where: { - latitude: { - not: { - equals: null, - }, - }, - }, - }); - - for (const gateway of gateways) { - //delay for 1 second - await new Promise((resolve) => setTimeout(resolve, 100)); - yield new GatewayStreamResponse({ - gateway: { - id: gateway.id, - latitude: gateway.latitude ?? undefined, - longitude: gateway.longitude ?? undefined, - createdAt: Timestamp.fromDate(gateway.createdAt), - updatedAt: Timestamp.fromDate(gateway.updatedAt), - channels: gateway.channels.map((channel: Channel) => { - return { - id: channel.id, - name: channel.name, - encrypted: channel.encrypted, - messagesCount: channel.messages, - createdAt: Timestamp.fromDate(channel.createdAt), - updatedAt: Timestamp.fromDate(channel.updatedAt), - }; - }), - }, - }); - } - - // while (true) { - // yield* yieldFromEvent(this.events.gateway, (data) => { - // return new GatewayStreamResponse({ - // gateway: data, - // }); - // }); - // } - } -} diff --git a/src/services/index.ts b/src/services/index.ts deleted file mode 100644 index e39dc18..0000000 --- a/src/services/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { Gateway } from "./gateway.js"; diff --git a/src/utils/yieldFromEvent.ts b/src/utils/yieldFromEvent.ts deleted file mode 100644 index c45d005..0000000 --- a/src/utils/yieldFromEvent.ts +++ /dev/null @@ -1,27 +0,0 @@ -import type { SubEvent } from "sub-events"; - -export function yieldFromEvent( - event: SubEvent, - generator: (data: T) => K, -): AsyncGenerator { - return { - next: async () => { - const value = await new Promise((resolve) => { - event.subscribe((e) => { - resolve(generator(e)); - }); - }); - - return { value, done: false }; - }, - async return() { - return await Promise.resolve({ value: undefined, done: true }); - }, - throw(error) { - return Promise.reject(error); - }, - [Symbol.asyncIterator]() { - return this; - }, - }; -}