13 Commits

20 changed files with 692 additions and 609 deletions

18
doc/todo.md Normal file
View File

@ -0,0 +1,18 @@
## Definitely
* Switch to "process" provider for AWS credentials (much less hacky)
* Session timeout (plain duration, or activity-based?)
* ~Fix rehide behavior when new request comes in while old one is still being resolved~
* Additional hotkey configuration (approve/deny at the very least)
* Logging
* Icon
* Auto-updates
* SSH key handling
## Maybe
* Flatten error type hierarchy
* Rehide after terminal launch from locked
* Generalize Request across both credentials and terminal launch?
* Make hotkey configuration a little more tolerant of slight mistiming
* Distinguish between request that was denied and request that was canceled (e.g. due to error)

262
package-lock.json generated
View File

@ -1,12 +1,12 @@
{ {
"name": "creddy", "name": "creddy",
"version": "0.2.2", "version": "0.3.1",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "creddy", "name": "creddy",
"version": "0.2.2", "version": "0.3.1",
"dependencies": { "dependencies": {
"@tauri-apps/api": "^1.0.2", "@tauri-apps/api": "^1.0.2",
"daisyui": "^2.51.5" "daisyui": "^2.51.5"
@ -78,9 +78,9 @@
} }
}, },
"node_modules/@jridgewell/resolve-uri": { "node_modules/@jridgewell/resolve-uri": {
"version": "3.1.0", "version": "3.1.1",
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz",
"integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==",
"engines": { "engines": {
"node": ">=6.0.0" "node": ">=6.0.0"
} }
@ -99,19 +99,14 @@
"integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg=="
}, },
"node_modules/@jridgewell/trace-mapping": { "node_modules/@jridgewell/trace-mapping": {
"version": "0.3.18", "version": "0.3.19",
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.19.tgz",
"integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==", "integrity": "sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw==",
"dependencies": { "dependencies": {
"@jridgewell/resolve-uri": "3.1.0", "@jridgewell/resolve-uri": "^3.1.0",
"@jridgewell/sourcemap-codec": "1.4.14" "@jridgewell/sourcemap-codec": "^1.4.14"
} }
}, },
"node_modules/@jridgewell/trace-mapping/node_modules/@jridgewell/sourcemap-codec": {
"version": "1.4.14",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz",
"integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw=="
},
"node_modules/@nodelib/fs.scandir": { "node_modules/@nodelib/fs.scandir": {
"version": "2.1.5", "version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@ -390,9 +385,9 @@
"integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==" "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg=="
}, },
"node_modules/autoprefixer": { "node_modules/autoprefixer": {
"version": "10.4.14", "version": "10.4.15",
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.14.tgz", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.15.tgz",
"integrity": "sha512-FQzyfOsTlwVzjHxKEqRIAdJx9niO6VCBCoEwax/VLSoQF29ggECcPuBqUMZ+u8jCZOPSy8b8/8KnuFbp0SaFZQ==", "integrity": "sha512-KCuPB8ZCIqFdA4HwKXsvz7j6gvSDNhDP7WnUjBleRkKjPdvCmHFuQ77ocavI8FT6NdvlBnE2UFr2H4Mycn8Vew==",
"funding": [ "funding": [
{ {
"type": "opencollective", "type": "opencollective",
@ -401,11 +396,15 @@
{ {
"type": "tidelift", "type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/autoprefixer" "url": "https://tidelift.com/funding/github/npm/autoprefixer"
},
{
"type": "github",
"url": "https://github.com/sponsors/ai"
} }
], ],
"dependencies": { "dependencies": {
"browserslist": "^4.21.5", "browserslist": "^4.21.10",
"caniuse-lite": "^1.0.30001464", "caniuse-lite": "^1.0.30001520",
"fraction.js": "^4.2.0", "fraction.js": "^4.2.0",
"normalize-range": "^0.1.2", "normalize-range": "^0.1.2",
"picocolors": "^1.0.0", "picocolors": "^1.0.0",
@ -455,9 +454,9 @@
} }
}, },
"node_modules/browserslist": { "node_modules/browserslist": {
"version": "4.21.9", "version": "4.21.10",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.9.tgz", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.10.tgz",
"integrity": "sha512-M0MFoZzbUrRU4KNfCrDLnvyE7gub+peetoTid3TBIqtunaDJyXlwhakT+/VkvSXcfIzFfK/nkCs4nmyTmxdNSg==", "integrity": "sha512-bipEBdZfVH5/pwrvqc+Ub0kUPVfGUhlKxbvfD+z1BDnPEO/X98ruXGA1WP5ASpAFKan7Qr6j736IacbZQuAlKQ==",
"funding": [ "funding": [
{ {
"type": "opencollective", "type": "opencollective",
@ -473,9 +472,9 @@
} }
], ],
"dependencies": { "dependencies": {
"caniuse-lite": "^1.0.30001503", "caniuse-lite": "^1.0.30001517",
"electron-to-chromium": "^1.4.431", "electron-to-chromium": "^1.4.477",
"node-releases": "^2.0.12", "node-releases": "^2.0.13",
"update-browserslist-db": "^1.0.11" "update-browserslist-db": "^1.0.11"
}, },
"bin": { "bin": {
@ -494,9 +493,9 @@
} }
}, },
"node_modules/caniuse-lite": { "node_modules/caniuse-lite": {
"version": "1.0.30001515", "version": "1.0.30001534",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001515.tgz", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001534.tgz",
"integrity": "sha512-eEFDwUOZbE24sb+Ecsx3+OvNETqjWIdabMy52oOkIgcUtAsQifjUG9q4U9dgTHJM2mfk4uEPxc0+xuFdJ629QA==", "integrity": "sha512-vlPVrhsCS7XaSh2VvWluIQEzVhefrUQcEsQWSS5A5V+dM07uv1qHeQzAOTGIMy9i3e9bH15+muvI/UHojVgS/Q==",
"funding": [ "funding": [
{ {
"type": "opencollective", "type": "opencollective",
@ -675,9 +674,9 @@
"integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==" "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA=="
}, },
"node_modules/electron-to-chromium": { "node_modules/electron-to-chromium": {
"version": "1.4.455", "version": "1.4.520",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.455.tgz", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.520.tgz",
"integrity": "sha512-8tgdX0Odl24LtmLwxotpJCVjIndN559AvaOtd67u+2mo+IDsgsTF580NB+uuDCqsHw8yFg53l5+imFV9Fw3cbA==" "integrity": "sha512-Frfus2VpYADsrh1lB3v/ft/WVFlVzOIm+Q0p7U7VqHI6qr7NWHYKe+Wif3W50n7JAFoBsWVsoU0+qDks6WQ60g=="
}, },
"node_modules/esbuild": { "node_modules/esbuild": {
"version": "0.15.18", "version": "0.15.18",
@ -1045,9 +1044,9 @@
} }
}, },
"node_modules/fast-glob": { "node_modules/fast-glob": {
"version": "3.3.0", "version": "3.3.1",
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.0.tgz", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz",
"integrity": "sha512-ChDuvbOypPuNjO8yIDf36x7BlZX1smcUMTTcyoIjycexOxd6DFsKsg21qVBzEmr3G7fUKIRy2/psii+CIUt7FA==", "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==",
"dependencies": { "dependencies": {
"@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.stat": "^2.0.2",
"@nodelib/fs.walk": "^1.2.3", "@nodelib/fs.walk": "^1.2.3",
@ -1095,15 +1094,15 @@
} }
}, },
"node_modules/fraction.js": { "node_modules/fraction.js": {
"version": "4.2.0", "version": "4.3.6",
"resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz", "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.6.tgz",
"integrity": "sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==", "integrity": "sha512-n2aZ9tNfYDwaHhvFTkhFErqOMIb8uyzSQ+vGJBjZyanAKZVbGUQ1sngfk9FdkBw7G26O7AgNjLcecLffD1c7eg==",
"engines": { "engines": {
"node": "*" "node": "*"
}, },
"funding": { "funding": {
"type": "patreon", "type": "patreon",
"url": "https://www.patreon.com/infusion" "url": "https://github.com/sponsors/rawify"
} }
}, },
"node_modules/fs.realpath": { "node_modules/fs.realpath": {
@ -1112,9 +1111,9 @@
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="
}, },
"node_modules/fsevents": { "node_modules/fsevents": {
"version": "2.3.2", "version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
"hasInstallScript": true, "hasInstallScript": true,
"optional": true, "optional": true,
"os": [ "os": [
@ -1201,9 +1200,9 @@
} }
}, },
"node_modules/is-core-module": { "node_modules/is-core-module": {
"version": "2.12.1", "version": "2.13.0",
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.1.tgz", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.0.tgz",
"integrity": "sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg==", "integrity": "sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==",
"dependencies": { "dependencies": {
"has": "^1.0.3" "has": "^1.0.3"
}, },
@ -1239,9 +1238,9 @@
} }
}, },
"node_modules/jiti": { "node_modules/jiti": {
"version": "1.19.1", "version": "1.20.0",
"resolved": "https://registry.npmjs.org/jiti/-/jiti-1.19.1.tgz", "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.20.0.tgz",
"integrity": "sha512-oVhqoRDaBXf7sjkll95LHVS6Myyyb1zaunVwk4Z0+WPSW4gjS0pl01zYKHScTuyEhQsFxV5L4DR5r+YqSyqyyg==", "integrity": "sha512-3TV69ZbrvV6U5DfQimop50jE9Dl6J8O1ja1dvBbMba/sZ3YBEQqJ2VZRoQPVnhlzjNtU1vaXRZVrVjU4qtm8yA==",
"bin": { "bin": {
"jiti": "bin/jiti.js" "jiti": "bin/jiti.js"
} }
@ -1435,9 +1434,9 @@
} }
}, },
"node_modules/postcss": { "node_modules/postcss": {
"version": "8.4.25", "version": "8.4.29",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.25.tgz", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.29.tgz",
"integrity": "sha512-7taJ/8t2av0Z+sQEvNzCkpDynl0tX3uJMCODi6nT3PfASC7dYCWV9aQ+uiCf+KBD4SEFcu+GvJdGdwzQ6OSjCw==", "integrity": "sha512-cbI+jaqIeu/VGqXEarWkRCCffhjgXc0qjBtXpqJhTBohMUjUQnbBr0xqX3vEKudc4iviTewcJo5ajcec5+wdJw==",
"funding": [ "funding": [
{ {
"type": "opencollective", "type": "opencollective",
@ -1597,11 +1596,11 @@
} }
}, },
"node_modules/resolve": { "node_modules/resolve": {
"version": "1.22.2", "version": "1.22.4",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.4.tgz",
"integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==", "integrity": "sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg==",
"dependencies": { "dependencies": {
"is-core-module": "^2.11.0", "is-core-module": "^2.13.0",
"path-parse": "^1.0.7", "path-parse": "^1.0.7",
"supports-preserve-symlinks-flag": "^1.0.0" "supports-preserve-symlinks-flag": "^1.0.0"
}, },
@ -1682,9 +1681,9 @@
"dev": true "dev": true
}, },
"node_modules/sucrase": { "node_modules/sucrase": {
"version": "3.32.0", "version": "3.34.0",
"resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.32.0.tgz", "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.34.0.tgz",
"integrity": "sha512-ydQOU34rpSyj2TGyz4D2p8rbktIOZ8QY9s+DGLvFU1i5pWJE8vkpruCjGCMHsdXwnD7JDcS+noSwM/a7zyNFDQ==", "integrity": "sha512-70/LQEZ07TEcxiU2dz51FKaE6hCTWC6vr7FOk3Gr0U60C3shtAN+H+BFr9XlYe5xqf3RA8nrc+VIwzCfnxuXJw==",
"dependencies": { "dependencies": {
"@jridgewell/gen-mapping": "^0.3.2", "@jridgewell/gen-mapping": "^0.3.2",
"commander": "^4.0.0", "commander": "^4.0.0",
@ -1723,21 +1722,21 @@
} }
}, },
"node_modules/svelte-hmr": { "node_modules/svelte-hmr": {
"version": "0.15.2", "version": "0.15.3",
"resolved": "https://registry.npmjs.org/svelte-hmr/-/svelte-hmr-0.15.2.tgz", "resolved": "https://registry.npmjs.org/svelte-hmr/-/svelte-hmr-0.15.3.tgz",
"integrity": "sha512-q/bAruCvFLwvNbeE1x3n37TYFb3mTBJ6TrCq6p2CoFbSTNhDE9oAtEfpy+wmc9So8AG0Tja+X0/mJzX9tSfvIg==", "integrity": "sha512-41snaPswvSf8TJUhlkoJBekRrABDXDMdpNpT2tfHIv4JuhgvHqLMhEPGtaQn0BmbNSTkuz2Ed20DF2eHw0SmBQ==",
"dev": true, "dev": true,
"engines": { "engines": {
"node": "^12.20 || ^14.13.1 || >= 16" "node": "^12.20 || ^14.13.1 || >= 16"
}, },
"peerDependencies": { "peerDependencies": {
"svelte": "^3.19.0 || ^4.0.0-next.0" "svelte": "^3.19.0 || ^4.0.0"
} }
}, },
"node_modules/tailwindcss": { "node_modules/tailwindcss": {
"version": "3.3.2", "version": "3.3.3",
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.3.2.tgz", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.3.3.tgz",
"integrity": "sha512-9jPkMiIBXvPc2KywkraqsUfbfj+dHDb+JPWtSJa9MLFdrPyazI7q6WX2sUrm7R9eVR7qqv3Pas7EvQFzxKnI6w==", "integrity": "sha512-A0KgSkef7eE4Mf+nKJ83i75TMyq8HqY3qmFIJSWy8bNt0v1lG7jUcpGpoTFxAwYcWOphcTBLPPJg+bDfhDf52w==",
"dependencies": { "dependencies": {
"@alloc/quick-lru": "^5.2.0", "@alloc/quick-lru": "^5.2.0",
"arg": "^5.0.2", "arg": "^5.0.2",
@ -1759,7 +1758,6 @@
"postcss-load-config": "^4.0.1", "postcss-load-config": "^4.0.1",
"postcss-nested": "^6.0.1", "postcss-nested": "^6.0.1",
"postcss-selector-parser": "^6.0.11", "postcss-selector-parser": "^6.0.11",
"postcss-value-parser": "^4.2.0",
"resolve": "^1.22.2", "resolve": "^1.22.2",
"sucrase": "^3.32.0" "sucrase": "^3.32.0"
}, },
@ -1909,9 +1907,9 @@
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
}, },
"node_modules/yaml": { "node_modules/yaml": {
"version": "2.3.1", "version": "2.3.2",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.1.tgz", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.2.tgz",
"integrity": "sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ==", "integrity": "sha512-N/lyzTPaJasoDmfV7YTrYCI0G/3ivm/9wdG0aHuheKowWQwGTsK0Eoiw6utmzAnI6pkJa0DUVygvp3spqqEKXg==",
"engines": { "engines": {
"node": ">= 14" "node": ">= 14"
} }
@ -1948,9 +1946,9 @@
} }
}, },
"@jridgewell/resolve-uri": { "@jridgewell/resolve-uri": {
"version": "3.1.0", "version": "3.1.1",
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz",
"integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==" "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA=="
}, },
"@jridgewell/set-array": { "@jridgewell/set-array": {
"version": "1.1.2", "version": "1.1.2",
@ -1963,19 +1961,12 @@
"integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg=="
}, },
"@jridgewell/trace-mapping": { "@jridgewell/trace-mapping": {
"version": "0.3.18", "version": "0.3.19",
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.19.tgz",
"integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==", "integrity": "sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw==",
"requires": { "requires": {
"@jridgewell/resolve-uri": "3.1.0", "@jridgewell/resolve-uri": "^3.1.0",
"@jridgewell/sourcemap-codec": "1.4.14" "@jridgewell/sourcemap-codec": "^1.4.14"
},
"dependencies": {
"@jridgewell/sourcemap-codec": {
"version": "1.4.14",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz",
"integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw=="
}
} }
}, },
"@nodelib/fs.scandir": { "@nodelib/fs.scandir": {
@ -2128,12 +2119,12 @@
"integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==" "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg=="
}, },
"autoprefixer": { "autoprefixer": {
"version": "10.4.14", "version": "10.4.15",
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.14.tgz", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.15.tgz",
"integrity": "sha512-FQzyfOsTlwVzjHxKEqRIAdJx9niO6VCBCoEwax/VLSoQF29ggECcPuBqUMZ+u8jCZOPSy8b8/8KnuFbp0SaFZQ==", "integrity": "sha512-KCuPB8ZCIqFdA4HwKXsvz7j6gvSDNhDP7WnUjBleRkKjPdvCmHFuQ77ocavI8FT6NdvlBnE2UFr2H4Mycn8Vew==",
"requires": { "requires": {
"browserslist": "^4.21.5", "browserslist": "^4.21.10",
"caniuse-lite": "^1.0.30001464", "caniuse-lite": "^1.0.30001520",
"fraction.js": "^4.2.0", "fraction.js": "^4.2.0",
"normalize-range": "^0.1.2", "normalize-range": "^0.1.2",
"picocolors": "^1.0.0", "picocolors": "^1.0.0",
@ -2168,13 +2159,13 @@
} }
}, },
"browserslist": { "browserslist": {
"version": "4.21.9", "version": "4.21.10",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.9.tgz", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.10.tgz",
"integrity": "sha512-M0MFoZzbUrRU4KNfCrDLnvyE7gub+peetoTid3TBIqtunaDJyXlwhakT+/VkvSXcfIzFfK/nkCs4nmyTmxdNSg==", "integrity": "sha512-bipEBdZfVH5/pwrvqc+Ub0kUPVfGUhlKxbvfD+z1BDnPEO/X98ruXGA1WP5ASpAFKan7Qr6j736IacbZQuAlKQ==",
"requires": { "requires": {
"caniuse-lite": "^1.0.30001503", "caniuse-lite": "^1.0.30001517",
"electron-to-chromium": "^1.4.431", "electron-to-chromium": "^1.4.477",
"node-releases": "^2.0.12", "node-releases": "^2.0.13",
"update-browserslist-db": "^1.0.11" "update-browserslist-db": "^1.0.11"
} }
}, },
@ -2184,9 +2175,9 @@
"integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==" "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA=="
}, },
"caniuse-lite": { "caniuse-lite": {
"version": "1.0.30001515", "version": "1.0.30001534",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001515.tgz", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001534.tgz",
"integrity": "sha512-eEFDwUOZbE24sb+Ecsx3+OvNETqjWIdabMy52oOkIgcUtAsQifjUG9q4U9dgTHJM2mfk4uEPxc0+xuFdJ629QA==" "integrity": "sha512-vlPVrhsCS7XaSh2VvWluIQEzVhefrUQcEsQWSS5A5V+dM07uv1qHeQzAOTGIMy9i3e9bH15+muvI/UHojVgS/Q=="
}, },
"chokidar": { "chokidar": {
"version": "3.5.3", "version": "3.5.3",
@ -2305,9 +2296,9 @@
"integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==" "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA=="
}, },
"electron-to-chromium": { "electron-to-chromium": {
"version": "1.4.455", "version": "1.4.520",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.455.tgz", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.520.tgz",
"integrity": "sha512-8tgdX0Odl24LtmLwxotpJCVjIndN559AvaOtd67u+2mo+IDsgsTF580NB+uuDCqsHw8yFg53l5+imFV9Fw3cbA==" "integrity": "sha512-Frfus2VpYADsrh1lB3v/ft/WVFlVzOIm+Q0p7U7VqHI6qr7NWHYKe+Wif3W50n7JAFoBsWVsoU0+qDks6WQ60g=="
}, },
"esbuild": { "esbuild": {
"version": "0.15.18", "version": "0.15.18",
@ -2485,9 +2476,9 @@
"integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw=="
}, },
"fast-glob": { "fast-glob": {
"version": "3.3.0", "version": "3.3.1",
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.0.tgz", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz",
"integrity": "sha512-ChDuvbOypPuNjO8yIDf36x7BlZX1smcUMTTcyoIjycexOxd6DFsKsg21qVBzEmr3G7fUKIRy2/psii+CIUt7FA==", "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==",
"requires": { "requires": {
"@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.stat": "^2.0.2",
"@nodelib/fs.walk": "^1.2.3", "@nodelib/fs.walk": "^1.2.3",
@ -2528,9 +2519,9 @@
} }
}, },
"fraction.js": { "fraction.js": {
"version": "4.2.0", "version": "4.3.6",
"resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz", "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.6.tgz",
"integrity": "sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==" "integrity": "sha512-n2aZ9tNfYDwaHhvFTkhFErqOMIb8uyzSQ+vGJBjZyanAKZVbGUQ1sngfk9FdkBw7G26O7AgNjLcecLffD1c7eg=="
}, },
"fs.realpath": { "fs.realpath": {
"version": "1.0.0", "version": "1.0.0",
@ -2538,9 +2529,9 @@
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="
}, },
"fsevents": { "fsevents": {
"version": "2.3.2", "version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
"optional": true "optional": true
}, },
"function-bind": { "function-bind": {
@ -2605,9 +2596,9 @@
} }
}, },
"is-core-module": { "is-core-module": {
"version": "2.12.1", "version": "2.13.0",
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.1.tgz", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.0.tgz",
"integrity": "sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg==", "integrity": "sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==",
"requires": { "requires": {
"has": "^1.0.3" "has": "^1.0.3"
} }
@ -2631,9 +2622,9 @@
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="
}, },
"jiti": { "jiti": {
"version": "1.19.1", "version": "1.20.0",
"resolved": "https://registry.npmjs.org/jiti/-/jiti-1.19.1.tgz", "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.20.0.tgz",
"integrity": "sha512-oVhqoRDaBXf7sjkll95LHVS6Myyyb1zaunVwk4Z0+WPSW4gjS0pl01zYKHScTuyEhQsFxV5L4DR5r+YqSyqyyg==" "integrity": "sha512-3TV69ZbrvV6U5DfQimop50jE9Dl6J8O1ja1dvBbMba/sZ3YBEQqJ2VZRoQPVnhlzjNtU1vaXRZVrVjU4qtm8yA=="
}, },
"kleur": { "kleur": {
"version": "4.1.5", "version": "4.1.5",
@ -2767,9 +2758,9 @@
"integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==" "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg=="
}, },
"postcss": { "postcss": {
"version": "8.4.25", "version": "8.4.29",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.25.tgz", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.29.tgz",
"integrity": "sha512-7taJ/8t2av0Z+sQEvNzCkpDynl0tX3uJMCODi6nT3PfASC7dYCWV9aQ+uiCf+KBD4SEFcu+GvJdGdwzQ6OSjCw==", "integrity": "sha512-cbI+jaqIeu/VGqXEarWkRCCffhjgXc0qjBtXpqJhTBohMUjUQnbBr0xqX3vEKudc4iviTewcJo5ajcec5+wdJw==",
"requires": { "requires": {
"nanoid": "^3.3.6", "nanoid": "^3.3.6",
"picocolors": "^1.0.0", "picocolors": "^1.0.0",
@ -2847,11 +2838,11 @@
} }
}, },
"resolve": { "resolve": {
"version": "1.22.2", "version": "1.22.4",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.4.tgz",
"integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==", "integrity": "sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg==",
"requires": { "requires": {
"is-core-module": "^2.11.0", "is-core-module": "^2.13.0",
"path-parse": "^1.0.7", "path-parse": "^1.0.7",
"supports-preserve-symlinks-flag": "^1.0.0" "supports-preserve-symlinks-flag": "^1.0.0"
} }
@ -2898,9 +2889,9 @@
"dev": true "dev": true
}, },
"sucrase": { "sucrase": {
"version": "3.32.0", "version": "3.34.0",
"resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.32.0.tgz", "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.34.0.tgz",
"integrity": "sha512-ydQOU34rpSyj2TGyz4D2p8rbktIOZ8QY9s+DGLvFU1i5pWJE8vkpruCjGCMHsdXwnD7JDcS+noSwM/a7zyNFDQ==", "integrity": "sha512-70/LQEZ07TEcxiU2dz51FKaE6hCTWC6vr7FOk3Gr0U60C3shtAN+H+BFr9XlYe5xqf3RA8nrc+VIwzCfnxuXJw==",
"requires": { "requires": {
"@jridgewell/gen-mapping": "^0.3.2", "@jridgewell/gen-mapping": "^0.3.2",
"commander": "^4.0.0", "commander": "^4.0.0",
@ -2923,16 +2914,16 @@
"dev": true "dev": true
}, },
"svelte-hmr": { "svelte-hmr": {
"version": "0.15.2", "version": "0.15.3",
"resolved": "https://registry.npmjs.org/svelte-hmr/-/svelte-hmr-0.15.2.tgz", "resolved": "https://registry.npmjs.org/svelte-hmr/-/svelte-hmr-0.15.3.tgz",
"integrity": "sha512-q/bAruCvFLwvNbeE1x3n37TYFb3mTBJ6TrCq6p2CoFbSTNhDE9oAtEfpy+wmc9So8AG0Tja+X0/mJzX9tSfvIg==", "integrity": "sha512-41snaPswvSf8TJUhlkoJBekRrABDXDMdpNpT2tfHIv4JuhgvHqLMhEPGtaQn0BmbNSTkuz2Ed20DF2eHw0SmBQ==",
"dev": true, "dev": true,
"requires": {} "requires": {}
}, },
"tailwindcss": { "tailwindcss": {
"version": "3.3.2", "version": "3.3.3",
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.3.2.tgz", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.3.3.tgz",
"integrity": "sha512-9jPkMiIBXvPc2KywkraqsUfbfj+dHDb+JPWtSJa9MLFdrPyazI7q6WX2sUrm7R9eVR7qqv3Pas7EvQFzxKnI6w==", "integrity": "sha512-A0KgSkef7eE4Mf+nKJ83i75TMyq8HqY3qmFIJSWy8bNt0v1lG7jUcpGpoTFxAwYcWOphcTBLPPJg+bDfhDf52w==",
"requires": { "requires": {
"@alloc/quick-lru": "^5.2.0", "@alloc/quick-lru": "^5.2.0",
"arg": "^5.0.2", "arg": "^5.0.2",
@ -2954,7 +2945,6 @@
"postcss-load-config": "^4.0.1", "postcss-load-config": "^4.0.1",
"postcss-nested": "^6.0.1", "postcss-nested": "^6.0.1",
"postcss-selector-parser": "^6.0.11", "postcss-selector-parser": "^6.0.11",
"postcss-value-parser": "^4.2.0",
"resolve": "^1.22.2", "resolve": "^1.22.2",
"sucrase": "^3.32.0" "sucrase": "^3.32.0"
} }
@ -3028,9 +3018,9 @@
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
}, },
"yaml": { "yaml": {
"version": "2.3.1", "version": "2.3.2",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.1.tgz", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.2.tgz",
"integrity": "sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ==" "integrity": "sha512-N/lyzTPaJasoDmfV7YTrYCI0G/3ivm/9wdG0aHuheKowWQwGTsK0Eoiw6utmzAnI6pkJa0DUVygvp3spqqEKXg=="
} }
} }
} }

View File

@ -1,6 +1,6 @@
{ {
"name": "creddy", "name": "creddy",
"version": "0.2.3", "version": "0.3.3",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",
"build": "vite build", "build": "vite build",

617
src-tauri/Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
[package] [package]
name = "creddy" name = "creddy"
version = "0.2.3" version = "0.3.3"
description = "A friendly AWS credentials manager" description = "A friendly AWS credentials manager"
authors = ["Joseph Montanaro"] authors = ["Joseph Montanaro"]
license = "" license = ""

View File

@ -43,6 +43,7 @@ pub fn run() -> tauri::Result<()> {
ipc::get_config, ipc::get_config,
ipc::save_config, ipc::save_config,
ipc::launch_terminal, ipc::launch_terminal,
ipc::get_setup_errors,
]) ])
.setup(|app| rt::block_on(setup(app))) .setup(|app| rt::block_on(setup(app)))
.build(tauri::generate_context!())? .build(tauri::generate_context!())?
@ -75,15 +76,33 @@ pub async fn connect_db() -> Result<SqlitePool, SetupError> {
async fn setup(app: &mut App) -> Result<(), Box<dyn Error>> { async fn setup(app: &mut App) -> Result<(), Box<dyn Error>> {
APP.set(app.handle()).unwrap(); APP.set(app.handle()).unwrap();
let is_first_launch = config::get_or_create_db_path()?.exists(); // get_or_create_db_path doesn't create the actual db file, just the directory
let is_first_launch = !config::get_or_create_db_path()?.exists();
let pool = connect_db().await?; let pool = connect_db().await?;
let conf = AppConfig::load(&pool).await?; let mut setup_errors: Vec<String> = vec![];
let conf = match AppConfig::load(&pool).await {
Ok(c) => c,
Err(SetupError::ConfigParseError(_)) => {
setup_errors.push(
"Could not load configuration from database. Reverting to defaults.".into()
);
AppConfig::default()
},
err => err?,
};
let session = Session::load(&pool).await?; let session = Session::load(&pool).await?;
let srv = Server::new(conf.listen_addr, conf.listen_port, app.handle()).await?; let srv = Server::new(conf.listen_addr, conf.listen_port, app.handle()).await?;
config::set_auto_launch(conf.start_on_login)?; config::set_auto_launch(conf.start_on_login)?;
config::register_hotkeys(&conf.hotkeys)?; if let Err(_e) = config::set_auto_launch(conf.start_on_login) {
setup_errors.push("Error: Failed to manage autolaunch.".into());
}
if let Err(e) = config::register_hotkeys(&conf.hotkeys) {
setup_errors.push(format!("{e}"));
}
// if session is empty, this is probably the first launch, so don't autohide // if session is empty, this is probably the first launch, so don't autohide
if !conf.start_minimized || is_first_launch { if !conf.start_minimized || is_first_launch {
app.get_window("main") app.get_window("main")
@ -91,7 +110,7 @@ async fn setup(app: &mut App) -> Result<(), Box<dyn Error>> {
.show()?; .show()?;
} }
let state = AppState::new(conf, session, srv, pool); let state = AppState::new(conf, session, srv, pool, setup_errors);
app.manage(state); app.manage(state);
Ok(()) Ok(())
} }

View File

@ -23,6 +23,7 @@ use crate::errors::*;
pub fn parser() -> Command<'static> { pub fn parser() -> Command<'static> {
Command::new("creddy") Command::new("creddy")
.version(env!("CARGO_PKG_VERSION"))
.about("A friendly AWS credentials manager") .about("A friendly AWS credentials manager")
.subcommand( .subcommand(
Command::new("run") Command::new("run")
@ -98,7 +99,7 @@ pub fn exec(args: &ArgMatches) -> Result<(), CliError> {
let name: OsString = cmd_name.into(); let name: OsString = cmd_name.into();
Err(ExecError::NotFound(name).into()) Err(ExecError::NotFound(name).into())
} }
e => Err(ExecError::ExecutionFailed(e).into()), _ => Err(ExecError::ExecutionFailed(e).into()),
} }
} }

View File

@ -22,9 +22,10 @@ use serde::{Serialize, Serializer, ser::SerializeMap};
pub trait ErrorPopup { pub trait ErrorPopup {
fn error_popup(self, title: &str); fn error_popup(self, title: &str);
fn error_popup_nowait(self, title: &str);
} }
impl<E: Error> ErrorPopup for Result<(), E> { impl<E: std::fmt::Display> ErrorPopup for Result<(), E> {
fn error_popup(self, title: &str) { fn error_popup(self, title: &str) {
if let Err(e) = self { if let Err(e) = self {
let (tx, rx) = mpsc::channel(); let (tx, rx) = mpsc::channel();
@ -35,6 +36,14 @@ impl<E: Error> ErrorPopup for Result<(), E> {
rx.recv().unwrap(); rx.recv().unwrap();
} }
} }
fn error_popup_nowait(self, title: &str) {
if let Err(e) = self {
MessageDialogBuilder::new(title, format!("{e}"))
.kind(MessageDialogKind::Error)
.show(|_| {})
}
}
} }
@ -116,6 +125,8 @@ pub enum SendResponseError {
NotFound, NotFound,
#[error("The specified request was already closed by the client")] #[error("The specified request was already closed by the client")]
Abandoned, Abandoned,
#[error("A response has already been received for the specified request")]
Fulfilled,
#[error("Could not renew AWS sesssion: {0}")] #[error("Could not renew AWS sesssion: {0}")]
SessionRenew(#[from] GetSessionError), SessionRenew(#[from] GetSessionError),
} }

View File

@ -85,3 +85,9 @@ pub async fn save_config(config: AppConfig, app_state: State<'_, AppState>) -> R
pub async fn launch_terminal(base: bool) -> Result<(), LaunchTerminalError> { pub async fn launch_terminal(base: bool) -> Result<(), LaunchTerminalError> {
terminal::launch(base).await terminal::launch(base).await
} }
#[tauri::command]
pub async fn get_setup_errors(app_state: State<'_, AppState>) -> Result<Vec<String>, ()> {
Ok(app_state.setup_errors.clone())
}

View File

@ -10,7 +10,7 @@ use tokio::net::{
TcpStream, TcpStream,
}; };
use tokio::io::{AsyncReadExt, AsyncWriteExt}; use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::sync::oneshot; use tokio::sync::oneshot::{self, Sender, Receiver};
use tokio::time::sleep; use tokio::time::sleep;
use tauri::{AppHandle, Manager}; use tauri::{AppHandle, Manager};
@ -23,24 +23,55 @@ use crate::ipc::{Request, Approval};
use crate::state::AppState; use crate::state::AppState;
#[derive(Debug)]
pub struct RequestWaiter {
pub rehide_after: bool,
pub sender: Option<Sender<Approval>>,
}
impl RequestWaiter {
pub fn notify(&mut self, approval: Approval) -> Result<(), SendResponseError> {
let chan = self.sender
.take()
.ok_or(SendResponseError::Fulfilled)?;
chan.send(approval)
.map_err(|_| SendResponseError::Abandoned)
}
}
struct Handler { struct Handler {
request_id: u64, request_id: u64,
stream: TcpStream, stream: TcpStream,
receiver: Option<oneshot::Receiver<Approval>>, rehide_after: bool,
receiver: Option<Receiver<Approval>>,
app: AppHandle, app: AppHandle,
} }
impl Handler { impl Handler {
async fn new(stream: TcpStream, app: AppHandle) -> Self { async fn new(stream: TcpStream, app: AppHandle) -> Result<Self, HandlerError> {
let state = app.state::<AppState>(); let state = app.state::<AppState>();
// determine whether we should re-hide the window after handling this request
let is_currently_visible = app.get_window("main")
.ok_or(HandlerError::NoMainWindow)?
.is_visible()?;
let rehide_after = state.current_rehide_status()
.await
.unwrap_or(!is_currently_visible);
let (chan_send, chan_recv) = oneshot::channel(); let (chan_send, chan_recv) = oneshot::channel();
let request_id = state.register_request(chan_send).await; let waiter = RequestWaiter {rehide_after, sender: Some(chan_send)};
Handler { let request_id = state.register_request(waiter).await;
let handler = Handler {
request_id, request_id,
stream, stream,
rehide_after,
receiver: Some(chan_recv), receiver: Some(chan_recv),
app app
} };
Ok(handler)
} }
async fn handle(mut self) { async fn handle(mut self) {
@ -62,7 +93,7 @@ impl Handler {
let req = Request {id: self.request_id, clients, base}; let req = Request {id: self.request_id, clients, base};
self.app.emit_all("credentials-request", &req)?; self.app.emit_all("credentials-request", &req)?;
let starting_visibility = self.show_window()?; self.show_window()?;
match self.wait_for_response().await? { match self.wait_for_response().await? {
Approval::Approved => { Approval::Approved => {
@ -94,9 +125,11 @@ impl Handler {
}; };
sleep(delay).await; sleep(delay).await;
if !starting_visibility && state.req_count().await == 0 { if self.rehide_after && state.req_count().await == 1 {
let window = self.app.get_window("main").ok_or(HandlerError::NoMainWindow)?; self.app
window.hide()?; .get_window("main")
.ok_or(HandlerError::NoMainWindow)?
.hide()?;
} }
Ok(()) Ok(())
@ -143,15 +176,14 @@ impl Handler {
false false
} }
fn show_window(&self) -> Result<bool, HandlerError> { fn show_window(&self) -> Result<(), HandlerError> {
let window = self.app.get_window("main").ok_or(HandlerError::NoMainWindow)?; let window = self.app.get_window("main").ok_or(HandlerError::NoMainWindow)?;
let starting_visibility = window.is_visible()?; if !window.is_visible()? {
if !starting_visibility {
window.unminimize()?; window.unminimize()?;
window.show()?; window.show()?;
} }
window.set_focus()?; window.set_focus()?;
Ok(starting_visibility) Ok(())
} }
async fn wait_for_response(&mut self) -> Result<Approval, HandlerError> { async fn wait_for_response(&mut self) -> Result<Approval, HandlerError> {
@ -231,12 +263,12 @@ impl Server {
loop { loop {
match listener.accept().await { match listener.accept().await {
Ok((stream, _)) => { Ok((stream, _)) => {
let handler = Handler::new(stream, app_handle.app_handle()).await; match Handler::new(stream, app_handle.app_handle()).await {
rt::spawn(handler.handle()); Ok(handler) => { rt::spawn(handler.handle()); }
Err(e) => { eprintln!("Error handling request: {e}"); }
}
}, },
Err(e) => { Err(e) => { eprintln!("Error accepting connection: {e}"); }
eprintln!("Error accepting connection: {e}");
}
} }
} }
} }

View File

@ -2,7 +2,6 @@ use std::collections::{HashMap, HashSet};
use std::time::Duration; use std::time::Duration;
use tokio::{ use tokio::{
sync::oneshot::Sender,
sync::RwLock, sync::RwLock,
time::sleep, time::sleep,
}; };
@ -20,7 +19,7 @@ use crate::{config, config::AppConfig};
use crate::ipc::{self, Approval}; use crate::ipc::{self, Approval};
use crate::clientinfo::Client; use crate::clientinfo::Client;
use crate::errors::*; use crate::errors::*;
use crate::server::Server; use crate::server::{Server, RequestWaiter};
#[derive(Debug)] #[derive(Debug)]
@ -28,22 +27,31 @@ pub struct AppState {
pub config: RwLock<AppConfig>, pub config: RwLock<AppConfig>,
pub session: RwLock<Session>, pub session: RwLock<Session>,
pub request_count: RwLock<u64>, pub request_count: RwLock<u64>,
pub open_requests: RwLock<HashMap<u64, Sender<ipc::Approval>>>, pub waiting_requests: RwLock<HashMap<u64, RequestWaiter>>,
pub pending_terminal_request: RwLock<bool>, pub pending_terminal_request: RwLock<bool>,
pub bans: RwLock<std::collections::HashSet<Option<Client>>>, pub bans: RwLock<std::collections::HashSet<Option<Client>>>,
// setup_errors is never modified and so doesn't need to be wrapped in RwLock
pub setup_errors: Vec<String>,
server: RwLock<Server>, server: RwLock<Server>,
pool: sqlx::SqlitePool, pool: sqlx::SqlitePool,
} }
impl AppState { impl AppState {
pub fn new(config: AppConfig, session: Session, server: Server, pool: SqlitePool) -> AppState { pub fn new(
config: AppConfig,
session: Session,
server: Server,
pool: SqlitePool,
setup_errors: Vec<String>,
) -> AppState {
AppState { AppState {
config: RwLock::new(config), config: RwLock::new(config),
session: RwLock::new(session), session: RwLock::new(session),
request_count: RwLock::new(0), request_count: RwLock::new(0),
open_requests: RwLock::new(HashMap::new()), waiting_requests: RwLock::new(HashMap::new()),
pending_terminal_request: RwLock::new(false), pending_terminal_request: RwLock::new(false),
bans: RwLock::new(HashSet::new()), bans: RwLock::new(HashSet::new()),
setup_errors,
server: RwLock::new(server), server: RwLock::new(server),
pool, pool,
} }
@ -84,26 +92,33 @@ impl AppState {
Ok(()) Ok(())
} }
pub async fn register_request(&self, chan: Sender<ipc::Approval>) -> u64 { pub async fn register_request(&self, waiter: RequestWaiter) -> u64 {
let count = { let count = {
let mut c = self.request_count.write().await; let mut c = self.request_count.write().await;
*c += 1; *c += 1;
c c
}; };
let mut open_requests = self.open_requests.write().await; let mut waiting_requests = self.waiting_requests.write().await;
open_requests.insert(*count, chan); // `count` is the request id waiting_requests.insert(*count, waiter); // `count` is the request id
*count *count
} }
pub async fn unregister_request(&self, id: u64) { pub async fn unregister_request(&self, id: u64) {
let mut open_requests = self.open_requests.write().await; let mut waiting_requests = self.waiting_requests.write().await;
open_requests.remove(&id); waiting_requests.remove(&id);
} }
pub async fn req_count(&self) -> usize { pub async fn req_count(&self) -> usize {
let open_requests = self.open_requests.read().await; let waiting_requests = self.waiting_requests.read().await;
open_requests.len() waiting_requests.len()
}
pub async fn current_rehide_status(&self) -> Option<bool> {
// since all requests that are pending at a given time should have the same
// value for rehide_after, it doesn't matter which one we use
let waiting_requests = self.waiting_requests.read().await;
waiting_requests.iter().next().map(|(_id, w)| w.rehide_after)
} }
pub async fn send_response(&self, response: ipc::RequestResponse) -> Result<(), SendResponseError> { pub async fn send_response(&self, response: ipc::RequestResponse) -> Result<(), SendResponseError> {
@ -112,14 +127,11 @@ impl AppState {
session.renew_if_expired().await?; session.renew_if_expired().await?;
} }
let mut open_requests = self.open_requests.write().await; let mut waiting_requests = self.waiting_requests.write().await;
let chan = open_requests waiting_requests
.remove(&response.id) .get_mut(&response.id)
.ok_or(SendResponseError::NotFound) .ok_or(SendResponseError::NotFound)?
?; .notify(response.approval)
chan.send(response.approval)
.map_err(|_e| SendResponseError::Abandoned)
} }
pub async fn add_ban(&self, client: Option<Client>) { pub async fn add_ban(&self, client: Option<Client>) {

View File

@ -8,7 +8,7 @@
}, },
"package": { "package": {
"productName": "creddy", "productName": "creddy",
"version": "0.2.3" "version": "0.3.3"
}, },
"tauri": { "tauri": {
"allowlist": { "allowlist": {

View File

@ -28,7 +28,12 @@ listen('launch-terminal-request', async (tauriEvent) => {
// else, session is unlocked, so do nothing // else, session is unlocked, so do nothing
// (although we shouldn't even get the event in that case) // (although we shouldn't even get the event in that case)
} }
}) });
invoke('get_setup_errors')
.then(errs => {
$appState.setupErrors = errs.map(e => ({msg: e, show: true}));
});
acceptRequest(); acceptRequest();
</script> </script>

View File

@ -8,6 +8,7 @@ export let appState = writable({
currentRequest: null, currentRequest: null,
pendingRequests: queue(), pendingRequests: queue(),
credentialStatus: 'locked', credentialStatus: 'locked',
setupErrors: [],
}); });

View File

@ -1,113 +1,42 @@
<script> <script>
export let color = 'base-content'; export let thickness = 8;
export let thickness = '2px';
let classes = ''; let classes = '';
export { classes as class }; export { classes as class };
const colorVars = { const radius = (100 - thickness) / 2;
'primary': 'p', // the px are fake, but we need them to satisfy css calc()
'primary-focus': 'pf', const circumference = `${2 * Math.PI * radius}px`;
'primary-content': 'pc',
'secondary': 's',
'secondary-focus': 'sf',
'secondary-content': 'sc',
'accent': 'a',
'accent-focus': 'af',
'accent-content': 'ac',
'neutral': 'n',
'neutral-focus': 'nf',
'neutral-content': 'nc',
'base-100': 'b1',
'base-200': 'b2',
'base-300': 'b3',
'base-content': 'bc',
'info': 'in',
'info-content': 'inc',
'success': 'su',
'success-content': 'suc',
'warning': 'wa',
'warning-content': 'wac',
'error': 'er',
'error-content': 'erc',
}
let arcStyle = `border-width: ${thickness};`;
arcStyle += `border-color: hsl(var(--${colorVars[color]})) transparent transparent transparent;`;
</script> </script>
<style>
#spinner {
position: relative;
animation: spin; <svg
animation-duration: 1.5s; style:--circumference={circumference}
animation-iteration-count: infinite; class={classes}
animation-timing-function: linear; viewBox="0 0 100 100"
stroke="currentColor"
>
<circle cx="50" cy="50" r={radius} stroke-width={thickness} />
</svg>
<style>
circle {
fill: transparent;
stroke-dasharray: var(--circumference);
transform: rotate(-90deg);
transform-origin: center;
animation: chase 3s infinite,
spin 1.5s linear infinite;
}
@keyframes chase {
0% { stroke-dashoffset: calc(-1 * var(--circumference)); }
50% { stroke-dashoffset: calc(-2 * var(--circumference)); }
100% { stroke-dashoffset: calc(-3 * var(--circumference)); }
} }
@keyframes spin { @keyframes spin {
50% { transform: rotate(225deg); } 50% { transform: rotate(135deg); }
100% { transform: rotate(360deg); } 100% { transform: rotate(270deg); }
} }
</style>
.arc {
position: absolute;
top: 0;
left: 0;
border-radius: 9999px;
}
.arc-top {
transform: rotate(-45deg);
}
.arc-right {
animation: spin-right;
animation-duration: 3s;
animation-iteration-count: infinite;
}
.arc-bottom {
animation: spin-bottom;
animation-duration: 3s;
animation-iteration-count: infinite;
}
.arc-left {
animation: spin-left;
animation-duration: 3s;
animation-iteration-count: infinite;
}
@keyframes spin-top {
0% { transform: rotate(-45deg); }
50% { transform: rotate(315deg); }
100% { transform: rotate(-45deg); }
}
@keyframes spin-right {
0% { transform: rotate(45deg); }
50% { transform: rotate(315deg); }
100% { transform: rotate(405deg); }
}
@keyframes spin-bottom {
0% { transform: rotate(135deg); }
50% { transform: rotate(315deg); }
100% { transform: rotate(495deg); }
}
@keyframes spin-left {
0% { transform: rotate(225deg); }
50% { transform: rotate(315deg); }
100% { transform: rotate(585deg); }
}
</style>
<div id="spinner" class="w-6 h-6 {classes}">
<div class="arc arc-top w-full h-full" style={arcStyle}></div>
<div class="arc arc-right w-full h-full" style={arcStyle}></div>
<div class="arc arc-bottom w-full h-full" style={arcStyle}></div>
<div class="arc arc-left w-full h-full" style={arcStyle}></div>
</div>

View File

@ -7,38 +7,49 @@
const id = Math.random().toString().slice(2); const id = Math.random().toString().slice(2);
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher();
const MODIFIERS = new Set(['Alt', 'AltGraph', 'Control', 'Fn', 'FnLock', 'Meta', 'Shift', 'Super', ]);
let listening = false; let listening = false;
let keysPressed = [];
function addModifiers(event) {
// add modifier key if it isn't already present
if (MODIFIERS.has(event.key) && keysPressed.indexOf(event.key) === -1) {
keysPressed.push(event.key);
}
}
function addMainKey(event) {
if (!MODIFIERS.has(event.key)) {
keysPressed.push(event.key);
value.keys = keysPressed.join('+');
dispatch('update', {value});
event.preventDefault();
event.stopPropagation();
unlisten();
}
}
function listen() { function listen() {
// don't re-listen if we already are // don't re-listen if we already are
if (listening) return; if (listening) return;
listening = true; listening = true;
window.addEventListener('keyup', setKeybind, {once: true}); window.addEventListener('keydown', addModifiers);
window.addEventListener('keyup', addMainKey);
// setTimeout avoids reacting to the click event that we are currently processing // setTimeout avoids reacting to the click event that we are currently processing
setTimeout(() => window.addEventListener('click', cancel, {once: true}), 0); setTimeout(() => window.addEventListener('click', unlisten), 0);
} }
function setKeybind(event) { function unlisten() {
console.log(event);
let keys = [];
if (event.ctrlKey) keys.push('ctrl');
if (event.altKey) keys.push('alt');
if (event.metaKey) keys.push('meta');
if (event.shiftKey) keys.push('shift');
keys.push(event.key);
value.keys = keys.join('+');
dispatch('update', {value});
listening = false; listening = false;
window.removeEventListener('click', cancel, {once: true}); keysPressed = [];
event.preventDefault(); window.removeEventListener('keydown', addModifiers);
event.stopPropagation(); window.removeEventListener('keyup', addMainKey);
} window.removeEventListener('click', unlisten);
function cancel() {
listening = false;
window.removeEventListener('keyup', setKeybind, {once: true});
} }
</script> </script>

View File

@ -79,8 +79,8 @@
<input type="password" placeholder="Re-enter passphrase" bind:value={confirmPassphrase} class="input input-bordered" on:change={confirm} /> <input type="password" placeholder="Re-enter passphrase" bind:value={confirmPassphrase} class="input input-bordered" on:change={confirm} />
<button type="submit" class="btn btn-primary"> <button type="submit" class="btn btn-primary">
{#if saving} {#if saving }
<Spinner class="w-5 h-5" color="primary-content" thickness="2px"/> <Spinner class="w-5 h-5" thickness="12"/>
{:else} {:else}
Submit Submit
{/if} {/if}

View File

@ -51,4 +51,17 @@
{/if} {/if}
{/await} {/await}
</div> </div>
</div> </div>
{#if $appState.setupErrors.some(e => e.show)}
<div class="toast">
{#each $appState.setupErrors as error}
{#if error.show}
<div class="alert alert-error shadow-lg">
{error.msg}
<button class="btn btn-sm btn-alert-error" on:click={() => error.show = false}>Ok</button>
</div>
{/if}
{/each}
</div>
{/if}

View File

@ -1,11 +1,6 @@
<script context="module">
import { type } from '@tauri-apps/api/os';
const osType = await type();
</script>
<script> <script>
import { invoke } from '@tauri-apps/api/tauri'; import { invoke } from '@tauri-apps/api/tauri';
import { type } from '@tauri-apps/api/os';
import { appState } from '../lib/state.js'; import { appState } from '../lib/state.js';
import Nav from '../ui/Nav.svelte'; import Nav from '../ui/Nav.svelte';
@ -30,6 +25,9 @@
$appState.config = await invoke('get_config'); $appState.config = await invoke('get_config');
} }
} }
let osType = null;
type().then(t => osType = t);
</script> </script>

View File

@ -76,7 +76,7 @@
<button type="submit" class="btn btn-primary"> <button type="submit" class="btn btn-primary">
{#if saving} {#if saving}
<Spinner class="w-5 h-5" color="primary-content" thickness="2px"/> <Spinner class="w-5 h-5" thickness="12"/>
{:else} {:else}
Submit Submit
{/if} {/if}