From e4a257b807b7b7548019f7455f5dd05c5027c39e Mon Sep 17 00:00:00 2001 From: Gregory Schier Date: Sat, 25 Feb 2023 16:39:18 -0800 Subject: [PATCH] Migrations and initial data stuff --- package-lock.json | 183 ++++++++++++- package.json | 3 +- src-tauri/Cargo.lock | 177 ++++++++++++- src-tauri/Cargo.toml | 11 +- src-tauri/build.rs | 2 +- src-tauri/migrations/20230225181302_init.sql | 38 +++ src-tauri/plugins/plugin.ts | 2 +- src-tauri/sqlx-data.json | 263 +++++++++++++++++++ src-tauri/src/main.rs | 209 +++++++++------ src-tauri/src/models.rs | 169 ++++++++++++ src-tauri/src/runtime.js | 2 +- src-tauri/src/runtime.rs | 14 +- src-web/App.tsx | 28 +- src-web/components/Sidebar.tsx | 29 +- src-web/hooks/useWorkspaces.ts | 62 +++++ src-web/main.tsx | 15 +- 16 files changed, 1079 insertions(+), 128 deletions(-) create mode 100644 src-tauri/migrations/20230225181302_init.sql create mode 100644 src-tauri/sqlx-data.json create mode 100644 src-tauri/src/models.rs create mode 100644 src-web/hooks/useWorkspaces.ts diff --git a/package-lock.json b/package-lock.json index a0d895b2..85c76643 100644 --- a/package-lock.json +++ b/package-lock.json @@ -30,7 +30,8 @@ "prettier": "^2.8.4", "react": "^18.2.0", "react-dom": "^18.2.0", - "react-helmet-async": "^1.3.0" + "react-helmet-async": "^1.3.0", + "react-query": "^3.39.3" }, "devDependencies": { "@tauri-apps/cli": "^1.2.2", @@ -2587,6 +2588,14 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, + "node_modules/big-integer": { + "version": "1.6.51", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz", + "integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==", + "engines": { + "node": ">=0.6" + } + }, "node_modules/binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", @@ -2616,6 +2625,21 @@ "node": ">=8" } }, + "node_modules/broadcast-channel": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/broadcast-channel/-/broadcast-channel-3.7.0.tgz", + "integrity": "sha512-cIAKJXAxGJceNZGTZSBzMxzyOn72cVgPnKx4dc6LRjQgbaJUQqhy5rzL3zbMxkMWsGKkv2hSFkPRMEXfoMZ2Mg==", + "dependencies": { + "@babel/runtime": "^7.7.2", + "detect-node": "^2.1.0", + "js-sha3": "0.8.0", + "microseconds": "0.2.0", + "nano-time": "1.0.0", + "oblivious-set": "1.0.0", + "rimraf": "3.0.2", + "unload": "2.2.0" + } + }, "node_modules/browserslist": { "version": "4.21.5", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz", @@ -3039,6 +3063,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==" + }, "node_modules/detect-node-es": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", @@ -4537,6 +4566,11 @@ "url": "https://opencollective.com/js-sdsl" } }, + "node_modules/js-sha3": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", + "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==" + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -4690,6 +4724,15 @@ "node": ">=12" } }, + "node_modules/match-sorter": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/match-sorter/-/match-sorter-6.3.1.tgz", + "integrity": "sha512-mxybbo3pPNuA+ZuCUhm5bwNkXrJTbsk5VWbR5wiwz/GC6LIiegBGn2w3O08UG/jdbYLinw51fSQ5xNU1U3MgBw==", + "dependencies": { + "@babel/runtime": "^7.12.5", + "remove-accents": "0.4.2" + } + }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -4710,6 +4753,11 @@ "node": ">=8.6" } }, + "node_modules/microseconds": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/microseconds/-/microseconds-0.2.0.tgz", + "integrity": "sha512-n7DHHMjR1avBbSpsTBj6fmMGh2AGrifVV4e+WYc3Q9lO+xnSZ3NyhcBND3vzzatt05LFhoKFRxrIyklmLlUtyA==" + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -4734,6 +4782,14 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, + "node_modules/nano-time": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/nano-time/-/nano-time-1.0.0.tgz", + "integrity": "sha512-flnngywOoQ0lLQOTRNexn2gGSNuM9bKj9RZAWSzhQ+UJYaAFG9bac4DW9VHjUAzrOaIcajHybCTHe/bkvozQqA==", + "dependencies": { + "big-integer": "^1.6.16" + } + }, "node_modules/nanoid": { "version": "3.3.4", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", @@ -4902,6 +4958,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/oblivious-set": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/oblivious-set/-/oblivious-set-1.0.0.tgz", + "integrity": "sha512-z+pI07qxo4c2CulUHCDf9lcqDlMSo72N/4rLUpRXf6fu+q8vjt8y0xS+Tlf8NTJDdTXHbdeO1n3MlbctwEoXZw==" + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -5275,6 +5336,31 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, + "node_modules/react-query": { + "version": "3.39.3", + "resolved": "https://registry.npmjs.org/react-query/-/react-query-3.39.3.tgz", + "integrity": "sha512-nLfLz7GiohKTJDuT4us4X3h/8unOh+00MLb2yJoGTPjxKs2bc1iDhkNx2bd5MKklXnOD3NrVZ+J2UXujA5In4g==", + "dependencies": { + "@babel/runtime": "^7.5.5", + "broadcast-channel": "^3.4.1", + "match-sorter": "^6.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + } + } + }, "node_modules/react-refresh": { "version": "0.14.0", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz", @@ -5419,6 +5505,11 @@ "url": "https://github.com/sponsors/mysticatea" } }, + "node_modules/remove-accents": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/remove-accents/-/remove-accents-0.4.2.tgz", + "integrity": "sha512-7pXIJqJOq5tFgG1A2Zxti3Ht8jJF337m4sowbuHsW30ZnkQFnDzy9qBNhgzX8ZLW4+UBcXiiR7SwR6pokHsxiA==" + }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -5942,6 +6033,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/unload": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/unload/-/unload-2.2.0.tgz", + "integrity": "sha512-B60uB5TNBLtN6/LsgAf3udH9saB5p7gqJwcFfbOEZ8BcBHnGwCf6G/TGiEqkRAxX7zAFIUtzdrXQSdL3Q/wqNA==", + "dependencies": { + "@babel/runtime": "^7.6.2", + "detect-node": "^2.0.4" + } + }, "node_modules/update-browserslist-db": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", @@ -8060,6 +8160,11 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, + "big-integer": { + "version": "1.6.51", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz", + "integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==" + }, "binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", @@ -8083,6 +8188,21 @@ "fill-range": "^7.0.1" } }, + "broadcast-channel": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/broadcast-channel/-/broadcast-channel-3.7.0.tgz", + "integrity": "sha512-cIAKJXAxGJceNZGTZSBzMxzyOn72cVgPnKx4dc6LRjQgbaJUQqhy5rzL3zbMxkMWsGKkv2hSFkPRMEXfoMZ2Mg==", + "requires": { + "@babel/runtime": "^7.7.2", + "detect-node": "^2.1.0", + "js-sha3": "0.8.0", + "microseconds": "0.2.0", + "nano-time": "1.0.0", + "oblivious-set": "1.0.0", + "rimraf": "3.0.2", + "unload": "2.2.0" + } + }, "browserslist": { "version": "4.21.5", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz", @@ -8384,6 +8504,11 @@ "integrity": "sha512-hsBd2qSVCRE+5PmNdHt1uzyrFu5d3RwmFDKzyNZMFq/EwDNJF7Ee5+D5oEKF0hU6LhtoUF1macFvOe4AskQC1Q==", "dev": true }, + "detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==" + }, "detect-node-es": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", @@ -9470,6 +9595,11 @@ "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.3.0.tgz", "integrity": "sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==" }, + "js-sha3": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", + "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==" + }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -9587,6 +9717,15 @@ "@jridgewell/sourcemap-codec": "^1.4.13" } }, + "match-sorter": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/match-sorter/-/match-sorter-6.3.1.tgz", + "integrity": "sha512-mxybbo3pPNuA+ZuCUhm5bwNkXrJTbsk5VWbR5wiwz/GC6LIiegBGn2w3O08UG/jdbYLinw51fSQ5xNU1U3MgBw==", + "requires": { + "@babel/runtime": "^7.12.5", + "remove-accents": "0.4.2" + } + }, "merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -9601,6 +9740,11 @@ "picomatch": "^2.3.1" } }, + "microseconds": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/microseconds/-/microseconds-0.2.0.tgz", + "integrity": "sha512-n7DHHMjR1avBbSpsTBj6fmMGh2AGrifVV4e+WYc3Q9lO+xnSZ3NyhcBND3vzzatt05LFhoKFRxrIyklmLlUtyA==" + }, "minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -9619,6 +9763,14 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, + "nano-time": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/nano-time/-/nano-time-1.0.0.tgz", + "integrity": "sha512-flnngywOoQ0lLQOTRNexn2gGSNuM9bKj9RZAWSzhQ+UJYaAFG9bac4DW9VHjUAzrOaIcajHybCTHe/bkvozQqA==", + "requires": { + "big-integer": "^1.6.16" + } + }, "nanoid": { "version": "3.3.4", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", @@ -9733,6 +9885,11 @@ "es-abstract": "^1.20.4" } }, + "oblivious-set": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/oblivious-set/-/oblivious-set-1.0.0.tgz", + "integrity": "sha512-z+pI07qxo4c2CulUHCDf9lcqDlMSo72N/4rLUpRXf6fu+q8vjt8y0xS+Tlf8NTJDdTXHbdeO1n3MlbctwEoXZw==" + }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -9961,6 +10118,16 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, + "react-query": { + "version": "3.39.3", + "resolved": "https://registry.npmjs.org/react-query/-/react-query-3.39.3.tgz", + "integrity": "sha512-nLfLz7GiohKTJDuT4us4X3h/8unOh+00MLb2yJoGTPjxKs2bc1iDhkNx2bd5MKklXnOD3NrVZ+J2UXujA5In4g==", + "requires": { + "@babel/runtime": "^7.5.5", + "broadcast-channel": "^3.4.1", + "match-sorter": "^6.0.2" + } + }, "react-refresh": { "version": "0.14.0", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz", @@ -10057,6 +10224,11 @@ "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==" }, + "remove-accents": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/remove-accents/-/remove-accents-0.4.2.tgz", + "integrity": "sha512-7pXIJqJOq5tFgG1A2Zxti3Ht8jJF337m4sowbuHsW30ZnkQFnDzy9qBNhgzX8ZLW4+UBcXiiR7SwR6pokHsxiA==" + }, "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -10435,6 +10607,15 @@ "which-boxed-primitive": "^1.0.2" } }, + "unload": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/unload/-/unload-2.2.0.tgz", + "integrity": "sha512-B60uB5TNBLtN6/LsgAf3udH9saB5p7gqJwcFfbOEZ8BcBHnGwCf6G/TGiEqkRAxX7zAFIUtzdrXQSdL3Q/wqNA==", + "requires": { + "@babel/runtime": "^7.6.2", + "detect-node": "^2.0.4" + } + }, "update-browserslist-db": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", diff --git a/package.json b/package.json index 0d767a7f..74a88721 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,8 @@ "prettier": "^2.8.4", "react": "^18.2.0", "react-dom": "^18.2.0", - "react-helmet-async": "^1.3.0" + "react-helmet-async": "^1.3.0", + "react-query": "^3.39.3" }, "devDependencies": { "@tauri-apps/cli": "^1.2.2", diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index ce991881..226fb289 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -59,6 +59,15 @@ dependencies = [ "alloc-no-stdlib", ] +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + [[package]] name = "anyhow" version = "1.0.69" @@ -295,6 +304,22 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chrono" +version = "0.4.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f" +dependencies = [ + "iana-time-zone", + "js-sys", + "num-integer", + "num-traits", + "serde", + "time 0.1.45", + "wasm-bindgen", + "winapi", +] + [[package]] name = "cocoa" version = "0.24.1" @@ -326,6 +351,16 @@ dependencies = [ "objc", ] +[[package]] +name = "codespan-reporting" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" +dependencies = [ + "termcolor", + "unicode-width", +] + [[package]] name = "color_quant" version = "1.1.0" @@ -504,6 +539,50 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35" +[[package]] +name = "cxx" +version = "1.0.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86d3488e7665a7a483b57e25bdd90d0aeb2bc7608c8d0346acf2ad3f1caf1d62" +dependencies = [ + "cc", + "cxxbridge-flags", + "cxxbridge-macro", + "link-cplusplus", +] + +[[package]] +name = "cxx-build" +version = "1.0.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48fcaf066a053a41a81dfb14d57d99738b767febb8b735c3016e469fac5da690" +dependencies = [ + "cc", + "codespan-reporting", + "once_cell", + "proc-macro2", + "quote", + "scratch", + "syn", +] + +[[package]] +name = "cxxbridge-flags" +version = "1.0.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2ef98b8b717a829ca5603af80e1f9e2e48013ab227b68ef37872ef84ee479bf" + +[[package]] +name = "cxxbridge-macro" +version = "1.0.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "086c685979a698443656e5cf7856c95c642295a38599f12fb1ff76fb28d19892" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "darling" version = "0.13.4" @@ -600,9 +679,9 @@ dependencies = [ [[package]] name = "deno_core" -version = "0.171.0" +version = "0.173.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dc41944f05dfeacfc2610e91f40ddcf246f3aeeac8ae4c26df46bfbf01a3902" +checksum = "e3f5ea1db875ff1eb020c04b900f75c810a9b66deec4a5437205b868bff9a7df" dependencies = [ "anyhow", "bytes", @@ -625,9 +704,9 @@ dependencies = [ [[package]] name = "deno_ops" -version = "0.49.0" +version = "0.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4740bc5738ad07dc1f523a232a4079a995fa2ad11efd71e09e8e32bf28f21ee1" +checksum = "1d06332c139917f0fe6722e6c9bd3aac45bfdf3e306627e7eb681a42bad56497" dependencies = [ "once_cell", "pmutil", @@ -736,6 +815,9 @@ name = "either" version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" +dependencies = [ + "serde", +] [[package]] name = "embed_plist" @@ -1432,6 +1514,30 @@ dependencies = [ "tokio-native-tls", ] +[[package]] +name = "iana-time-zone" +version = "0.1.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "winapi", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca" +dependencies = [ + "cxx", + "cxx-build", +] + [[package]] name = "ico" version = "0.1.0" @@ -1786,6 +1892,15 @@ dependencies = [ "safemem", ] +[[package]] +name = "link-cplusplus" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5" +dependencies = [ + "cc", +] + [[package]] name = "lock_api" version = "0.4.9" @@ -2426,7 +2541,7 @@ dependencies = [ "line-wrap", "quick-xml", "serde", - "time", + "time 0.3.17", ] [[package]] @@ -2842,6 +2957,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +[[package]] +name = "scratch" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddccb15bcce173023b3fedd9436f882a0739b8dfb45e4f6b6002bee5929f61b2" + [[package]] name = "sct" version = "0.7.0" @@ -3003,9 +3124,9 @@ dependencies = [ [[package]] name = "serde_v8" -version = "0.82.0" +version = "0.84.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c060fd38f18c420e82ab21592ec1f088b39bccb6897b1dda394d63628e22158d" +checksum = "94c6b0ea68c2368070520883b5caac9b5bff17597cfac77aebc07280e8585f7f" dependencies = [ "bytes", "derive_more", @@ -3221,6 +3342,7 @@ dependencies = [ "bitflags", "byteorder", "bytes", + "chrono", "crc", "crossbeam-queue", "dotenvy", @@ -3245,12 +3367,15 @@ dependencies = [ "percent-encoding", "rustls", "rustls-pemfile", + "serde", + "serde_json", "sha2", "smallvec", "sqlformat", "sqlx-rt", "stringprep", "thiserror", + "time 0.3.17", "tokio-stream", "url", "webpki-roots", @@ -3265,9 +3390,12 @@ dependencies = [ "dotenvy", "either", "heck 0.4.1", + "hex", "once_cell", "proc-macro2", "quote", + "serde", + "serde_json", "sha2", "sqlx-core", "sqlx-rt", @@ -3848,12 +3976,14 @@ dependencies = [ name = "tauri-app" version = "0.0.0" dependencies = [ + "chrono", "cocoa", "deno_ast", "deno_core", "futures", "http", "objc", + "rand 0.8.5", "reqwest", "serde", "serde_json", @@ -3861,6 +3991,7 @@ dependencies = [ "tauri", "tauri-build", "tokio", + "uuid 1.3.0", ] [[package]] @@ -3900,7 +4031,7 @@ dependencies = [ "sha2", "tauri-utils", "thiserror", - "time", + "time 0.3.17", "uuid 1.3.0", "walkdir", ] @@ -4013,6 +4144,15 @@ dependencies = [ "utf-8", ] +[[package]] +name = "termcolor" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" +dependencies = [ + "winapi-util", +] + [[package]] name = "text_lines" version = "0.6.0" @@ -4058,6 +4198,17 @@ dependencies = [ "once_cell", ] +[[package]] +name = "time" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" +dependencies = [ + "libc", + "wasi 0.10.0+wasi-snapshot-preview1", + "winapi", +] + [[package]] name = "time" version = "0.3.17" @@ -4386,9 +4537,9 @@ dependencies = [ [[package]] name = "v8" -version = "0.60.1" +version = "0.63.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07fd5b3ed559897ff02c0f62bc0a5f300bfe79bb4c77a50031b8df771701c628" +checksum = "547e58962ac268fe0b1fbfb653ed341a08e3953994f7f7c978e46ec30afdf8f0" dependencies = [ "bitflags", "fslock", @@ -4453,6 +4604,12 @@ version = "0.9.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index c9c9b8fb..b26a472c 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -13,18 +13,21 @@ edition = "2021" tauri-build = { version = "1.2", features = [] } [dependencies] -serde_json = { version = "1.0" } +serde_json = { version = "1.0", features = ["raw_value"] } serde = { version = "1.0", features = ["derive"] } tauri = { version = "1.2", features = ["config-toml", "devtools", "shell-open", "system-tray", "window-start-dragging"] } http = { version = "0.2.8" } reqwest = { version = "0.11.14", features = ["json"] } -tokio = { version = "1.25.0", features = [ "sync"] } +tokio = { version = "1.25.0", features = ["sync"] } futures = { version = "0.3.26" } -deno_core = { version = "0.171.0" } +deno_core = { version = "0.173.0" } deno_ast = { version = "0.24.0", features = ["transpiling"] } objc = { version = "0.2.7" } cocoa = { version = "0.24.1" } -sqlx = { version = "0.6.2", features = ["sqlite", "runtime-tokio-rustls"] } +sqlx = { version = "0.6.2", features = ["sqlite", "runtime-tokio-rustls", "json", "chrono", "time", "offline"] } +uuid = { version = "1.3.0" } +rand = { version = "0.8.5" } +chrono = { version = "0.4.23", features = ["serde"] } [features] # by default Tauri runs in production mode diff --git a/src-tauri/build.rs b/src-tauri/build.rs index 795b9b7c..d860e1e6 100644 --- a/src-tauri/build.rs +++ b/src-tauri/build.rs @@ -1,3 +1,3 @@ fn main() { - tauri_build::build() + tauri_build::build() } diff --git a/src-tauri/migrations/20230225181302_init.sql b/src-tauri/migrations/20230225181302_init.sql new file mode 100644 index 00000000..6a7b571a --- /dev/null +++ b/src-tauri/migrations/20230225181302_init.sql @@ -0,0 +1,38 @@ +CREATE TABLE workspaces +( + id TEXT NOT NULL PRIMARY KEY, + created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + deleted_at DATETIME, + name TEXT NOT NULL, + description TEXT NOT NULL +); + +CREATE TABLE requests +( + id TEXT NOT NULL PRIMARY KEY, + workspace_id TEXT NOT NULL REFERENCES workspaces (id) ON DELETE CASCADE, + created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + deleted_at DATETIME, + name TEXT NOT NULL, + url TEXT NOT NULL, + method TEXT NOT NULL, + headers TEXT NOT NULL, + body TEXT +); + +CREATE TABLE responses +( + id TEXT NOT NULL PRIMARY KEY, + request_id TEXT NOT NULL REFERENCES requests (id) ON DELETE CASCADE, + workspace_id TEXT NOT NULL REFERENCES workspaces (id) ON DELETE CASCADE, + created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + deleted_at DATETIME, + elapsed INTEGER NOT NULL, + status INTEGER NOT NULL, + status_reason TEXT NOT NULL, + body TEXT NOT NULL, + headers TEXT NOT NULL +); diff --git a/src-tauri/plugins/plugin.ts b/src-tauri/plugins/plugin.ts index e139c284..c68da85e 100644 --- a/src-tauri/plugins/plugin.ts +++ b/src-tauri/plugins/plugin.ts @@ -2,4 +2,4 @@ console.log('---------------------------'); console.log('- 👋 Hello from plugin.ts -'); console.log('---------------------------'); -Deno.core.ops.op_hello('World'); +Deno.core.opAsync('op_hello', 'World'); diff --git a/src-tauri/sqlx-data.json b/src-tauri/sqlx-data.json new file mode 100644 index 00000000..a25d7acd --- /dev/null +++ b/src-tauri/sqlx-data.json @@ -0,0 +1,263 @@ +{ + "db": "SQLite", + "74850a49fa21f4cb5f30905b8ede1fa76935c1ff7ad13c105c6de772d10ff742": { + "describe": { + "columns": [], + "nullable": [], + "parameters": { + "Right": 6 + } + }, + "query": "\n INSERT INTO requests (id, workspace_id, name, url, method, body, updated_at, headers)\n VALUES (?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP, '{}')\n ON CONFLICT (id) DO UPDATE SET\n name = excluded.name,\n method = excluded.method,\n body = excluded.body,\n url = excluded.url;\n " + }, + "8069c0bd326f659faca7b45b03e5317d7339a168f4cd7776d9f84304bb7ae7ac": { + "describe": { + "columns": [ + { + "name": "id", + "ordinal": 0, + "type_info": "Text" + }, + { + "name": "created_at", + "ordinal": 1, + "type_info": "Datetime" + }, + { + "name": "updated_at", + "ordinal": 2, + "type_info": "Datetime" + }, + { + "name": "deleted_at", + "ordinal": 3, + "type_info": "Datetime" + }, + { + "name": "name", + "ordinal": 4, + "type_info": "Text" + }, + { + "name": "description", + "ordinal": 5, + "type_info": "Text" + } + ], + "nullable": [ + false, + false, + false, + true, + false, + false + ], + "parameters": { + "Right": 0 + } + }, + "query": "\n SELECT id, created_at, updated_at, deleted_at, name, description\n FROM workspaces\n " + }, + "d461b9471bdc1fd3f85ca9351f686def07634b4906c8429eeef343b11992b445": { + "describe": { + "columns": [ + { + "name": "id", + "ordinal": 0, + "type_info": "Text" + }, + { + "name": "workspace_id", + "ordinal": 1, + "type_info": "Text" + }, + { + "name": "created_at", + "ordinal": 2, + "type_info": "Datetime" + }, + { + "name": "updated_at", + "ordinal": 3, + "type_info": "Datetime" + }, + { + "name": "deleted_at", + "ordinal": 4, + "type_info": "Datetime" + }, + { + "name": "name", + "ordinal": 5, + "type_info": "Text" + }, + { + "name": "url", + "ordinal": 6, + "type_info": "Text" + }, + { + "name": "method", + "ordinal": 7, + "type_info": "Text" + }, + { + "name": "body", + "ordinal": 8, + "type_info": "Text" + }, + { + "name": "headers", + "ordinal": 9, + "type_info": "Text" + } + ], + "nullable": [ + false, + false, + false, + false, + true, + false, + false, + false, + true, + false + ], + "parameters": { + "Right": 1 + } + }, + "query": "\n SELECT id, workspace_id, created_at, updated_at, deleted_at, name, url, method, body, headers\n FROM requests\n WHERE id = ?\n " + }, + "da08ebedec0942fd5c54ed1e180d7dc399629f83bfa1341c1c09a048123adac1": { + "describe": { + "columns": [ + { + "name": "id", + "ordinal": 0, + "type_info": "Text" + }, + { + "name": "workspace_id", + "ordinal": 1, + "type_info": "Text" + }, + { + "name": "created_at", + "ordinal": 2, + "type_info": "Datetime" + }, + { + "name": "updated_at", + "ordinal": 3, + "type_info": "Datetime" + }, + { + "name": "deleted_at", + "ordinal": 4, + "type_info": "Datetime" + }, + { + "name": "name", + "ordinal": 5, + "type_info": "Text" + }, + { + "name": "url", + "ordinal": 6, + "type_info": "Text" + }, + { + "name": "method", + "ordinal": 7, + "type_info": "Text" + }, + { + "name": "body", + "ordinal": 8, + "type_info": "Text" + }, + { + "name": "headers", + "ordinal": 9, + "type_info": "Text" + } + ], + "nullable": [ + false, + false, + false, + false, + true, + false, + false, + false, + true, + false + ], + "parameters": { + "Right": 1 + } + }, + "query": "\n SELECT id, workspace_id, created_at, updated_at, deleted_at, name, url, method, body, headers\n FROM requests\n WHERE workspace_id = ?;\n " + }, + "f116d8cf9aad828135bb8c3a4c8b8e6b857ae13303989e9133a33b2d1cf20e96": { + "describe": { + "columns": [], + "nullable": [], + "parameters": { + "Right": 3 + } + }, + "query": "\n INSERT INTO workspaces (id, name, description)\n VALUES (?, ?, ?)\n " + }, + "fa5011146663ba5675764f33bf55a86b8274aa18737aff427fd1e3fb74ef3535": { + "describe": { + "columns": [ + { + "name": "id", + "ordinal": 0, + "type_info": "Text" + }, + { + "name": "created_at", + "ordinal": 1, + "type_info": "Datetime" + }, + { + "name": "updated_at", + "ordinal": 2, + "type_info": "Datetime" + }, + { + "name": "deleted_at", + "ordinal": 3, + "type_info": "Datetime" + }, + { + "name": "name", + "ordinal": 4, + "type_info": "Text" + }, + { + "name": "description", + "ordinal": 5, + "type_info": "Text" + } + ], + "nullable": [ + false, + false, + false, + true, + false, + false + ], + "parameters": { + "Right": 1 + } + }, + "query": "\n SELECT id, created_at, updated_at, deleted_at, name, description\n FROM workspaces\n WHERE id = ?\n " + } +} \ No newline at end of file diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 5db5cb2c..5e747f72 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -9,81 +9,25 @@ extern crate objc; use std::collections::HashMap; use std::fs::create_dir_all; -use tokio::sync::Mutex; +use std::path::Path; use http::header::{HeaderName, USER_AGENT}; use http::{HeaderMap, HeaderValue, Method}; use reqwest::redirect::Policy; +use sqlx::migrate::Migrator; use sqlx::sqlite::SqlitePoolOptions; use sqlx::{Pool, Sqlite}; use tauri::{AppHandle, State, Wry}; use tauri::{CustomMenuItem, Manager, SystemTray, SystemTrayEvent, SystemTrayMenu, WindowEvent}; +use tokio::sync::Mutex; +use crate::models::{create_workspace, find_workspaces, Request, Workspace}; use window_ext::WindowExt; +mod models; mod runtime; mod window_ext; -fn main() { - // here `"quit".to_string()` defines the menu item id, and the second parameter is the menu item label. - let quit = CustomMenuItem::new("quit".to_string(), "Quit"); - let tray_menu = SystemTrayMenu::new().add_item(quit); - let system_tray = SystemTray::new().with_menu(tray_menu); - - tauri::Builder::default() - .system_tray(system_tray) - .setup(|app| { - let win = app.get_window("main").unwrap(); - win.position_traffic_lights(); - Ok(()) - }) - .setup(|app| { - let dir = app.path_resolver().app_data_dir().unwrap(); - create_dir_all(dir.clone()).expect("Problem creating App directory!"); - let p = dir.join("db.sqlite"); - let p_string = p.to_string_lossy().replace(" ", "%20"); - let url = format!("sqlite://{}?mode=rwc", p_string); - println!("DB URL: {}", url); - tauri::async_runtime::block_on(async move { - let pool = SqlitePoolOptions::new() - .connect(url.as_str()) - .await - .expect("Failed to connect to database"); - app.manage(Mutex::new(pool)); - println!("CONNECTED!"); - Ok(()) - }) - }) - .on_system_tray_event(|app, event| match event { - SystemTrayEvent::MenuItemClick { id, .. } => match id.as_str() { - "quit" => { - std::process::exit(0); - } - "hide" => { - let window = app.get_window("main").unwrap(); - window.hide().unwrap(); - } - _ => {} - }, - _ => {} - }) - .on_window_event(|e| { - let apply_offset = || { - let win = e.window(); - win.position_traffic_lights(); - }; - - match e.event() { - WindowEvent::Resized(..) => apply_offset(), - WindowEvent::ThemeChanged(..) => apply_offset(), - _ => {} - } - }) - .invoke_handler(tauri::generate_handler![send_request, greet, load_db,]) - .run(tauri::generate_context!()) - .expect("error while running tauri application"); -} - #[derive(serde::Serialize)] pub struct CustomResponse { status: u16, @@ -98,18 +42,11 @@ pub struct CustomResponse { #[tauri::command] async fn load_db(db_instance: State<'_, Mutex>>) -> Result<(), String> { - println!("INITIALIZING DB"); - sqlx::query( - "CREATE TABLE IF NOT EXISTS responses ( - id INTEGER PRIMARY KEY, - body TEXT NOT NULL, - status INT NOT NULL - )", - ) - .execute(&*db_instance.lock().await) - .await - .expect("Failed to create table"); - + let pool = &*db_instance.lock().await; + let m = Migrator::new(Path::new("./migrations")) + .await + .expect("Failed to load migrations"); + m.run(pool).await.expect("Failed to run migrations"); Ok(()) } @@ -142,9 +79,7 @@ async fn send_request( ); let m = Method::from_bytes(method.to_uppercase().as_bytes()).unwrap(); - let builder = client - .request(m, abs_url.to_string()) - .headers(headers); + let builder = client.request(m, abs_url.to_string()).headers(headers); let req = match body { Some(b) => builder.body(b.to_string()).build(), @@ -160,7 +95,6 @@ async fn send_request( }; let resp = client.execute(req).await; - let elapsed = start.elapsed().as_millis(); let p = app_handle @@ -183,12 +117,6 @@ async fn send_request( .collect::>(); let body = v.text().await.unwrap(); let elapsed2 = start.elapsed().as_millis(); - sqlx::query("INSERT INTO responses (body, status) VALUES (?, ?)") - .bind(body.clone()) - .bind(status.clone()) - .execute(&*db_instance.lock().await) - .await - .unwrap(); Ok(CustomResponse { status, status_reason, @@ -207,7 +135,122 @@ async fn send_request( } } +#[tauri::command] +async fn upsert_request( + id: Option<&str>, + workspace_id: &str, + name: &str, + db_instance: State<'_, Mutex>>, +) -> Result { + let pool = &*db_instance.lock().await; + models::upsert_request( + id, + workspace_id, + name, + "GET", + None, + "https://google.com", + pool, + ) + .await + .map_err(|e| e.to_string()) +} + +#[tauri::command] +async fn requests( + workspace_id: &str, + db_instance: State<'_, Mutex>>, +) -> Result, String> { + let pool = &*db_instance.lock().await; + models::find_requests(workspace_id, pool) + .await + .map_err(|e| e.to_string()) +} + +#[tauri::command] +async fn workspaces(db_instance: State<'_, Mutex>>) -> Result, String> { + let pool = &*db_instance.lock().await; + let workspaces = find_workspaces(pool) + .await + .expect("Failed to find workspaces"); + if workspaces.is_empty() { + let workspace = create_workspace("Default", "This is the default workspace", pool) + .await + .expect("Failed to create workspace"); + Ok(vec![workspace]) + } else { + Ok(workspaces) + } +} + #[tauri::command] fn greet(name: &str) -> String { format!("Hello, {}! You've been greeted from Rust!", name) } + +fn main() { + // here `"quit".to_string()` defines the menu item id, and the second parameter is the menu item label. + let quit = CustomMenuItem::new("quit".to_string(), "Quit"); + let tray_menu = SystemTrayMenu::new().add_item(quit); + let system_tray = SystemTray::new().with_menu(tray_menu); + + tauri::Builder::default() + .system_tray(system_tray) + .setup(|app| { + let win = app.get_window("main").unwrap(); + win.position_traffic_lights(); + Ok(()) + }) + .setup(|app| { + let dir = app.path_resolver().app_data_dir().unwrap(); + create_dir_all(dir.clone()).expect("Problem creating App directory!"); + let p = dir.join("db.sqlite"); + let p_string = p.to_string_lossy().replace(" ", "%20"); + let url = format!("sqlite://{}?mode=rwc", p_string); + println!("DB PATH: {}", p_string); + tauri::async_runtime::block_on(async move { + let pool = SqlitePoolOptions::new() + .connect(url.as_str()) + .await + .expect("Failed to connect to database"); + app.manage(Mutex::new(pool)); + Ok(()) + }) + }) + .on_system_tray_event(|app, event| { + if let SystemTrayEvent::MenuItemClick { id, .. } = event { + match id.as_str() { + "quit" => { + std::process::exit(0); + } + "hide" => { + let window = app.get_window("main").unwrap(); + window.hide().unwrap(); + } + _ => {} + } + } + }) + .on_window_event(|e| { + let apply_offset = || { + let win = e.window(); + win.position_traffic_lights(); + }; + + match e.event() { + WindowEvent::Resized(..) => apply_offset(), + WindowEvent::ThemeChanged(..) => apply_offset(), + _ => {} + } + }) + .invoke_handler(tauri::generate_handler![ + send_request, + greet, + load_db, + workspaces, + requests, + upsert_request, + ]) + .run(tauri::generate_context!()) + .expect("error while running tauri application"); +} diff --git a/src-tauri/src/models.rs b/src-tauri/src/models.rs new file mode 100644 index 00000000..8b17996c --- /dev/null +++ b/src-tauri/src/models.rs @@ -0,0 +1,169 @@ +use std::collections::HashMap; + +use rand::distributions::{Alphanumeric, DistString}; +use serde::{Deserialize, Serialize}; +use sqlx::types::chrono::NaiveDateTime; +use sqlx::{Pool, Sqlite}; + +#[derive(sqlx::FromRow, Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Workspace { + pub id: String, + pub created_at: NaiveDateTime, + pub updated_at: NaiveDateTime, + pub deleted_at: Option, + pub name: String, + pub description: String, +} + +#[derive(sqlx::FromRow, Debug, Clone, Serialize, Deserialize)] +pub struct Request { + pub id: String, + pub created_at: NaiveDateTime, + pub updated_at: NaiveDateTime, + pub deleted_at: Option, + pub workspace_id: String, + pub name: String, + pub url: String, + pub method: String, + pub body: Option, + pub headers: String, +} + +#[derive(sqlx::FromRow, Debug, Clone, Serialize, Deserialize)] +pub struct Response { + pub id: String, + pub workspace_id: String, + pub request_id: String, + pub created_at: NaiveDateTime, + pub updated_at: NaiveDateTime, + pub deleted_at: Option, + pub name: String, + pub status: u16, + pub status_reason: Option<&'static str>, + pub body: String, + pub url: String, + pub method: String, + pub elapsed: u128, + pub elapsed2: u128, + pub headers: HashMap, +} + +pub async fn find_workspaces(pool: &Pool) -> Result, sqlx::Error> { + sqlx::query_as!( + Workspace, + r#" + SELECT id, created_at, updated_at, deleted_at, name, description + FROM workspaces + "#, + ) + .fetch_all(pool) + .await +} + +pub async fn get_workspace(id: &str, pool: &Pool) -> Result { + sqlx::query_as!( + Workspace, + r#" + SELECT id, created_at, updated_at, deleted_at, name, description + FROM workspaces + WHERE id = ? + "#, + id, + ) + .fetch_one(pool) + .await +} + +pub async fn create_workspace( + name: &str, + description: &str, + pool: &Pool, +) -> Result { + let id = generate_id("wrk"); + sqlx::query!( + r#" + INSERT INTO workspaces (id, name, description) + VALUES (?, ?, ?) + "#, + id, + name, + description, + ) + .execute(pool) + .await + .expect("Failed to insert new workspace"); + + get_workspace(&id, pool).await +} + +pub async fn upsert_request( + id: Option<&str>, + workspace_id: &str, + name: &str, + method: &str, + body: Option<&str>, + url: &str, + pool: &Pool, +) -> Result { + let id = generate_id("wrk"); + sqlx::query!( + r#" + INSERT INTO requests (id, workspace_id, name, url, method, body, updated_at, headers) + VALUES (?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP, '{}') + ON CONFLICT (id) DO UPDATE SET + name = excluded.name, + method = excluded.method, + body = excluded.body, + url = excluded.url; + "#, + id, + workspace_id, + name, + url, + method, + body, + ) + .execute(pool) + .await + .expect("Failed to insert new request"); + get_request(&id, pool).await +} + +pub async fn find_requests( + workspace_id: &str, + pool: &Pool, +) -> Result, sqlx::Error> { + sqlx::query_as!( + Request, + r#" + SELECT id, workspace_id, created_at, updated_at, deleted_at, name, url, method, body, headers + FROM requests + WHERE workspace_id = ?; + "#, + workspace_id, + ) + .fetch_all(pool) + .await +} + +pub async fn get_request(id: &str, pool: &Pool) -> Result { + sqlx::query_as!( + Request, + r#" + SELECT id, workspace_id, created_at, updated_at, deleted_at, name, url, method, body, headers + FROM requests + WHERE id = ? + "#, + id, + ) + .fetch_one(pool) + .await +} + +fn generate_id(prefix: &str) -> String { + format!( + "{prefix}_{}", + Alphanumeric.sample_string(&mut rand::thread_rng(), 10) + ) +} diff --git a/src-tauri/src/runtime.js b/src-tauri/src/runtime.js index 4a8e686e..51e0a013 100644 --- a/src-tauri/src/runtime.js +++ b/src-tauri/src/runtime.js @@ -1,5 +1,5 @@ (function (globalThis) { - Deno.core.initializeAsyncOps(); + // Deno.core.print(Object.keys(Deno.core).join('\n')); function argsToMessage(...args) { return args.map((arg) => JSON.stringify(arg)).join(' '); diff --git a/src-tauri/src/runtime.rs b/src-tauri/src/runtime.rs index aef51fc3..fca39a29 100644 --- a/src-tauri/src/runtime.rs +++ b/src-tauri/src/runtime.rs @@ -24,12 +24,18 @@ pub async fn run_plugin(file_path: &str) -> Result<(), AnyError> { runtime .execute_script("", include_str!("runtime.js")) - .unwrap(); + .expect("Failed to execute runtime.js"); - let main_module = deno_core::resolve_path(file_path)?; - let mod_id = runtime.load_main_module(&main_module, None).await?; + let main_module = deno_core::resolve_path(file_path).expect("Failed to resolve path"); + let mod_id = runtime + .load_main_module(&main_module, None) + .await + .expect("Failed to load main module"); let result = runtime.mod_evaluate(mod_id); - runtime.run_event_loop(false).await?; + runtime + .run_event_loop(false) + .await + .expect("Failed to run event loop"); result.await? } diff --git a/src-web/App.tsx b/src-web/App.tsx index 286f6b61..080b784b 100644 --- a/src-web/App.tsx +++ b/src-web/App.tsx @@ -9,6 +9,7 @@ import { Sidebar } from './components/Sidebar'; import { UrlBar } from './components/UrlBar'; import { Grid } from './components/Grid'; import { motion } from 'framer-motion'; +import { useRequests, useWorkspace, useWorkspaces } from './hooks/useWorkspaces'; interface Response { url: string; @@ -21,6 +22,8 @@ interface Response { } function App() { + const { data } = useWorkspace(); + console.log('DATA', data); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const [response, setResponse] = useState(null); @@ -31,6 +34,19 @@ function App() { value: 'GET', }); + useEffect(() => { + const listener = async (e: KeyboardEvent) => { + if (e.metaKey && (e.key === 'Enter' || e.key === 'r')) { + await sendRequest(); + } + }; + document.documentElement.addEventListener('keypress', listener); + return () => document.documentElement.removeEventListener('keypress', listener); + }, []); + + if (!data) return null; + const { requests, workspace } = data; + async function sendRequest() { setLoading(true); setError(null); @@ -54,20 +70,10 @@ function App() { const contentType = response?.headers['content-type']?.split(';')[0] ?? 'text/plain'; - useEffect(() => { - const listener = async (e: KeyboardEvent) => { - if (e.metaKey && (e.key === 'Enter' || e.key === 'r')) { - await sendRequest(); - } - }; - document.documentElement.addEventListener('keypress', listener); - return () => document.documentElement.removeEventListener('keypress', listener); - }, []); - return ( <>
- + diff --git a/src-web/components/Sidebar.tsx b/src-web/components/Sidebar.tsx index 5c44527b..50172497 100644 --- a/src-web/components/Sidebar.tsx +++ b/src-web/components/Sidebar.tsx @@ -5,10 +5,15 @@ import { Button } from './Button'; import useTheme from '../hooks/useTheme'; import { HStack } from './Stacks'; import { WindowDragRegion } from './WindowDragRegion'; +import { Request } from '../hooks/useWorkspaces'; +import { invoke } from '@tauri-apps/api'; -type Props = Omit, 'children'>; +interface Props extends Omit, 'children'> { + workspaceId: string; + requests: Request[]; +} -export function Sidebar({ className, ...props }: Props) { +export function Sidebar({ className, workspaceId, requests, ...props }: Props) { const { toggleTheme } = useTheme(); return (
+ { + const req = await invoke('upsert_request', { + workspaceId, + id: null, + name: 'Test Request', + }); + console.log('UPSERTED', req); + }} + />
    - {['Test Request', 'Another Request', 'Something else', 'And Another'].map((v, i) => ( -
  • + {requests.map((r) => ( +
  • ))} diff --git a/src-web/hooks/useWorkspaces.ts b/src-web/hooks/useWorkspaces.ts new file mode 100644 index 00000000..fc8deeff --- /dev/null +++ b/src-web/hooks/useWorkspaces.ts @@ -0,0 +1,62 @@ +import { invoke } from '@tauri-apps/api'; +import { useQuery, UseQueryResult } from 'react-query'; + +interface BaseModel { + id: string; + createdAt: Date; + updatedAt: Date; + deletedAt: Date | null; +} + +export interface Request extends BaseModel { + name: string; + url: string; + body: string | null; + method: string; +} + +export interface Workspace extends BaseModel { + name: string; + description: string; +} + +export function useWorkspaces(): UseQueryResult { + return useQuery('workspaces', async () => { + const workspaces = (await invoke('workspaces')) as Workspace[]; + return workspaces.map(convertDates); + }); +} + +export function useRequests(): UseQueryResult { + return useQuery('requests', async () => { + const workspaces = (await invoke('workspaces')) as Workspace[]; + const requests = (await invoke('requests', { workspaceId: workspaces[0].id })) as Request[]; + return requests.map(convertDates); + }); +} + +export function useWorkspace(): UseQueryResult<{ workspace: Workspace; requests: Request[] }> { + return useQuery('workspace', async () => { + const workspaces = (await invoke('workspaces')) as Workspace[]; + const requests = (await invoke('requests', { workspaceId: workspaces[0].id })) as Request[]; + return { + workspace: convertDates(workspaces[0]), + requests: requests.map(convertDates), + }; + }); +} + +function convertDates(m: T): T { + return { + ...m, + createdAt: convertDate(m.createdAt), + updatedAt: convertDate(m.updatedAt), + deletedAt: m.deletedAt ? convertDate(m.deletedAt) : null, + }; +} + +function convertDate(d: string | Date): Date { + const date = new Date(d); + const userTimezoneOffset = date.getTimezoneOffset() * 60000; + return new Date(date.getTime() - userTimezoneOffset); +} diff --git a/src-web/main.tsx b/src-web/main.tsx index ed45a463..052f74ea 100644 --- a/src-web/main.tsx +++ b/src-web/main.tsx @@ -8,6 +8,7 @@ import { invoke } from '@tauri-apps/api'; import { setTheme } from './lib/theme'; import './main.css'; +import { QueryClient, QueryClientProvider } from 'react-query'; setTheme(); @@ -15,12 +16,16 @@ await init(); greet(); await invoke('load_db'); +const queryClient = new QueryClient(); + ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render( - - - - - + + + + + + + , );