diff --git a/package.json b/package.json index 0789af2d..26622cb6 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,6 @@ "@tanstack/query-core": "^4.33.0", "@tanstack/react-virtual": "3.0.0-beta.54", "@tauri-apps/api": "^1.4.0", - "axios": "^1.5.0", "byte-size": "^8.1.1", "classnames": "^2.3.2", "clipboardy": "^3.0.0", @@ -68,8 +67,10 @@ "react-markdown": "^8.0.7", "react-qr-code": "^2.0.12", "react-router-dom": "^6.15.0", + "recharts": "^2.8.0", "rehype-sanitize": "^6.0.0", "rxjs": "^7.8.1", + "tauri-plugin-log-api": "github:tauri-apps/tauri-plugin-log", "use-breakpoint": "^3.1.1", "zod": "^3.22.2", "zustand": "^4.4.1" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e6e21b03..a324bb92 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -26,9 +26,6 @@ dependencies: '@tauri-apps/api': specifier: ^1.4.0 version: 1.4.0 - axios: - specifier: ^1.5.0 - version: 1.5.0 byte-size: specifier: ^8.1.1 version: 8.1.1 @@ -89,12 +86,18 @@ dependencies: react-router-dom: specifier: ^6.15.0 version: 6.15.0(react-dom@18.2.0)(react@18.2.0) + recharts: + specifier: ^2.8.0 + version: 2.8.0(prop-types@15.8.1)(react-dom@18.2.0)(react@18.2.0) rehype-sanitize: specifier: ^6.0.0 version: 6.0.0 rxjs: specifier: ^7.8.1 version: 7.8.1 + tauri-plugin-log-api: + specifier: github:tauri-apps/tauri-plugin-log + version: github.com/tauri-apps/tauri-plugin-log/f7142fcf10841cdabab2fbe8650e9adc3f0bb212 use-breakpoint: specifier: ^3.1.1 version: 3.1.1(react-dom@18.2.0)(react@18.2.0) @@ -402,7 +405,6 @@ packages: engines: {node: '>=6.9.0'} dependencies: regenerator-runtime: 0.14.0 - dev: true /@babel/template@7.22.5: resolution: {integrity: sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==} @@ -1346,6 +1348,11 @@ packages: engines: {node: '>= 14.6.0', npm: '>= 6.6.0', yarn: '>= 1.19.1'} dev: false + /@tauri-apps/api@1.5.0: + resolution: {integrity: sha512-yQY9wpVNuiYhLLuyDlu1nBpqJELT1fGp7OctN4rW9I2W1T2p7A3tqPxsEzQprEwneQRBAlPM9vC8NsnMbct+pg==} + engines: {node: '>= 14.6.0', npm: '>= 6.6.0', yarn: '>= 1.19.1'} + dev: false + /@tauri-apps/cli-darwin-arm64@1.4.0: resolution: {integrity: sha512-nA/ml0SfUt6/CYLVbHmT500Y+ijqsuv5+s9EBnVXYSLVg9kbPUZJJHluEYK+xKuOj6xzyuT/+rZFMRapmJD3jQ==} engines: {node: '>= 10'} @@ -1458,6 +1465,48 @@ packages: engines: {node: '>=10.13.0'} dev: true + /@types/d3-array@3.0.8: + resolution: {integrity: sha512-2xAVyAUgaXHX9fubjcCbGAUOqYfRJN1em1EKR2HfzWBpObZhwfnZKvofTN4TplMqJdFQao61I+NVSai/vnBvDQ==} + dev: false + + /@types/d3-color@3.1.1: + resolution: {integrity: sha512-CSAVrHAtM9wfuLJ2tpvvwCU/F22sm7rMHNN+yh9D6O6hyAms3+O0cgMpC1pm6UEUMOntuZC8bMt74PteiDUdCg==} + dev: false + + /@types/d3-ease@3.0.0: + resolution: {integrity: sha512-aMo4eaAOijJjA6uU+GIeW018dvy9+oH5Y2VPPzjjfxevvGQ/oRDs+tfYC9b50Q4BygRR8yE2QCLsrT0WtAVseA==} + dev: false + + /@types/d3-interpolate@3.0.2: + resolution: {integrity: sha512-zAbCj9lTqW9J9PlF4FwnvEjXZUy75NQqPm7DMHZXuxCFTpuTrdK2NMYGQekf4hlasL78fCYOLu4EE3/tXElwow==} + dependencies: + '@types/d3-color': 3.1.1 + dev: false + + /@types/d3-path@3.0.0: + resolution: {integrity: sha512-0g/A+mZXgFkQxN3HniRDbXMN79K3CdTpLsevj+PXiTcb2hVyvkZUBg37StmgCQkaD84cUJ4uaDAWq7UJOQy2Tg==} + dev: false + + /@types/d3-scale@4.0.5: + resolution: {integrity: sha512-w/C++3W394MHzcLKO2kdsIn5KKNTOqeQVzyPSGPLzQbkPw/jpeaGtSRlakcKevGgGsjJxGsbqS0fPrVFDbHrDA==} + dependencies: + '@types/d3-time': 3.0.1 + dev: false + + /@types/d3-shape@3.1.3: + resolution: {integrity: sha512-cHMdIq+rhF5IVwAV7t61pcEXfEHsEsrbBUPkFGBwTXuxtTAkBBrnrNA8++6OWm3jwVsXoZYQM8NEekg6CPJ3zw==} + dependencies: + '@types/d3-path': 3.0.0 + dev: false + + /@types/d3-time@3.0.1: + resolution: {integrity: sha512-5j/AnefKAhCw4HpITmLDTPlf4vhi8o/dES+zbegfPb7LaGfNyqkLxBR6E+4yvTAgnJLmhe80EXFMzUs38fw4oA==} + dev: false + + /@types/d3-timer@3.0.0: + resolution: {integrity: sha512-HNB/9GHqu7Fo8AQiugyJbv6ZxYz58wef0esl4Mv828w1ZKpAshw/uFWVDUcIB9KKFeFKoxS3cHY07FFgtTRZ1g==} + dev: false + /@types/debug@4.1.8: resolution: {integrity: sha512-/vPO1EPOs306Cvhwv7KfVfYvOJqA/S/AXjaHQiJboCZzcNDb+TIJFN9/2C9DZ//ijSKWioNyUxD792QmDJ+HKQ==} dependencies: @@ -1852,10 +1901,6 @@ packages: has-symbols: 1.0.3 dev: true - /asynckit@0.4.0: - resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} - dev: false - /autoprefixer@10.4.15(postcss@8.4.29): resolution: {integrity: sha512-KCuPB8ZCIqFdA4HwKXsvz7j6gvSDNhDP7WnUjBleRkKjPdvCmHFuQ77ocavI8FT6NdvlBnE2UFr2H4Mycn8Vew==} engines: {node: ^10 || ^12 || >=14} @@ -1877,16 +1922,6 @@ packages: engines: {node: '>= 0.4'} dev: true - /axios@1.5.0: - resolution: {integrity: sha512-D4DdjDo5CY50Qms0qGQTTw6Q44jl7zRwY7bthds06pUGfChBCTcQs+N743eFWGEd6pRTMd6A+I87aWyFV5wiZQ==} - dependencies: - follow-redirects: 1.15.2 - form-data: 4.0.0 - proxy-from-env: 1.1.0 - transitivePeerDependencies: - - debug - dev: false - /babel-plugin-macros@3.1.0: resolution: {integrity: sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==} engines: {node: '>=10', npm: '>=6'} @@ -2074,13 +2109,6 @@ packages: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} dev: true - /combined-stream@1.0.8: - resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} - engines: {node: '>= 0.8'} - dependencies: - delayed-stream: 1.0.0 - dev: false - /comma-separated-tokens@2.0.3: resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} dev: false @@ -2176,6 +2204,10 @@ packages: source-map-js: 1.0.2 dev: true + /css-unit-converter@1.1.2: + resolution: {integrity: sha512-IiJwMC8rdZE0+xiEZHeru6YoONC4rfPMqGm2W85jMIbkFvv5nFTwJVFHam2eFrN6txmoUYFAFXiv8ICVeTO0MA==} + dev: false + /css-what@6.1.0: resolution: {integrity: sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==} engines: {node: '>= 6'} @@ -2191,6 +2223,77 @@ packages: /csstype@3.1.2: resolution: {integrity: sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==} + /d3-array@3.2.4: + resolution: {integrity: sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==} + engines: {node: '>=12'} + dependencies: + internmap: 2.0.3 + dev: false + + /d3-color@3.1.0: + resolution: {integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==} + engines: {node: '>=12'} + dev: false + + /d3-ease@3.0.1: + resolution: {integrity: sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==} + engines: {node: '>=12'} + dev: false + + /d3-format@3.1.0: + resolution: {integrity: sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==} + engines: {node: '>=12'} + dev: false + + /d3-interpolate@3.0.1: + resolution: {integrity: sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==} + engines: {node: '>=12'} + dependencies: + d3-color: 3.1.0 + dev: false + + /d3-path@3.1.0: + resolution: {integrity: sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==} + engines: {node: '>=12'} + dev: false + + /d3-scale@4.0.2: + resolution: {integrity: sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==} + engines: {node: '>=12'} + dependencies: + d3-array: 3.2.4 + d3-format: 3.1.0 + d3-interpolate: 3.0.1 + d3-time: 3.1.0 + d3-time-format: 4.1.0 + dev: false + + /d3-shape@3.2.0: + resolution: {integrity: sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==} + engines: {node: '>=12'} + dependencies: + d3-path: 3.1.0 + dev: false + + /d3-time-format@4.1.0: + resolution: {integrity: sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==} + engines: {node: '>=12'} + dependencies: + d3-time: 3.1.0 + dev: false + + /d3-time@3.1.0: + resolution: {integrity: sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==} + engines: {node: '>=12'} + dependencies: + d3-array: 3.2.4 + dev: false + + /d3-timer@3.0.1: + resolution: {integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==} + engines: {node: '>=12'} + dev: false + /dashify@2.0.0: resolution: {integrity: sha512-hpA5C/YrPjucXypHPPc0oJ1l9Hf6wWbiOL7Ik42cxnsUOhWiCB/fylKbKqqJalW9FgkNQCw16YO8uW9Hs0Iy1A==} engines: {node: '>=4'} @@ -2222,6 +2325,10 @@ packages: dependencies: ms: 2.1.2 + /decimal.js-light@2.5.1: + resolution: {integrity: sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==} + dev: false + /decode-named-character-reference@1.0.2: resolution: {integrity: sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==} dependencies: @@ -2268,11 +2375,6 @@ packages: object-keys: 1.1.1 dev: true - /delayed-stream@1.0.0: - resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} - engines: {node: '>=0.4.0'} - dev: false - /dequal@2.0.3: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} engines: {node: '>=6'} @@ -2307,6 +2409,12 @@ packages: esutils: 2.0.3 dev: true + /dom-helpers@3.4.0: + resolution: {integrity: sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA==} + dependencies: + '@babel/runtime': 7.22.11 + dev: false + /dom-serializer@2.0.0: resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} dependencies: @@ -2734,6 +2842,10 @@ packages: engines: {node: '>=0.10.0'} dev: true + /eventemitter3@4.0.7: + resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} + dev: false + /execa@5.1.1: resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} engines: {node: '>=10'} @@ -2774,6 +2886,11 @@ packages: resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==} dev: true + /fast-equals@5.0.1: + resolution: {integrity: sha512-WF1Wi8PwwSY7/6Kx0vKXtw8RwuSGoM1bvDaJbu7MxDlR1vovZjIAKrnzyrThgAjm6JDTu0fVgWXDlMGspodfoQ==} + engines: {node: '>=6.0.0'} + dev: false + /fast-glob@3.3.0: resolution: {integrity: sha512-ChDuvbOypPuNjO8yIDf36x7BlZX1smcUMTTcyoIjycexOxd6DFsKsg21qVBzEmr3G7fUKIRy2/psii+CIUt7FA==} engines: {node: '>=8.6.0'} @@ -2841,31 +2958,12 @@ packages: resolution: {integrity: sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==} dev: true - /follow-redirects@1.15.2: - resolution: {integrity: sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==} - engines: {node: '>=4.0'} - peerDependencies: - debug: '*' - peerDependenciesMeta: - debug: - optional: true - dev: false - /for-each@0.3.3: resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} dependencies: is-callable: 1.2.7 dev: true - /form-data@4.0.0: - resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==} - engines: {node: '>= 6'} - dependencies: - asynckit: 0.4.0 - combined-stream: 1.0.8 - mime-types: 2.1.35 - dev: false - /fraction.js@4.2.0: resolution: {integrity: sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==} dev: true @@ -3174,6 +3272,11 @@ packages: side-channel: 1.0.4 dev: true + /internmap@2.0.3: + resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==} + engines: {node: '>=12'} + dev: false + /is-array-buffer@3.0.2: resolution: {integrity: sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==} dependencies: @@ -3526,7 +3629,6 @@ packages: /lodash@4.17.21: resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} - dev: true /loose-envify@1.4.0: resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} @@ -3813,18 +3915,6 @@ packages: picomatch: 2.3.1 dev: true - /mime-db@1.52.0: - resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} - engines: {node: '>= 0.6'} - dev: false - - /mime-types@2.1.35: - resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} - engines: {node: '>= 0.6'} - dependencies: - mime-db: 1.52.0 - dev: false - /mimic-fn@2.1.0: resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} engines: {node: '>=6'} @@ -4151,6 +4241,10 @@ packages: engines: {node: '>=4'} dev: true + /postcss-value-parser@3.3.1: + resolution: {integrity: sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==} + dev: false + /postcss-value-parser@4.2.0: resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} dev: true @@ -4199,10 +4293,6 @@ packages: resolution: {integrity: sha512-kma4U7AFCTwpqq5twzC1YVIDXSqg6qQK6JN0smOw8fgRy1OkMi0CYSzFmsy6dnqSenamAtj0CyXMUJ1Mf6oROg==} dev: false - /proxy-from-env@1.1.0: - resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} - dev: false - /punycode@2.3.0: resolution: {integrity: sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==} engines: {node: '>=6'} @@ -4256,6 +4346,10 @@ packages: resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==} dev: false + /react-lifecycles-compat@3.0.4: + resolution: {integrity: sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==} + dev: false + /react-loading-skeleton@3.3.1(react@18.2.0): resolution: {integrity: sha512-NilqqwMh2v9omN7LteiDloEVpFyMIa0VGqF+ukqp0ncVlYu1sKYbYGX9JEl+GtOT9TKsh04zCHAbavnQ2USldA==} peerDependencies: @@ -4314,6 +4408,17 @@ packages: engines: {node: '>=0.10.0'} dev: true + /react-resize-detector@8.1.0(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-S7szxlaIuiy5UqLhLL1KY3aoyGHbZzsTpYal9eYMwCyKqoqoVLCmIgAgNyIM1FhnP2KyBygASJxdhejrzjMb+w==} + peerDependencies: + react: ^16.0.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.0.0 || ^17.0.0 || ^18.0.0 + dependencies: + lodash: 4.17.21 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + /react-router-dom@6.15.0(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-aR42t0fs7brintwBGAv2+mGlCtgtFQeOzK0BM1/OiqEzRejOZtpMZepvgkscpMUnKb8YO84G7s3LsHnnDNonbQ==} engines: {node: '>=14.0.0'} @@ -4345,6 +4450,34 @@ packages: react-dom: 18.2.0(react@18.2.0) dev: true + /react-smooth@2.0.4(prop-types@15.8.1)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-OkFsrrMBTvQUwEJthE1KXSOj79z57yvEWeFefeXPib+RmQEI9B1Ub1PgzlzzUyBOvl/TjXt5nF2hmD4NsgAh8A==} + peerDependencies: + prop-types: ^15.6.0 + react: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 + react-dom: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 + dependencies: + fast-equals: 5.0.1 + prop-types: 15.8.1 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + react-transition-group: 2.9.0(react-dom@18.2.0)(react@18.2.0) + dev: false + + /react-transition-group@2.9.0(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg==} + peerDependencies: + react: '>=15.0.0' + react-dom: '>=15.0.0' + dependencies: + dom-helpers: 3.4.0 + loose-envify: 1.4.0 + prop-types: 15.8.1 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + react-lifecycles-compat: 3.0.4 + dev: false + /react@18.2.0: resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==} engines: {node: '>=0.10.0'} @@ -4367,6 +4500,41 @@ packages: picomatch: 2.3.1 dev: true + /recharts-scale@0.4.5: + resolution: {integrity: sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==} + dependencies: + decimal.js-light: 2.5.1 + dev: false + + /recharts@2.8.0(prop-types@15.8.1)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-nciXqQDh3aW8abhwUlA4EBOBusRHLNiKHfpRZiG/yjups1x+auHb2zWPuEcTn/IMiN47vVMMuF8Sr+vcQJtsmw==} + engines: {node: '>=12'} + peerDependencies: + prop-types: ^15.6.0 + react: ^16.0.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.0.0 || ^17.0.0 || ^18.0.0 + dependencies: + classnames: 2.3.2 + eventemitter3: 4.0.7 + lodash: 4.17.21 + prop-types: 15.8.1 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + react-is: 16.13.1 + react-resize-detector: 8.1.0(react-dom@18.2.0)(react@18.2.0) + react-smooth: 2.0.4(prop-types@15.8.1)(react-dom@18.2.0)(react@18.2.0) + recharts-scale: 0.4.5 + reduce-css-calc: 2.1.8 + victory-vendor: 36.6.11 + dev: false + + /reduce-css-calc@2.1.8: + resolution: {integrity: sha512-8liAVezDmUcH+tdzoEGrhfbGcP7nOV4NkGE3a74+qqvE7nt9i4sKLGBuZNOnpI4WiGksiNPklZxva80061QiPg==} + dependencies: + css-unit-converter: 1.1.2 + postcss-value-parser: 3.3.1 + dev: false + /reflect.getprototypeof@1.0.3: resolution: {integrity: sha512-TTAOZpkJ2YLxl7mVHWrNo3iDMEkYlva/kgFcXndqMgbo/AZUmmavEkdXV+hXtE4P8xdyEKRzalaFqZVuwIk/Nw==} engines: {node: '>= 0.4'} @@ -4381,7 +4549,6 @@ packages: /regenerator-runtime@0.14.0: resolution: {integrity: sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==} - dev: true /regexp.prototype.flags@1.5.0: resolution: {integrity: sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==} @@ -5095,6 +5262,25 @@ packages: vfile-message: 3.1.4 dev: false + /victory-vendor@36.6.11: + resolution: {integrity: sha512-nT8kCiJp8dQh8g991J/R5w5eE2KnO8EAIP0xocWlh9l2okngMWglOPoMZzJvek8Q1KUc4XE/mJxTZnvOB1sTYg==} + dependencies: + '@types/d3-array': 3.0.8 + '@types/d3-ease': 3.0.0 + '@types/d3-interpolate': 3.0.2 + '@types/d3-scale': 4.0.5 + '@types/d3-shape': 3.1.3 + '@types/d3-time': 3.0.1 + '@types/d3-timer': 3.0.0 + d3-array: 3.2.4 + d3-ease: 3.0.1 + d3-interpolate: 3.0.1 + d3-scale: 4.0.2 + d3-shape: 3.2.0 + d3-time: 3.1.0 + d3-timer: 3.0.1 + dev: false + /vite@4.4.9(@types/node@20.5.7)(sass@1.66.1): resolution: {integrity: sha512-2mbUn2LlUmNASWwSCNSJ/EG2HuSRTnVNaydp6vMCm5VIqJsjMfbIWtbH2kDuwUVW5mMUKKZvGPX/rqeqVvv1XA==} engines: {node: ^14.18.0 || >=16.0.0} @@ -5247,3 +5433,11 @@ packages: react: 18.2.0 use-sync-external-store: 1.2.0(react@18.2.0) dev: false + + github.com/tauri-apps/tauri-plugin-log/f7142fcf10841cdabab2fbe8650e9adc3f0bb212: + resolution: {tarball: https://codeload.github.com/tauri-apps/tauri-plugin-log/tar.gz/f7142fcf10841cdabab2fbe8650e9adc3f0bb212} + name: tauri-plugin-log-api + version: 0.0.0 + dependencies: + '@tauri-apps/api': 1.5.0 + dev: false diff --git a/src-tauri/.sqlx/query-cb7af9d124e73a075dcdb336b79cbdda1ce0ef7bb17946eead21fb5029f42735.json b/src-tauri/.sqlx/query-11cf4e78002243610918571f79ead5f08daf083a59f77ba96f7642f545ce1d10.json similarity index 76% rename from src-tauri/.sqlx/query-cb7af9d124e73a075dcdb336b79cbdda1ce0ef7bb17946eead21fb5029f42735.json rename to src-tauri/.sqlx/query-11cf4e78002243610918571f79ead5f08daf083a59f77ba96f7642f545ce1d10.json index d60c3b0b..6aa36b0f 100644 --- a/src-tauri/.sqlx/query-cb7af9d124e73a075dcdb336b79cbdda1ce0ef7bb17946eead21fb5029f42735.json +++ b/src-tauri/.sqlx/query-11cf4e78002243610918571f79ead5f08daf083a59f77ba96f7642f545ce1d10.json @@ -1,6 +1,6 @@ { "db_name": "SQLite", - "query": "\n SELECT id, location_id, upload, download, last_handshake, collected_at\n FROM location_stats\n WHERE location_id = $1\n ", + "query": "\n SELECT id, location_id, upload, download, last_handshake, collected_at as \"collected_at: _\"\n FROM location_stats\n WHERE location_id = $1\n ", "describe": { "columns": [ { @@ -29,7 +29,7 @@ "type_info": "Int64" }, { - "name": "collected_at", + "name": "collected_at: _", "ordinal": 5, "type_info": "Datetime" } @@ -46,5 +46,5 @@ false ] }, - "hash": "cb7af9d124e73a075dcdb336b79cbdda1ce0ef7bb17946eead21fb5029f42735" + "hash": "11cf4e78002243610918571f79ead5f08daf083a59f77ba96f7642f545ce1d10" } diff --git a/src-tauri/.sqlx/query-827b70bc272ec1b731a6af60d77e65680370ff4b01600138ebdca569ee1b4248.json b/src-tauri/.sqlx/query-827b70bc272ec1b731a6af60d77e65680370ff4b01600138ebdca569ee1b4248.json new file mode 100644 index 00000000..40dfabcf --- /dev/null +++ b/src-tauri/.sqlx/query-827b70bc272ec1b731a6af60d77e65680370ff4b01600138ebdca569ee1b4248.json @@ -0,0 +1,44 @@ +{ + "db_name": "SQLite", + "query": "\n SELECT id, location_id, connected_from, start, end\n FROM connection\n WHERE location_id = $1\n ORDER BY end DESC\n LIMIT 1\n ", + "describe": { + "columns": [ + { + "name": "id", + "ordinal": 0, + "type_info": "Int64" + }, + { + "name": "location_id", + "ordinal": 1, + "type_info": "Int64" + }, + { + "name": "connected_from", + "ordinal": 2, + "type_info": "Text" + }, + { + "name": "start", + "ordinal": 3, + "type_info": "Datetime" + }, + { + "name": "end", + "ordinal": 4, + "type_info": "Datetime" + } + ], + "parameters": { + "Right": 1 + }, + "nullable": [ + false, + false, + false, + false, + false + ] + }, + "hash": "827b70bc272ec1b731a6af60d77e65680370ff4b01600138ebdca569ee1b4248" +} diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 84804b62..5353100d 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -368,6 +368,16 @@ version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" +[[package]] +name = "byte-unit" +version = "4.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da78b32057b8fdfc352504708feeba7216dcd65a2c9ab02978cbd288d1279b6c" +dependencies = [ + "serde", + "utf8-width", +] + [[package]] name = "bytemuck" version = "1.14.0" @@ -776,6 +786,7 @@ dependencies = [ "chrono", "dirs", "local-ip-address", + "log", "prost", "prost-build", "serde", @@ -783,6 +794,7 @@ dependencies = [ "sqlx", "tauri", "tauri-build", + "tauri-plugin-log", "tauri-plugin-single-instance", "thiserror", "tokio", @@ -1048,6 +1060,15 @@ dependencies = [ "simd-adler32", ] +[[package]] +name = "fern" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9f0c14694cbd524c8720dd69b0e3179344f04ebb5f90f2e4a440c6ea3b2f1ee" +dependencies = [ + "log", +] + [[package]] name = "fiat-crypto" version = "0.2.1" @@ -2103,6 +2124,9 @@ name = "log" version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +dependencies = [ + "value-bag", +] [[package]] name = "loom" @@ -2515,6 +2539,15 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "num_threads" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" +dependencies = [ + "libc", +] + [[package]] name = "objc" version = "0.2.7" @@ -4217,10 +4250,25 @@ dependencies = [ "tauri-utils", ] +[[package]] +name = "tauri-plugin-log" +version = "0.0.0" +source = "git+https://github.com/tauri-apps/plugins-workspace?branch=v1#9b96996b5a90a6a57d587ce4312975f13a4d8bc2" +dependencies = [ + "byte-unit", + "fern", + "log", + "serde", + "serde_json", + "serde_repr", + "tauri", + "time", +] + [[package]] name = "tauri-plugin-single-instance" version = "0.0.0" -source = "git+https://github.com/tauri-apps/plugins-workspace?branch=v1#5b814f56e6368fdec46c4ddb04a07e0923ff995a" +source = "git+https://github.com/tauri-apps/plugins-workspace?branch=v1#9b96996b5a90a6a57d587ce4312975f13a4d8bc2" dependencies = [ "log", "serde", @@ -4379,6 +4427,8 @@ checksum = "17f6bb557fd245c28e6411aa56b6403c689ad95061f50e4be16c274e70a17e48" dependencies = [ "deranged", "itoa 1.0.9", + "libc", + "num_threads", "serde", "time-core", "time-macros", @@ -4686,6 +4736,12 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" +[[package]] +name = "utf8-width" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5190c9442dcdaf0ddd50f37420417d219ae5261bbf5db120d0f9bab996c9cba1" + [[package]] name = "uuid" version = "1.4.1" @@ -4701,6 +4757,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" +[[package]] +name = "value-bag" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d92ccd67fb88503048c01b59152a04effd0782d035a83a6d256ce6085f08f4a3" + [[package]] name = "vcpkg" version = "0.2.15" diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 7fddf6ec..efc93ab2 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -33,8 +33,10 @@ prost = "0.11" tauri = { version = "1.4.1", features = [ "http-all", "window-all", "system-tray"] } tauri-plugin-single-instance = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v1" } +tauri-plugin-log = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v1" } local-ip-address = "0.5.5" tokio = { version = "1.32", features = ["full"] } +log = "0.4.20" [features] # this feature is used for production builds or when `devPath` points to the filesystem and the built-in dev server is disabled. diff --git a/src-tauri/src/commands.rs b/src-tauri/src/commands.rs index 9dfa4aeb..63063796 100644 --- a/src-tauri/src/commands.rs +++ b/src-tauri/src/commands.rs @@ -27,6 +27,10 @@ pub async fn connect(location_id: i64, handle: tauri::AppHandle) -> Result<(), S .await .map_err(|err| err.to_string())? { + debug!( + "Creating new interface connection for location: {}", + location.name + ); setup_interface(&location, &state.get_pool()) .await .map_err(|err| err.to_string())?; @@ -59,9 +63,12 @@ pub async fn connect(location_id: i64, handle: tauri::AppHandle) -> Result<(), S let _ = location_stats.save(&state.get_pool()).await; } } - Err(e) => println!("error: {}", e), + Err(_) => { + debug!("Stopped stats thread for location: {}", location.name); + break; + } } - sleep(Duration::from_secs(20)).await; + sleep(Duration::from_secs(60)).await; } }); } @@ -103,7 +110,7 @@ pub struct Device { pub created_at: i64, } -#[derive(Serialize, Deserialize)] +#[derive(Serialize, Deserialize, Debug)] pub struct DeviceConfig { pub network_id: i64, pub network_name: String, @@ -126,7 +133,7 @@ pub fn device_config_to_location(device_config: DeviceConfig, instance_id: i64) allowed_ips: device_config.allowed_ips, } } -#[derive(Serialize, Deserialize)] +#[derive(Serialize, Deserialize, Debug)] pub struct InstanceResponse { // uuid pub id: String, @@ -134,7 +141,7 @@ pub struct InstanceResponse { pub url: String, } -#[derive(Serialize, Deserialize)] +#[derive(Serialize, Deserialize, Debug)] pub struct CreateDeviceResponse { instance: InstanceResponse, configs: Vec, @@ -147,6 +154,7 @@ pub async fn save_device_config( response: CreateDeviceResponse, app_state: State<'_, AppState>, ) -> Result<(), String> { + debug!("Received device configuration: {:#?}", response); let mut transaction = app_state .get_pool() .begin() @@ -173,6 +181,7 @@ pub async fn save_device_config( .map_err(|err| err.to_string())?; } transaction.commit().await.map_err(|err| err.to_string())?; + info!("Instance created"); Ok(()) } @@ -327,3 +336,19 @@ pub async fn all_connections( .await .map_err(|err| err.to_string()) } + +#[tauri::command] +pub async fn last_connection( + location_id: i64, + app_state: State<'_, AppState>, +) -> Result { + if let Some(connection) = Connection::latest_by_location_id(&app_state.get_pool(), location_id) + .await + .map_err(|err| err.to_string())? + { + println!("Returning connection: {:#?}", connection); + Ok(connection) + } else { + Err("No connections for this device".into()) + } +} diff --git a/src-tauri/src/database/mod.rs b/src-tauri/src/database/mod.rs index 952cf256..ef5d134e 100644 --- a/src-tauri/src/database/mod.rs +++ b/src-tauri/src/database/mod.rs @@ -16,10 +16,19 @@ pub async fn init_db(app_handle: &AppHandle) -> Result { .ok_or(Error::Config)?; let db_path = app_dir.join(DB_NAME); if !db_path.exists() { + debug!( + "Database not found creating database file at: {}", + db_path.to_string_lossy() + ); fs::File::create(&db_path)?; + info!("Database file succesfully created.") + } else { + info!("Database exists skipping creating database.") } let pool = DbPool::connect(&format!("sqlite://{}", db_path.to_str().unwrap())).await?; + debug!("Running migrations."); sqlx::migrate!().run(&pool).await?; + info!("Applied migrations."); Ok(pool) } diff --git a/src-tauri/src/database/models/connection.rs b/src-tauri/src/database/models/connection.rs index f75bff39..fb7415c1 100644 --- a/src-tauri/src/database/models/connection.rs +++ b/src-tauri/src/database/models/connection.rs @@ -54,4 +54,23 @@ impl Connection { .await?; Ok(connections) } + pub async fn latest_by_location_id( + pool: &DbPool, + location_id: i64, + ) -> Result, Error> { + let connection = query_as!( + Connection, + r#" + SELECT id, location_id, connected_from, start, end + FROM connection + WHERE location_id = $1 + ORDER BY end DESC + LIMIT 1 + "#, + location_id + ) + .fetch_optional(pool) + .await?; + Ok(connection) + } } diff --git a/src-tauri/src/database/models/location.rs b/src-tauri/src/database/models/location.rs index f3f4ce2f..a28a4b8a 100644 --- a/src-tauri/src/database/models/location.rs +++ b/src-tauri/src/database/models/location.rs @@ -32,8 +32,8 @@ pub struct LocationStats { pub async fn peer_to_location_stats(peer: &Peer, pool: &DbPool) -> Result { let location = Location::find_by_public_key(pool, &peer.public_key.to_string()).await?; Ok(LocationStats { - id: None, // Set to None or your desired default value - location_id: location.id.unwrap(), // Set to the appropriate location_id value + id: None, + location_id: location.id.unwrap(), upload: peer.tx_bytes as i64, download: peer.rx_bytes as i64, last_handshake: peer.last_handshake.map_or(0, |ts| { @@ -212,7 +212,7 @@ impl LocationStats { let stats = query_as!( LocationStats, r#" - SELECT id, location_id, upload, download, last_handshake, collected_at + SELECT id, location_id, upload, download, last_handshake, collected_at as "collected_at: _" FROM location_stats WHERE location_id = $1 "#, diff --git a/src-tauri/src/database/models/wireguard_keys.rs b/src-tauri/src/database/models/wireguard_keys.rs index 918b2b25..64d1a2d3 100644 --- a/src-tauri/src/database/models/wireguard_keys.rs +++ b/src-tauri/src/database/models/wireguard_keys.rs @@ -2,6 +2,7 @@ use crate::{database::DbPool, error::Error}; use sqlx::{query, query_as, Error as SqlxError}; // User key pair +#[derive(Debug)] pub struct WireguardKeys { pub id: Option, pub instance_id: i64, diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index cd5dc22b..a7cee2f4 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -9,11 +9,12 @@ pub mod utils; use appstate::AppState; use tauri::{Manager, State}; +use tauri_plugin_log::LogTarget; use tauri::SystemTrayEvent; mod tray; use crate::commands::{ - all_instances, all_locations, connect, disconnect, location_stats, save_device_config, update_instance + all_instances, all_locations, connect, disconnect, location_stats, save_device_config, update_instance, last_connection, }; use crate::tray::create_tray_menu; @@ -23,6 +24,12 @@ struct Payload { cwd: String, } +#[macro_use] +extern crate log; + +// Specify log targets +const LOG_TARGETS: [LogTarget; 2] = [LogTarget::Stdout, LogTarget::LogDir]; + // TODO: Refactor later #[allow(clippy::single_match)] fn main() { @@ -38,6 +45,7 @@ fn main() { disconnect, update_instance, location_stats, + last_connection ]) .on_window_event(|event| match event.event() { tauri::WindowEvent::CloseRequested { api, .. } => { @@ -87,15 +95,23 @@ fn main() { app.emit_all("single-instance", Payload { args: argv, cwd }) .unwrap(); })) + .plugin( + tauri_plugin_log::Builder::default() + .targets(LOG_TARGETS) + .build(), + ) .manage(AppState::default()) .setup(|app| { let handle = app.handle(); tauri::async_runtime::spawn(async move { + debug!("Initializing database connection"); let app_state: State = handle.state(); let db = database::init_db(&handle) .await - .expect("Database initialize failed"); + .expect("Database initialization failed"); *app_state.db.lock().unwrap() = Some(db); + info!("Database initialization completed"); + info!("Starting main app thread.") }); Ok(()) }) diff --git a/src-tauri/src/utils.rs b/src-tauri/src/utils.rs index ac347c52..65fa46f5 100644 --- a/src-tauri/src/utils.rs +++ b/src-tauri/src/utils.rs @@ -15,8 +15,12 @@ use crate::{ /// Setup client interface pub async fn setup_interface(location: &Location, pool: &DbPool) -> Result<(), Error> { let interface_name = remove_whitespace(&location.name); + debug!("Creating interface: {}", interface_name); create_interface(&interface_name)?; + info!("Created interface: {}", interface_name); address_interface(&interface_name, &IpAddrMask::from_str(&location.address)?)?; + info!("Adressed interface: {}", interface_name); + let api = WGApi::new(interface_name.clone(), false); let mut host = api.read_host()?; @@ -26,7 +30,8 @@ pub async fn setup_interface(location: &Location, pool: &DbPool) -> Result<(), E host.private_key = Some(private_key); let peer_key: Key = Key::from_str(&location.pubkey).unwrap(); let mut peer = Peer::new(peer_key); - let endpoint: SocketAddr = location.endpoint.parse()?; + println!("{}", location.endpoint); + let endpoint: SocketAddr = location.endpoint.parse().unwrap(); peer.endpoint = Some(endpoint); peer.persistent_keepalive_interval = Some(25); let allowed_ips: Vec = location @@ -34,6 +39,7 @@ pub async fn setup_interface(location: &Location, pool: &DbPool) -> Result<(), E .split(',') .map(str::to_string) .collect(); + debug!("Routing allowed ips"); for allowed_ip in allowed_ips { match IpAddrMask::from_str(&allowed_ip) { Ok(addr) => { @@ -50,7 +56,7 @@ pub async fn setup_interface(location: &Location, pool: &DbPool) -> Result<(), E } Err(err) => { // Handle the error from IpAddrMask::from_str, if needed - eprintln!("Error parsing IP address {}: {}", allowed_ip, err); + error!("Error parsing IP address {}: {}", allowed_ip, err); // Continue to the next iteration of the loop continue; } @@ -58,6 +64,7 @@ pub async fn setup_interface(location: &Location, pool: &DbPool) -> Result<(), E } api.write_host(&host)?; api.write_peer(&peer)?; + info!("created peer {:#?}", peer); }; Ok(()) diff --git a/src/i18n/en/index.ts b/src/i18n/en/index.ts index b2e04dcc..3e7d705a 100644 --- a/src/i18n/en/index.ts +++ b/src/i18n/en/index.ts @@ -75,7 +75,10 @@ const en = { connectionLabels: { lastConnectedFrom: 'Last connected from', lastConnected: 'Last connected', + connectedFrom: 'Connected from', assignedIp: 'Assigned IP', + active: 'Active', + neverConnected: 'Never connected', }, locationNoData: 'This device was never connected to this location, connect to view statistics and information about connection', diff --git a/src/i18n/i18n-types.ts b/src/i18n/i18n-types.ts index d100b282..e1e2affa 100644 --- a/src/i18n/i18n-types.ts +++ b/src/i18n/i18n-types.ts @@ -193,10 +193,22 @@ type RootTranslation = { * L​a​s​t​ ​c​o​n​n​e​c​t​e​d */ lastConnected: string + /** + * C​o​n​n​e​c​t​e​d​ ​f​r​o​m + */ + connectedFrom: string /** * A​s​s​i​g​n​e​d​ ​I​P */ assignedIp: string + /** + * A​c​t​i​v​e + */ + active: string + /** + * N​e​v​e​r​ ​c​o​n​n​e​c​t​e​d + */ + neverConnected: string } /** * T​h​i​s​ ​d​e​v​i​c​e​ ​w​a​s​ ​n​e​v​e​r​ ​c​o​n​n​e​c​t​e​d​ ​t​o​ ​t​h​i​s​ ​l​o​c​a​t​i​o​n​,​ ​c​o​n​n​e​c​t​ ​t​o​ ​v​i​e​w​ ​s​t​a​t​i​s​t​i​c​s​ ​a​n​d​ ​i​n​f​o​r​m​a​t​i​o​n​ ​a​b​o​u​t​ ​c​o​n​n​e​c​t​i​o​n @@ -835,10 +847,22 @@ export type TranslationFunctions = { * Last connected */ lastConnected: () => LocalizedString + /** + * Connected from + */ + connectedFrom: () => LocalizedString /** * Assigned IP */ assignedIp: () => LocalizedString + /** + * Active + */ + active: () => LocalizedString + /** + * Never connected + */ + neverConnected: () => LocalizedString } /** * This device was never connected to this location, connect to view statistics and information about connection diff --git a/src/pages/client/ClientPage.tsx b/src/pages/client/ClientPage.tsx index e5500386..e59b2f97 100644 --- a/src/pages/client/ClientPage.tsx +++ b/src/pages/client/ClientPage.tsx @@ -1,21 +1,31 @@ import './style.scss'; import { useQuery } from '@tanstack/react-query'; +import { useCallback, useState } from 'react'; import { useI18nContext } from '../../i18n/i18n-react'; +import { Select } from '../../shared/defguard-ui/components/Layout/Select/Select'; +import { SelectSelectedValue } from '../../shared/defguard-ui/components/Layout/Select/types'; import { clientApi } from './clientAPI/clientApi'; import { ClientSideBar } from './components/ClientSideBar/ClientSideBar'; import { LocationsList } from './components/LocationsList/LocationsList'; import { AddInstanceModal } from './components/modals/AddInstanceModal/AddInstanceModal'; import { useClientStore } from './hooks/useClientStore'; import { clientQueryKeys } from './query'; +import { error, info } from 'tauri-plugin-log-api'; const { getInstances } = clientApi; +enum LayoutType { + GRID = 'GRID', + DETAIL = 'DETAIL', +} + export const ClientPage = () => { const { LL } = useI18nContext(); const pageLL = LL.pages.client; const setInstances = useClientStore((state) => state.setInstances); + const [layoutType, setLayoutType] = useState(LayoutType.GRID); // Initialize with GRID layout useQuery({ queryKey: [clientQueryKeys.getInstances], @@ -24,16 +34,51 @@ export const ClientPage = () => { refetchOnWindowFocus: false, onSuccess: (res) => { setInstances(res); + info('Retrieved instances'); + }, + onError: (err) => { + error(String(err)); }, }); + const renderSelected = useCallback((selectedValue: LayoutType): SelectSelectedValue => { + const options = [ + { value: LayoutType.GRID, label: 'Grid View' }, + { value: LayoutType.DETAIL, label: 'Detail View' }, + ]; + const selectedOption = options.find((option) => option.value === selectedValue); + + if (!selectedOption) { + return { + key: 'none', + displayValue: '', + }; + } + + return { + key: selectedOption.value, + displayValue: selectedOption.label, + }; + }, []); + return ( <>

{pageLL.title()}

+