├── .editorconfig ├── .eslintrc.js ├── .github ├── dependabot.yml └── workflows │ ├── cd.yaml │ ├── release_build_linux.yaml │ ├── release_build_macos.yaml │ ├── release_build_windows.yaml │ └── release_notifications.yaml ├── .gitignore ├── .pre-commit-config.yaml ├── .prettierignore ├── LICENSE ├── README.md ├── RELEASE.md ├── app-icon.png ├── bumpversion.sh ├── index.html ├── package-lock.json ├── package.json ├── public ├── beiboot-logo-dark.svg ├── beiboot-logo-light.png └── logo.svg ├── rollout.sh ├── screenshot.png ├── src-tauri ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── build.rs ├── icons │ ├── 128x128.png │ ├── 128x128@2x.png │ ├── 32x32.png │ ├── 32x32.svg │ ├── Square107x107Logo.png │ ├── Square142x142Logo.png │ ├── Square150x150Logo.png │ ├── Square284x284Logo.png │ ├── Square30x30Logo.png │ ├── Square310x310Logo.png │ ├── Square44x44Logo.png │ ├── Square71x71Logo.png │ ├── Square89x89Logo.png │ ├── StoreLogo.png │ ├── icon.icns │ ├── icon.ico │ └── icon.png ├── src │ ├── connection.rs │ ├── connection │ │ └── ghostunnel.rs │ ├── heartbeat.rs │ ├── lib.rs │ ├── main.rs │ └── util.rs ├── tauri.conf.json └── tests │ └── connectors.rs ├── src ├── App.vue ├── auth │ └── keycloak.ts ├── beibootctl.d.ts ├── beibootctl.ts ├── components │ ├── ClusterTable.vue │ └── Home.vue ├── layouts │ └── default │ │ ├── Default.vue │ │ └── NoSidebar.vue ├── main.d.ts ├── main.ts ├── plugins │ ├── index.d.ts │ ├── index.ts │ ├── vuetify.d.ts │ ├── vuetify.ts │ ├── webfontloader.d.ts │ └── webfontloader.ts ├── router │ ├── index.d.ts │ └── index.ts ├── store │ ├── app.d.ts │ ├── app.ts │ ├── index.d.ts │ └── index.ts ├── styles │ └── settings.scss ├── views │ ├── ClusterCreate.vue │ ├── Clusters.vue │ ├── Home.vue │ └── Login.vue └── vite-env.d.ts ├── tsconfig.json ├── tsconfig.node.json ├── tsconfig.tsbuildinfo ├── updater.json ├── vite.config.d.ts └── vite.config.ts /.editorconfig: -------------------------------------------------------------------------------- 1 | [*.{js,jsx,ts,tsx,vue}] 2 | indent_style = space 3 | indent_size = 2 4 | trim_trailing_whitespace = true 5 | insert_final_newline = true 6 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | node: true, 5 | }, 6 | extends: [ 7 | 'plugin:vue/vue3-essential', 8 | 'eslint:recommended', 9 | '@vue/eslint-config-typescript', 10 | ], 11 | rules: { 12 | 'vue/multi-word-component-names': 'off', 13 | }, 14 | } 15 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "cargo" # See documentation for possible values 9 | directory: "src-tauri/" # Location of package manifests 10 | assignees: 11 | - "tobifroe" 12 | schedule: 13 | interval: "weekly" 14 | 15 | - package-ecosystem: "npm" 16 | directory: "/" 17 | assignees: 18 | - "tobifroe" 19 | schedule: 20 | interval: "weekly" 21 | -------------------------------------------------------------------------------- /.github/workflows/cd.yaml: -------------------------------------------------------------------------------- 1 | name: "test-and-build-on-pr" 2 | on: 3 | push: 4 | branches: 5 | - main 6 | pull_request: 7 | branches: 8 | - main 9 | 10 | jobs: 11 | test: 12 | strategy: 13 | fail-fast: false 14 | 15 | runs-on: ubuntu-20.04 16 | steps: 17 | - uses: actions/checkout@v3 18 | - name: Create dist dir 19 | run: mkdir dist 20 | - name: setup node 21 | uses: actions/setup-node@v3 22 | with: 23 | node-version: 16 24 | - name: install Rust stable 25 | uses: dtolnay/rust-toolchain@stable 26 | - name: install dependencies 27 | run: | 28 | sudo apt-get update 29 | sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.0-dev libappindicator3-dev librsvg2-dev patchelf 30 | - name: Lint 31 | uses: actions-rs/cargo@v1 32 | with: 33 | command: clippy 34 | args: --manifest-path ./src-tauri/Cargo.toml 35 | - name: Execute tests 36 | uses: actions-rs/cargo@v1 37 | with: 38 | command: test 39 | args: --manifest-path ./src-tauri/Cargo.toml 40 | - uses: taiki-e/install-action@cargo-llvm-cov 41 | - name: Collect Coverage 42 | run: cd src-tauri && cargo llvm-cov --all-features --workspace --lcov --output-path lcov.info 43 | - name: Coveralls 44 | uses: coverallsapp/github-action@v2 45 | 46 | 47 | build-debug: 48 | if: ${{ github.event.label.name == 'testbuild' }} 49 | needs: test 50 | strategy: 51 | fail-fast: false 52 | matrix: 53 | platform: [ubuntu-20.04, windows-2022, macos-latest] # TODO add all platforms 54 | runs-on: ${{ matrix.platform }} 55 | steps: 56 | - uses: actions/checkout@v3 57 | - name: setup node 58 | uses: actions/setup-node@v3 59 | with: 60 | node-version: 16 61 | - name: install Rust stable 62 | uses: dtolnay/rust-toolchain@stable 63 | - name: install dependencies (ubuntu only) 64 | if: matrix.platform == 'ubuntu-20.04' 65 | run: | 66 | sudo apt-get update 67 | sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.0-dev libappindicator3-dev librsvg2-dev patchelf 68 | - name: install app dependencies and build with debug 69 | run: npm install && npm run deploy-debug 70 | - name: Upload Linux debug artifact 71 | uses: actions/upload-artifact@v3 72 | if: matrix.platform == 'ubuntu-20.04' 73 | with: 74 | name: beiboot-desktop-${{ runner.os }}-${{ runner.arch }}-debug 75 | path: | 76 | src-tauri/target/debug/bundle/appimage/getdeck-desktop_0.1.0_amd64.AppImage 77 | /src-tauri/target/debug/bundle/deb/getdeck-desktop_0.1.0_amd64.deb 78 | retention-days: 5 79 | - name: Upload debug artifact 80 | uses: actions/upload-artifact@v3 81 | if: matrix.platform != 'ubuntu-20.04' 82 | with: 83 | name: beiboot-desktop-${{ runner.os }}-${{ runner.arch }}-debug 84 | path: | 85 | src-tauri/target/debug/bundle/ 86 | retention-days: 5 87 | 88 | build: 89 | if: ${{ github.event.label.name == 'testbuild' }} 90 | needs: test 91 | strategy: 92 | fail-fast: false 93 | matrix: 94 | platform: [ubuntu-20.04, windows-2022, macos-latest] # TODO add all platforms 95 | 96 | runs-on: ${{ matrix.platform }} 97 | steps: 98 | - uses: actions/checkout@v3 99 | - name: setup node 100 | uses: actions/setup-node@v3 101 | with: 102 | node-version: 16 103 | - name: install Rust stable 104 | uses: dtolnay/rust-toolchain@stable 105 | - name: install dependencies (ubuntu only) 106 | if: matrix.platform == 'ubuntu-20.04' 107 | run: | 108 | sudo apt-get update 109 | sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.0-dev libappindicator3-dev librsvg2-dev patchelf 110 | - name: build for prod 111 | run: npm install && npm run deploy 112 | - name: Upload Linux prod artifact 113 | uses: actions/upload-artifact@v3 114 | if: matrix.platform == 'ubuntu-20.04' 115 | with: 116 | name: beiboot-desktop-${{ runner.os }}-${{ runner.arch }} 117 | path: | 118 | src-tauri/target/release/bundle/appimage/getdeck-desktop_0.1.0_amd64.AppImage 119 | /src-tauri/target/release/bundle/deb/getdeck-desktop_0.1.0_amd64.deb 120 | retention-days: 5 121 | - name: Upload prod Artifact 122 | uses: actions/upload-artifact@v3 123 | if: matrix.platform != 'ubuntu-20.04' 124 | with: 125 | name: beiboot-desktop-${{ runner.os }}-${{ runner.arch }} 126 | path: | 127 | src-tauri/target/release/bundle/ 128 | retention-days: 5 129 | -------------------------------------------------------------------------------- /.github/workflows/release_build_linux.yaml: -------------------------------------------------------------------------------- 1 | name: "release-build-linux" 2 | on: 3 | push: 4 | tags: 5 | - "*.*.*" 6 | 7 | jobs: 8 | build: 9 | strategy: 10 | fail-fast: false 11 | 12 | runs-on: ubuntu-20.04 13 | steps: 14 | - uses: actions/checkout@v3 15 | - name: Set Tauri signature private key 16 | run: echo "TAURI_PRIVATE_KEY=${{ secrets.TAURI_PRIVATE_KEY }}" >> $GITHUB_ENV 17 | - name: Set Tauri signature private key password 18 | run: echo "TAURI_KEY_PASSWORD=${{ secrets.TAURI_KEY_PASSWORD }}" >> $GITHUB_ENV 19 | - name: setup node 20 | uses: actions/setup-node@v3 21 | with: 22 | node-version: 16 23 | - name: install Rust stable 24 | uses: dtolnay/rust-toolchain@stable 25 | - name: install dependencies (ubuntu only) 26 | run: | 27 | sudo apt-get update 28 | sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.0-dev libappindicator3-dev librsvg2-dev patchelf 29 | - name: build 30 | run: npm install && npm run deploy 31 | - name: Attach files to release 32 | uses: softprops/action-gh-release@v1 33 | with: 34 | files: | 35 | src-tauri/target/release/bundle/appimage/getdeck-desktop_${{ github.ref_name }}_amd64.AppImage 36 | src-tauri/target/release/bundle/appimage/getdeck-desktop_${{ github.ref_name }}_amd64.AppImage.tar.gz 37 | src-tauri/target/release/bundle/appimage/getdeck-desktop_${{ github.ref_name }}_amd64.AppImage.tar.gz.sig 38 | src-tauri/target/release/bundle/deb/getdeck-desktop_${{ github.ref_name }}_amd64.deb 39 | 40 | -------------------------------------------------------------------------------- /.github/workflows/release_build_macos.yaml: -------------------------------------------------------------------------------- 1 | name: "release-build-macos" 2 | on: 3 | push: 4 | tags: 5 | - "*.*.*" 6 | 7 | jobs: 8 | build: 9 | strategy: 10 | fail-fast: false 11 | 12 | runs-on: macos-latest 13 | steps: 14 | - uses: actions/checkout@v3 15 | - name: Set Tauri signature private key 16 | run: echo "TAURI_PRIVATE_KEY=${{ secrets.TAURI_PRIVATE_KEY }}" >> $GITHUB_ENV 17 | - name: Set Tauri signature private key password 18 | run: echo "TAURI_KEY_PASSWORD=${{ secrets.TAURI_KEY_PASSWORD }}" >> $GITHUB_ENV 19 | - name: setup node 20 | uses: actions/setup-node@v3 21 | with: 22 | node-version: 16 23 | - name: install Rust stable 24 | uses: dtolnay/rust-toolchain@stable 25 | - name: build 26 | env: 27 | APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }} 28 | APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }} 29 | APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} 30 | APPLE_ID: ${{ secrets.APPLE_ID }} 31 | APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }} 32 | run: npm install && npm run deploy 33 | - name: Attach files to release 34 | uses: softprops/action-gh-release@v1 35 | with: 36 | files: | 37 | src-tauri/target/release/bundle/dmg/Getdeck-Desktop_${{ github.ref_name }}_x64.dmg 38 | src-tauri/target/release/bundle/macos/Getdeck-Desktop.app 39 | src-tauri/target/release/bundle/macos/Getdeck-Desktop.app.tar.gz 40 | src-tauri/target/release/bundle/macos/Getdeck-Desktop.app.tar.gz.sig 41 | 42 | 43 | -------------------------------------------------------------------------------- /.github/workflows/release_build_windows.yaml: -------------------------------------------------------------------------------- 1 | name: "release-build-windows" 2 | on: 3 | push: 4 | tags: 5 | - "*.*.*" 6 | 7 | jobs: 8 | build: 9 | strategy: 10 | fail-fast: false 11 | 12 | runs-on: windows-2022 13 | steps: 14 | - uses: actions/checkout@v3 15 | - name: Set Tauri Signature Private Key 16 | run: echo "TAURI_PRIVATE_KEY=${{ secrets.TAURI_PRIVATE_KEY }}" | Out-File -FilePath $env:GITHUB_ENV 17 | - name: Set Tauri Sig Password 18 | run: echo "TAURI_KEY_PASSWORD=${{ secrets.TAURI_KEY_PASSWORD }}" | Out-File -FilePath $env:GITHUB_ENV 19 | - name: setup node 20 | uses: actions/setup-node@v3 21 | with: 22 | node-version: 16 23 | - name: install Rust stable 24 | uses: dtolnay/rust-toolchain@stable 25 | - name: build 26 | run: npm install && npm run deploy 27 | - name: Attach files to release 28 | uses: softprops/action-gh-release@v1 29 | with: 30 | files: | 31 | src-tauri/target/release/bundle/msi/getdeck-desktop_${{ github.ref_name }}_x64_en-US.msi 32 | src-tauri/target/release/bundle/msi/getdeck-desktop_${{ github.ref_name }}_x64_en-US.msi.zip 33 | src-tauri/target/release/bundle/msi/getdeck-desktop_${{ github.ref_name }}_x64_en-US.msi.zip.sig 34 | -------------------------------------------------------------------------------- /.github/workflows/release_notifications.yaml: -------------------------------------------------------------------------------- 1 | name: Send mail on release 2 | 3 | on: 4 | workflow_dispatch: {} 5 | release: 6 | types: 7 | - published 8 | 9 | jobs: 10 | post-discord-announcement: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: Ilshidur/action-discord@0.3.2 14 | name: Send Discord Release Announcement 15 | # add random emoji to the message 16 | env: 17 | DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }} 18 | with: 19 | args: "Beiboot Desktop Version ${{ github.event.release.tag_name }} is out! 🚀 Here's the changelog: https://github.com/Getdeck/beiboot-desktop/releases/tag/${{ github.event.release.tag_name }}" 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | 26 | src-tauri/bin 27 | tsconfig.tsbuildinfo 28 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/pre-commit/mirrors-prettier 3 | rev: "cafd550" # Use the sha or tag you want to point at 4 | hooks: 5 | - id: prettier 6 | additional_dependencies: 7 | - prettier-plugin-svelte 8 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | 26 | src-tauri/binaries 27 | src-tauri/target 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | 11 | 12 | 13 | 14 | 21 | [![Contributors][contributors-shield]][contributors-url] 22 | [![Stargazers][stars-shield]][stars-url] 23 | [![Issues][issues-shield]][issues-url] 24 | [![MIT License][license-shield]][license-url] 25 | ![Coveralls branch](https://img.shields.io/coverallsCoverage/github/Getdeck/Getdeck-Desktop?branch=main&style=for-the-badge) 26 | 27 | 28 | 29 | 30 | 31 |
32 |
33 | 34 | Logo 35 | 36 | 37 |

Getdeck Desktop

38 | 39 |

40 | A tool suite to provide reproducible Kubernetes environments for development and testing. 41 |
42 | Install 43 | · 44 | Report Bug 45 | · 46 | Request Feature 47 |

48 |
49 | 50 | 51 | 52 | 53 |
54 | Table of Contents 55 |
    56 |
  1. 57 | About The Project 58 |
  2. 59 |
  3. 60 | Getting Started 61 | 65 |
  4. 66 |
  5. Usage
  6. 67 |
  7. Roadmap
  8. 68 |
  9. Contributing
  10. 69 |
  11. License
  12. 70 |
  13. Contact
  14. 71 |
  15. Acknowledgments
  16. 72 |
73 |
74 | 75 | 76 | 77 | 78 | ## About The Project 79 | 80 | [![Product Name Screen Shot][product-screenshot]](https://example.com) 81 | 82 |

(back to top)

83 | 84 | 85 | ## Getting Started 86 | ### Prerequisites 87 | In order to use Getdeck Desktop to connect to a remote cluster, [Docker Engine](https://docs.docker.com/engine/install/) needs to run on your machine. 88 | Running the AppImage on Linux requires [Fuse](https://docs.appimage.org/user-guide/troubleshooting/fuse.html). 89 | 90 | ### Installation 91 | Note: The tarballs attached to releases are update packages. 92 | 93 | 1. Download a version of Getdeck Desktop from the [releases page](https://github.com/Getdeck/Getdeck-Desktop/releases) that fits your operating system. Currently, we support Linux, MacOS and Windows. 94 | 2. Follow the installation process for your operating system. 95 | 3. After starting Getdeck Desktop, create an account. (It's free!) 96 | 4. You are now able to create a ephemeral, remote kubernetes cluster and connect your machine to it. 🚀 97 | 98 |

(back to top)

99 | 100 | 101 | 102 | ## Usage 103 | 104 | Getdeck Desktop allows you to create virtual, remote kubernetes clusters easily through the UI. 105 | First, create a new cluster through the UI via the `create` dialog. Pick a name, choose ports that should be available for you later and hit create. 106 | Once the cluster is ready, you will see a green `RUNNING` badge in the cluster list. 107 | Next, hit the connect icon to enable port forwardings between the remote virtual cluster and your machine. As soon as the connection is established, you 108 | can copy the path to the kubeconfig, set the `KUBECONFIG` env variable in your favorite shell and use any tool that can talk to the Kubernetes API server!🚀 109 | 110 |

(back to top)

111 | 112 | 113 | 114 | 115 | ## Roadmap 116 | 117 | - [ ] Shelf clusters 118 | - [ ] ... 119 | - [ ] ... 120 | 121 | See the [open issues](https://github.com/Getdeck/Getdeck-Desktop/issues) for a full list of proposed features (and known issues). 122 | 123 |

(back to top)

124 | 125 | 126 | 127 | 128 | ## Contributing 129 | 130 | Contributions are what make the open source community such an amazing place to learn, inspire, and create. Any contributions you make are **greatly appreciated**. 131 | 132 | If you have a suggestion that would make this better, please fork the repo and create a pull request. You can also simply open an issue with the tag "enhancement". 133 | Don't forget to give the project a star! Thanks again! 134 | 135 | 1. Fork the Project 136 | 2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`) 137 | 3. Commit your Changes (`git commit -m 'Add some AmazingFeature'`) 138 | 4. Push to the Branch (`git push origin feature/AmazingFeature`) 139 | 5. Open a Pull Request 140 | 141 |

(back to top)

142 | 143 | 144 | 145 | 146 | ## License 147 | 148 | Distributed under the Apache 2.0 License. See `LICENSE` for more information. 149 | 150 |

(back to top)

151 | 152 | 153 | 154 | ## Contact 155 | 156 | If you're stuck or just want to have a chat, please join the [Team Blueshoe Discord](https://discord.gg/qb5gjmzr)! 157 | 158 |

(back to top)

159 | 160 | 161 | 162 | 163 | [contributors-shield]: https://img.shields.io/github/contributors/Getdeck/Getdeck-Desktop.svg?style=for-the-badge 164 | [contributors-url]: https://github.com/Getdeck/Getdeck-Desktop/graphs/contributors 165 | [stars-shield]: https://img.shields.io/github/stars/Getdeck/Getdeck-Desktop.svg?style=for-the-badge 166 | [stars-url]: https://github.com/Getdeck/Getdeck-Desktop/stargazers 167 | [issues-shield]: https://img.shields.io/github/issues/Getdeck/Getdeck-Desktop.svg?style=for-the-badge 168 | [issues-url]: https://github.com/Getdeck/Getdeck-Desktop/issues 169 | [license-shield]: https://img.shields.io/github/license/Getdeck/Getdeck-Desktop.svg?style=for-the-badge 170 | [license-url]: https://github.com/Getdeck/Getdeck-Desktop/blob/main/LICENSE 171 | [product-screenshot]: screenshot.png 172 | -------------------------------------------------------------------------------- /RELEASE.md: -------------------------------------------------------------------------------- 1 | # Releases 2 | 3 | ## How to release a new version of Getdeck Desktop 4 | 5 | 1. Make sure you have `jq` and `toml-cli` installed and available in your path 6 | 2. From the project root, run `./bumpversion.sh $VERSION_NUMBER` and commit the result 7 | 3. Create a new tag with the version number that you are publishing 8 | 4. Push the tag and watch the CI/CD pipeline build bundles and create a new release on Github 9 | 5. Once the pipeline finishes (make sure every OS build went through smoothly and all files are present), 10 | run `./rollout.sh $VERSION_NUMBER` to adapt `updater.json`. 11 | 6. Commit the result, Tauri Updater will now notify users that new version is ready to download. 12 | -------------------------------------------------------------------------------- /app-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Getdeck/Getdeck-Desktop/289b8c3e66d97ed9a4d717745a243a5ad8bc763c/app-icon.png -------------------------------------------------------------------------------- /bumpversion.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # This script is used to bump the version of Getdeck Desktop in all relevant files. 4 | # This script requires toml-cli (https://github.com/gnprice/toml-cli) to work correctly. 5 | 6 | VERSION=$1 7 | 8 | if [ -z "$VERSION" ] 9 | then 10 | echo "Please provide a version number." 11 | exit 1 12 | fi 13 | 14 | jq --arg VERSION "$VERSION" '.version = $VERSION' package.json > package.json.tmp && mv package.json.tmp package.json 15 | jq --arg VERSION "$VERSION" '.package.version = $VERSION' src-tauri/tauri.conf.json > src-tauri/tauri.conf.json.tmp && mv src-tauri/tauri.conf.json.tmp src-tauri/tauri.conf.json 16 | toml set src-tauri/Cargo.toml package.version "$VERSION" > src-tauri/Cargo.toml.tmp && mv src-tauri/Cargo.toml.tmp src-tauri/Cargo.toml 17 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Welcome to Vuetify 3 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "beiboot-desktop", 3 | "private": true, 4 | "version": "0.1.2", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "tsc && vite build", 9 | "preview": "vite preview", 10 | "tauri": "cross-env NODE_ENV=debug tauri", 11 | "deploy": "tauri build", 12 | "deploy-debug": "cross-env NODE_ENV=debug tauri build --debug" 13 | }, 14 | "devDependencies": { 15 | "@babel/types": "^7.22.10", 16 | "@mdi/font": "7.2.96", 17 | "@tauri-apps/api": "^1.3.0", 18 | "@tauri-apps/cli": "^1.3.0", 19 | "@types/node": "^18.11.11", 20 | "@types/webfontloader": "^1.6.35", 21 | "@vitejs/plugin-vue": "^3.0.3", 22 | "@vue/eslint-config-typescript": "^11.0.3", 23 | "core-js": "^3.8.3", 24 | "cross-env": "^7.0.3", 25 | "eslint": "^8.22.0", 26 | "eslint-plugin-vue": "^9.3.0", 27 | "pinia": "^2.1.6", 28 | "prettier": "^2.8.8", 29 | "roboto-fontface": "*", 30 | "sass": "^1.66.1", 31 | "typescript": "^4.6.4", 32 | "vite": "^3.2.3", 33 | "vite-plugin-vuetify": "^1.0.0-alpha.12", 34 | "vue": "^3.2.13", 35 | "vue-router": "^4.0.0", 36 | "vuetify": "3.2.4", 37 | "webfontloader": "^1.0.0" 38 | }, 39 | "dependencies": { 40 | "@sentry/vue": "^7.55.2", 41 | "axios": "^1.2.5", 42 | "beiboot-api-client": "^0.1.4", 43 | "keycloak-js": "^21.0.1", 44 | "tauri-plugin-store-api": "github:tauri-apps/tauri-plugin-store" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /public/beiboot-logo-dark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 19 | 20 | 21 | 26 | 32 | 35 | 41 | 47 | 53 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 67 | 70 | 71 | 74 | 77 | 80 | 81 | 82 | 83 | 84 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /public/beiboot-logo-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Getdeck/Getdeck-Desktop/289b8c3e66d97ed9a4d717745a243a5ad8bc763c/public/beiboot-logo-light.png -------------------------------------------------------------------------------- /public/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 8 | 9 | 12 | 15 | 16 | 19 | 22 | 25 | 26 | 27 | 28 | 29 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /rollout.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # This script is used to rollout the new version of Getdeck Desktop via Tauri Updater. 4 | 5 | VERSION=$1 6 | 7 | if [ -z "$VERSION" ] 8 | then 9 | echo "Please provide a version number." 10 | exit 1 11 | fi 12 | 13 | MACOS_SIG=$(wget -q -O- https://github.com/Getdeck/Getdeck-Desktop/releases/download/$VERSION/Getdeck-Desktop.app.tar.gz.sig) 14 | LINUX_SIG=$(wget -q -O- https://github.com/Getdeck/Getdeck-Desktop/releases/download/$VERSION/getdeck-desktop_${VERSION}_amd64.AppImage.tar.gz.sig) 15 | WINDOWS_SIG=$(wget -q -O- https://github.com/Getdeck/Getdeck-Desktop/releases/download/$VERSION/getdeck-desktop_${VERSION}_x64_en-US.msi.zip.sig) 16 | 17 | MACOS_DOWNLOAD="https://github.com/Getdeck/Getdeck-Desktop/releases/download/$VERSION/Getdeck-Desktop.app.tar.gz" 18 | LINUX_DOWNLOAD="https://github.com/Getdeck/Getdeck-Desktop/releases/download/$VERSION/getdeck-desktop_${VERSION}_amd64.AppImage.tar.gz" 19 | WINDOWS_DOWNLOAD="https://github.com/Getdeck/Getdeck-Desktop/releases/download/$VERSION/getdeck-desktop_${VERSION}_x64_en-US.msi.zip" 20 | 21 | NOTES="Getdeck Desktop $VERSION is now available." 22 | 23 | 24 | jq --arg MACOS_SIG "$MACOS_SIG" --arg LINUX_SIG "$LINUX_SIG" --arg WINDOWS_SIG "$WINDOWS_SIG" '.platforms."darwin-x86_64".signature = $MACOS_SIG | 25 | .platforms."linux-x86_64".signature = $LINUX_SIG | 26 | .platforms."windows-x86_64".signature = $WINDOWS_SIG' updater.json > updater.json.tmp && mv updater.json.tmp updater.json 27 | 28 | jq --arg MACOS_DOWNLOAD "$MACOS_DOWNLOAD" --arg LINUX_DOWNLOAD "$LINUX_DOWNLOAD" --arg WINDOWS_DOWNLOAD "$WINDOWS_DOWNLOAD" '.platforms."darwin-x86_64".url = $MACOS_DOWNLOAD | 29 | .platforms."linux-x86_64".url = $LINUX_DOWNLOAD | 30 | .platforms."windows-x86_64".url = $WINDOWS_DOWNLOAD' updater.json > updater.json.tmp && mv updater.json.tmp updater.json 31 | 32 | jq --arg VERSION "$VERSION" '.version = $VERSION' updater.json > updater.json.tmp && mv updater.json.tmp updater.json 33 | jq --arg NOTES "$NOTES" '.notes = $NOTES' updater.json > updater.json.tmp && mv updater.json.tmp updater.json 34 | 35 | 36 | -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Getdeck/Getdeck-Desktop/289b8c3e66d97ed9a4d717745a243a5ad8bc763c/screenshot.png -------------------------------------------------------------------------------- /src-tauri/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | -------------------------------------------------------------------------------- /src-tauri/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "beiboot_desktop" 3 | version = "0.1.2" 4 | description = "Getdeck-Desktop allows to manage Getdeck Kubernetes clusters and connect to them from a local machine." 5 | authors = ["Tobias Frölich ", "Michael Schilonka "] 6 | license = "../LICENSE" 7 | repository = "https://github.com/Getdeck/Getdeck-Desktop/" 8 | default-run = "beiboot_desktop" 9 | edition = "2021" 10 | rust-version = "1.59" 11 | publish = false 12 | 13 | [build-dependencies] 14 | tauri-build = { version = "1.4.0", features = [] } 15 | 16 | [dependencies] 17 | serde_json = "1.0" 18 | serde = { version = "1.0", features = ["derive"] } 19 | tauri = { version = "1.3.0", features = ["process-command-api", "shell-open", "updater"] } 20 | tauri-plugin-store = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "dev" } 21 | strum = "0.25.0" 22 | strum_macros = "0.25.1" 23 | bollard = "0.14.0" 24 | futures = "0.3.28" 25 | tokio = "1.29.1" 26 | shellexpand = "3.1.0" 27 | futures-util = "0.3" 28 | tauri-plugin-oauth = "0.0.0-alpha.0" 29 | sentry = "0.31.5" 30 | tauri-plugin-localhost = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v1" } 31 | portpicker = "0.1" 32 | tokio-tungstenite = { version = "0.19.0", features = ["native-tls"] } 33 | httparse = "1.8.0" 34 | reqwest = "0.11.18" 35 | tokio-test = "0.4.2" 36 | mockito = "1.1.0" 37 | 38 | [features] 39 | # by default Tauri runs in production mode 40 | # when `tauri dev` runs it is executed with `cargo run --no-default-features` if `devPath` is an URL 41 | default = ["custom-protocol"] 42 | # this feature is used for production builds where `devPath` points to the filesystem 43 | # DO NOT remove this 44 | custom-protocol = ["tauri/custom-protocol"] 45 | -------------------------------------------------------------------------------- /src-tauri/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | tauri_build::build() 3 | } 4 | -------------------------------------------------------------------------------- /src-tauri/icons/128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Getdeck/Getdeck-Desktop/289b8c3e66d97ed9a4d717745a243a5ad8bc763c/src-tauri/icons/128x128.png -------------------------------------------------------------------------------- /src-tauri/icons/128x128@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Getdeck/Getdeck-Desktop/289b8c3e66d97ed9a4d717745a243a5ad8bc763c/src-tauri/icons/128x128@2x.png -------------------------------------------------------------------------------- /src-tauri/icons/32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Getdeck/Getdeck-Desktop/289b8c3e66d97ed9a4d717745a243a5ad8bc763c/src-tauri/icons/32x32.png -------------------------------------------------------------------------------- /src-tauri/icons/32x32.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | image/svg+xml 46 | 49 | 51 | 53 | 58 | 62 | 66 | 67 | 68 | 69 | 72 | 73 | -------------------------------------------------------------------------------- /src-tauri/icons/Square107x107Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Getdeck/Getdeck-Desktop/289b8c3e66d97ed9a4d717745a243a5ad8bc763c/src-tauri/icons/Square107x107Logo.png -------------------------------------------------------------------------------- /src-tauri/icons/Square142x142Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Getdeck/Getdeck-Desktop/289b8c3e66d97ed9a4d717745a243a5ad8bc763c/src-tauri/icons/Square142x142Logo.png -------------------------------------------------------------------------------- /src-tauri/icons/Square150x150Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Getdeck/Getdeck-Desktop/289b8c3e66d97ed9a4d717745a243a5ad8bc763c/src-tauri/icons/Square150x150Logo.png -------------------------------------------------------------------------------- /src-tauri/icons/Square284x284Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Getdeck/Getdeck-Desktop/289b8c3e66d97ed9a4d717745a243a5ad8bc763c/src-tauri/icons/Square284x284Logo.png -------------------------------------------------------------------------------- /src-tauri/icons/Square30x30Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Getdeck/Getdeck-Desktop/289b8c3e66d97ed9a4d717745a243a5ad8bc763c/src-tauri/icons/Square30x30Logo.png -------------------------------------------------------------------------------- /src-tauri/icons/Square310x310Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Getdeck/Getdeck-Desktop/289b8c3e66d97ed9a4d717745a243a5ad8bc763c/src-tauri/icons/Square310x310Logo.png -------------------------------------------------------------------------------- /src-tauri/icons/Square44x44Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Getdeck/Getdeck-Desktop/289b8c3e66d97ed9a4d717745a243a5ad8bc763c/src-tauri/icons/Square44x44Logo.png -------------------------------------------------------------------------------- /src-tauri/icons/Square71x71Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Getdeck/Getdeck-Desktop/289b8c3e66d97ed9a4d717745a243a5ad8bc763c/src-tauri/icons/Square71x71Logo.png -------------------------------------------------------------------------------- /src-tauri/icons/Square89x89Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Getdeck/Getdeck-Desktop/289b8c3e66d97ed9a4d717745a243a5ad8bc763c/src-tauri/icons/Square89x89Logo.png -------------------------------------------------------------------------------- /src-tauri/icons/StoreLogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Getdeck/Getdeck-Desktop/289b8c3e66d97ed9a4d717745a243a5ad8bc763c/src-tauri/icons/StoreLogo.png -------------------------------------------------------------------------------- /src-tauri/icons/icon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Getdeck/Getdeck-Desktop/289b8c3e66d97ed9a4d717745a243a5ad8bc763c/src-tauri/icons/icon.icns -------------------------------------------------------------------------------- /src-tauri/icons/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Getdeck/Getdeck-Desktop/289b8c3e66d97ed9a4d717745a243a5ad8bc763c/src-tauri/icons/icon.ico -------------------------------------------------------------------------------- /src-tauri/icons/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Getdeck/Getdeck-Desktop/289b8c3e66d97ed9a4d717745a243a5ad8bc763c/src-tauri/icons/icon.png -------------------------------------------------------------------------------- /src-tauri/src/connection.rs: -------------------------------------------------------------------------------- 1 | use std::{error::Error, fmt}; 2 | use serde::Deserialize; 3 | 4 | pub mod ghostunnel; 5 | 6 | pub fn get_connector_context<'a>(beiboot_name: &'a str, strategy: &str) -> ConnectorContext<'a> { 7 | let imp_strategy = match strategy { 8 | "GhostunnelDocker" => ghostunnel::GhostunnelDocker, 9 | _ => ghostunnel::GhostunnelDocker, 10 | }; 11 | ConnectorContext { 12 | connector: Box::new(imp_strategy), 13 | name: beiboot_name, 14 | } 15 | } 16 | 17 | #[derive(Debug)] 18 | pub struct ConnectError { 19 | details: String, 20 | } 21 | 22 | impl ConnectError { 23 | fn new(msg: &str) -> ConnectError { 24 | ConnectError { 25 | details: msg.to_string(), 26 | } 27 | } 28 | } 29 | 30 | impl fmt::Display for ConnectError { 31 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 32 | write!(f, "{}", self.details) 33 | } 34 | } 35 | 36 | impl Error for ConnectError { 37 | fn description(&self) -> &str { 38 | &self.details 39 | } 40 | } 41 | 42 | pub struct ConnectorContext<'a> { 43 | pub connector: Box, 44 | pub name: &'a str, 45 | } 46 | 47 | impl<'a> ConnectorContext<'a> { 48 | pub fn connect(&self, ports: &[PortMapping], mtls: &TLSFiles) -> Result<(), ConnectError> { 49 | println!("Connecting to Beiboot {}", self.name); 50 | let result = self.connector.establish(self.name, ports, mtls); 51 | println!("Connection to Beiboot {} established", self.name); 52 | result 53 | } 54 | pub fn disconnect(&self) -> Result<(), ConnectError> { 55 | println!("Disconnect preamble"); 56 | let result = self.connector.terminate(self.name); 57 | println!("Disconnect postamble"); 58 | result 59 | } 60 | pub fn check_running(&self) -> Result, ConnectError> { 61 | 62 | self.connector.check_running() 63 | } 64 | } 65 | 66 | #[derive(Deserialize)] 67 | pub struct PortMapping<'a> { 68 | pub target: u16, 69 | pub endpoint: &'a str, 70 | } 71 | 72 | pub struct TLSFiles<'a> { 73 | pub ca_cert_path: &'a str, 74 | pub client_key_path: &'a str, 75 | pub client_cert_path: &'a str, 76 | } 77 | 78 | pub trait Connector { 79 | fn establish( 80 | &self, 81 | name: &str, 82 | ports: &[PortMapping], 83 | mtls: &TLSFiles, 84 | ) -> Result<(), ConnectError>; 85 | fn check_running(&self) -> Result, ConnectError>; 86 | fn terminate(&self, name: &str) -> Result<(), ConnectError>; 87 | } 88 | 89 | #[cfg(test)] 90 | mod connection_tests { 91 | #[test] 92 | fn test_get_connector_context() { 93 | let connector = super::get_connector_context("test", "GhostunnelDocker"); 94 | assert_eq!("test", connector.name); 95 | let other = super::get_connector_context("other", "other"); 96 | assert_eq!("other", other.name); 97 | } 98 | 99 | #[test] 100 | fn test_connecterror() { 101 | let err = super::ConnectError::new("test"); 102 | assert_eq!("test", err.details); 103 | assert_eq!(format!("{}", err), "test"); 104 | } 105 | } -------------------------------------------------------------------------------- /src-tauri/src/connection/ghostunnel.rs: -------------------------------------------------------------------------------- 1 | use bollard::container::{Config, CreateContainerOptions, StartContainerOptions, ListContainersOptions, RemoveContainerOptions}; 2 | use bollard::service::{HostConfig, PortBinding, PortMap, RestartPolicy, RestartPolicyNameEnum}; 3 | use bollard::Docker; 4 | 5 | use std::collections::HashMap; 6 | use futures_util::TryStreamExt; 7 | 8 | use crate::connection::{Connector, PortMapping, TLSFiles}; 9 | 10 | use super::ConnectError; 11 | 12 | pub struct GhostunnelDocker; 13 | 14 | static GHOSTUNNEL_IMAGE: &str = "ghostunnel/ghostunnel:v1.7.1"; 15 | 16 | impl Connector for GhostunnelDocker { 17 | fn establish(&self, name: &str, ports: &[PortMapping], mtls: &TLSFiles) -> Result<(), ConnectError> { 18 | tokio::runtime::Builder::new_multi_thread() 19 | .enable_all() 20 | .build() 21 | .unwrap() 22 | .block_on(async move { 23 | let docker = match Docker::connect_with_socket_defaults() { 24 | Err(why) => return Err(ConnectError::new(format!("Docker error: {}", why).as_str())), 25 | Ok(docker) => docker, 26 | }; 27 | 28 | let version = docker.version().await.unwrap(); 29 | let platform = format!("linux/{}", version.arch.unwrap()); 30 | 31 | for port in ports { 32 | let container_name = format!( 33 | "getdeck-beiboot-{name}-{local_port}", 34 | name = name, 35 | local_port = port.target 36 | ); 37 | let fcmd = format!( 38 | "client --listen 0.0.0.0:{local_port} --unsafe-listen --target {endpoint} --cert /crt/client.crt --key /crt/client.key --cacert /crt/ca.crt", 39 | local_port = port.target, 40 | endpoint = port.endpoint 41 | ); 42 | let cmd: Vec<&str> = fcmd.split(' ').collect(); 43 | let options = Some(CreateContainerOptions { 44 | name: container_name.as_str(), 45 | platform: Some(&platform), 46 | }); 47 | let labels = HashMap::from([ 48 | ("beiboot.getdeck.dev/name", name) 49 | ]); 50 | 51 | let iport = port.target.to_string(); 52 | let exposed_ports = HashMap::from( 53 | [(iport.as_str(), HashMap::from([]))] 54 | ); 55 | 56 | let port_map: PortMap = HashMap::from( 57 | [ 58 | (iport.clone(), 59 | Some(vec![ 60 | PortBinding {host_ip: Some("127.0.0.1".to_string()), host_port: Some(iport.clone())} 61 | ]) 62 | ) 63 | ]); 64 | let cacrt = format!("{}:{}", mtls.ca_cert_path, "/crt/ca.crt"); 65 | let clientcrt = format!("{}:{}", mtls.client_cert_path, "/crt/client.crt"); 66 | let clientkey = format!("{}:{}", mtls.client_key_path, "/crt/client.key"); 67 | let bind_mounts = vec![cacrt, clientcrt, clientkey]; 68 | 69 | let hostconfig = HostConfig { 70 | auto_remove: Some(false), 71 | restart_policy: Some(RestartPolicy { name: Some(RestartPolicyNameEnum::UNLESS_STOPPED), maximum_retry_count: None}), 72 | binds: Some(bind_mounts), 73 | port_bindings: Some(port_map), 74 | ..Default::default() 75 | }; 76 | 77 | let _ghostunnel_image = docker.create_image( 78 | Some(bollard::image::CreateImageOptions { 79 | from_image: GHOSTUNNEL_IMAGE, 80 | ..Default::default() 81 | }), 82 | None, 83 | None 84 | ).try_collect::>().await; 85 | 86 | let ghostunnel_config = Config { 87 | image: Some(GHOSTUNNEL_IMAGE), 88 | cmd: Some(cmd), 89 | exposed_ports: Some(exposed_ports), 90 | host_config: Some(hostconfig), 91 | labels: Some(labels), 92 | ..Default::default() 93 | }; 94 | 95 | if let Err(why) = docker.create_container(options, ghostunnel_config).await { 96 | return Err(ConnectError::new(format!("Error creating container: {}", why).as_str())) 97 | } 98 | 99 | println!( 100 | "Creating forwarding from {} to {}", 101 | port.target, port.endpoint 102 | ); 103 | println!( 104 | "with CA {}, Client {}, Key {}", 105 | mtls.ca_cert_path, mtls.client_cert_path, mtls.client_key_path 106 | ); 107 | let sval = docker 108 | .start_container(container_name.as_str(), None::>) 109 | .await; 110 | 111 | match sval { 112 | Ok(sval) => sval, 113 | Err(why) => return Err(ConnectError::new(format!("Could not start container: {}", why).as_str())), 114 | } 115 | } 116 | Ok(()) 117 | }) 118 | 119 | } 120 | 121 | fn terminate(&self, name: &str) -> Result<(), ConnectError> { 122 | tokio::runtime::Builder::new_multi_thread() 123 | .enable_all() 124 | .build() 125 | .unwrap() 126 | .block_on(async move { 127 | let docker = match Docker::connect_with_socket_defaults() { 128 | Err(why) => return Err(ConnectError::new(format!("Docker error: {}", why).as_str())), 129 | Ok(docker) => docker, 130 | }; 131 | 132 | 133 | let name_label = if name.is_empty() { "beiboot.getdeck.dev/name".to_string() } else { format!("beiboot.getdeck.dev/name={name}", name=name) }; 134 | 135 | 136 | let filters = HashMap::from([ 137 | ("label", vec![name_label.as_str()]) 138 | ]); 139 | 140 | let options = Some(ListContainersOptions{ 141 | filters, 142 | ..Default::default() 143 | }); 144 | 145 | let remove_options = Some(RemoveContainerOptions { 146 | force: true, 147 | ..Default::default() 148 | }); 149 | 150 | 151 | 152 | let rcontainers = docker.list_containers(options).await; 153 | let containers = match rcontainers { 154 | Ok(containers) => containers, 155 | Err(why) => return Err(ConnectError::new(format!("Could not find containers: {}", why).as_str())), 156 | }; 157 | for container in containers{ 158 | match container.id { 159 | None => None, 160 | Some(i) => Some(docker.remove_container(i.as_str(), remove_options).await), 161 | }; 162 | } 163 | 164 | 165 | 166 | 167 | Ok(()) 168 | }) 169 | 170 | } 171 | 172 | fn check_running(&self) -> Result, ConnectError> { 173 | tokio::runtime::Builder::new_multi_thread() 174 | .enable_all() 175 | .build() 176 | .unwrap() 177 | .block_on(async move { 178 | let docker = match Docker::connect_with_socket_defaults() { 179 | Err(why) => return Err(ConnectError::new(format!("Docker error: {}", why).as_str())), 180 | Ok(docker) => docker, 181 | }; 182 | 183 | let name_label = "beiboot.getdeck.dev/name".to_string(); 184 | 185 | let filters = HashMap::from([ 186 | ("label", vec![name_label.as_str()]) 187 | ]); 188 | 189 | let options = Some(ListContainersOptions{ 190 | filters, 191 | ..Default::default() 192 | }); 193 | 194 | let mut result = vec![]; 195 | 196 | let rcontainers = docker.list_containers(options).await; 197 | match rcontainers { 198 | Ok(containers) => { 199 | for container in &containers { 200 | match &container.labels.clone().expect("Could not get labels.").get("beiboot.getdeck.dev/name") { 201 | None => return Err(ConnectError::new(format!("Could not find containers: {}", "No name label").as_str())), 202 | Some(name) => result.push(name.to_string()), 203 | } 204 | } 205 | Ok(result) 206 | }, 207 | Err(why) => return Err(ConnectError::new(format!("Could not find containers: {}", why).as_str())), 208 | } 209 | }) 210 | } 211 | } 212 | -------------------------------------------------------------------------------- /src-tauri/src/heartbeat.rs: -------------------------------------------------------------------------------- 1 | use beiboot_desktop::connection::ConnectError; 2 | 3 | pub async fn establish_heartbeat_connection(cluster_id: &str, token: &str, getdeck_url: &str) -> Result { 4 | let resp = reqwest::Client::new() 5 | .post(format!("{}/clusters/{}/heartbeat", getdeck_url, cluster_id).as_str()) 6 | .header(reqwest::header::AUTHORIZATION, format!("Bearer {}", token)) 7 | .send() 8 | .await 9 | .expect("Failed to send"); 10 | Ok(format!("Heartbeat status: {}", resp.status())) 11 | } 12 | 13 | #[cfg(test)] 14 | mod tests { 15 | use super::establish_heartbeat_connection; 16 | 17 | #[tokio::test] 18 | async fn test_establish_heartbeat_connection_success() { 19 | let mut server = mockito::Server::new(); 20 | let url = server.url(); 21 | 22 | let mock = server.mock("POST", "/clusters/123/heartbeat") 23 | .with_status(200) 24 | .create(); 25 | 26 | let result = establish_heartbeat_connection("123", "token", &url).await; 27 | mock.assert(); 28 | assert_eq!("Heartbeat status: 200 OK", result.unwrap()) 29 | } 30 | 31 | #[tokio::test] 32 | async fn test_establish_heartbeat_connection_failure() { 33 | let mut server = mockito::Server::new(); 34 | let url = server.url(); 35 | 36 | let mock = server.mock("POST", "/clusters/123/heartbeat") 37 | .with_status(500) 38 | .create(); 39 | 40 | let result = establish_heartbeat_connection("123", "token", &url).await; 41 | mock.assert(); 42 | assert_eq!("Heartbeat status: 500 Internal Server Error", result.unwrap()) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src-tauri/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod connection; 2 | -------------------------------------------------------------------------------- /src-tauri/src/main.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr( 2 | all(not(debug_assertions), target_os = "windows"), 3 | windows_subsystem = "windows" 4 | )] 5 | 6 | use beiboot_desktop::connection::{get_connector_context, PortMapping, TLSFiles}; 7 | use tauri_plugin_oauth::start; 8 | use tauri::{utils::config::AppUrl, window::WindowBuilder, WindowUrl}; 9 | 10 | mod util; 11 | mod heartbeat; 12 | 13 | fn main() { 14 | let _guard = sentry::init(("https://a64f388330914f08b0a015e6068dac3d@o146863.ingest.sentry.io/4505356777357312", sentry::ClientOptions { 15 | release: sentry::release_name!(), 16 | ..Default::default() 17 | })); 18 | let mut context = tauri::generate_context!(); 19 | let mut builder = tauri::Builder::default(); 20 | 21 | let port = portpicker::pick_unused_port().expect("failed to find an available port"); 22 | #[cfg(dev)] 23 | { 24 | let port = 5173; 25 | } 26 | let url = format!("http://localhost:{}", port).parse().unwrap(); 27 | let window_url = WindowUrl::External(url); 28 | context.config_mut().build.dist_dir = AppUrl::Url(window_url.clone()); 29 | 30 | builder = builder 31 | .invoke_handler(tauri::generate_handler![connect_beiboot_ghostunnel, disconnect_beiboot_ghostunnel, write_kubeconfig, cleanup, start_server, check_running_connects, establish_heartbeat_connection, check_docker_engine]) 32 | .plugin(tauri_plugin_localhost::Builder::new(port).build()) 33 | .plugin(tauri_plugin_store::Builder::default().build()) 34 | .setup(move |app| { 35 | WindowBuilder::new( 36 | app, 37 | "main".to_string(), 38 | if cfg!(dev) { 39 | Default::default() 40 | } else { 41 | window_url 42 | } 43 | ) 44 | .fullscreen(false) 45 | .inner_size(1200.0, 800.0) 46 | .title("Getdeck Desktop") 47 | .build()?; 48 | Ok(()) 49 | }) 50 | .on_window_event(|event| { 51 | match event.event() { 52 | tauri::WindowEvent::CloseRequested { .. } | tauri::WindowEvent::Destroyed { .. } => { 53 | get_connector_context("", "GhostunnelDocker").disconnect().unwrap(); 54 | } 55 | _ => {} 56 | } 57 | }); 58 | 59 | builder 60 | .run(context) 61 | .expect("error while running tauri application"); 62 | } 63 | 64 | #[tauri::command] 65 | async fn start_server(window: tauri::Window) -> Result { 66 | let tauri_url = window.url(); 67 | start(move |url| { 68 | let params = url.split('#').collect::>(); 69 | window.eval(format!("window.location.replace('{}#{}')", tauri_url, params[1]).as_str()).unwrap(); 70 | }) 71 | .map_err(|e| e.to_string()) 72 | } 73 | 74 | #[tauri::command] 75 | fn connect_beiboot_ghostunnel(beiboot_name: String, ports: Vec, ca: &str, cl_cert: &str, cl_key: &str) -> Result { 76 | let connector = get_connector_context(&beiboot_name, "GhostunnelDocker"); 77 | let ca_cert_path = util::write_conf_file(beiboot_name.clone(), ca, "ca.crt").unwrap(); 78 | let client_cert_path = util::write_conf_file(beiboot_name.clone(), cl_cert, "client.crt").unwrap(); 79 | let client_key_path = util::write_conf_file(beiboot_name.clone(), cl_key, "client.key").unwrap(); 80 | 81 | let tls = TLSFiles { 82 | ca_cert_path: &ca_cert_path, 83 | client_cert_path: &client_cert_path, 84 | client_key_path: &client_key_path, 85 | }; 86 | 87 | match connector.connect(&ports, &tls) { 88 | Ok(_) => Ok("Cluster connected successfully".into()), 89 | Err(why) => { 90 | println!("{}", why); 91 | Err(format!("{}", why)) 92 | } 93 | } 94 | } 95 | 96 | #[tauri::command] 97 | fn disconnect_beiboot_ghostunnel(beiboot_name: String) -> Result { 98 | let connector = get_connector_context(&beiboot_name, "GhostunnelDocker"); 99 | match connector.disconnect() { 100 | Ok(_) => Ok("Cluster disconnected successfully".into()), 101 | Err(why) => { 102 | println!("{}", why); 103 | Err(format!("{}", why)) 104 | } 105 | } 106 | } 107 | 108 | #[tauri::command] 109 | fn write_kubeconfig(beiboot_name: String, kubeconfig: String) -> Result { 110 | match util::write_conf_file(beiboot_name, &kubeconfig, "kubeconfig.yaml") { 111 | Ok(path) => Ok(path), 112 | Err(why) => { 113 | println!("{}", why); 114 | Err(why) 115 | } 116 | } 117 | } 118 | 119 | #[tauri::command] 120 | fn cleanup(beiboot_name: String) -> Result<(), String> { 121 | match util::cleanup(beiboot_name) { 122 | Ok(_) => Ok(()), 123 | Err(why) => { 124 | println!("{}", why); 125 | Err(why) 126 | } 127 | } 128 | } 129 | 130 | #[tauri::command] 131 | fn check_running_connects() -> Result, String> { 132 | let connector = get_connector_context("any", "GhostunnelDocker"); 133 | match connector.check_running() { 134 | Ok(running) => Ok(running), 135 | Err(why) => { 136 | println!("{}", why); 137 | Err(format!("{}", why)) 138 | } 139 | } 140 | } 141 | 142 | #[tauri::command] 143 | async fn establish_heartbeat_connection(cluster_id: &str, token: &str) -> Result<(), String> { 144 | match heartbeat::establish_heartbeat_connection(cluster_id, token, "https://api.getdeck.dev").await { 145 | Ok(_) => Ok(()), 146 | Err(why) => { 147 | println!("{}", why); 148 | Err(format!("{}", why)) 149 | } 150 | } 151 | } 152 | 153 | #[tauri::command] 154 | async fn check_docker_engine() -> Result { 155 | match util::check_docker_engine().await { 156 | Ok(res) => Ok(res), 157 | Err(why) => { 158 | println!("{}", why); 159 | Err(format!("{}", why)) 160 | } 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /src-tauri/src/util.rs: -------------------------------------------------------------------------------- 1 | use std::fs::{File,create_dir_all}; 2 | use std::io::prelude::*; 3 | use std::env::temp_dir; 4 | use std::path::PathBuf; 5 | use bollard::Docker; 6 | 7 | pub fn write_conf_file(beiboot_name: String, content: &str, file_type: &str) -> Result { 8 | 9 | let tmp = temp_dir(); 10 | let mut path = PathBuf::from(&tmp); 11 | path.push("beiboot"); 12 | path.push(&beiboot_name); 13 | let dir_result = create_dir_all(path.clone()); 14 | match dir_result { 15 | Ok(_) => (), 16 | Err(why) => { 17 | println!("{}", why); 18 | return Err(format!("{}", why)); 19 | } 20 | } 21 | 22 | path.push(file_type); 23 | let file_result = File::create(&path); 24 | let mut file = match file_result { 25 | Ok(file) => file, 26 | Err(why) => { 27 | println!("{}", why); 28 | return Err(format!("{}", why)); 29 | } 30 | }; 31 | file.write_all(content.as_bytes()).unwrap(); 32 | 33 | Ok(path.to_str().unwrap().to_string()) 34 | } 35 | 36 | pub fn cleanup(beiboot_name: String) -> Result<(), String> { 37 | let tmp = temp_dir(); 38 | let mut path = PathBuf::from(&tmp); 39 | path.push("beiboot"); 40 | path.push(&beiboot_name); 41 | let dir_result = std::fs::remove_dir_all(path); 42 | match dir_result { 43 | Ok(_) => Ok(()), 44 | Err(why) => { 45 | println!("{}", why); 46 | Err(format!("{}", why)) 47 | } 48 | } 49 | } 50 | 51 | pub async fn check_docker_engine() -> Result { 52 | let docker = Docker::connect_with_local_defaults().unwrap(); 53 | match docker.info().await { 54 | Ok(_) => Ok("Docker engine is running".to_string()), 55 | Err(why) => Err(format!("{}", why)) 56 | } 57 | } 58 | 59 | #[cfg(test)] 60 | mod tests { 61 | #[test] 62 | fn test_write_cleanup_tls_files() { 63 | let beiboot_name = "test".to_string(); 64 | let content = "test".to_string(); 65 | let cert_type = "ca.crt".to_string(); 66 | let result = super::write_conf_file(beiboot_name.clone(), &content, &cert_type); 67 | assert!(result.is_ok()); 68 | 69 | // Note: this assertion will fail if the temp directory is not /tmp, 70 | // eg on windows or macos. To successfully run this on macos, set TMPDIR=/tmp 71 | assert_eq!(result.unwrap(), "/tmp/beiboot/test/ca.crt".to_string()); 72 | let cleanup_result = super::cleanup(beiboot_name); 73 | assert!(cleanup_result.is_ok()); 74 | } 75 | 76 | #[tokio::test] 77 | async fn test_check_docker_engine() { 78 | let result = super::check_docker_engine().await; 79 | assert!(result.is_ok()); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src-tauri/tauri.conf.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../node_modules/@tauri-apps/cli/schema.json", 3 | "build": { 4 | "beforeBuildCommand": "npm run build", 5 | "beforeDevCommand": "npm run dev", 6 | "devPath": "http://localhost:5173", 7 | "distDir": "../dist" 8 | }, 9 | "package": { 10 | "productName": "Getdeck-Desktop", 11 | "version": "0.1.2" 12 | }, 13 | "tauri": { 14 | "allowlist": { 15 | "shell": { 16 | "open": true 17 | } 18 | }, 19 | "updater": { 20 | "active": true, 21 | "endpoints": [ 22 | "https://raw.githubusercontent.com/Getdeck/Getdeck-Desktop/main/updater.json" 23 | ], 24 | "pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IDdCRjA3QjMyOTBEQTREMTMKUldRVFRkcVFNbnZ3ZTIrTmJVT1NQL0Ftdi9pRnBQcW5aV1locEdLU2RjY0Z2K1VvMWZqL3pUNHIK" 25 | }, 26 | "bundle": { 27 | "active": true, 28 | "category": "DeveloperTool", 29 | "copyright": "", 30 | "deb": { 31 | "depends": [] 32 | }, 33 | "icon": [ 34 | "icons/32x32.png", 35 | "icons/128x128.png", 36 | "icons/128x128@2x.png", 37 | "icons/icon.icns", 38 | "icons/icon.ico" 39 | ], 40 | "identifier": "dev.getdeck.desktop", 41 | "longDescription": "", 42 | "macOS": { 43 | "entitlements": null, 44 | "exceptionDomain": "", 45 | "frameworks": [], 46 | "providerShortName": null, 47 | "signingIdentity": null 48 | }, 49 | "resources": [], 50 | "shortDescription": "", 51 | "targets": "all", 52 | "windows": { 53 | "certificateThumbprint": null, 54 | "digestAlgorithm": "sha256", 55 | "timestampUrl": "" 56 | } 57 | }, 58 | "security": { 59 | "csp": null, 60 | "dangerousRemoteDomainIpcAccess": [ 61 | { 62 | "domain": "getdeck.dev", 63 | "windows": [ 64 | "Getdeck Desktop" 65 | ] 66 | } 67 | ] 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src-tauri/tests/connectors.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod ghostunnel_docker_tests { 3 | use beiboot_desktop::connection::{get_connector_context, PortMapping, TLSFiles}; 4 | 5 | //FIXME: This test fails after the first run because of a container name conflict. 6 | #[test] 7 | fn test_a_connect_ghostunnel_docker() { 8 | let connector = get_connector_context("test", "GhostunnelDocker"); 9 | 10 | let ports = vec![PortMapping { 11 | target: 6443, 12 | endpoint: "34.88.204.180:31477", 13 | }, PortMapping { 14 | target: 8080, 15 | endpoint: "34.88.204.180.31477" 16 | }]; 17 | let ca_cert_path = shellexpand::tilde("~/.getdeck/test/mtls/ca.crt").into_owned(); 18 | let client_cert_path = shellexpand::tilde("~/.getdeck/test/mtls/client.crt").into_owned(); 19 | let client_key_path = shellexpand::tilde("~/.getdeck/test/mtls/client.key").into_owned(); 20 | 21 | let tls = TLSFiles { 22 | ca_cert_path: &ca_cert_path, 23 | client_cert_path: &client_cert_path, 24 | client_key_path: &client_key_path, 25 | }; 26 | 27 | let res = match connector.connect(&ports, &tls) { 28 | Ok(_) => 0, 29 | Err(why) => { 30 | println!("{}", why); 31 | 1 32 | } 33 | }; 34 | assert_eq!(0, res) 35 | } 36 | 37 | #[test] 38 | fn test_c_disconnect_ghostunnel_docker() { 39 | let connector = get_connector_context("test", "GhostunnelDocker"); 40 | let res = match connector.disconnect() { 41 | Ok(_) => 0, 42 | Err(why) => { 43 | println!("{}", why); 44 | 1 45 | } 46 | }; 47 | assert_eq!(0, res) 48 | } 49 | #[test] 50 | fn test_b_check_running() { 51 | let connector = get_connector_context("test", "GhostunnelDocker"); 52 | let _res = match connector.check_running() { 53 | Ok(container_summary) => { 54 | println!("{:?}", container_summary); 55 | 0 56 | }, 57 | Err(why) => { 58 | println!("{}", why); 59 | 1 60 | } 61 | }; 62 | } 63 | #[test] 64 | fn test_d_check_not_running() { 65 | let connector = get_connector_context("test", "GhostunnelDocker"); 66 | let _res = match connector.check_running() { 67 | Ok(container_summary) => { 68 | println!("{:?}", container_summary); 69 | 0 70 | }, 71 | Err(why) => { 72 | println!("{}", why); 73 | 1 74 | } 75 | }; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 8 | -------------------------------------------------------------------------------- /src/auth/keycloak.ts: -------------------------------------------------------------------------------- 1 | import Keycloak from 'keycloak-js'; 2 | import axios from 'axios'; 3 | import { Store } from "tauri-plugin-store-api"; 4 | import { OpenAPI } from "beiboot-api-client"; 5 | 6 | import router from "@/router"; 7 | import { useAppStore } from "@/store/app"; 8 | import { open } from "@tauri-apps/api/shell"; 9 | import { listen } from "@tauri-apps/api/event"; 10 | import { startOAuthServer } from '@/beibootctl'; 11 | 12 | export interface Token { 13 | token: string; 14 | refreshToken: string; 15 | } 16 | 17 | let initOptions = { 18 | url: 'https://login.getdeck.dev/auth/', 19 | realm: 'Getdeck', 20 | clientId: 'beiboot-api' 21 | } 22 | 23 | const openLoginBrowser = async (keycloak: Keycloak.KeycloakInstance) => { 24 | const port = await startOAuthServer(); 25 | const redirectUri = `http://localhost:${port}`; 26 | keycloak.init({ 27 | checkLoginIframe: false, 28 | }); 29 | keycloak.login({ 30 | redirectUri: redirectUri 31 | }); 32 | console.log("opening...") 33 | 34 | } 35 | 36 | export async function getInitialToken(keycloak: Keycloak.KeycloakInstance) { 37 | const store = new Store(".settings.dat"); 38 | listen('redirect_uri', (data) => { 39 | console.log(data.payload); 40 | }); 41 | openLoginBrowser(keycloak); 42 | } 43 | 44 | export async function initKeycloak() { 45 | let initOptions = { 46 | url: 'https://login.getdeck.dev/auth/', 47 | realm: 'Getdeck', 48 | clientId: 'beiboot-api' 49 | } 50 | const appStore = useAppStore(); 51 | const store = new Store(".settings2.dat"); 52 | 53 | const storeToken = await store.get("token"); 54 | const storeRefreshToken = await store.get("refreshToken"); 55 | 56 | console.log(storeToken) 57 | console.log(storeRefreshToken) 58 | 59 | const keycloak = new Keycloak(initOptions); 60 | keycloak.init({ 61 | checkLoginIframe: false, 62 | onLoad: 'check-sso', 63 | enableLogging: true, 64 | // @ts-ignore 65 | token: storeToken?.value, 66 | // @ts-ignore 67 | refreshToken: storeRefreshToken?.value 68 | }).then(async (authenticated) => { 69 | console.log(authenticated) 70 | if (authenticated) { 71 | store.set("token", {value: keycloak.token}) 72 | store.set("refreshToken", {value: keycloak.refreshToken}) 73 | OpenAPI.TOKEN = keycloak.token; 74 | keycloak.loadUserProfile().then((profile) => { 75 | appStore.auth.authenticated = true; 76 | appStore.auth.user = profile.firstName || ""; 77 | appStore.auth.token = keycloak.token || ""; 78 | appStore.auth.keycloak = keycloak; 79 | store.set("user", { value: profile }); 80 | router.push("/clusters"); 81 | }); 82 | await store.save(); 83 | } else { 84 | store.clear(); 85 | store.save(); 86 | getInitialToken(keycloak); 87 | } 88 | }).catch((err) => { 89 | console.log(err) 90 | }) 91 | setInterval(() => { 92 | keycloak.updateToken(0).then((refreshed) => { 93 | console.debug(refreshed) 94 | if (refreshed) { 95 | console.debug('Token refreshed' + refreshed); 96 | OpenAPI.TOKEN = keycloak.token; 97 | } else { 98 | console.debug('Token not refreshed, still valid.'); 99 | } 100 | }).catch(() => { 101 | console.debug('Failed to refresh token'); 102 | }); 103 | }, 6000) 104 | } 105 | -------------------------------------------------------------------------------- /src/beibootctl.d.ts: -------------------------------------------------------------------------------- 1 | export declare function getVersion(): Promise; 2 | export declare function createCluster(name: string): Promise; 3 | export declare function deleteCluster(name: string): Promise; 4 | export declare function listCluster(): Promise; 5 | -------------------------------------------------------------------------------- /src/beibootctl.ts: -------------------------------------------------------------------------------- 1 | import { Command } from "@tauri-apps/api/shell"; 2 | import { invoke } from "@tauri-apps/api/tauri"; 3 | import { useAppStore } from "./store/app"; 4 | 5 | export async function getVersion() { 6 | return "1.0.0"; 7 | } 8 | 9 | export async function createCluster(name: string) { 10 | console.log("Ich laufe"); 11 | console.log(name); 12 | 13 | return "Created cluster " + name; 14 | } 15 | 16 | export async function deleteCluster(name: string) { 17 | return "Deleted cluster " + name; 18 | } 19 | 20 | export async function listCluster() { 21 | const output = "No Beiboots"; 22 | console.log(output); 23 | } 24 | 25 | export async function connectCluster(name: string, portMapping: any, ca: string, clCert: string, clKey: string) { 26 | let res = await invoke("connect_beiboot_ghostunnel", { 27 | beibootName: name, 28 | ports: portMapping, 29 | ca, 30 | clCert, 31 | clKey 32 | }) 33 | return res 34 | } 35 | 36 | export async function writeKubeconfig(name: string, kubeconfig: string): Promise { 37 | let res: string = await invoke("write_kubeconfig", { 38 | beibootName: name, 39 | kubeconfig 40 | }) 41 | return res 42 | } 43 | 44 | export async function disconnectCluster(name: string) { 45 | let res = await invoke("disconnect_beiboot_ghostunnel", { beibootName: name }) 46 | return res 47 | } 48 | 49 | export async function startOAuthServer() { 50 | let res = await invoke("start_server", {}) 51 | return res 52 | } 53 | 54 | export async function checkRunningConnects() { 55 | const appStore = useAppStore(); 56 | 57 | try { 58 | let res: string[] = await invoke("check_running_connects", {}) 59 | if (res.length > 0) { 60 | appStore.connection.clusterName = res[0] 61 | appStore.connection.connected = true; 62 | } else { 63 | appStore.connection.clusterName = "" 64 | appStore.connection.connected = false; 65 | } 66 | return res 67 | } catch { 68 | appStore.connection.clusterName = "" 69 | appStore.connection.connected = false; 70 | console.log("no docker client here.") 71 | return "Docker Client unresponsive." 72 | } 73 | } 74 | export async function checkDockerEngine() { 75 | let res: string = await invoke("check_docker_engine", {}) 76 | return res 77 | } 78 | -------------------------------------------------------------------------------- /src/components/ClusterTable.vue: -------------------------------------------------------------------------------- 1 | 55 | 56 | 199 | -------------------------------------------------------------------------------- /src/components/Home.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 13 | -------------------------------------------------------------------------------- /src/layouts/default/Default.vue: -------------------------------------------------------------------------------- 1 | 79 | 80 | 122 | -------------------------------------------------------------------------------- /src/layouts/default/NoSidebar.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 11 | -------------------------------------------------------------------------------- /src/main.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * main.ts 3 | * 4 | * Bootstraps Vuetify and other plugins then mounts the App` 5 | */ 6 | export {}; 7 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * main.ts 3 | * 4 | * Bootstraps Vuetify and other plugins then mounts the App` 5 | */ 6 | 7 | // Components 8 | import App from './App.vue' 9 | 10 | // Composables 11 | import { createApp } from 'vue' 12 | 13 | // Plugins 14 | import { registerPlugins } from '@/plugins' 15 | 16 | import router from '@/router' 17 | import { OpenAPI } from "beiboot-api-client"; 18 | import { initKeycloak } from '@/auth/keycloak'; 19 | import * as Sentry from "@sentry/vue"; 20 | import { invoke } from '@tauri-apps/api/tauri'; 21 | import { useAppStore } from './store/app'; 22 | import { checkDockerEngine } from './beibootctl'; 23 | 24 | const app = createApp(App) 25 | 26 | registerPlugins(app) 27 | 28 | OpenAPI.BASE = "https://api.getdeck.dev" 29 | 30 | 31 | if (process.env.NODE_ENV !== 'dev') { 32 | Sentry.init({ 33 | app, 34 | dsn: "https://dd838e60a73d4902aa336ca38093e530@o146863.ingest.sentry.io/4505356797018112", 35 | integrations: [ 36 | new Sentry.BrowserTracing({ 37 | tracePropagationTargets: ["localhost", "tauri.localhost"], 38 | routingInstrumentation: Sentry.vueRouterInstrumentation(router), 39 | }), 40 | new Sentry.Replay(), 41 | ], 42 | // Performance Monitoring 43 | tracesSampleRate: 0.1, 44 | // Session Replay 45 | replaysSessionSampleRate: 0.1, // This sets the sample rate at 10%. You may want to change it to 100% while in development and then sample at a lower rate in production. 46 | replaysOnErrorSampleRate: 1.0, // If you're not already sampling the entire session, change the sample rate to 100% when sampling sessions where errors occur. 47 | }); 48 | } 49 | 50 | app.mount('#app') 51 | 52 | if (process.env.NODE_ENV !== 'debug') { 53 | document.addEventListener('contextmenu', event => event.preventDefault()); 54 | } 55 | 56 | const appStore = useAppStore(); 57 | 58 | checkDockerEngine().then((res) => { 59 | console.log("docker engine: ", res); 60 | appStore.docker = true; 61 | }).catch((err) => { 62 | console.log("docker engine: ", err); 63 | appStore.docker = false; 64 | }) 65 | 66 | initKeycloak(); 67 | -------------------------------------------------------------------------------- /src/plugins/index.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * plugins/index.ts 3 | * 4 | * Automatically included in `./src/main.ts` 5 | */ 6 | import type { App } from 'vue'; 7 | export declare function registerPlugins(app: App): void; 8 | -------------------------------------------------------------------------------- /src/plugins/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * plugins/index.ts 3 | * 4 | * Automatically included in `./src/main.ts` 5 | */ 6 | 7 | // Plugins 8 | import { loadFonts } from './webfontloader' 9 | import vuetify from './vuetify' 10 | import pinia from '../store' 11 | import router from '../router' 12 | 13 | // Types 14 | import type { App } from 'vue' 15 | 16 | export function registerPlugins (app: App) { 17 | loadFonts() 18 | app 19 | .use(vuetify) 20 | .use(router) 21 | .use(pinia) 22 | } 23 | -------------------------------------------------------------------------------- /src/plugins/vuetify.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * plugins/vuetify.ts 3 | * 4 | * Framework documentation: https://vuetifyjs.com` 5 | */ 6 | /// 7 | import '@mdi/font/css/materialdesignicons.css'; 8 | import 'vuetify/styles'; 9 | declare const _default: { 10 | install: (app: import("vue").App) => void; 11 | defaults: import("vue").Ref; 12 | display: import("vuetify").DisplayInstance; 13 | theme: import("vuetify").ThemeInstance & { 14 | install: (app: import("vue").App) => void; 15 | }; 16 | icons: Record; 17 | locale: { 18 | isRtl: import("vue").Ref; 19 | rtl: import("vue").Ref>; 20 | rtlClasses: import("vue").Ref; 21 | name: string; 22 | messages: import("vue").Ref; 23 | current: import("vue").Ref; 24 | fallback: import("vue").Ref; 25 | t: (key: string, ...params: unknown[]) => string; 26 | n: (value: number) => string; 27 | provide: (props: import("vuetify").LocaleOptions) => import("vuetify").LocaleInstance; 28 | }; 29 | }; 30 | export default _default; 31 | -------------------------------------------------------------------------------- /src/plugins/vuetify.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * plugins/vuetify.ts 3 | * 4 | * Framework documentation: https://vuetifyjs.com` 5 | */ 6 | 7 | // Styles 8 | import '@mdi/font/css/materialdesignicons.css' 9 | import 'vuetify/styles' 10 | 11 | // Composables 12 | import { createVuetify } from 'vuetify' 13 | 14 | // https://vuetifyjs.com/en/introduction/why-vuetify/#feature-guides 15 | export default createVuetify({ 16 | defaults: { 17 | global: { 18 | ripple: false, 19 | }, 20 | VTooltip: { 21 | location: "bottom", 22 | offset: "2" 23 | }, 24 | VTextField: { 25 | variant: "outlined", 26 | density: "compact" 27 | } 28 | }, 29 | theme: { 30 | themes: { 31 | light: { 32 | colors: { 33 | "on-background": "#303846", 34 | primary: "#ff1654", 35 | secondary: "#303846", 36 | 'secondary-2': "#303846" 37 | }, 38 | }, 39 | }, 40 | }, 41 | }) 42 | -------------------------------------------------------------------------------- /src/plugins/webfontloader.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * plugins/webfontloader.ts 3 | * 4 | * webfontloader documentation: https://github.com/typekit/webfontloader 5 | */ 6 | export declare function loadFonts(): Promise; 7 | -------------------------------------------------------------------------------- /src/plugins/webfontloader.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * plugins/webfontloader.ts 3 | * 4 | * webfontloader documentation: https://github.com/typekit/webfontloader 5 | */ 6 | 7 | export async function loadFonts () { 8 | const webFontLoader = await import(/* webpackChunkName: "webfontloader" */'webfontloader') 9 | 10 | webFontLoader.load({ 11 | google: { 12 | families: ['Roboto:100,300,400,500,700,900&display=swap'], 13 | }, 14 | }) 15 | } 16 | -------------------------------------------------------------------------------- /src/router/index.d.ts: -------------------------------------------------------------------------------- 1 | declare const router: import("vue-router").Router; 2 | export default router; 3 | -------------------------------------------------------------------------------- /src/router/index.ts: -------------------------------------------------------------------------------- 1 | // Composables 2 | import { createRouter, createWebHistory } from 'vue-router'; 3 | const routes = [ 4 | { 5 | path: '/', 6 | component: () => import('@/layouts/default/Default.vue'), 7 | children: [ 8 | { 9 | path: '/home', 10 | name: 'Home', 11 | // route level code-splitting 12 | // this generates a separate chunk (about.[hash].js) for this route 13 | // which is lazy-loaded when the route is visited. 14 | component: () => import(/* webpackChunkName: "home" */ '@/views/Home.vue'), 15 | }, 16 | { 17 | path: '/clusters', 18 | name: 'Clusters', 19 | // route level code-splitting 20 | // this generates a separate chunk (about.[hash].js) for this route 21 | // which is lazy-loaded when the route is visited. 22 | component: () => import(/* webpackChunkName: "home" */ '@/views/Clusters.vue'), 23 | }, 24 | { 25 | path: '/clusters/create', 26 | name: 'Cluster Create', 27 | // route level code-splitting 28 | // this generates a separate chunk (about.[hash].js) for this route 29 | // which is lazy-loaded when the route is visited. 30 | component: () => import(/* webpackChunkName: "home" */ '@/views/ClusterCreate.vue'), 31 | }, 32 | ], 33 | }, 34 | { 35 | path: '/login', 36 | component: () => import('@/layouts/default/NoSidebar.vue'), 37 | children: [ 38 | 39 | { 40 | path: '', 41 | name: 'Login', 42 | component: () => import('@/views/Login.vue'), 43 | }, 44 | ] 45 | } 46 | ]; 47 | const router = createRouter({ 48 | history: createWebHistory(), 49 | routes, 50 | }); 51 | export default router; 52 | -------------------------------------------------------------------------------- /src/store/app.d.ts: -------------------------------------------------------------------------------- 1 | export declare const useAppStore: import("pinia").StoreDefinition<"app", {}, {}, {}>; 2 | -------------------------------------------------------------------------------- /src/store/app.ts: -------------------------------------------------------------------------------- 1 | // Utilities 2 | import Keycloak from 'keycloak-js'; 3 | import { defineStore } from 'pinia' 4 | import { Store } from 'tauri-plugin-store-api'; 5 | 6 | export const useAppStore = defineStore('appStore', { 7 | state: () => { 8 | return { 9 | connection: { 10 | clusterName: "", 11 | clusterId: "", 12 | kubeconfigPath: "", 13 | connected: false, 14 | }, 15 | docker: false, 16 | auth: { 17 | authenticated: false, 18 | user: "", 19 | keycloak: null as Keycloak | null, 20 | token: "" 21 | } 22 | } 23 | }, 24 | actions: { 25 | logout() { 26 | const store = new Store(".settings.dat"); 27 | store.set("token", ""); 28 | store.clear(); 29 | store.save(); 30 | this.auth.authenticated = false; 31 | this.auth.user = ""; 32 | if (this.auth.keycloak) { 33 | this.auth.keycloak.logout({}) 34 | } 35 | } 36 | } 37 | }) 38 | -------------------------------------------------------------------------------- /src/store/index.d.ts: -------------------------------------------------------------------------------- 1 | declare const _default: import("pinia").Pinia; 2 | export default _default; 3 | -------------------------------------------------------------------------------- /src/store/index.ts: -------------------------------------------------------------------------------- 1 | // Utilities 2 | import { createPinia } from 'pinia' 3 | 4 | export default createPinia() 5 | -------------------------------------------------------------------------------- /src/styles/settings.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * src/styles/settings.scss 3 | * 4 | * Configures SASS variables and Vuetify overwrites 5 | */ 6 | 7 | // https://next.vuetifyjs.com/features/sass-variables/` 8 | @use 'vuetify/settings' with ( 9 | $button-text-transform: none, 10 | $list-item-icon-margin-end: 15px, 11 | $tooltip-background-color: rgba(var(--v-theme-surface-variant), .9), 12 | $text-field-border-radius: 0, 13 | ); 14 | .v-theme--light { 15 | --v-medium-emphasis-opacity: 1; 16 | } 17 | 18 | h1, h2, h3, h4, h5, h6 { 19 | font-weight: 500; 20 | } 21 | 22 | .router-link-btn { 23 | color: currentcolor; 24 | text-decoration: none; 25 | } 26 | 27 | -------------------------------------------------------------------------------- /src/views/ClusterCreate.vue: -------------------------------------------------------------------------------- 1 | 69 | 70 | 155 | 156 | -------------------------------------------------------------------------------- /src/views/Clusters.vue: -------------------------------------------------------------------------------- 1 | 33 | 34 | 46 | -------------------------------------------------------------------------------- /src/views/Home.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 8 | -------------------------------------------------------------------------------- /src/views/Login.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 39 | 40 | -------------------------------------------------------------------------------- /src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | declare module '*.vue' { 4 | import type { DefineComponent } from 'vue' 5 | const component: DefineComponent<{}, {}, any> 6 | export default component 7 | } 8 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "target": "esnext", 5 | "useDefineForClassFields": true, 6 | "allowSyntheticDefaultImports": true, 7 | "composite": true, 8 | "module": "esnext", 9 | "moduleResolution": "node", 10 | "strict": true, 11 | "jsx": "preserve", 12 | "sourceMap": false, 13 | "resolveJsonModule": true, 14 | "esModuleInterop": true, 15 | "lib": ["esnext", "dom"], 16 | "outDir": "dist", 17 | "types": [ 18 | "node", 19 | "vuetify" 20 | ], 21 | "paths": { 22 | "@/*": ["src/*"] 23 | }, 24 | }, 25 | "include": [ 26 | "src/**/*.ts", 27 | "src/**/*.d.ts", 28 | "src/**/*.tsx", 29 | "src/**/*.vue", 30 | "vite.config.ts" 31 | ] 32 | } 33 | -------------------------------------------------------------------------------- /tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "module": "ESNext", 5 | "moduleResolution": "Node", 6 | "allowSyntheticDefaultImports": true 7 | }, 8 | "include": ["vite.config.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /tsconfig.tsbuildinfo: -------------------------------------------------------------------------------- 1 | {"program":{"fileNames":["./node_modules/typescript/lib/lib.es5.d.ts","./node_modules/typescript/lib/lib.es2015.d.ts","./node_modules/typescript/lib/lib.es2016.d.ts","./node_modules/typescript/lib/lib.es2017.d.ts","./node_modules/typescript/lib/lib.es2018.d.ts","./node_modules/typescript/lib/lib.es2019.d.ts","./node_modules/typescript/lib/lib.es2020.d.ts","./node_modules/typescript/lib/lib.es2021.d.ts","./node_modules/typescript/lib/lib.es2022.d.ts","./node_modules/typescript/lib/lib.esnext.d.ts","./node_modules/typescript/lib/lib.dom.d.ts","./node_modules/typescript/lib/lib.es2015.core.d.ts","./node_modules/typescript/lib/lib.es2015.collection.d.ts","./node_modules/typescript/lib/lib.es2015.generator.d.ts","./node_modules/typescript/lib/lib.es2015.iterable.d.ts","./node_modules/typescript/lib/lib.es2015.promise.d.ts","./node_modules/typescript/lib/lib.es2015.proxy.d.ts","./node_modules/typescript/lib/lib.es2015.reflect.d.ts","./node_modules/typescript/lib/lib.es2015.symbol.d.ts","./node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","./node_modules/typescript/lib/lib.es2016.array.include.d.ts","./node_modules/typescript/lib/lib.es2017.object.d.ts","./node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts","./node_modules/typescript/lib/lib.es2017.string.d.ts","./node_modules/typescript/lib/lib.es2017.intl.d.ts","./node_modules/typescript/lib/lib.es2017.typedarrays.d.ts","./node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts","./node_modules/typescript/lib/lib.es2018.asynciterable.d.ts","./node_modules/typescript/lib/lib.es2018.intl.d.ts","./node_modules/typescript/lib/lib.es2018.promise.d.ts","./node_modules/typescript/lib/lib.es2018.regexp.d.ts","./node_modules/typescript/lib/lib.es2019.array.d.ts","./node_modules/typescript/lib/lib.es2019.object.d.ts","./node_modules/typescript/lib/lib.es2019.string.d.ts","./node_modules/typescript/lib/lib.es2019.symbol.d.ts","./node_modules/typescript/lib/lib.es2019.intl.d.ts","./node_modules/typescript/lib/lib.es2020.bigint.d.ts","./node_modules/typescript/lib/lib.es2020.date.d.ts","./node_modules/typescript/lib/lib.es2020.promise.d.ts","./node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts","./node_modules/typescript/lib/lib.es2020.string.d.ts","./node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts","./node_modules/typescript/lib/lib.es2020.intl.d.ts","./node_modules/typescript/lib/lib.es2020.number.d.ts","./node_modules/typescript/lib/lib.es2021.promise.d.ts","./node_modules/typescript/lib/lib.es2021.string.d.ts","./node_modules/typescript/lib/lib.es2021.weakref.d.ts","./node_modules/typescript/lib/lib.es2021.intl.d.ts","./node_modules/typescript/lib/lib.es2022.array.d.ts","./node_modules/typescript/lib/lib.es2022.error.d.ts","./node_modules/typescript/lib/lib.es2022.intl.d.ts","./node_modules/typescript/lib/lib.es2022.object.d.ts","./node_modules/typescript/lib/lib.es2022.sharedmemory.d.ts","./node_modules/typescript/lib/lib.es2022.string.d.ts","./node_modules/typescript/lib/lib.esnext.intl.d.ts","./node_modules/@tauri-apps/api/shell-cbf4da8b.d.ts","./node_modules/@tauri-apps/api/shell.d.ts","./src/beibootctl.ts","./node_modules/@babel/types/lib/index.d.ts","./node_modules/@vue/shared/dist/shared.d.ts","./node_modules/@babel/parser/typings/babel-parser.d.ts","./node_modules/source-map/source-map.d.ts","./node_modules/@vue/compiler-core/dist/compiler-core.d.ts","./node_modules/@vue/compiler-dom/dist/compiler-dom.d.ts","./node_modules/@vue/reactivity/dist/reactivity.d.ts","./node_modules/@vue/runtime-core/dist/runtime-core.d.ts","./node_modules/csstype/index.d.ts","./node_modules/@vue/runtime-dom/dist/runtime-dom.d.ts","./node_modules/vue/dist/vue.d.ts","./node_modules/@types/webfontloader/index.d.ts","./src/plugins/webfontloader.ts","./node_modules/vue-router/dist/vue-router.d.ts","./node_modules/vuetify/lib/components/index.d.ts","./node_modules/vuetify/lib/index.d.ts","./src/plugins/vuetify.ts","./node_modules/pinia/node_modules/vue-demi/lib/index.d.ts","./node_modules/pinia/dist/pinia.d.ts","./src/store/index.ts","./src/router/index.ts","./src/plugins/index.ts","./src/main.ts","./node_modules/vite/types/hmrpayload.d.ts","./node_modules/vite/types/customevent.d.ts","./node_modules/vite/types/hot.d.ts","./node_modules/vite/types/importglob.d.ts","./node_modules/vite/types/importmeta.d.ts","./node_modules/vite/client.d.ts","./src/vite-env.d.ts","./src/store/app.ts","./node_modules/@types/node/assert.d.ts","./node_modules/@types/node/assert/strict.d.ts","./node_modules/@types/node/globals.d.ts","./node_modules/@types/node/async_hooks.d.ts","./node_modules/@types/node/buffer.d.ts","./node_modules/@types/node/child_process.d.ts","./node_modules/@types/node/cluster.d.ts","./node_modules/@types/node/console.d.ts","./node_modules/@types/node/constants.d.ts","./node_modules/@types/node/crypto.d.ts","./node_modules/@types/node/dgram.d.ts","./node_modules/@types/node/diagnostics_channel.d.ts","./node_modules/@types/node/dns.d.ts","./node_modules/@types/node/dns/promises.d.ts","./node_modules/@types/node/domain.d.ts","./node_modules/@types/node/dom-events.d.ts","./node_modules/@types/node/events.d.ts","./node_modules/@types/node/fs.d.ts","./node_modules/@types/node/fs/promises.d.ts","./node_modules/@types/node/http.d.ts","./node_modules/@types/node/http2.d.ts","./node_modules/@types/node/https.d.ts","./node_modules/@types/node/inspector.d.ts","./node_modules/@types/node/module.d.ts","./node_modules/@types/node/net.d.ts","./node_modules/@types/node/os.d.ts","./node_modules/@types/node/path.d.ts","./node_modules/@types/node/perf_hooks.d.ts","./node_modules/@types/node/process.d.ts","./node_modules/@types/node/punycode.d.ts","./node_modules/@types/node/querystring.d.ts","./node_modules/@types/node/readline.d.ts","./node_modules/@types/node/readline/promises.d.ts","./node_modules/@types/node/repl.d.ts","./node_modules/@types/node/stream.d.ts","./node_modules/@types/node/stream/promises.d.ts","./node_modules/@types/node/stream/consumers.d.ts","./node_modules/@types/node/stream/web.d.ts","./node_modules/@types/node/string_decoder.d.ts","./node_modules/@types/node/test.d.ts","./node_modules/@types/node/timers.d.ts","./node_modules/@types/node/timers/promises.d.ts","./node_modules/@types/node/tls.d.ts","./node_modules/@types/node/trace_events.d.ts","./node_modules/@types/node/tty.d.ts","./node_modules/@types/node/url.d.ts","./node_modules/@types/node/util.d.ts","./node_modules/@types/node/v8.d.ts","./node_modules/@types/node/vm.d.ts","./node_modules/@types/node/wasi.d.ts","./node_modules/@types/node/worker_threads.d.ts","./node_modules/@types/node/zlib.d.ts","./node_modules/@types/node/globals.global.d.ts","./node_modules/@types/node/index.d.ts","./node_modules/esbuild/lib/main.d.ts","./node_modules/rollup/dist/rollup.d.ts","./node_modules/source-map-js/source-map.d.ts","./node_modules/postcss/lib/comment.d.ts","./node_modules/postcss/lib/at-rule.d.ts","./node_modules/postcss/lib/rule.d.ts","./node_modules/postcss/lib/container.d.ts","./node_modules/postcss/lib/declaration.d.ts","./node_modules/postcss/lib/previous-map.d.ts","./node_modules/postcss/lib/input.d.ts","./node_modules/postcss/lib/css-syntax-error.d.ts","./node_modules/postcss/lib/warning.d.ts","./node_modules/postcss/lib/document.d.ts","./node_modules/postcss/lib/root.d.ts","./node_modules/postcss/lib/lazy-result.d.ts","./node_modules/postcss/lib/no-work-result.d.ts","./node_modules/postcss/lib/processor.d.ts","./node_modules/postcss/lib/result.d.ts","./node_modules/postcss/lib/node.d.ts","./node_modules/postcss/lib/list.d.ts","./node_modules/postcss/lib/postcss.d.ts","./node_modules/vite/dist/node/index.d.ts","./node_modules/magic-string/index.d.ts","./node_modules/@vue/reactivity-transform/dist/reactivity-transform.d.ts","./node_modules/@vue/compiler-sfc/dist/compiler-sfc.d.ts","./node_modules/vue/compiler-sfc/index.d.ts","./node_modules/@vitejs/plugin-vue/dist/index.d.ts","./node_modules/@vuetify/loader-shared/dist/imports/generateimports.d.ts","./node_modules/@vuetify/loader-shared/dist/styles/writestyles.d.ts","./node_modules/@vuetify/loader-shared/dist/index.d.ts","./node_modules/vite-plugin-vuetify/dist/index.d.ts","./vite.config.ts"],"fileInfos":[{"version":"8730f4bf322026ff5229336391a18bcaa1f94d4f82416c8b2f3954e2ccaae2ba","affectsGlobalScope":true},"dc47c4fa66b9b9890cf076304de2a9c5201e94b740cffdf09f87296d877d71f6","7a387c58583dfca701b6c85e0adaf43fb17d590fb16d5b2dc0a2fbd89f35c467","8a12173c586e95f4433e0c6dc446bc88346be73ffe9ca6eec7aa63c8f3dca7f9","5f4e733ced4e129482ae2186aae29fde948ab7182844c3a5a51dd346182c7b06","4b421cbfb3a38a27c279dec1e9112c3d1da296f77a1a85ddadf7e7a425d45d18","1fc5ab7a764205c68fa10d381b08417795fc73111d6dd16b5b1ed36badb743d9","746d62152361558ea6d6115cf0da4dd10ede041d14882ede3568bce5dc4b4f1f","d11a03592451da2d1065e09e61f4e2a9bf68f780f4f6623c18b57816a9679d17","aea179452def8a6152f98f63b191b84e7cbd69b0e248c91e61fb2e52328abe8c",{"version":"3aafcb693fe5b5c3bd277bd4c3a617b53db474fe498fc5df067c5603b1eebde7","affectsGlobalScope":true},{"version":"adb996790133eb33b33aadb9c09f15c2c575e71fb57a62de8bf74dbf59ec7dfb","affectsGlobalScope":true},{"version":"8cc8c5a3bac513368b0157f3d8b31cfdcfe78b56d3724f30f80ed9715e404af8","affectsGlobalScope":true},{"version":"cdccba9a388c2ee3fd6ad4018c640a471a6c060e96f1232062223063b0a5ac6a","affectsGlobalScope":true},{"version":"c5c05907c02476e4bde6b7e76a79ffcd948aedd14b6a8f56e4674221b0417398","affectsGlobalScope":true},{"version":"5f406584aef28a331c36523df688ca3650288d14f39c5d2e555c95f0d2ff8f6f","affectsGlobalScope":true},{"version":"22f230e544b35349cfb3bd9110b6ef37b41c6d6c43c3314a31bd0d9652fcec72","affectsGlobalScope":true},{"version":"7ea0b55f6b315cf9ac2ad622b0a7813315bb6e97bf4bb3fbf8f8affbca7dc695","affectsGlobalScope":true},{"version":"3013574108c36fd3aaca79764002b3717da09725a36a6fc02eac386593110f93","affectsGlobalScope":true},{"version":"eb26de841c52236d8222f87e9e6a235332e0788af8c87a71e9e210314300410a","affectsGlobalScope":true},{"version":"3be5a1453daa63e031d266bf342f3943603873d890ab8b9ada95e22389389006","affectsGlobalScope":true},{"version":"17bb1fc99591b00515502d264fa55dc8370c45c5298f4a5c2083557dccba5a2a","affectsGlobalScope":true},{"version":"7ce9f0bde3307ca1f944119f6365f2d776d281a393b576a18a2f2893a2d75c98","affectsGlobalScope":true},{"version":"6a6b173e739a6a99629a8594bfb294cc7329bfb7b227f12e1f7c11bc163b8577","affectsGlobalScope":true},{"version":"81cac4cbc92c0c839c70f8ffb94eb61e2d32dc1c3cf6d95844ca099463cf37ea","affectsGlobalScope":true},{"version":"b0124885ef82641903d232172577f2ceb5d3e60aed4da1153bab4221e1f6dd4e","affectsGlobalScope":true},{"version":"0eb85d6c590b0d577919a79e0084fa1744c1beba6fd0d4e951432fa1ede5510a","affectsGlobalScope":true},{"version":"da233fc1c8a377ba9e0bed690a73c290d843c2c3d23a7bd7ec5cd3d7d73ba1e0","affectsGlobalScope":true},{"version":"d154ea5bb7f7f9001ed9153e876b2d5b8f5c2bb9ec02b3ae0d239ec769f1f2ae","affectsGlobalScope":true},{"version":"bb2d3fb05a1d2ffbca947cc7cbc95d23e1d053d6595391bd325deb265a18d36c","affectsGlobalScope":true},{"version":"c80df75850fea5caa2afe43b9949338ce4e2de086f91713e9af1a06f973872b8","affectsGlobalScope":true},{"version":"9d57b2b5d15838ed094aa9ff1299eecef40b190722eb619bac4616657a05f951","affectsGlobalScope":true},{"version":"6c51b5dd26a2c31dbf37f00cfc32b2aa6a92e19c995aefb5b97a3a64f1ac99de","affectsGlobalScope":true},{"version":"6e7997ef61de3132e4d4b2250e75343f487903ddf5370e7ce33cf1b9db9a63ed","affectsGlobalScope":true},{"version":"2ad234885a4240522efccd77de6c7d99eecf9b4de0914adb9a35c0c22433f993","affectsGlobalScope":true},{"version":"5e5e095c4470c8bab227dbbc61374878ecead104c74ab9960d3adcccfee23205","affectsGlobalScope":true},{"version":"09aa50414b80c023553090e2f53827f007a301bc34b0495bfb2c3c08ab9ad1eb","affectsGlobalScope":true},{"version":"d7f680a43f8cd12a6b6122c07c54ba40952b0c8aa140dcfcf32eb9e6cb028596","affectsGlobalScope":true},{"version":"3787b83e297de7c315d55d4a7c546ae28e5f6c0a361b7a1dcec1f1f50a54ef11","affectsGlobalScope":true},{"version":"e7e8e1d368290e9295ef18ca23f405cf40d5456fa9f20db6373a61ca45f75f40","affectsGlobalScope":true},{"version":"faf0221ae0465363c842ce6aa8a0cbda5d9296940a8e26c86e04cc4081eea21e","affectsGlobalScope":true},{"version":"06393d13ea207a1bfe08ec8d7be562549c5e2da8983f2ee074e00002629d1871","affectsGlobalScope":true},{"version":"2768ef564cfc0689a1b76106c421a2909bdff0acbe87da010785adab80efdd5c","affectsGlobalScope":true},{"version":"b248e32ca52e8f5571390a4142558ae4f203ae2f94d5bac38a3084d529ef4e58","affectsGlobalScope":true},{"version":"6c55633c733c8378db65ac3da7a767c3cf2cf3057f0565a9124a16a3a2019e87","affectsGlobalScope":true},{"version":"fb4416144c1bf0323ccbc9afb0ab289c07312214e8820ad17d709498c865a3fe","affectsGlobalScope":true},{"version":"5b0ca94ec819d68d33da516306c15297acec88efeb0ae9e2b39f71dbd9685ef7","affectsGlobalScope":true},{"version":"34c839eaaa6d78c8674ae2c37af2236dee6831b13db7b4ef4df3ec889a04d4f2","affectsGlobalScope":true},{"version":"34478567f8a80171f88f2f30808beb7da15eac0538ae91282dd33dce928d98ed","affectsGlobalScope":true},{"version":"ab7d58e6161a550ff92e5aff755dc37fe896245348332cd5f1e1203479fe0ed1","affectsGlobalScope":true},{"version":"6bda95ea27a59a276e46043b7065b55bd4b316c25e70e29b572958fa77565d43","affectsGlobalScope":true},{"version":"aedb8de1abb2ff1095c153854a6df7deae4a5709c37297f9d6e9948b6806fa66","affectsGlobalScope":true},{"version":"a4da0551fd39b90ca7ce5f68fb55d4dc0c1396d589b612e1902f68ee090aaada","affectsGlobalScope":true},{"version":"11ffe3c281f375fff9ffdde8bbec7669b4dd671905509079f866f2354a788064","affectsGlobalScope":true},{"version":"52d1bb7ab7a3306fd0375c8bff560feed26ed676a5b0457fa8027b563aecb9a4","affectsGlobalScope":true},"893a6d115d83d9c6ea6864a25862a73b8172ee3856366c2f3a026e038478592f","28df0e4d4e5882cc6b0f258f324eccd372e76adcfb3df6a8bdc588ea946b1223",{"version":"515cb86dc06635a6da9e0cae293335fcf8d40390683953a79829f9f91ec89580","signature":"085522f2914d5bb3998b1d00393b2360271d3d11eb660ba20bf15441b8a114c1"},"f4617bbd5403ec5b058db53b242dcb1421952e2652bd5c80abf6a1c4ea5656d6","ae746a298a6f28df1327022f30c25f4e6f3b685f76412d242a4e7dc9c1864055","230d323ef7f2ffadfc0ceae494492c4d2faa2b4eaec07a4b71424d084b97ebb8","2887592574fcdfd087647c539dcb0fbe5af2521270dad4a37f9d17c16190d579","d6f2eb888c21f8714116684379d28ed90b97e8330134a07fdf245681a8df22d2","712fc12e4021b820f7853112d09096e4c5a0f4475915d49f11e076c86fc97f8b","4a7ee458d2b28030d4d3f927c04d84dea97ed6868a50cc08bd3992bd3df37d17",{"version":"021e8b48479ad58e3320f931070c9d18a9497b140e97907992a2296f8a1e123f","affectsGlobalScope":true},"c609b66caa6f5b80a9166ce7505730b398ae7c0eddc0c1feed28a17814cf8609",{"version":"9e1df80422a8188330971a363f767d19ca53804487eb47fc78acebb6a9e75af6","affectsGlobalScope":true},"f79e452d452ab10eac3f96b3b3930cf78ec6c9037c4cd9a062dae61744f634fc","f9391b2ae78058a9fae3280d650e4faad27c24bd2dd4e6fb9f4ff16a61a21f91",{"version":"2787126681c1f3c9499513c33f0d52399311841868d2ba5462a6eab7330dae3b","signature":"f8dd311f5e65a8f5efe4e76653af8eb481e2d2c248057c3f5b16a25867534e65"},"f907b85bf67c02e243690844227315d8f3be4da810fd5e5274737c66e1f2efa6","a9fe220c14fe2f0d5f8b18c839796d2b5915f3077b3bfa3d6d5bf066d164a3a4",{"version":"99bad98e5b9a61da13557aad9121e423a71cab28686d9c0a979413bbef00c0f3","affectsGlobalScope":true},{"version":"eebfa5888ebfdbd1053f9f918e5b0ef200d8301ac0e6b19dc318fb0cdddbe541","signature":"93ee1b9963e1e04b6ac4cf23143cf2538031b552abb2b9f150feebd37247cc26"},"52f5c39e78a90c1d8ed7db18f39d890b2e8464a3f44d4233617893f6648e317d","a5a9d9140415b4563b8fbdb9abec882accc12b5ad26c9071041331a4b275f29a",{"version":"85cc9fdfca89085a65924923cf92b3cd56fde3978df028891dde47913b106eb1","signature":"3f06fbd98cdd8815ad6cf5210ede4d4adec9f26259fb4bf0f01b790b9b1b2b08"},{"version":"dcb9e01a9d9a082e04237a0a4798b3dd52dde76b89d0ca7368a98865d621256a","signature":"2484eadb07b268ddd6b1ca48525126faacaabfe37fb08d37f1fd514919bb681b"},{"version":"2be44db98f7ce25ae1513ab29d5169f7a0969c5a04d5fbdcb3a5922be1adb444","signature":"0e0947bc071a3dc1414ab99304190e1174365e86e90aad223091e9c07460c393"},{"version":"fbeff035b4daef70f40c47ef5d430d07c55c28108bc0e31ea1348c3df6bbefcf","signature":"6a4cc82f07a76e2c185ce63e1968e3f381b16acbdb0c853db9c4faa58355e12e"},"bcb6ea18f23dae2c48459d7b86d3adccd6898f824fcbf9da08b935f559896580","08121bfe3615014d8f667e5069070aefc21dae4301036bee0bbfddafbf90acb5","bd668758155705240608e67d5664e5a34f2ec38724c95272dc134ce3c150dfe1","3cead9ca29f12a4887d2000124caaabe24b8146578b8630890001e7a9cdaa735",{"version":"df93bb67d5ae7be3d599323de42e12cb8da59f0b490c3186ae91d493632b5e36","affectsGlobalScope":true},{"version":"3db18a74a8bd232014ae25149007e17fabe41993a13ff02d2e3e390933344b4e","affectsGlobalScope":true},"cb3a8a70a87f0b1872244cd0bbe5bb5ddd602d820b7766a4d515b04eab8c40c7",{"version":"a1f3faba121854904b8d040f26cdbc60d9f2e8add0b756e3e9937e5f94424564","signature":"b2b4eaf580951cc3905d32c484229e33bdedb76db366857b46b0183bacee718a"},"7e771891adaa85b690266bc37bd6eb43bc57eecc4b54693ead36467e7369952a","a69c09dbea52352f479d3e7ac949fde3d17b195abe90b045d619f747b38d6d1a",{"version":"ca72190df0eb9b09d4b600821c8c7b6c9747b75a1c700c4d57dc0bb72abc074c","affectsGlobalScope":true},"21a167fec8f933752fb8157f06d28fab6817af3ad9b0bdb1908a10762391eab9",{"version":"bb65c6267c5d6676be61acbf6604cf0a4555ac4b505df58ac15c831fcbff4e3e","affectsGlobalScope":true},"374ca798f244e464346f14301dc2a8b4b111af1a83b49fffef5906c338a1f922","5a94487653355b56018122d92392beb2e5f4a6c63ba5cef83bbe1c99775ef713",{"version":"d5135ad93b33adcce80b18f8065087934cdc1730d63db58562edcf017e1aad9b","affectsGlobalScope":true},"82408ed3e959ddc60d3e9904481b5a8dc16469928257af22a3f7d1a3bc7fd8c4","dab86d9604fe40854ef3c0a6f9e8948873dc3509213418e5e457f410fd11200f","bb9c4ffa5e6290c6980b63c815cdd1625876dadb2efaf77edbe82984be93e55e","489532ff54b714f0e0939947a1c560e516d3ae93d51d639ab02e907a0e950114","f30bb836526d930a74593f7b0f5c1c46d10856415a8f69e5e2fc3db80371e362","14b5aa23c5d0ae1907bc696ac7b6915d88f7d85799cc0dc2dcf98fbce2c5a67c","5c439dafdc09abe4d6c260a96b822fa0ba5be7203c71a63ab1f1423cd9e838ea",{"version":"6b526a5ec4a401ca7c26cfe6a48e641d8f30af76673bad3b06a1b4504594a960","affectsGlobalScope":true},{"version":"816ad2e607a96de5bcac7d437f843f5afd8957f1fa5eefa6bba8e4ed7ca8fd84","affectsGlobalScope":true},"cec36af22f514322f870e81d30675c78df82ae8bf4863f5fd4e4424c040c678d","d903fafe96674bc0b2ac38a5be4a8fc07b14c2548d1cdb165a80ea24c44c0c54","5eec82ac21f84d83586c59a16b9b8502d34505d1393393556682fe7e7fde9ef2","04eb6578a588d6a46f50299b55f30e3a04ef27d0c5a46c57d8fcc211cd530faa","8d3c583a07e0c37e876908c2d5da575019f689df8d9fa4c081d99119d53dba22","2c828a5405191d006115ab34e191b8474bc6c86ffdc401d1a9864b1b6e088a58",{"version":"e8b18c6385ff784228a6f369694fcf1a6b475355ba89090a88de13587a9391d5","affectsGlobalScope":true},"d076fede3cb042e7b13fc29442aaa03a57806bc51e2b26a67a01fbc66a7c0c12","7c013aa892414a7fdcfd861ae524a668eaa3ede8c7c0acafaf611948122c8d93","b0973c3cbcdc59b37bf477731d468696ecaf442593ec51bab497a613a580fe30",{"version":"4989e92ba5b69b182d2caaea6295af52b7dc73a4f7a2e336a676722884e7139d","affectsGlobalScope":true},{"version":"b3624aed92dab6da8484280d3cb3e2f4130ec3f4ef3f8201c95144ae9e898bb6","affectsGlobalScope":true},"5153a2fd150e46ce57bb3f8db1318d33f6ad3261ed70ceeff92281c0608c74a3","210d54cd652ec0fec8c8916e4af59bb341065576ecda039842f9ffb2e908507c","36b03690b628eab08703d63f04eaa89c5df202e5f1edf3989f13ad389cd2c091","0effadd232a20498b11308058e334d3339cc5bf8c4c858393e38d9d4c0013dcf","25846d43937c672bab7e8195f3d881f93495df712ee901860effc109918938cc","fd93cee2621ff42dabe57b7be402783fd1aa69ece755bcba1e0290547ae60513","1b952304137851e45bc009785de89ada562d9376177c97e37702e39e60c2f1ff","69ee23dd0d215b09907ad30d23f88b7790c93329d1faf31d7835552a10cf7cbf","44b8b584a338b190a59f4f6929d072431950c7bd92ec2694821c11bce180c8a5","23b89798789dffbd437c0c423f5d02d11f9736aea73d6abf16db4f812ff36eda","223c37f62ce09a3d99e77498acdee7b2705a4ae14552fbdb4093600cd9164f3f",{"version":"970a90f76d4d219ad60819d61f5994514087ba94c985647a3474a5a3d12714ed","affectsGlobalScope":true},"e10177274a35a9d07c825615340b2fcde2f610f53f3fb40269fd196b4288dda6","4c8525f256873c7ba3135338c647eaf0ca7115a1a2805ae2d0056629461186ce","3c13ef48634e7b5012fcf7e8fce7496352c2d779a7201389ca96a2a81ee4314d","5d0a25ec910fa36595f85a67ac992d7a53dd4064a1ba6aea1c9f14ab73a023f2",{"version":"f0900cd5d00fe1263ff41201fb8073dbeb984397e4af3b8002a5c207a30bdc33","affectsGlobalScope":true},{"version":"4c50342e1b65d3bee2ed4ab18f84842d5724ad11083bd666d8705dc7a6079d80","affectsGlobalScope":true},"06d7c42d256f0ce6afe1b2b6cfbc97ab391f29dadb00dd0ae8e8f23f5bc916c3","ec4bd1b200670fb567920db572d6701ed42a9641d09c4ff6869768c8f81b404c","e59a892d87e72733e2a9ca21611b9beb52977be2696c7ba4b216cbbb9a48f5aa",{"version":"da26af7362f53d122283bc69fed862b9a9fe27e01bc6a69d1d682e0e5a4df3e6","affectsGlobalScope":true},"8a300fa9b698845a1f9c41ecbe2c5966634582a8e2020d51abcace9b55aa959e",{"version":"ab9b9a36e5284fd8d3bf2f7d5fcbc60052f25f27e4d20954782099282c60d23e","affectsGlobalScope":true},"8dbe725f8d237e70310977afcfa011629804d101ebaa0266cafda6b61ad72236","6cb4fc48f1299d40a9d84282f691e722dfea2d53c92721861166c713928e6f93","eced89c8bebaf21ffa42987fcb24bc4f753db4761b8e90031b605508ed6eef5f","858d0d831826c6eb563df02f7db71c90e26deadd0938652096bea3cc14899700","d1c89db652113258e4ba4bbdf5cc7a2a3a600403d4d864a2087b95186253cd5b","11a90d2cb2eaf7fdf931a63b58279e8161f1477a1bd1e914ae026c1bbf9afed3","af18e30f3ba06e9870b61dfa4a109215caabdaa337590c51b4a044a9f338ce96","ace603f7b60599f2dcdbd71c07137b60a747dd33be540f4a294b890f9e0b89dc","7658fbdd425c656fb1849b44932ae7431e8c3198d22c65ce1490deb582743b52","7786c75c1b46e93b33c63dccf689143a5f47ff451a6b3bd9b10e4801cdeadcc2","615b7264db151461b896cd79719414d63f7b2b2100b275828e53cab95a356e2f","31491a01ed7466e0b3b0ef8407f2524683055eceb955b1d5ccf7096129468b39","f4b12f7dde4fc0e386648318481bdcfe861b566be246bebf0e8a11ebd909adf9","e8966f7c424780bb0b9d411ebe13eda8555ca15aa675603316c2952bc027b0e3","df0e5f3c4a518111d160cf3bebc9a3ac7d39c6e3bfb7a21d43c304896c3015e2","df4e2f161f74870708c2cc5e1036a6405b878496408fda1ee50d5b10e50d6601","bf791da347fb1c0ffc1e2fcd35867e64bb8355270ae26278198c521bdcf94569","e0e0e3c068e145fbb322120979299ff130ffdd39f0dcd0d5aeaa9f3f8a0d01d9","fde91356172e35b9ea68bbdf33721f7c80307a4ce65b82105eac800e9e744995","9bd5e5a4a1e66b35efe3c48ddac1116537ef86e041717f3a9b9f1e060c74efa6","d7e4a5f4ccfb749c3033fafc233073b4d1dcca0249785186c589602a81f9d86f","68161b6f3004fc10f8bb47a4986cef13c3b0728fb1ca3e1dc7316227d09b2c8d","fe4a4ed3b2acff61e22d55bd43456809679ed1887b180d42db000939fdd2f2fd","dd6a4b050f1016c0318291b42c98ab068e07e208b1ae8e4e27167c2b8007406f","4a569e0f6b06bbfd08cbf394f080226608892a9266e3df0b796e611a6b6f5b48","241e274f2e15e8b9d3c8f4187dec9b4f61b4439b5753f10de71b33ca6d4244d8","3feec212c0aeb91e5a6e62caaf9f128954590210f8c302910ea377c088f6b61a","85a093a118d4f2267af7d2aee23444685c05c5d02aaa4482fa4c77317767c8f8","c0ec47ff5e245830967be282a58e34bde9b46a5fb8cb521921558d13583b0746","66e756d6327409624e4e67abeb6f47704e9fd167f0af061b14b2770a55b662f1","98f0142f3b31b14979ba4164ac073ea9e4ec1c4b226b0a48b80bd0081d24f88a","6feb067eed5e88f784c69125ddb10759d73cc710ea0ef1464684b0ac495d4b45",{"version":"aed2c405c9847d860d47e1ee083a8363aa8ee9aa7a1dc37681337f9cf570126d","signature":"ec7ffb6cebb230fb92af9b7fd96b4a9fd9df8001305603bbef1770a77dad9a57"}],"options":{"allowSyntheticDefaultImports":true,"composite":true,"esModuleInterop":true,"jsx":1,"module":99,"sourceMap":false,"strict":true,"target":99,"useDefineForClassFields":true},"fileIdsList":[[59,136],[136],[56,136],[90,136],[93,136],[94,99,127,136],[95,106,107,114,124,135,136],[95,96,106,114,136],[97,136],[98,99,107,115,136],[99,124,132,136],[100,102,106,114,136],[101,136],[102,103,136],[106,136],[104,106,136],[106,107,108,124,135,136],[106,107,108,121,124,127,136],[136,140],[102,109,114,124,135,136],[106,107,109,110,114,124,132,135,136],[109,111,124,132,135,136],[90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142],[106,112,136],[113,135,136],[102,106,114,124,136],[115,136],[116,136],[93,117,136],[118,134,136,140],[119,136],[120,136],[106,121,122,136],[121,123,136,138],[94,106,124,125,126,127,136],[94,124,126,136],[124,125,136],[127,136],[128,136],[106,130,131,136],[130,131,136],[99,114,124,132,136],[133,136],[114,134,136],[94,109,120,135,136],[99,136],[124,136,137],[136,138],[136,139],[94,99,106,108,117,124,135,136,138,140],[124,136,141],[136,165,169],[59,60,61,62,136],[63,136],[59,61,62,63,136,164,166,167],[59,61,136,166],[60,136],[60,65,66,68,72,74,77,136],[65,66,67,72,74,77,136],[136,171,172],[66,72,74,76,136],[69,74,136],[136,150],[136,150,162],[136,147,148,149,151,162],[136,153],[136,150,157,161,164],[136,152,164],[136,155,157,160,161,164],[136,155,157,158,160,161,164],[136,147,148,149,150,151,153,154,155,156,157,161,164],[136,146,147,148,149,150,151,153,154,155,156,157,158,160,161,162,163],[136,146,164],[136,157,158,159,161,164],[136,160,164],[136,150,156,161,164],[136,154,162],[136,146],[136,165,173],[86,136],[82,83,85,106,107,109,111,114,124,132,135,136,141,143,144,145,164],[82,136],[83,136],[84,85,136],[66,69,74,77,136],[136,168],[64,68,74,136],[69,72,74,136],[66,68,69,72,73,74,77,136],[57,136],[69,74,80,88,136],[69,71,74,75,78,79,136],[74,87,136],[70,136],[72,88,136],[77,136],[69,74,87,136],[135,136,165,170,174],[60,61,62,136],[61,62,63,136,164,166,167],[61,136,166],[56],[69,74],[69,74,87],[72],[77],[165]],"referencedMap":[[61,1],[59,2],[56,2],[57,3],[90,4],[91,4],[93,5],[94,6],[95,7],[96,8],[97,9],[98,10],[99,11],[100,12],[101,13],[102,14],[103,14],[105,15],[104,16],[106,15],[107,17],[108,18],[92,19],[142,2],[109,20],[110,21],[111,22],[143,23],[112,24],[113,25],[114,26],[115,27],[116,28],[117,29],[118,30],[119,31],[120,32],[121,33],[122,33],[123,34],[124,35],[126,36],[125,37],[127,38],[128,39],[129,2],[130,40],[131,41],[132,42],[133,43],[134,44],[135,45],[136,46],[137,47],[138,48],[139,49],[140,50],[141,51],[70,2],[170,52],[63,53],[64,54],[168,55],[167,56],[65,57],[66,58],[68,59],[60,2],[171,2],[173,60],[172,2],[67,2],[144,2],[166,2],[77,61],[76,62],[148,63],[147,64],[150,65],[154,66],[151,64],[156,67],[153,68],[158,69],[163,2],[159,70],[162,71],[164,72],[152,73],[160,74],[161,75],[157,76],[149,63],[155,77],[145,2],[146,78],[62,2],[11,2],[13,2],[12,2],[2,2],[14,2],[15,2],[16,2],[17,2],[18,2],[19,2],[20,2],[21,2],[3,2],[4,2],[25,2],[22,2],[23,2],[24,2],[26,2],[27,2],[28,2],[5,2],[29,2],[30,2],[31,2],[32,2],[6,2],[36,2],[33,2],[34,2],[35,2],[37,2],[7,2],[38,2],[43,2],[44,2],[39,2],[40,2],[41,2],[42,2],[8,2],[48,2],[45,2],[46,2],[47,2],[49,2],[9,2],[50,2],[51,2],[52,2],[53,2],[54,2],[1,2],[10,2],[55,2],[174,79],[87,80],[165,81],[83,82],[82,2],[84,83],[85,2],[86,84],[72,85],[169,86],[69,87],[73,88],[74,89],[58,90],[81,91],[80,92],[75,93],[71,94],[79,95],[89,96],[78,96],[88,97],[175,98]],"exportedModulesMap":[[61,2],[59,2],[56,2],[57,3],[90,4],[91,4],[93,5],[94,6],[95,7],[96,8],[97,9],[98,10],[99,11],[100,12],[101,13],[102,14],[103,14],[105,15],[104,16],[106,15],[107,17],[108,18],[92,19],[142,2],[109,20],[110,21],[111,22],[143,23],[112,24],[113,25],[114,26],[115,27],[116,28],[117,29],[118,30],[119,31],[120,32],[121,33],[122,33],[123,34],[124,35],[126,36],[125,37],[127,38],[128,39],[129,2],[130,40],[131,41],[132,42],[133,43],[134,44],[135,45],[136,46],[137,47],[138,48],[139,49],[140,50],[141,51],[70,2],[170,52],[63,99],[64,54],[168,100],[167,101],[65,57],[66,58],[68,59],[60,2],[171,2],[173,60],[172,2],[67,2],[144,2],[166,2],[77,61],[76,62],[148,63],[147,64],[150,65],[154,66],[151,64],[156,67],[153,68],[158,69],[163,2],[159,70],[162,71],[164,72],[152,73],[160,74],[161,75],[157,76],[149,63],[155,77],[145,2],[146,78],[62,2],[11,2],[13,2],[12,2],[2,2],[14,2],[15,2],[16,2],[17,2],[18,2],[19,2],[20,2],[21,2],[3,2],[4,2],[25,2],[22,2],[23,2],[24,2],[26,2],[27,2],[28,2],[5,2],[29,2],[30,2],[31,2],[32,2],[6,2],[36,2],[33,2],[34,2],[35,2],[37,2],[7,2],[38,2],[43,2],[44,2],[39,2],[40,2],[41,2],[42,2],[8,2],[48,2],[45,2],[46,2],[47,2],[49,2],[9,2],[50,2],[51,2],[52,2],[53,2],[54,2],[1,2],[10,2],[55,2],[174,79],[87,80],[165,81],[83,82],[82,2],[84,83],[85,2],[86,84],[72,85],[169,86],[69,87],[73,88],[74,89],[58,102],[80,103],[75,104],[79,105],[89,106],[78,106],[88,97],[175,107]],"semanticDiagnosticsPerFile":[61,59,56,57,90,91,93,94,95,96,97,98,99,100,101,102,103,105,104,106,107,108,92,142,109,110,111,143,112,113,114,115,116,117,118,119,120,121,122,123,124,126,125,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,70,170,63,64,168,167,65,66,68,60,171,173,172,67,144,166,77,76,148,147,150,154,151,156,153,158,163,159,162,164,152,160,161,157,149,155,145,146,62,11,13,12,2,14,15,16,17,18,19,20,21,3,4,25,22,23,24,26,27,28,5,29,30,31,32,6,36,33,34,35,37,7,38,43,44,39,40,41,42,8,48,45,46,47,49,9,50,51,52,53,54,1,10,55,174,87,165,83,82,84,85,86,72,169,69,73,74,58,81,80,75,71,79,89,78,88,175],"latestChangedDtsFile":"./vite.config.d.ts"},"version":"4.9.4"} -------------------------------------------------------------------------------- /updater.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.1.2", 3 | "notes": "Getdeck Desktop 0.1.2 is now available. Changelog: https://github.com/Getdeck/Getdeck-Desktop/releases/tag/0.1.2", 4 | "platforms": { 5 | "darwin-x86_64": { 6 | "signature": "dW50cnVzdGVkIGNvbW1lbnQ6IHNpZ25hdHVyZSBmcm9tIHRhdXJpIHNlY3JldCBrZXkKUlVRVFRkcVFNbnZ3ZXdxSXN3RzdGWVBDb1h2WEJiVnN2S056UmVROVozRUtOYU5CR1NPQ1VkRndFZjJYeTlwZ1RtdGhhNVUrb2daSC9CNFJYbGptVGxyV3ZIVFppQ2IzNVFvPQp0cnVzdGVkIGNvbW1lbnQ6IHRpbWVzdGFtcDoxNjkwNDcwMjcwCWZpbGU6R2V0ZGVjay1EZXNrdG9wLmFwcC50YXIuZ3oKRVpNTEIyaU15WHVYWEptbDM2TGx3Ynl4TkVCTzFZZFh3SjAxWWJpSWRyUlZPY29sZVgwSTZpcUIxOXdaaXpuREFLWlFkOGo4cDJHalRhcG0wM240Q3c9PQo=", 7 | "url": "https://github.com/Getdeck/Getdeck-Desktop/releases/download/0.1.2/Getdeck-Desktop.app.tar.gz" 8 | }, 9 | "linux-x86_64": { 10 | "signature": "dW50cnVzdGVkIGNvbW1lbnQ6IHNpZ25hdHVyZSBmcm9tIHRhdXJpIHNlY3JldCBrZXkKUlVRVFRkcVFNbnZ3ZXlyR3VNQzRmK0VORDExbGVUTVJyblQ5dm9QaXlCV0trU3AvUFlxYTJqOGY0NlNLUTN6aEVIMVR2YnJ5d2JyR3QwSElFTFFiZXFlaG5nMUxVcHpkY3c0PQp0cnVzdGVkIGNvbW1lbnQ6IHRpbWVzdGFtcDoxNjkwNDcwMTI5CWZpbGU6Z2V0ZGVjay1kZXNrdG9wXzAuMS4yX2FtZDY0LkFwcEltYWdlLnRhci5negpCOXJQNFordnM2S1JsL2p2WmEwekR3bHVta3ByWFFMVzYwUlRxNTRyaHVOd2NRd21zcVBRVzZ6Q2VsR3ltWjY4UjhYVjFUQzVPN2dkSHlZMGp4dFlDdz09Cg==", 11 | "url": "https://github.com/Getdeck/Getdeck-Desktop/releases/download/0.1.2/getdeck-desktop_0.1.2_amd64.AppImage.tar.gz" 12 | }, 13 | "windows-x86_64": { 14 | "signature": "dW50cnVzdGVkIGNvbW1lbnQ6IHNpZ25hdHVyZSBmcm9tIHRhdXJpIHNlY3JldCBrZXkKUlVRVFRkcVFNbnZ3ZXdNNXQza3NwUDArcnpScEdwTnpXUHdsNGFBTzl0Q24wRVN0MSthZXFYUllSM3JrYlppVGt4MExITWh2SXJUY2t3MEpacWpKendmelpVSXFoSnJmY2dFPQp0cnVzdGVkIGNvbW1lbnQ6IHRpbWVzdGFtcDoxNjkwNDcwMjQ2CWZpbGU6R2V0ZGVjay1EZXNrdG9wXzAuMS4yX3g2NF9lbi1VUy5tc2kuemlwClNBM2pySmM2Wi9hYXhaWmZWTFlUUHYzRWl0dFR6MjNVVnczMmUxSU1NMzhNQllFblpNejJUUFZqNU1LUmdDeXZVcThsV3loUHNGRFJvQmNqUlBuY0JnPT0K", 15 | "url": "https://github.com/Getdeck/Getdeck-Desktop/releases/download/0.1.2/getdeck-desktop_0.1.2_x64_en-US.msi.zip" 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /vite.config.d.ts: -------------------------------------------------------------------------------- 1 | declare const _default: import("vite").UserConfigExport; 2 | export default _default; 3 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vite"; 2 | import vue from '@vitejs/plugin-vue' 3 | import vuetify, { transformAssetUrls } from 'vite-plugin-vuetify' 4 | import { fileURLToPath, URL } from 'node:url' 5 | 6 | export default defineConfig({ 7 | // prevent vite from obscuring rust errors 8 | clearScreen: false, 9 | // Tauri expects a fixed port, fail if that port is not available 10 | server: { 11 | strictPort: true, 12 | }, 13 | define: { 'process.env': {} }, 14 | // to make use of `TAURI_PLATFORM`, `TAURI_ARCH`, `TAURI_FAMILY`, 15 | // `TAURI_PLATFORM_VERSION`, `TAURI_PLATFORM_TYPE` and `TAURI_DEBUG` 16 | // env variables 17 | envPrefix: ["VITE_", "TAURI_"], 18 | resolve: { 19 | alias: { 20 | '@': fileURLToPath(new URL('./src', import.meta.url)) 21 | }, 22 | extensions: [ 23 | '.js', 24 | '.json', 25 | '.jsx', 26 | '.mjs', 27 | '.ts', 28 | '.tsx', 29 | '.vue', 30 | ], 31 | }, 32 | build: { 33 | // Tauri supports es2021 34 | target: ["es2021", "chrome100", "safari13"], 35 | // don't minify for debug builds 36 | minify: !process.env.TAURI_DEBUG ? "esbuild" : false, 37 | // produce sourcemaps for debug builds 38 | sourcemap: !!process.env.TAURI_DEBUG, 39 | }, 40 | plugins: [ 41 | vue({ 42 | template: { transformAssetUrls } 43 | }), 44 | // https://github.com/vuetifyjs/vuetify-loader/tree/next/packages/vite-plugin 45 | vuetify({ 46 | autoImport: true, 47 | styles: { 48 | configFile: 'src/styles/settings.scss', 49 | }, 50 | }), 51 | ], 52 | }); 53 | --------------------------------------------------------------------------------