├── .github └── workflows │ └── releases_build.yaml ├── .gitignore ├── LICENSE ├── README.md ├── aa_test.go ├── app.go ├── backup └── commadn.txt ├── build ├── README.md ├── appicon.png ├── darwin │ └── Info.plist └── windows │ ├── icon.ico │ ├── info.json │ ├── installer │ ├── project.nsi │ └── wails_tools.nsh │ └── wails.exe.manifest ├── common ├── consts │ └── appConfig.go └── structs │ ├── BaseInfo.go │ └── redis.go ├── frontend ├── .gitignore ├── favicon.ico ├── index.html ├── package.json ├── src │ ├── App.vue │ ├── Aside.vue │ ├── Header.vue │ ├── Welcome.vue │ ├── assets │ │ ├── css │ │ │ ├── font.css │ │ │ └── reset.css │ │ ├── fonts │ │ │ ├── JetBrainsMono-Bold.woff2 │ │ │ ├── JetBrainsMono-BoldItalic.woff2 │ │ │ ├── JetBrainsMono-ExtraBold.woff2 │ │ │ ├── JetBrainsMono-ExtraBoldItalic.woff2 │ │ │ ├── JetBrainsMono-ExtraLight.woff2 │ │ │ ├── JetBrainsMono-ExtraLightItalic.woff2 │ │ │ ├── JetBrainsMono-Italic.woff2 │ │ │ ├── JetBrainsMono-Light.woff2 │ │ │ ├── JetBrainsMono-LightItalic.woff2 │ │ │ ├── JetBrainsMono-Medium.woff2 │ │ │ ├── JetBrainsMono-MediumItalic.woff2 │ │ │ ├── JetBrainsMono-Regular.woff2 │ │ │ ├── JetBrainsMono-SemiBold.woff2 │ │ │ ├── JetBrainsMono-SemiBoldItalic.woff2 │ │ │ ├── JetBrainsMono-Thin.woff2 │ │ │ ├── JetBrainsMono-ThinItalic.woff2 │ │ │ ├── OFL.txt │ │ │ └── nunito-v16-latin-regular.woff2 │ │ ├── images │ │ │ ├── comeon.gif │ │ │ ├── logo-universal.png │ │ │ └── quick │ │ │ │ ├── about.png │ │ │ │ ├── conn.png │ │ │ │ ├── export.png │ │ │ │ ├── import.png │ │ │ │ ├── select.png │ │ │ │ └── table.png │ │ ├── line.png │ │ ├── logo.png │ │ └── logos │ │ │ └── logo.png │ ├── i18n │ │ ├── index.js │ │ └── messages │ │ │ ├── en.json │ │ │ ├── fr.json │ │ │ └── zh-Hans.json │ ├── index.html │ ├── main.css │ ├── main.js │ ├── public │ │ ├── favicon.ico │ │ ├── info │ │ │ ├── close.png │ │ │ └── key.png │ │ ├── leftNavigation │ │ │ └── redis.png │ │ └── status │ │ │ ├── history.png │ │ │ ├── kv.png │ │ │ ├── memory.png │ │ │ └── service.png │ ├── router │ │ └── index.js │ ├── views │ │ ├── pop │ │ │ ├── AddListValue.vue │ │ │ └── NewConnection.vue │ │ └── redis │ │ │ ├── Content_hash_info.vue │ │ │ ├── Content_list_info.vue │ │ │ ├── Content_set_info.vue │ │ │ ├── Content_status.vue │ │ │ ├── Content_stream_info.vue │ │ │ ├── Content_string_info.vue │ │ │ └── Content_zset_info.vue │ └── wailsjs │ │ ├── go │ │ ├── bindings.d.ts │ │ ├── bindings.js │ │ ├── models.ts │ │ └── package.json │ │ └── runtime │ │ ├── ipcdev.js │ │ ├── package.json │ │ ├── runtime.d.ts │ │ ├── runtime.js │ │ └── runtimedev.js ├── vite.config.js └── wailsjs │ └── runtime │ ├── ipcdev.js │ ├── package.json │ ├── runtime.d.ts │ ├── runtime.js │ └── runtimedev.js ├── go.mod ├── go.sum ├── jb_beam.png ├── kit ├── dialog.go ├── os │ ├── file.go │ └── os_test.go ├── redis │ ├── redis.go │ └── redis_test.go ├── response.go ├── strKit.go └── update.go ├── main.go ├── menu ├── macOSMenu.go ├── menu.go └── winMenu.go ├── scripts ├── build-macos-arm.sh ├── build-macos-intel.sh ├── build-macos.sh ├── build-windows.sh ├── build.sh └── install-wails-cli.sh ├── service ├── general.go └── redis.go ├── todb.png └── wails.json /.github/workflows/releases_build.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | tags: 4 | - "v*" 5 | jobs: 6 | package: 7 | strategy: 8 | matrix: 9 | go-version: [1.17] 10 | os: [macos-11, windows-latest] 11 | runs-on: ${{ matrix.os }} 12 | steps: 13 | - name: Install Go 14 | uses: actions/setup-go@v2 15 | with: 16 | go-version: ${{ matrix.go-version }} 17 | - name: Checkout code 18 | uses: actions/checkout@v2 19 | - name: Set Version 20 | run: echo "VERSION=$(git rev-parse --short HEAD)" >> $GITHUB_ENV 21 | # - name: Get Wails dependencies 22 | # run: sudo apt update && sudo apt install -y libgtk-3-dev libwebkit2gtk-4.0-dev 23 | # if: matrix.os == 'ubuntu-latest' 24 | - name: Get Wails 25 | run: go install github.com/wailsapp/wails/v2/cmd/wails@v2.0.0-beta.34 26 | 27 | # - name: Import Code-Signing Certificates 28 | # uses: Apple-Actions/import-codesign-certs@v1 29 | # with: 30 | # # The certificates in a PKCS12 file encoded as a base64 string 31 | # p12-file-base64: ${{ secrets.APPLE_DEVELOPER_CERTIFICATE_P12_BASE64 }} 32 | # # The password used to import the PKCS12 file. 33 | # p12-password: ${{ secrets.APPLE_DEVELOPER_CERTIFICATE_PASSWORD }} 34 | if: matrix.os == 'macos-11' 35 | 36 | - name: Install gon via HomeBrew for code signing and app notarization 37 | run: | 38 | brew tap mitchellh/gon 39 | brew install mitchellh/gon/gon 40 | if: matrix.os == 'macos-11' 41 | 42 | - name: Build and Sign MacOS Binaries 43 | # env: 44 | # AC_USERNAME: ${{ secrets.AC_USERNAME }} 45 | # AC_PASSWORD: ${{ secrets.AC_PASSWORD }} 46 | run: | 47 | ./build-macos.sh 48 | if: matrix.os == 'macos-11' 49 | # - name: Build package linux 50 | # run: | 51 | # export PATH=$PATH:$(go env GOPATH)/bin 52 | # echo "building on ${{ matrix.os }}" 53 | # echo ${{ env.GITHUB_REF }} 54 | # echo ${{ env.GITHUB_HEAD_REF }} 55 | # mkdir -p ~/.wails 56 | # cp wails.json ~/.wails/ 57 | # export LOG_LEVEL=debug 58 | # export GODEBUG=1 59 | # wails build 60 | # tar -czvf riftshare.tar.gz ./build/riftshare 61 | # # wget https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage 62 | # # chmod +x linuxdeploy*.AppImage 63 | # # ls ./ 64 | # # ./linuxdeploy*.AppImage --appdir AppDir --executable ./build/riftshare --desktop-file=riftshare.AppImage.desktop --icon-file=appicon--512.png --output appimage 65 | # if: matrix.os == 'ubuntu-latest' 66 | - name: Build package windows 67 | run: | 68 | $GP = (go env GOPATH) 69 | $env:path = "$env:path;$GP\bin" 70 | echo "building on ${{ matrix.os }}" 71 | choco install mingw 72 | wails build -platform windows/amd64 -clean 73 | Compress-Archive -Path .\build\bin\ToDb.exe -DestinationPath .\ToDb-windows-amd64.zip 74 | if: matrix.os == 'windows-latest' 75 | - name: upload artifacts macOS-AMD64 76 | uses: actions/upload-artifact@v2 77 | with: 78 | name: todb-macOS 79 | path: ToDb-*.zip 80 | if: matrix.os == 'macos-11' 81 | # - name: upload artifact linux 82 | # uses: actions/upload-artifact@v2-preview 83 | # with: 84 | # name: riftshare-linux 85 | # path: riftshare.tar.gz 86 | # if: matrix.os == 'ubuntu-latest' 87 | # - name: upload artifact linux appimage 88 | # uses: actions/upload-artifact@v2-preview 89 | # with: 90 | # name: riftshare-linux-appimage 91 | # path: riftshare-${{ env.VERSION }}-x86_64.AppImage 92 | # if: matrix.os == 'ubuntu-latest' 93 | - name: upload artifact windows 94 | uses: actions/upload-artifact@v2 95 | with: 96 | name: todb-windows 97 | path: ToDb-windows-amd64.zip 98 | if: matrix.os == 'windows-latest' 99 | 100 | release: 101 | runs-on: ubuntu-latest 102 | needs: package 103 | steps: 104 | - name: Download Windows Package 105 | uses: actions/download-artifact@v2 106 | with: 107 | name: todb-windows 108 | - name: Download macOS packages 109 | uses: actions/download-artifact@v2 110 | with: 111 | name: todb-macOS 112 | - name: Create Release 113 | id: create_release 114 | uses: "marvinpinto/action-automatic-releases@latest" 115 | with: 116 | repo_token: "${{ secrets.GITHUB_TOKEN }}" 117 | prerelease: false 118 | files: ToDb*.zip 119 | # - name: Download linux package 120 | # uses: actions/download-artifact@v1 121 | # with: 122 | # name: riftshare-linux 123 | # - name: Upload Linux package to release 124 | # id: upload-linux-release-asset 125 | # uses: actions/upload-release-asset@v1 126 | # env: 127 | # GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 128 | # with: 129 | # upload_url: ${{ steps.create_release.outputs.upload_url }} 130 | # asset_path: ./riftshare-linux/riftshare.tar.gz 131 | # asset_name: riftshare_${{ github.ref }}_linux_x86_64.tar.gz 132 | # asset_content_type: application/octet-stream 133 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ToDb.iml 2 | .vscode/settings.json 3 | build/bin/ToDb.app/Contents/Info.plist 4 | build/bin/ToDb.app/Contents/MacOS/ToDb 5 | build/bin/ToDb.app/Contents/Resources/iconfile.icns 6 | .idea/ToDb.iml 7 | .idea/ 8 | .history/ 9 | node_modules/ 10 | build/bin/ 11 | .gitignore 12 | /frontend/package-lock.json 13 | /frontend/package.json.md5 14 | backup/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 木兰宽松许可证, 第2版 2 | 3 | 木兰宽松许可证, 第2版 4 | 2020年1月 http://license.coscl.org.cn/MulanPSL2 5 | 6 | 7 | 您对“软件”的复制、使用、修改及分发受木兰宽松许可证,第2版(“本许可证”)的如下条款的约束: 8 | 9 | 0. 定义 10 | 11 | “软件”是指由“贡献”构成的许可在“本许可证”下的程序和相关文档的集合。 12 | 13 | “贡献”是指由任一“贡献者”许可在“本许可证”下的受版权法保护的作品。 14 | 15 | “贡献者”是指将受版权法保护的作品许可在“本许可证”下的自然人或“法人实体”。 16 | 17 | “法人实体”是指提交贡献的机构及其“关联实体”。 18 | 19 | “关联实体”是指,对“本许可证”下的行为方而言,控制、受控制或与其共同受控制的机构,此处的控制是指有受控方或共同受控方至少50%直接或间接的投票权、资金或其他有价证券。 20 | 21 | 1. 授予版权许可 22 | 23 | 每个“贡献者”根据“本许可证”授予您永久性的、全球性的、免费的、非独占的、不可撤销的版权许可,您可以复制、使用、修改、分发其“贡献”,不论修改与否。 24 | 25 | 2. 授予专利许可 26 | 27 | 每个“贡献者”根据“本许可证”授予您永久性的、全球性的、免费的、非独占的、不可撤销的(根据本条规定撤销除外)专利许可,供您制造、委托制造、使用、许诺销售、销售、进口其“贡献”或以其他方式转移其“贡献”。前述专利许可仅限于“贡献者”现在或将来拥有或控制的其“贡献”本身或其“贡献”与许可“贡献”时的“软件”结合而将必然会侵犯的专利权利要求,不包括对“贡献”的修改或包含“贡献”的其他结合。如果您或您的“关联实体”直接或间接地,就“软件”或其中的“贡献”对任何人发起专利侵权诉讼(包括反诉或交叉诉讼)或其他专利维权行动,指控其侵犯专利权,则“本许可证”授予您对“软件”的专利许可自您提起诉讼或发起维权行动之日终止。 28 | 29 | 3. 无商标许可 30 | 31 | “本许可证”不提供对“贡献者”的商品名称、商标、服务标志或产品名称的商标许可,但您为满足第4条规定的声明义务而必须使用除外。 32 | 33 | 4. 分发限制 34 | 35 | 您可以在任何媒介中将“软件”以源程序形式或可执行形式重新分发,不论修改与否,但您必须向接收者提供“本许可证”的副本,并保留“软件”中的版权、商标、专利及免责声明。 36 | 37 | 5. 免责声明与责任限制 38 | 39 | “软件”及其中的“贡献”在提供时不带任何明示或默示的担保。在任何情况下,“贡献者”或版权所有者不对任何人因使用“软件”或其中的“贡献”而引发的任何直接或间接损失承担责任,不论因何种原因导致或者基于何种法律理论,即使其曾被建议有此种损失的可能性。 40 | 41 | 6. 语言 42 | “本许可证”以中英文双语表述,中英文版本具有同等法律效力。如果中英文版本存在任何冲突不一致,以中文版为准。 43 | 44 | 条款结束 45 | 46 | 如何将木兰宽松许可证,第2版,应用到您的软件 47 | 48 | 如果您希望将木兰宽松许可证,第2版,应用到您的新软件,为了方便接收者查阅,建议您完成如下三步: 49 | 50 | 1, 请您补充如下声明中的空白,包括软件名、软件的首次发表年份以及您作为版权人的名字; 51 | 52 | 2, 请您在软件包的一级目录下创建以“LICENSE”为名的文件,将整个许可证文本放入该文件中; 53 | 54 | 3, 请将如下声明文本放入每个源文件的头部注释中。 55 | 56 | Copyright (c) [Year] [name of copyright holder] 57 | [Software Name] is licensed under Mulan PSL v2. 58 | You can use this software according to the terms and conditions of the Mulan PSL v2. 59 | You may obtain a copy of Mulan PSL v2 at: 60 | http://license.coscl.org.cn/MulanPSL2 61 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 62 | See the Mulan PSL v2 for more details. 63 | 64 | 65 | Mulan Permissive Software License,Version 2 66 | 67 | Mulan Permissive Software License,Version 2 (Mulan PSL v2) 68 | January 2020 http://license.coscl.org.cn/MulanPSL2 69 | 70 | Your reproduction, use, modification and distribution of the Software shall be subject to Mulan PSL v2 (this License) with the following terms and conditions: 71 | 72 | 0. Definition 73 | 74 | Software means the program and related documents which are licensed under this License and comprise all Contribution(s). 75 | 76 | Contribution means the copyrightable work licensed by a particular Contributor under this License. 77 | 78 | Contributor means the Individual or Legal Entity who licenses its copyrightable work under this License. 79 | 80 | Legal Entity means the entity making a Contribution and all its Affiliates. 81 | 82 | Affiliates means entities that control, are controlled by, or are under common control with the acting entity under this License, ‘control’ means direct or indirect ownership of at least fifty percent (50%) of the voting power, capital or other securities of controlled or commonly controlled entity. 83 | 84 | 1. Grant of Copyright License 85 | 86 | Subject to the terms and conditions of this License, each Contributor hereby grants to you a perpetual, worldwide, royalty-free, non-exclusive, irrevocable copyright license to reproduce, use, modify, or distribute its Contribution, with modification or not. 87 | 88 | 2. Grant of Patent License 89 | 90 | Subject to the terms and conditions of this License, each Contributor hereby grants to you a perpetual, worldwide, royalty-free, non-exclusive, irrevocable (except for revocation under this Section) patent license to make, have made, use, offer for sale, sell, import or otherwise transfer its Contribution, where such patent license is only limited to the patent claims owned or controlled by such Contributor now or in future which will be necessarily infringed by its Contribution alone, or by combination of the Contribution with the Software to which the Contribution was contributed. The patent license shall not apply to any modification of the Contribution, and any other combination which includes the Contribution. If you or your Affiliates directly or indirectly institute patent litigation (including a cross claim or counterclaim in a litigation) or other patent enforcement activities against any individual or entity by alleging that the Software or any Contribution in it infringes patents, then any patent license granted to you under this License for the Software shall terminate as of the date such litigation or activity is filed or taken. 91 | 92 | 3. No Trademark License 93 | 94 | No trademark license is granted to use the trade names, trademarks, service marks, or product names of Contributor, except as required to fulfill notice requirements in Section 4. 95 | 96 | 4. Distribution Restriction 97 | 98 | You may distribute the Software in any medium with or without modification, whether in source or executable forms, provided that you provide recipients with a copy of this License and retain copyright, patent, trademark and disclaimer statements in the Software. 99 | 100 | 5. Disclaimer of Warranty and Limitation of Liability 101 | 102 | THE SOFTWARE AND CONTRIBUTION IN IT ARE PROVIDED WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED. IN NO EVENT SHALL ANY CONTRIBUTOR OR COPYRIGHT HOLDER BE LIABLE TO YOU FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO ANY DIRECT, OR INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES ARISING FROM YOUR USE OR INABILITY TO USE THE SOFTWARE OR THE CONTRIBUTION IN IT, NO MATTER HOW IT’S CAUSED OR BASED ON WHICH LEGAL THEORY, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 103 | 104 | 6. Language 105 | 106 | THIS LICENSE IS WRITTEN IN BOTH CHINESE AND ENGLISH, AND THE CHINESE VERSION AND ENGLISH VERSION SHALL HAVE THE SAME LEGAL EFFECT. IN THE CASE OF DIVERGENCE BETWEEN THE CHINESE AND ENGLISH VERSIONS, THE CHINESE VERSION SHALL PREVAIL. 107 | 108 | END OF THE TERMS AND CONDITIONS 109 | 110 | How to Apply the Mulan Permissive Software License,Version 2 (Mulan PSL v2) to Your Software 111 | 112 | To apply the Mulan PSL v2 to your work, for easy identification by recipients, you are suggested to complete following three steps: 113 | 114 | i Fill in the blanks in following statement, including insert your software name, the year of the first publication of your software, and your name identified as the copyright owner; 115 | 116 | ii Create a file named “LICENSE” which contains the whole context of this License in the first directory of your software package; 117 | 118 | iii Attach the statement to the appropriate annotated syntax at the beginning of each source file. 119 | 120 | 121 | Copyright (c) [Year] [name of copyright holder] 122 | [Software Name] is licensed under Mulan PSL v2. 123 | You can use this software according to the terms and conditions of the Mulan PSL v2. 124 | You may obtain a copy of Mulan PSL v2 at: 125 | http://license.coscl.org.cn/MulanPSL2 126 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 127 | See the Mulan PSL v2 for more details. 128 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 |
3 |

4 | 5 | # `ToDb` 6 | 7 | `ToDb`一款连接数据库的工具 8 | 9 | ## 特点 10 | 11 | - 支持多种数据库 12 | - 全平台 13 | - 配置可视化 14 | 15 | ## 准备处理的数据库 16 | 17 | - [x] `Redis` 18 | - [ ] `MySQL` 19 | - [ ] ...... 20 | 21 | ## 预览 22 | 23 | ![](https://symbol-node.oss-cn-shanghai.aliyuncs.com/git/ToDb/1.png) 24 | 25 | ![](https://symbol-node.oss-cn-shanghai.aliyuncs.com/git/ToDb/2.png) 26 | 27 | ## 常见问题 28 | 29 | - 它是什么? 30 | 31 | 这是一款数据库连接工具,可以连接`Redis`、`MySQL`等数据库,可视化处理 32 | 33 | - 这个项目针对的是哪些人? 34 | 35 | 这个项目针对的是程序员,但是也有非程序员的使用者,比如`DBA`、管理员等 36 | 37 | ## 贡献者 38 | 39 | --- 40 | 41 | 42 | 43 | 44 | ## 特别提及 45 | 46 | 如果没有以下成员,此项目或许就该胎死腹中了: 47 | 48 | - [reciment](https://gitee.com/recimentPackage) 49 | - [ProudMuBai](https://gitee.com/proudmubai) 50 | 51 | ## 特别感谢 52 | 53 | A special thank you to JetBrains for donating licenses to us!

54 | Please click the logo to let them know your appreciation!

55 | 56 |

57 | -------------------------------------------------------------------------------- /aa_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: symbol 3 | * @Date: 2022-05-04 10:22:52 4 | * @LastEditors: symbol 5 | * @LastEditTime: 2022-05-19 17:15:31 6 | * @FilePath: /todb/aa_test.go 7 | * @Description: 8 | * 9 | * Copyright (c) 2022 by symbol, All Rights Reserved. 10 | */ 11 | package main 12 | 13 | import ( 14 | "ToDb/kit" 15 | "ToDb/service" 16 | "context" 17 | "fmt" 18 | "testing" 19 | ) 20 | 21 | func TestTt(t *testing.T) { 22 | aa := `{"alias":"11","hostURL":"11","port":"11","username":"11","password":"11","savePassword":true}` 23 | service.Redis().Ok(context.Background(), aa) 24 | } 25 | 26 | func TestReadFile(t *testing.T) { 27 | //communication.LoadingBaseHistoryInfo() 28 | service.Redis().LoadingHistoryInfo("这是2") 29 | } 30 | 31 | func TestGetHistoryInfo(t *testing.T) { 32 | fmt.Println(service.Redis().LoadingHistoryInfo("这是2")) 33 | } 34 | 35 | func TestFilePath(t *testing.T) { 36 | fmt.Println(service.Redis().LoadingBaseHistoryInfo(context.Background())) 37 | } 38 | 39 | func TestTest(t *testing.T) { 40 | //redis返回数据: [1:2:3, 1:2:4, 1111, 12312] 41 | //sl := []string{"1:2:3:9:5", "1:2:4:6", "1111", "12312:333"} 42 | sl := []string{"234234", "1:2:4", "1:2:3", "1111:33333", "12312", "1:2:5", "1111:2222"} 43 | v := kit.StrKit().PackageTree(sl) 44 | fmt.Printf("%s\n", v) 45 | } 46 | 47 | func TestMP(t *testing.T) { 48 | //v := []int{0, 2, 2, 0, 0} 49 | sl := []string{"234234", "1:2:4", "1:2:3", "1111:33333", "12312", "1:2:5", "1111:2222"} 50 | fmt.Println(kit.StrKit().BubbleDescSort(sl)) 51 | } 52 | 53 | func TestApp_GetNodeData(t *testing.T) { 54 | fmt.Println(service.Redis().GetNodeData(context.Background(), "redis", "localhost", "13")) 55 | } 56 | -------------------------------------------------------------------------------- /app.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "ToDb/kit" 5 | "ToDb/menu" 6 | communication "ToDb/service" 7 | "context" 8 | "encoding/json" 9 | "errors" 10 | "net/http" 11 | "strings" 12 | 13 | "github.com/tidwall/gjson" 14 | 15 | "github.com/wailsapp/wails/v2/pkg/runtime" 16 | ) 17 | 18 | // App struct 19 | type App struct { 20 | ctx context.Context 21 | } 22 | 23 | // NewApp creates a new App application struct 24 | // NewApp 创建一个新的 App 应用程序 25 | func NewApp() *App { 26 | return &App{} 27 | } 28 | 29 | // startup is called at application startup 30 | // startup 在应用程序启动时调用 31 | func (a *App) startup(ctx context.Context) { 32 | // Perform your setup here 33 | // 在这里执行初始化设置 34 | a.ctx = ctx 35 | menu.InitMenu(ctx, icon) 36 | } 37 | 38 | // domReady is called after the front-end dom has been loaded 39 | // domReady 在前端Dom加载完毕后调用 40 | func (a *App) domReady(ctx context.Context) { 41 | // Add your action here 42 | // 在这里添加你的操作 43 | } 44 | 45 | // beforeClose is called when the application is about to quit, 46 | // either by clicking the window close button or calling runtime.Quit. 47 | // Returning true will cause the application to continue, 48 | // false will continue shutdown as normal. 49 | // beforeClose在单击窗口关闭按钮或调用runtime.Quit即将退出应用程序时被调用. 50 | // 返回 true 将导致应用程序继续,false 将继续正常关闭。 51 | func (a *App) beforeClose(ctx context.Context) (prevent bool) { 52 | return false 53 | } 54 | 55 | // shutdown is called at application termination 56 | // 在应用程序终止时被调用 57 | func (a *App) shutdown(ctx context.Context) { 58 | // Perform your teardown here 59 | // 在此处做一些资源释放的操作 60 | } 61 | 62 | // ImportConn 导入 63 | func (a *App) ImportConn() { 64 | err := communication.General().ImportConn(a.ctx) 65 | if err != nil { 66 | kit.DiaLogKit().DefaultDialog(a.ctx, "错误", err.Error(), icon) 67 | return 68 | } 69 | kit.DiaLogKit().DefaultDialog(a.ctx, "成功", "导入成功", icon) 70 | // 导入成功后窗口重载 71 | runtime.WindowReload(a.ctx) 72 | } 73 | 74 | // ExportConn 导出 75 | func (a *App) ExportConn() { 76 | communication.General().ExportConn(a.ctx) 77 | } 78 | 79 | // TestConnection 测试连接 80 | func (a *App) TestConnection(connectionInfo string) { 81 | //var responseJson lib.JsonResponse 82 | code, message := communication.Redis().RedisPing(connectionInfo) 83 | if code != http.StatusOK { 84 | kit.DiaLogKit().DefaultDialog(a.ctx, "错误", message, icon) 85 | } else { 86 | kit.DiaLogKit().DefaultDialog(a.ctx, "成功", message, icon) 87 | } 88 | } 89 | 90 | // Ok 确定按钮进行的操作 91 | func (a App) Ok(connectionInfo string) string { 92 | var responseJson kit.JsonResponse 93 | code, message := communication.Redis().Ok(a.ctx, connectionInfo) 94 | responseJson = kit.JsonResponse{ 95 | Code: code, 96 | Message: message, 97 | } 98 | if code != http.StatusOK { 99 | kit.DiaLogKit().DefaultDialog(a.ctx, "错误", message, icon) 100 | } 101 | runtime.WindowReload(a.ctx) 102 | return responseJson.String() 103 | } 104 | 105 | // LoadingConnKey 加载已保存的连接信息 106 | func (a *App) LoadingConnKey() string { 107 | return communication.Redis().LoadingBaseHistoryInfo(a.ctx) 108 | } 109 | 110 | // LoadingConnInfo 获取链接信息详情 111 | func (a *App) LoadingConnInfo(dbType, fileName string) string { 112 | var code int 113 | var message string 114 | switch dbType { 115 | case "redis": 116 | code, message = communication.Redis().LoadingHistoryInfo(fileName) 117 | default: 118 | code = http.StatusNotFound 119 | message = "暂不支持的数据库类型" 120 | } 121 | if code != http.StatusOK { 122 | kit.DiaLogKit().DefaultDialog(a.ctx, "错误", message, icon) 123 | return "" 124 | } 125 | return message 126 | } 127 | 128 | // LoadingDbResource 加载数据库资源消耗信息 129 | func (a *App) LoadingDbResource(key string) string { 130 | return communication.Redis().LoadingDbResource(a.ctx, key) 131 | } 132 | 133 | // ChangeRightWindowStyle 改变右侧窗口样式 134 | func (a *App) ChangeRightWindowStyle(parentNode, nextParentNode, node string) string { 135 | // 连接类型 136 | connType := gjson.Get(parentNode, "connType").String() 137 | // 文件名 138 | fileName := gjson.Get(parentNode, "title").String() 139 | switch connType { 140 | case "redis": 141 | default: 142 | kit.DiaLogKit().DefaultDialog(a.ctx, "错误", "暂不支持的数据库类型", icon) 143 | return "" 144 | } 145 | // 获取db信息 146 | dbId := gjson.Get(nextParentNode, "key").String() 147 | // 获取指定的节点key 148 | fullKey := gjson.Get(node, "fullStr").String() 149 | // 指定的key数据类型 150 | dataType, err := communication.Redis().GetValueType(a.ctx, fileName, dbId, fullKey) 151 | if err != nil { 152 | kit.DiaLogKit().DefaultDialog(a.ctx, "错误", err.Error(), icon) 153 | } 154 | return strings.ToLower(dataType) 155 | } 156 | 157 | // GetNodeData 获取节点数据 158 | func (a *App) GetNodeData(connType, connName, nodeIdStr string) string { 159 | strs := "" 160 | var err error 161 | switch connType { 162 | case "redis": 163 | // 获取redis节点数据 164 | strs, err = communication.Redis().GetNodeData(a.ctx, connType, connName, nodeIdStr) 165 | default: 166 | err = errors.New("暂不支持此连接类型") 167 | } 168 | if err != nil { 169 | kit.DiaLogKit().DefaultDialog(a.ctx, "错误", err.Error(), icon) 170 | } 171 | if strs == "null" { 172 | strs = "" 173 | } 174 | return strs 175 | } 176 | 177 | // RedisGetData 从redis获取数据 178 | func (a *App) RedisGetData(connType, connName, nodeIdStr, key string) string { 179 | v := "" 180 | switch connType { 181 | case "redis": 182 | // 获取redis节点数据 183 | getValue, err := communication.Redis().RedisGetData(a.ctx, connType, connName, nodeIdStr, key) 184 | if err != nil { 185 | kit.DiaLogKit().DefaultDialog(a.ctx, "错误", err.Error(), icon) 186 | return "" 187 | } 188 | _v, _ := json.Marshal(getValue) 189 | v = string(_v) 190 | default: 191 | v = "暂不支持" 192 | kit.DiaLogKit().DefaultDialog(a.ctx, "错误", v, icon) 193 | return v 194 | } 195 | return v 196 | } 197 | 198 | // RedisReName redis key重命名 199 | func (a *App) RedisReName(connType, connName, nodeIdStr, oldKey, newKey string) { 200 | v := "" 201 | switch connType { 202 | case "redis": 203 | v = communication.Redis().RedisReName(a.ctx, connType, connName, 204 | nodeIdStr, oldKey, newKey) 205 | default: 206 | v = "暂不支持" 207 | } 208 | if v != "success" { 209 | kit.DiaLogKit().DefaultDialog(a.ctx, "错误", v, icon) 210 | } else { 211 | kit.DiaLogKit().DefaultDialog(a.ctx, "成功", "修改成功", icon) 212 | } 213 | } 214 | 215 | // RedisUpTtl 更新redis剩余时间 216 | func (a *App) RedisUpTtl(connType, connName, nodeIdStr, key, ttlStr string) { 217 | v := "" 218 | switch connType { 219 | case "redis": 220 | v = communication.Redis().RedisUpTtl(a.ctx, connType, connName, nodeIdStr, key, ttlStr) 221 | default: 222 | v = "暂不支持" 223 | } 224 | if v != "success" { 225 | kit.DiaLogKit().DefaultDialog(a.ctx, "错误", v, icon) 226 | } else { 227 | kit.DiaLogKit().DefaultDialog(a.ctx, "成功", "修改成功", icon) 228 | } 229 | } 230 | 231 | // RedisDelKey 删除键 232 | func (a *App) RedisDelKey(connType, connName, nodeIdStr, key string) { 233 | selection, _ := runtime.MessageDialog(a.ctx, runtime.MessageDialogOptions{ 234 | Type: runtime.InfoDialog, 235 | Title: "删除", 236 | Message: "确定删除该key吗?", 237 | Buttons: []string{"Cancel", "Ok"}, 238 | }) 239 | if selection != "Ok" { 240 | return 241 | } 242 | v := communication.Redis().RedisDel(a.ctx, connType, connName, nodeIdStr, key) 243 | 244 | if v != "success" { 245 | kit.DiaLogKit().DefaultDialog(a.ctx, "错误", v, icon) 246 | } else { 247 | kit.DiaLogKit().DefaultDialog(a.ctx, "成功", "删除成功", icon) 248 | } 249 | } 250 | 251 | // RedisSaveStringValue 更新redis值 252 | func (a *App) RedisSaveStringValue(connType, connName, nodeIdStr, key, value, ttl string) { 253 | var err error 254 | switch connType { 255 | case "redis": 256 | err = communication.Redis().RedisUpdateStringValue(a.ctx, connType, connName, 257 | nodeIdStr, key, value, ttl) 258 | default: 259 | err = errors.New("暂不支持") 260 | } 261 | if err != nil { 262 | kit.DiaLogKit().DefaultDialog(a.ctx, "错误", err.Error(), icon) 263 | } else { 264 | kit.DiaLogKit().DefaultDialog(a.ctx, "成功", "修改成功", icon) 265 | } 266 | } 267 | -------------------------------------------------------------------------------- /backup/commadn.txt: -------------------------------------------------------------------------------- 1 | string 2 | SET "1:2:34" "你好啊😂" 3 | 4 | hash 5 | HMSET "1:2:hash" "New field" "New value" "123" "321" 6 | 7 | list 8 | RPUSH "1:2:list" "New member" "12312213" 9 | 10 | set 11 | SADD "1:2:set" "New member" "sdfsdf" 12 | 13 | stream 14 | XADD "1:2:stream" 1650445322163-0 "New key" "New value" 15 | XADD "1:2:stream" 21312312312312-0 "New key" "New value" 16 | 17 | zset 18 | ZADD "1:2:zset" 12 "321" 0 "New member" -------------------------------------------------------------------------------- /build/README.md: -------------------------------------------------------------------------------- 1 | # Build Directory 2 | 3 | The build directory is used to house all the build files and assets for your application. 4 | 5 | The structure is: 6 | 7 | * bin - Output directory 8 | * dialog - Icons for dialogs 9 | * tray - Icons for the system tray 10 | * mac - MacOS specific files 11 | * linux - Linux specific files 12 | * windows - Windows specific files 13 | 14 | ## Dialog Icons 15 | 16 | Place any PNG file in this directory to be able to use them in message dialogs. 17 | The files should have names in the following format: `name[-(light|dark)][2x].png` 18 | 19 | Examples: 20 | 21 | * `mypic.png` - Standard definition icon with ID `mypic` 22 | * `mypic-light.png` - Standard definition icon with ID `mypic`, used when system theme is light 23 | * `mypic-dark.png` - Standard definition icon with ID `mypic`, used when system theme is dark 24 | * `mypic2x.png` - High definition icon with ID `mypic` 25 | * `mypic-light2x.png` - High definition icon with ID `mypic`, used when system theme is light 26 | * `mypic-dark2x.png` - High definition icon with ID `mypic`, used when system theme is dark 27 | 28 | ### Order of preference 29 | 30 | Icons are selected with the following order of preference: 31 | 32 | For High Definition displays: 33 | * name-(theme)2x.png 34 | * name2x.png 35 | * name-(theme).png 36 | * name.png 37 | 38 | For Standard Definition displays: 39 | * name-(theme).png 40 | * name.png 41 | 42 | ## Tray 43 | 44 | Place any PNG file in this directory to be able to use them as tray icons. 45 | The name of the filename will be the ID to reference the image. 46 | 47 | Example: 48 | 49 | * `mypic.png` - May be referenced using `runtime.Tray.SetIcon("mypic")` 50 | 51 | ## Mac 52 | 53 | The `darwin` directory holds files specific to Mac builds, such as `Info.plist`. 54 | These may be customised and used as part of the build. To return these files to the default state, simply delete them and 55 | build with the `-package` flag. 56 | 57 | ## Windows 58 | 59 | The `windows` directory contains the manifest and rc files used when building with the `-package` flag. 60 | These may be customised for your application. To return these files to the default state, simply delete them and 61 | build with the `-package` flag. -------------------------------------------------------------------------------- /build/appicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xingcxb/ToDb/e039f594c6d84c56122e5d4ee4bb0e67e403f56c/build/appicon.png -------------------------------------------------------------------------------- /build/darwin/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | CFBundlePackageTypeAPPL 4 | CFBundleName{{.Info.ProductName}} 5 | CFBundleExecutable{{.Name}} 6 | CFBundleIdentifiercom.wails.{{.Name}} 7 | CFBundleVersion{{.Info.ProductVersion}} 8 | CFBundleGetInfoString{{.Info.Comments}} 9 | CFBundleShortVersionString{{.Info.ProductVersion}} 10 | CFBundleIconFileiconfile 11 | LSMinimumSystemVersion10.13.0 12 | NSHighResolutionCapabletrue 13 | NSHumanReadableCopyright{{.Info.Copyright}} 14 | -------------------------------------------------------------------------------- /build/windows/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xingcxb/ToDb/e039f594c6d84c56122e5d4ee4bb0e67e403f56c/build/windows/icon.ico -------------------------------------------------------------------------------- /build/windows/info.json: -------------------------------------------------------------------------------- 1 | { 2 | "fixed": { 3 | "file_version": "{{.Info.ProductVersion}}" 4 | }, 5 | "info": { 6 | "0000": { 7 | "ProductVersion": "{{.Info.ProductVersion}}", 8 | "CompanyName": "{{.Info.CompanyName}}", 9 | "FileDescription": "{{.Info.ProductName}}", 10 | "LegalCopyright": "{{.Info.Copyright}}", 11 | "ProductName": "{{.Info.ProductName}}", 12 | "Comments": "{{.Info.Comments}}" 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /build/windows/installer/project.nsi: -------------------------------------------------------------------------------- 1 | Unicode true 2 | 3 | #### 4 | ## Please note: Template replacements don't work in this file. They are provided with default defines like 5 | ## mentioned underneath. 6 | ## If the keyword is not defined, "wails_tools.nsh" will populate them with the values from ProjectInfo. 7 | ## If they are defined here, "wails_tools.nsh" will not touch them. This allows to use this project.nsi manually 8 | ## from outside of Wails for debugging and development of the installer. 9 | ## 10 | ## For development first make a wails nsis build to populate the "wails_tools.nsh": 11 | ## > wails build --target windows/amd64 --nsis 12 | ## Then you can call makensis on this file with specifying the path to your binary: 13 | ## For a AMD64 only installer: 14 | ## > makensis -DARG_WAILS_AMD64_BINARY=..\..\bin\app.exe 15 | ## For a ARM64 only installer: 16 | ## > makensis -DARG_WAILS_ARM64_BINARY=..\..\bin\app.exe 17 | ## For a installer with both architectures: 18 | ## > makensis -DARG_WAILS_AMD64_BINARY=..\..\bin\app-amd64.exe -DARG_WAILS_ARM64_BINARY=..\..\bin\app-arm64.exe 19 | #### 20 | ## The following information is taken from the ProjectInfo file, but they can be overwritten here. 21 | #### 22 | ## !define INFO_PROJECTNAME "MyProject" # Default "{{.Name}}" 23 | ## !define INFO_COMPANYNAME "MyCompany" # Default "{{.Info.CompanyName}}" 24 | ## !define INFO_PRODUCTNAME "MyProduct" # Default "{{.Info.ProductName}}" 25 | ## !define INFO_PRODUCTVERSION "1.0.0" # Default "{{.Info.ProductVersion}}" 26 | ## !define INFO_COPYRIGHT "Copyright" # Default "{{.Info.Copyright}}" 27 | ### 28 | ## !define PRODUCT_EXECUTABLE "Application.exe" # Default "${INFO_PROJECTNAME}.exe" 29 | ## !define UNINST_KEY_NAME "UninstKeyInRegistry" # Default "${INFO_COMPANYNAME}${INFO_PRODUCTNAME}" 30 | #### 31 | ## !define REQUEST_EXECUTION_LEVEL "admin" # Default "admin" see also https://nsis.sourceforge.io/Docs/Chapter4.html 32 | #### 33 | ## Include the wails tools 34 | #### 35 | !include "wails_tools.nsh" 36 | 37 | # The version information for this two must consist of 4 parts 38 | VIProductVersion "${INFO_PRODUCTVERSION}.0" 39 | VIFileVersion "${INFO_PRODUCTVERSION}.0" 40 | 41 | VIAddVersionKey "CompanyName" "${INFO_COMPANYNAME}" 42 | VIAddVersionKey "FileDescription" "${INFO_PRODUCTNAME} Installer" 43 | VIAddVersionKey "ProductVersion" "${INFO_PRODUCTVERSION}" 44 | VIAddVersionKey "FileVersion" "${INFO_PRODUCTVERSION}" 45 | VIAddVersionKey "LegalCopyright" "${INFO_COPYRIGHT}" 46 | VIAddVersionKey "ProductName" "${INFO_PRODUCTNAME}" 47 | 48 | !include "MUI.nsh" 49 | 50 | !define MUI_ICON "..\icon.ico" 51 | !define MUI_UNICON "..\icon.ico" 52 | # !define MUI_WELCOMEFINISHPAGE_BITMAP "resources\leftimage.bmp" #Include this to add a bitmap on the left side of the Welcome Page. Must be a size of 164x314 53 | !define MUI_FINISHPAGE_NOAUTOCLOSE # Wait on the INSTFILES page so the user can take a look into the details of the installation steps 54 | !define MUI_ABORTWARNING # This will warn the user if they exit from the installer. 55 | 56 | !insertmacro MUI_PAGE_WELCOME # Welcome to the installer page. 57 | # !insertmacro MUI_PAGE_LICENSE "resources\eula.txt" # Adds a EULA page to the installer 58 | !insertmacro MUI_PAGE_DIRECTORY # In which folder install page. 59 | !insertmacro MUI_PAGE_INSTFILES # Installing page. 60 | !insertmacro MUI_PAGE_FINISH # Finished installation page. 61 | 62 | !insertmacro MUI_UNPAGE_INSTFILES # Uinstalling page 63 | 64 | !insertmacro MUI_LANGUAGE "English" # Set the Language of the installer 65 | 66 | ## The following two statements can be used to sign the installer and the uninstaller. The path to the binaries are provided in %1 67 | #!uninstfinalize 'signtool --file "%1"' 68 | #!finalize 'signtool --file "%1"' 69 | 70 | Name "${INFO_PRODUCTNAME}" 71 | OutFile "..\..\bin\${INFO_PROJECTNAME}-${ARCH}-installer.exe" # Name of the installer's file. 72 | InstallDir "$PROGRAMFILES64\${INFO_COMPANYNAME}\${INFO_PRODUCTNAME}" # Default installing folder ($PROGRAMFILES is Program Files folder). 73 | ShowInstDetails show # This will always show the installation details. 74 | 75 | Function .onInit 76 | !insertmacro wails.checkArchitecture 77 | FunctionEnd 78 | 79 | Section 80 | !insertmacro wails.webview2runtime 81 | 82 | SetOutPath $INSTDIR 83 | 84 | !insertmacro wails.files 85 | 86 | CreateShortcut "$SMPROGRAMS\${INFO_PRODUCTNAME}.lnk" "$INSTDIR\${PRODUCT_EXECUTABLE}" 87 | CreateShortCut "$DESKTOP\${INFO_PRODUCTNAME}.lnk" "$INSTDIR\${PRODUCT_EXECUTABLE}" 88 | 89 | !insertmacro wails.writeUninstaller 90 | SectionEnd 91 | 92 | Section "uninstall" 93 | RMDir /r "$AppData\${PRODUCT_EXECUTABLE}" # Remove the WebView2 DataPath 94 | 95 | RMDir /r $INSTDIR 96 | 97 | Delete "$SMPROGRAMS\${INFO_PRODUCTNAME}.lnk" 98 | Delete "$DESKTOP\${INFO_PRODUCTNAME}.lnk" 99 | 100 | !insertmacro wails.deleteUninstaller 101 | SectionEnd 102 | -------------------------------------------------------------------------------- /build/windows/installer/wails_tools.nsh: -------------------------------------------------------------------------------- 1 | # DO NOT EDIT - Generated automatically by `wails build` 2 | 3 | !include "x64.nsh" 4 | !include "WinVer.nsh" 5 | !include "FileFunc.nsh" 6 | 7 | !ifndef INFO_PROJECTNAME 8 | !define INFO_PROJECTNAME "{{.Name}}" 9 | !endif 10 | !ifndef INFO_COMPANYNAME 11 | !define INFO_COMPANYNAME "{{.Info.CompanyName}}" 12 | !endif 13 | !ifndef INFO_PRODUCTNAME 14 | !define INFO_PRODUCTNAME "{{.Info.ProductName}}" 15 | !endif 16 | !ifndef INFO_PRODUCTVERSION 17 | !define INFO_PRODUCTVERSION "{{.Info.ProductVersion}}" 18 | !endif 19 | !ifndef INFO_COPYRIGHT 20 | !define INFO_COPYRIGHT "{{.Info.Copyright}}" 21 | !endif 22 | !ifndef PRODUCT_EXECUTABLE 23 | !define PRODUCT_EXECUTABLE "${INFO_PROJECTNAME}.exe" 24 | !endif 25 | !ifndef UNINST_KEY_NAME 26 | !define UNINST_KEY_NAME "${INFO_COMPANYNAME}${INFO_PRODUCTNAME}" 27 | !endif 28 | !define UNINST_KEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\${UNINST_KEY_NAME}" 29 | 30 | !ifndef REQUEST_EXECUTION_LEVEL 31 | !define REQUEST_EXECUTION_LEVEL "admin" 32 | !endif 33 | 34 | RequestExecutionLevel "${REQUEST_EXECUTION_LEVEL}" 35 | 36 | !ifdef ARG_WAILS_AMD64_BINARY 37 | !define SUPPORTS_AMD64 38 | !endif 39 | 40 | !ifdef ARG_WAILS_ARM64_BINARY 41 | !define SUPPORTS_ARM64 42 | !endif 43 | 44 | !ifdef SUPPORTS_AMD64 45 | !ifdef SUPPORTS_ARM64 46 | !define ARCH "amd64_arm64" 47 | !else 48 | !define ARCH "amd64" 49 | !endif 50 | !else 51 | !ifdef SUPPORTS_ARM64 52 | !define ARCH "arm64" 53 | !else 54 | !error "Wails: Undefined ARCH, please provide at least one of ARG_WAILS_AMD64_BINARY or ARG_WAILS_ARM64_BINARY" 55 | !endif 56 | !endif 57 | 58 | !macro wails.checkArchitecture 59 | !ifndef WAILS_WIN10_REQUIRED 60 | !define WAILS_WIN10_REQUIRED "This product is only supported on Windows 10 (Server 2016) and later." 61 | !endif 62 | 63 | !ifndef WAILS_ARCHITECTURE_NOT_SUPPORTED 64 | !define WAILS_ARCHITECTURE_NOT_SUPPORTED "This product can't be installed on the current Windows architecture. Supports: ${ARCH}" 65 | !endif 66 | 67 | ${If} ${AtLeastWin10} 68 | !ifdef SUPPORTS_AMD64 69 | ${if} ${IsNativeAMD64} 70 | Goto ok 71 | ${EndIf} 72 | !endif 73 | 74 | !ifdef SUPPORTS_ARM64 75 | ${if} ${IsNativeARM64} 76 | Goto ok 77 | ${EndIf} 78 | !endif 79 | 80 | IfSilent silentArch notSilentArch 81 | silentArch: 82 | SetErrorLevel 65 83 | Abort 84 | notSilentArch: 85 | MessageBox MB_OK "${WAILS_ARCHITECTURE_NOT_SUPPORTED}" 86 | Quit 87 | ${else} 88 | IfSilent silentWin notSilentWin 89 | silentWin: 90 | SetErrorLevel 64 91 | Abort 92 | notSilentWin: 93 | MessageBox MB_OK "${WAILS_WIN10_REQUIRED}" 94 | Quit 95 | ${EndIf} 96 | 97 | ok: 98 | !macroend 99 | 100 | !macro wails.files 101 | !ifdef SUPPORTS_AMD64 102 | ${if} ${IsNativeAMD64} 103 | File "/oname=${PRODUCT_EXECUTABLE}" "${ARG_WAILS_AMD64_BINARY}" 104 | ${EndIf} 105 | !endif 106 | 107 | !ifdef SUPPORTS_ARM64 108 | ${if} ${IsNativeARM64} 109 | File "/oname=${PRODUCT_EXECUTABLE}" "${ARG_WAILS_ARM64_BINARY}" 110 | ${EndIf} 111 | !endif 112 | !macroend 113 | 114 | !macro wails.writeUninstaller 115 | WriteUninstaller "$INSTDIR\uninstall.exe" 116 | 117 | SetRegView 64 118 | WriteRegStr HKLM "${UNINST_KEY}" "Publisher" "${INFO_COMPANYNAME}" 119 | WriteRegStr HKLM "${UNINST_KEY}" "DisplayName" "${INFO_PRODUCTNAME}" 120 | WriteRegStr HKLM "${UNINST_KEY}" "DisplayVersion" "${INFO_PRODUCTVERSION}" 121 | WriteRegStr HKLM "${UNINST_KEY}" "DisplayIcon" "$INSTDIR\${PRODUCT_EXECUTABLE}" 122 | WriteRegStr HKLM "${UNINST_KEY}" "UninstallString" "$\"$INSTDIR\uninstall.exe$\"" 123 | WriteRegStr HKLM "${UNINST_KEY}" "QuietUninstallString" "$\"$INSTDIR\uninstall.exe$\" /S" 124 | 125 | ${GetSize} "$INSTDIR" "/S=0K" $0 $1 $2 126 | IntFmt $0 "0x%08X" $0 127 | WriteRegDWORD HKLM "${UNINST_KEY}" "EstimatedSize" "$0" 128 | !macroend 129 | 130 | !macro wails.deleteUninstaller 131 | Delete "$INSTDIR\uninstall.exe" 132 | 133 | SetRegView 64 134 | DeleteRegKey HKLM "${UNINST_KEY}" 135 | !macroend 136 | 137 | # Install webview2 by launching the bootstrapper 138 | # See https://docs.microsoft.com/en-us/microsoft-edge/webview2/concepts/distribution#online-only-deployment 139 | !macro wails.webview2runtime 140 | !ifndef WAILS_INSTALL_WEBVIEW_DETAILPRINT 141 | !define WAILS_INSTALL_WEBVIEW_DETAILPRINT "Installing: WebView2 Runtime" 142 | !endif 143 | 144 | SetRegView 64 145 | # If the admin key exists and is not empty then webview2 is already installed 146 | ReadRegStr $0 HKLM "SOFTWARE\WOW6432Node\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}" "pv" 147 | ${If} $0 != "" 148 | Goto ok 149 | ${EndIf} 150 | 151 | ${If} ${REQUEST_EXECUTION_LEVEL} == "user" 152 | # If the installer is run in user level, check the user specific key exists and is not empty then webview2 is already installed 153 | ReadRegStr $0 HKCU "Software\Microsoft\EdgeUpdate\Clients{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}" "pv" 154 | ${If} $0 != "" 155 | Goto ok 156 | ${EndIf} 157 | ${EndIf} 158 | 159 | SetDetailsPrint both 160 | DetailPrint "${WAILS_INSTALL_WEBVIEW_DETAILPRINT}" 161 | SetDetailsPrint listonly 162 | 163 | InitPluginsDir 164 | CreateDirectory "$pluginsdir\webview2bootstrapper" 165 | SetOutPath "$pluginsdir\webview2bootstrapper" 166 | File "tmp\MicrosoftEdgeWebview2Setup.exe" 167 | ExecWait '"$pluginsdir\webview2bootstrapper\MicrosoftEdgeWebview2Setup.exe" /silent /install' 168 | 169 | SetDetailsPrint both 170 | ok: 171 | !macroend -------------------------------------------------------------------------------- /build/windows/wails.exe.manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | true/pm 12 | permonitorv2,permonitor 13 | 14 | 15 | -------------------------------------------------------------------------------- /common/consts/appConfig.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: symbol 3 | * @Date: 2022-05-23 14:41:57 4 | * @LastEditors: symbol 5 | * @LastEditTime: 2022-05-23 22:12:12 6 | * @FilePath: \ToDb\common\consts\appConfig.go 7 | * @Description: 应用配置 8 | * 9 | * Copyright (c) 2022 by symbol, All Rights Reserved. 10 | */ 11 | 12 | package consts 13 | 14 | // 应用 15 | const ( 16 | AppName = "ToDb" 17 | Version = "0.1.0" 18 | Description = "又一个装模作样的数据库连接工具🙄️" 19 | BtnConfirmText = "确定" 20 | BtnCancelText = "取消" 21 | ) 22 | -------------------------------------------------------------------------------- /common/structs/BaseInfo.go: -------------------------------------------------------------------------------- 1 | // package structs 2 | // @Author: easymbol 3 | // @Date: 2022/5/3 4 | // @LastEditors: easymbol 5 | // @LastEditTime: 2022/5/3 22:39 6 | // @FilePath: 7 | // @Description: 对象结构体 8 | 9 | // Copyright (c) 2022 by easymbol, All Rights Reserved. 10 | 11 | package structs 12 | 13 | // ConnectionType 基础连接信息 14 | type ConnectionType struct { 15 | Type string `tag:"类型" json:"type"` //类型 16 | Alias string `tag:"连接名" json:"alias"` //别名 17 | HostURL string `tag:"连接地址" json:"hostURL"` //连接地址 18 | Port string `tag:"端口号" json:"port"` //端口号 19 | Username string `tag:"用户名" json:"username"` //用户名 20 | Password string `tag:"密码" json:"password"` //密码 21 | } 22 | 23 | // BaseTreeInfo 基础连接信息 24 | type BaseTreeInfo struct { 25 | Label string `json:"label"` //适配tree 26 | Title string `json:"title"` //别名 27 | ConnType string `json:"connType"` //类型 28 | IconPath string `json:"iconPath"` //图标路径 29 | ConnFileAddr string `json:"ConnFileAddr"` //连接信息文件存放地址 30 | //Children string `json:"children"` //子节点 31 | } 32 | 33 | // DbTreeInfo 数据库内部信息 34 | type DbTreeInfo struct { 35 | Label string `json:"label"` //名称 36 | Title string `json:"title"` //别名 37 | Key string `json:"key"` //键 38 | IsLeaf bool `json:"isLeaf"` //是否是叶子节点 39 | Children string `json:"children"` //子节点 40 | } 41 | -------------------------------------------------------------------------------- /common/structs/redis.go: -------------------------------------------------------------------------------- 1 | package structs 2 | 3 | // @Author: symbol 4 | // @Date: 2022-04-28 10:57:39 5 | // @LastEditors: symbol 6 | // @LastEditTime: 2022-04-30 17:02:36 7 | // @FilePath: /todb/model/redis.go 8 | // @Description: redis返回信息结构体 9 | 10 | // Copyright (c) 2022 by symbol, All Rights Reserved. 11 | 12 | // GetValue redis的详情页面返回结构体 13 | type GetValue struct { 14 | Type string `json:"type"` // value类型 15 | Key string `json:"key"` // key键 16 | Ttl string `json:"ttl"` // ttl剩余时间 17 | Value interface{} `json:"value"` // value值 18 | Size int `json:"size"` // value大小 19 | CommandStr string `json:"commandStr"` // redis命令 20 | } 21 | 22 | // RedisList redis List数据 23 | type RedisList struct { 24 | Id int `json:"id"` 25 | Value string `json:"value"` 26 | } 27 | 28 | // RedisHash redis hash数据 29 | type RedisHash struct { 30 | Id int `json:"id"` 31 | Key string `json:"key"` 32 | Value string `json:"value"` 33 | } 34 | -------------------------------------------------------------------------------- /frontend/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | /dist/ 3 | -------------------------------------------------------------------------------- /frontend/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xingcxb/ToDb/e039f594c6d84c56122e5d4ee4bb0e67e403f56c/frontend/favicon.ico -------------------------------------------------------------------------------- /frontend/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | ToDb 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ToDb", 3 | "version": "0.1.0", 4 | "author": "easymbol", 5 | "private": true, 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "dependencies": { 12 | "@ant-design/icons-vue": "^6.1.0", 13 | "@soerenmartius/vue3-clipboard": "^0.1.2", 14 | "ant-design-vue": "^3.2.3", 15 | "element-plus": "^2.1.11", 16 | "timers": "^0.1.1", 17 | "vue": "^3.2.31", 18 | "vue-i18n": "^9.1.9", 19 | "vue-router": "^4.0.12" 20 | }, 21 | "devDependencies": { 22 | "@vicons/antd": "^0.12.0", 23 | "@vicons/carbon": "^0.12.0", 24 | "@vicons/fa": "^0.12.0", 25 | "@vicons/fluent": "^0.12.0", 26 | "@vicons/ionicons4": "^0.12.0", 27 | "@vicons/ionicons5": "^0.12.0", 28 | "@vicons/material": "^0.12.0", 29 | "@vicons/tabler": "^0.12.0", 30 | "@vitejs/plugin-vue": "^2.3.1", 31 | "sass": "^1.49.9", 32 | "vite": "^2.9.7", 33 | "vite-plugin-html": "^3.2.0", 34 | "vue-tsc": "^0.3.0" 35 | } 36 | } -------------------------------------------------------------------------------- /frontend/src/App.vue: -------------------------------------------------------------------------------- 1 | 11 | 42 | 43 | 59 | 65 | 89 | -------------------------------------------------------------------------------- /frontend/src/Aside.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 223 | 224 | 229 | -------------------------------------------------------------------------------- /frontend/src/Header.vue: -------------------------------------------------------------------------------- 1 | 108 | 109 | 147 | 148 | 171 | -------------------------------------------------------------------------------- /frontend/src/Welcome.vue: -------------------------------------------------------------------------------- 1 | 11 | 40 | 41 | 46 | 47 | 66 | -------------------------------------------------------------------------------- /frontend/src/assets/css/font.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: "JetBrainsMono"; 3 | src: url("../fonts/JetBrainsMono-Medium.woff2"); 4 | } 5 | -------------------------------------------------------------------------------- /frontend/src/assets/css/reset.css: -------------------------------------------------------------------------------- 1 | html, 2 | body, 3 | div, 4 | span, 5 | applet, 6 | object, 7 | iframe, 8 | h1, 9 | h2, 10 | h3, 11 | h4, 12 | h5, 13 | h6, 14 | p, 15 | blockquote, 16 | pre, 17 | a, 18 | abbr, 19 | acronym, 20 | address, 21 | big, 22 | cite, 23 | code, 24 | del, 25 | dfn, 26 | em, 27 | img, 28 | ins, 29 | kbd, 30 | q, 31 | s, 32 | samp, 33 | small, 34 | strike, 35 | strong, 36 | sub, 37 | sup, 38 | tt, 39 | var, 40 | b, 41 | u, 42 | i, 43 | center, 44 | dl, 45 | dt, 46 | dd, 47 | ol, 48 | ul, 49 | li, 50 | fieldset, 51 | form, 52 | label, 53 | legend, 54 | table, 55 | caption, 56 | tbody, 57 | tfoot, 58 | thead, 59 | tr, 60 | th, 61 | td, 62 | article, 63 | aside, 64 | canvas, 65 | details, 66 | embed, 67 | figure, 68 | figcaption, 69 | footer, 70 | header, 71 | hgroup, 72 | menu, 73 | nav, 74 | output, 75 | ruby, 76 | section, 77 | summary, 78 | time, 79 | mark, 80 | audio, 81 | video { 82 | margin: 0; 83 | padding: 0; 84 | border: 0; 85 | font-size: 100%; 86 | font: inherit; 87 | vertical-align: baseline; 88 | } 89 | /* HTML5 display-role reset for older browsers */ 90 | article, 91 | aside, 92 | details, 93 | figcaption, 94 | figure, 95 | footer, 96 | header, 97 | hgroup, 98 | menu, 99 | nav, 100 | section { 101 | display: block; 102 | } 103 | body { 104 | line-height: 1; 105 | } 106 | ol, 107 | ul { 108 | list-style: none; 109 | } 110 | blockquote, 111 | q { 112 | quotes: none; 113 | } 114 | blockquote:before, 115 | blockquote:after, 116 | q:before, 117 | q:after { 118 | content: ""; 119 | content: none; 120 | } 121 | table { 122 | border-collapse: collapse; 123 | border-spacing: 0; 124 | } 125 | -------------------------------------------------------------------------------- /frontend/src/assets/fonts/JetBrainsMono-Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xingcxb/ToDb/e039f594c6d84c56122e5d4ee4bb0e67e403f56c/frontend/src/assets/fonts/JetBrainsMono-Bold.woff2 -------------------------------------------------------------------------------- /frontend/src/assets/fonts/JetBrainsMono-BoldItalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xingcxb/ToDb/e039f594c6d84c56122e5d4ee4bb0e67e403f56c/frontend/src/assets/fonts/JetBrainsMono-BoldItalic.woff2 -------------------------------------------------------------------------------- /frontend/src/assets/fonts/JetBrainsMono-ExtraBold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xingcxb/ToDb/e039f594c6d84c56122e5d4ee4bb0e67e403f56c/frontend/src/assets/fonts/JetBrainsMono-ExtraBold.woff2 -------------------------------------------------------------------------------- /frontend/src/assets/fonts/JetBrainsMono-ExtraBoldItalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xingcxb/ToDb/e039f594c6d84c56122e5d4ee4bb0e67e403f56c/frontend/src/assets/fonts/JetBrainsMono-ExtraBoldItalic.woff2 -------------------------------------------------------------------------------- /frontend/src/assets/fonts/JetBrainsMono-ExtraLight.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xingcxb/ToDb/e039f594c6d84c56122e5d4ee4bb0e67e403f56c/frontend/src/assets/fonts/JetBrainsMono-ExtraLight.woff2 -------------------------------------------------------------------------------- /frontend/src/assets/fonts/JetBrainsMono-ExtraLightItalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xingcxb/ToDb/e039f594c6d84c56122e5d4ee4bb0e67e403f56c/frontend/src/assets/fonts/JetBrainsMono-ExtraLightItalic.woff2 -------------------------------------------------------------------------------- /frontend/src/assets/fonts/JetBrainsMono-Italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xingcxb/ToDb/e039f594c6d84c56122e5d4ee4bb0e67e403f56c/frontend/src/assets/fonts/JetBrainsMono-Italic.woff2 -------------------------------------------------------------------------------- /frontend/src/assets/fonts/JetBrainsMono-Light.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xingcxb/ToDb/e039f594c6d84c56122e5d4ee4bb0e67e403f56c/frontend/src/assets/fonts/JetBrainsMono-Light.woff2 -------------------------------------------------------------------------------- /frontend/src/assets/fonts/JetBrainsMono-LightItalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xingcxb/ToDb/e039f594c6d84c56122e5d4ee4bb0e67e403f56c/frontend/src/assets/fonts/JetBrainsMono-LightItalic.woff2 -------------------------------------------------------------------------------- /frontend/src/assets/fonts/JetBrainsMono-Medium.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xingcxb/ToDb/e039f594c6d84c56122e5d4ee4bb0e67e403f56c/frontend/src/assets/fonts/JetBrainsMono-Medium.woff2 -------------------------------------------------------------------------------- /frontend/src/assets/fonts/JetBrainsMono-MediumItalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xingcxb/ToDb/e039f594c6d84c56122e5d4ee4bb0e67e403f56c/frontend/src/assets/fonts/JetBrainsMono-MediumItalic.woff2 -------------------------------------------------------------------------------- /frontend/src/assets/fonts/JetBrainsMono-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xingcxb/ToDb/e039f594c6d84c56122e5d4ee4bb0e67e403f56c/frontend/src/assets/fonts/JetBrainsMono-Regular.woff2 -------------------------------------------------------------------------------- /frontend/src/assets/fonts/JetBrainsMono-SemiBold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xingcxb/ToDb/e039f594c6d84c56122e5d4ee4bb0e67e403f56c/frontend/src/assets/fonts/JetBrainsMono-SemiBold.woff2 -------------------------------------------------------------------------------- /frontend/src/assets/fonts/JetBrainsMono-SemiBoldItalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xingcxb/ToDb/e039f594c6d84c56122e5d4ee4bb0e67e403f56c/frontend/src/assets/fonts/JetBrainsMono-SemiBoldItalic.woff2 -------------------------------------------------------------------------------- /frontend/src/assets/fonts/JetBrainsMono-Thin.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xingcxb/ToDb/e039f594c6d84c56122e5d4ee4bb0e67e403f56c/frontend/src/assets/fonts/JetBrainsMono-Thin.woff2 -------------------------------------------------------------------------------- /frontend/src/assets/fonts/JetBrainsMono-ThinItalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xingcxb/ToDb/e039f594c6d84c56122e5d4ee4bb0e67e403f56c/frontend/src/assets/fonts/JetBrainsMono-ThinItalic.woff2 -------------------------------------------------------------------------------- /frontend/src/assets/fonts/OFL.txt: -------------------------------------------------------------------------------- 1 | Copyright 2016 The Nunito Project Authors (contact@sansoxygen.com), 2 | 3 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 4 | This license is copied below, and is also available with a FAQ at: 5 | http://scripts.sil.org/OFL 6 | 7 | 8 | ----------------------------------------------------------- 9 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 10 | ----------------------------------------------------------- 11 | 12 | PREAMBLE 13 | The goals of the Open Font License (OFL) are to stimulate worldwide 14 | development of collaborative font projects, to support the font creation 15 | efforts of academic and linguistic communities, and to provide a free and 16 | open framework in which fonts may be shared and improved in partnership 17 | with others. 18 | 19 | The OFL allows the licensed fonts to be used, studied, modified and 20 | redistributed freely as long as they are not sold by themselves. The 21 | fonts, including any derivative works, can be bundled, embedded, 22 | redistributed and/or sold with any software provided that any reserved 23 | names are not used by derivative works. The fonts and derivatives, 24 | however, cannot be released under any other type of license. The 25 | requirement for fonts to remain under this license does not apply 26 | to any document created using the fonts or their derivatives. 27 | 28 | DEFINITIONS 29 | "Font Software" refers to the set of files released by the Copyright 30 | Holder(s) under this license and clearly marked as such. This may 31 | include source files, build scripts and documentation. 32 | 33 | "Reserved Font Name" refers to any names specified as such after the 34 | copyright statement(s). 35 | 36 | "Original Version" refers to the collection of Font Software components as 37 | distributed by the Copyright Holder(s). 38 | 39 | "Modified Version" refers to any derivative made by adding to, deleting, 40 | or substituting -- in part or in whole -- any of the components of the 41 | Original Version, by changing formats or by porting the Font Software to a 42 | new environment. 43 | 44 | "Author" refers to any designer, engineer, programmer, technical 45 | writer or other person who contributed to the Font Software. 46 | 47 | PERMISSION & CONDITIONS 48 | Permission is hereby granted, free of charge, to any person obtaining 49 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 50 | redistribute, and sell modified and unmodified copies of the Font 51 | Software, subject to the following conditions: 52 | 53 | 1) Neither the Font Software nor any of its individual components, 54 | in Original or Modified Versions, may be sold by itself. 55 | 56 | 2) Original or Modified Versions of the Font Software may be bundled, 57 | redistributed and/or sold with any software, provided that each copy 58 | contains the above copyright notice and this license. These can be 59 | included either as stand-alone text files, human-readable headers or 60 | in the appropriate machine-readable metadata fields within text or 61 | binary files as long as those fields can be easily viewed by the user. 62 | 63 | 3) No Modified Version of the Font Software may use the Reserved Font 64 | Name(s) unless explicit written permission is granted by the corresponding 65 | Copyright Holder. This restriction only applies to the primary font name as 66 | presented to the users. 67 | 68 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 69 | Software shall not be used to promote, endorse or advertise any 70 | Modified Version, except to acknowledge the contribution(s) of the 71 | Copyright Holder(s) and the Author(s) or with their explicit written 72 | permission. 73 | 74 | 5) The Font Software, modified or unmodified, in part or in whole, 75 | must be distributed entirely under this license, and must not be 76 | distributed under any other license. The requirement for fonts to 77 | remain under this license does not apply to any document created 78 | using the Font Software. 79 | 80 | TERMINATION 81 | This license becomes null and void if any of the above conditions are 82 | not met. 83 | 84 | DISCLAIMER 85 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 86 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 87 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 88 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 89 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 90 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 91 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 92 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 93 | OTHER DEALINGS IN THE FONT SOFTWARE. 94 | -------------------------------------------------------------------------------- /frontend/src/assets/fonts/nunito-v16-latin-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xingcxb/ToDb/e039f594c6d84c56122e5d4ee4bb0e67e403f56c/frontend/src/assets/fonts/nunito-v16-latin-regular.woff2 -------------------------------------------------------------------------------- /frontend/src/assets/images/comeon.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xingcxb/ToDb/e039f594c6d84c56122e5d4ee4bb0e67e403f56c/frontend/src/assets/images/comeon.gif -------------------------------------------------------------------------------- /frontend/src/assets/images/logo-universal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xingcxb/ToDb/e039f594c6d84c56122e5d4ee4bb0e67e403f56c/frontend/src/assets/images/logo-universal.png -------------------------------------------------------------------------------- /frontend/src/assets/images/quick/about.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xingcxb/ToDb/e039f594c6d84c56122e5d4ee4bb0e67e403f56c/frontend/src/assets/images/quick/about.png -------------------------------------------------------------------------------- /frontend/src/assets/images/quick/conn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xingcxb/ToDb/e039f594c6d84c56122e5d4ee4bb0e67e403f56c/frontend/src/assets/images/quick/conn.png -------------------------------------------------------------------------------- /frontend/src/assets/images/quick/export.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xingcxb/ToDb/e039f594c6d84c56122e5d4ee4bb0e67e403f56c/frontend/src/assets/images/quick/export.png -------------------------------------------------------------------------------- /frontend/src/assets/images/quick/import.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xingcxb/ToDb/e039f594c6d84c56122e5d4ee4bb0e67e403f56c/frontend/src/assets/images/quick/import.png -------------------------------------------------------------------------------- /frontend/src/assets/images/quick/select.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xingcxb/ToDb/e039f594c6d84c56122e5d4ee4bb0e67e403f56c/frontend/src/assets/images/quick/select.png -------------------------------------------------------------------------------- /frontend/src/assets/images/quick/table.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xingcxb/ToDb/e039f594c6d84c56122e5d4ee4bb0e67e403f56c/frontend/src/assets/images/quick/table.png -------------------------------------------------------------------------------- /frontend/src/assets/line.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xingcxb/ToDb/e039f594c6d84c56122e5d4ee4bb0e67e403f56c/frontend/src/assets/line.png -------------------------------------------------------------------------------- /frontend/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xingcxb/ToDb/e039f594c6d84c56122e5d4ee4bb0e67e403f56c/frontend/src/assets/logo.png -------------------------------------------------------------------------------- /frontend/src/assets/logos/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xingcxb/ToDb/e039f594c6d84c56122e5d4ee4bb0e67e403f56c/frontend/src/assets/logos/logo.png -------------------------------------------------------------------------------- /frontend/src/i18n/index.js: -------------------------------------------------------------------------------- 1 | import { createI18n } from "vue-i18n"; 2 | 3 | import zhHans from "./messages/zh-Hans.json"; 4 | import en from "./messages/en.json"; 5 | import fr from "./messages/fr.json"; 6 | 7 | const i18n = createI18n({ 8 | locale: "en", 9 | fallbackLocale: "en", 10 | messages: { 11 | "zh-Hans": zhHans, 12 | en: en, 13 | fr: fr, 14 | }, 15 | }); 16 | 17 | export default i18n; 18 | -------------------------------------------------------------------------------- /frontend/src/i18n/messages/en.json: -------------------------------------------------------------------------------- 1 | { 2 | "nav": { 3 | "home": "Home", 4 | "about": "About" 5 | }, 6 | "languages": { 7 | "en": "English", 8 | "zh-Hans": "简体中文", 9 | "fr": "Français" 10 | }, 11 | "topbar": { 12 | "minimise": "Minimise", 13 | "quit": "Quit" 14 | }, 15 | "homepage": { 16 | "welcome": "Welcome to use Wails program developed based on Vue", 17 | "getting-started": "Getting Started", 18 | "star-me": "Star Me" 19 | }, 20 | "aboutpage": { 21 | "title": "Wails Template Vue", 22 | "project-repository": "Project Repository", 23 | "author": "Author", 24 | "misitebao": "Misitebao", 25 | "wails-repository": "Wails Repository", 26 | "thanks": "Thank you all for your support🙏!" 27 | }, 28 | "global": { 29 | "not-supported": "Because it is a beta version, it can't be done for the time being, it will be completed later.", 30 | "click-link": "The currently clicked link is: " 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /frontend/src/i18n/messages/fr.json: -------------------------------------------------------------------------------- 1 | { 2 | "nav": { 3 | "home": "Page d'accueil", 4 | "about": "À propos" 5 | }, 6 | "languages": { 7 | "en": "English", 8 | "zh-Hans": "简体中文", 9 | "fr": "Français" 10 | }, 11 | "topbar": { 12 | "minimise": "Minimiser", 13 | "quit": "Quitter" 14 | }, 15 | "homepage": { 16 | "welcome": "Bienvenue à utiliser le programme Wails développé sur la base de Vue", 17 | "getting-started": "Commencer", 18 | "star-me": "Étoile moi" 19 | }, 20 | "aboutpage": { 21 | "title": "Wails Template Vue", 22 | "project-repository": "Référentiel de projets", 23 | "author": "Auteur", 24 | "misitebao": "Misitebao", 25 | "wails-repository": "Wails Dépôt", 26 | "thanks": "Merci à tous pour votre soutien🙏!" 27 | }, 28 | "global": { 29 | "not-supported": "Parce qu'il s'agit d'une version bêta, cela ne peut pas être fait pour le moment, il sera complété plus tard.", 30 | "click-link": "Le lien actuellement cliqué est: " 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /frontend/src/i18n/messages/zh-Hans.json: -------------------------------------------------------------------------------- 1 | { 2 | "nav": { 3 | "home": "主页", 4 | "about": "关于" 5 | }, 6 | "languages": { 7 | "en": "English", 8 | "zh-Hans": "简体中文", 9 | "fr": "Français" 10 | }, 11 | "topbar": { 12 | "minimise": "最小化", 13 | "quit": "退出" 14 | }, 15 | "homepage": { 16 | "welcome": "欢迎使用基于Vue开发的Wails程序", 17 | "getting-started": "新手入门", 18 | "star-me": "给我点星" 19 | }, 20 | "aboutpage": { 21 | "title": "Wails Template Vue", 22 | "project-repository": "项目仓库", 23 | "author": "作者", 24 | "misitebao": "米司特包", 25 | "wails-repository": "Wails 仓库", 26 | "thanks": "感谢各位大佬的支持🙏!" 27 | }, 28 | "global": { 29 | "not-supported": "由于是测试版,所以暂时做不了,后续会完成它。", 30 | "click-link": "当前点击的链接是: " 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /frontend/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | test 7 | 8 | 9 | 10 | 11 |
12 | 13 |
Please enter your name below 👇
14 |
15 | 16 | 17 |
18 |
19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /frontend/src/main.css: -------------------------------------------------------------------------------- 1 | html { 2 | background-color: rgba(27, 38, 54, 1); 3 | text-align: center; 4 | color: white; 5 | } 6 | 7 | body { 8 | margin: 0; 9 | color: white; 10 | font-family: "Nunito", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", 11 | "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", 12 | sans-serif; 13 | } 14 | 15 | @font-face { 16 | font-family: "Nunito"; 17 | font-style: normal; 18 | font-weight: 400; 19 | src: local(""), 20 | url("assets/fonts/nunito-v16-latin-regular.woff2") format("woff2"); 21 | } 22 | 23 | #app { 24 | height: 100vh; 25 | text-align: center; 26 | } 27 | 28 | .logo { 29 | display: block; 30 | width: 50%; 31 | height: 50%; 32 | margin: auto; 33 | padding: 10% 0 0; 34 | background-position: center; 35 | background-repeat: no-repeat; 36 | background-image: url("./assets/images/logo-universal.png"); 37 | background-size: 100% 100%; 38 | background-origin: content-box; 39 | } 40 | .result { 41 | height: 20px; 42 | line-height: 20px; 43 | margin: 1.5rem auto; 44 | } 45 | .input-box .btn { 46 | width: 60px; 47 | height: 30px; 48 | line-height: 30px; 49 | border-radius: 3px; 50 | border: none; 51 | margin: 0 0 0 20px; 52 | padding: 0 8px; 53 | cursor: pointer; 54 | } 55 | .input-box .btn:hover { 56 | background-image: linear-gradient(to top, #cfd9df 0%, #e2ebf0 100%); 57 | color: #333333; 58 | } 59 | 60 | .input-box .input { 61 | border: none; 62 | border-radius: 3px; 63 | outline: none; 64 | height: 30px; 65 | line-height: 30px; 66 | padding: 0 10px; 67 | background-color: rgba(240, 240, 240, 1); 68 | -webkit-font-smoothing: antialiased; 69 | } 70 | 71 | .input-box .input:hover { 72 | border: none; 73 | background-color: rgba(255, 255, 255, 1); 74 | } 75 | 76 | .input-box .input:focus { 77 | border: none; 78 | background-color: rgba(255, 255, 255, 1); 79 | } 80 | -------------------------------------------------------------------------------- /frontend/src/main.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: symbol 3 | * @Date: 2022-04-30 11:18:03 4 | * @LastEditors: symbol 5 | * @LastEditTime: 2022-05-20 16:09:11 6 | * @FilePath: /todb/frontend/src/main.js 7 | * @Description: 8 | * 9 | * Copyright (c) 2022 by symbol, All Rights Reserved. 10 | */ 11 | import { createApp } from "vue"; 12 | import App from "./App.vue"; 13 | import router from "./router"; 14 | import i18n from "./i18n"; 15 | import ElementPlus from "element-plus"; 16 | import "element-plus/dist/index.css"; 17 | import { VueClipboard } from "@soerenmartius/vue3-clipboard"; 18 | 19 | createApp(App) 20 | .use(router) 21 | .use(i18n) 22 | .use(ElementPlus) 23 | .use(VueClipboard) 24 | .mount("#app"); 25 | -------------------------------------------------------------------------------- /frontend/src/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xingcxb/ToDb/e039f594c6d84c56122e5d4ee4bb0e67e403f56c/frontend/src/public/favicon.ico -------------------------------------------------------------------------------- /frontend/src/public/info/close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xingcxb/ToDb/e039f594c6d84c56122e5d4ee4bb0e67e403f56c/frontend/src/public/info/close.png -------------------------------------------------------------------------------- /frontend/src/public/info/key.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xingcxb/ToDb/e039f594c6d84c56122e5d4ee4bb0e67e403f56c/frontend/src/public/info/key.png -------------------------------------------------------------------------------- /frontend/src/public/leftNavigation/redis.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xingcxb/ToDb/e039f594c6d84c56122e5d4ee4bb0e67e403f56c/frontend/src/public/leftNavigation/redis.png -------------------------------------------------------------------------------- /frontend/src/public/status/history.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xingcxb/ToDb/e039f594c6d84c56122e5d4ee4bb0e67e403f56c/frontend/src/public/status/history.png -------------------------------------------------------------------------------- /frontend/src/public/status/kv.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xingcxb/ToDb/e039f594c6d84c56122e5d4ee4bb0e67e403f56c/frontend/src/public/status/kv.png -------------------------------------------------------------------------------- /frontend/src/public/status/memory.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xingcxb/ToDb/e039f594c6d84c56122e5d4ee4bb0e67e403f56c/frontend/src/public/status/memory.png -------------------------------------------------------------------------------- /frontend/src/public/status/service.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xingcxb/ToDb/e039f594c6d84c56122e5d4ee4bb0e67e403f56c/frontend/src/public/status/service.png -------------------------------------------------------------------------------- /frontend/src/router/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: symbol 3 | * @Date: 2022-04-30 11:18:03 4 | * @LastEditors: symbol 5 | * @LastEditTime: 2022-05-28 17:16:50 6 | * @FilePath: \ToDb\frontend\src\router\index.js 7 | * @Description:路由 8 | * 9 | * Copyright (c) 2022 by symbol, All Rights Reserved. 10 | */ 11 | import Default from "../Welcome.vue"; 12 | import NewConnect from "../views/pop/NewConnection.vue"; 13 | import Status from "../views/redis/Content_status.vue"; 14 | import StringInfo from "../views/redis/Content_string_info.vue"; 15 | import ListInfo from "../views/redis/Content_list_info.vue"; 16 | import HashInfo from "../views/redis/Content_hash_info.vue"; 17 | import SetInfo from "../views/redis/Content_set_info.vue"; 18 | import ZSetInfo from "../views/redis/Content_zset_info.vue"; 19 | import StreamInfo from "../views/redis/Content_stream_info.vue"; 20 | import { createRouter, createWebHashHistory } from "vue-router"; 21 | 22 | const routes = [ 23 | { 24 | // 新建连接 25 | path: "/newConnection", 26 | name: "newConnection", 27 | component: NewConnect, 28 | }, 29 | { 30 | // 右侧默认页面 31 | path: "/rightContent/default", 32 | name: "default", 33 | component: Default, 34 | }, 35 | { 36 | // 右侧状态页面 37 | path: "/rightContent/status", 38 | name: "status", 39 | component: Status, 40 | }, 41 | { 42 | // 右侧详情页面string类型 43 | path: "/rightContent/value_string", 44 | name: "stringInfo", 45 | component: StringInfo, 46 | }, 47 | { 48 | // 右侧详情页面list类型 49 | path: "/rightContent/value_list", 50 | name: "listInfo", 51 | component: ListInfo, 52 | }, 53 | { 54 | // 右侧详情页面hash类型 55 | path: "/rightContent/value_hash", 56 | name: "hashInfo", 57 | component: HashInfo, 58 | }, 59 | { 60 | // 右侧详情页面set类型 61 | path: "/rightContent/value_set", 62 | name: "setInfo", 63 | component: SetInfo, 64 | }, 65 | { 66 | // 右侧详情页面stream类型 67 | path: "/rightContent/value_stream", 68 | name: "streamInfo", 69 | component: StreamInfo, 70 | }, 71 | { 72 | // 右侧详情页面zset类型 73 | path: "/rightContent/value_zset", 74 | name: "zsetInfo", 75 | component: ZSetInfo, 76 | }, 77 | ]; 78 | 79 | const router = createRouter({ 80 | history: createWebHashHistory(), 81 | routes, 82 | }); 83 | 84 | export default router; 85 | -------------------------------------------------------------------------------- /frontend/src/views/pop/AddListValue.vue: -------------------------------------------------------------------------------- 1 | 11 | -------------------------------------------------------------------------------- /frontend/src/views/pop/NewConnection.vue: -------------------------------------------------------------------------------- 1 | 78 | 79 | 154 | 155 | 164 | -------------------------------------------------------------------------------- /frontend/src/views/redis/Content_hash_info.vue: -------------------------------------------------------------------------------- 1 | 139 | 140 | 254 | 255 | 260 | -------------------------------------------------------------------------------- /frontend/src/views/redis/Content_list_info.vue: -------------------------------------------------------------------------------- 1 | 156 | 157 | 277 | 278 | 283 | -------------------------------------------------------------------------------- /frontend/src/views/redis/Content_set_info.vue: -------------------------------------------------------------------------------- 1 | 138 | 139 | 252 | 253 | 258 | -------------------------------------------------------------------------------- /frontend/src/views/redis/Content_status.vue: -------------------------------------------------------------------------------- 1 | 159 | 160 | 289 | 290 | 313 | -------------------------------------------------------------------------------- /frontend/src/views/redis/Content_stream_info.vue: -------------------------------------------------------------------------------- 1 | 140 | 141 | 255 | 256 | 261 | -------------------------------------------------------------------------------- /frontend/src/views/redis/Content_string_info.vue: -------------------------------------------------------------------------------- 1 | 133 | 134 | 299 | 300 | 308 | -------------------------------------------------------------------------------- /frontend/src/views/redis/Content_zset_info.vue: -------------------------------------------------------------------------------- 1 | 146 | 147 | 261 | 262 | 267 | -------------------------------------------------------------------------------- /frontend/src/wailsjs/go/bindings.d.ts: -------------------------------------------------------------------------------- 1 | import * as models from './models'; 2 | 3 | export interface go { 4 | "main": { 5 | "App": { 6 | ChangeRightWindowStyle(arg1:string,arg2:string,arg3:string):Promise 7 | ExportConn():Promise 8 | GetNodeData(arg1:string,arg2:string,arg3:string):Promise 9 | ImportConn():Promise 10 | LoadingConnInfo(arg1:string,arg2:string):Promise 11 | LoadingConnKey():Promise 12 | LoadingDbResource(arg1:string):Promise 13 | Ok(arg1:string):Promise 14 | RedisDelKey(arg1:string,arg2:string,arg3:string,arg4:string):Promise 15 | RedisGetData(arg1:string,arg2:string,arg3:string,arg4:string):Promise 16 | RedisReName(arg1:string,arg2:string,arg3:string,arg4:string,arg5:string):Promise 17 | RedisSaveStringValue(arg1:string,arg2:string,arg3:string,arg4:string,arg5:string,arg6:string):Promise 18 | RedisUpTtl(arg1:string,arg2:string,arg3:string,arg4:string,arg5:string):Promise 19 | TestConnection(arg1:string):Promise 20 | }, 21 | } 22 | 23 | } 24 | 25 | declare global { 26 | interface Window { 27 | go: go; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /frontend/src/wailsjs/go/bindings.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | // Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL 3 | // This file is automatically generated. DO NOT EDIT 4 | const go = { 5 | "main": { 6 | "App": { 7 | /** 8 | * ChangeRightWindowStyle 9 | * @param {string} arg1 - Go Type: string 10 | * @param {string} arg2 - Go Type: string 11 | * @param {string} arg3 - Go Type: string 12 | * @returns {Promise} - Go Type: string 13 | */ 14 | "ChangeRightWindowStyle": (arg1, arg2, arg3) => { 15 | return window.go.main.App.ChangeRightWindowStyle(arg1, arg2, arg3); 16 | }, 17 | /** 18 | * ExportConn 19 | * @returns {Promise} 20 | */ 21 | "ExportConn": () => { 22 | return window.go.main.App.ExportConn(); 23 | }, 24 | /** 25 | * GetNodeData 26 | * @param {string} arg1 - Go Type: string 27 | * @param {string} arg2 - Go Type: string 28 | * @param {string} arg3 - Go Type: string 29 | * @returns {Promise} - Go Type: string 30 | */ 31 | "GetNodeData": (arg1, arg2, arg3) => { 32 | return window.go.main.App.GetNodeData(arg1, arg2, arg3); 33 | }, 34 | /** 35 | * ImportConn 36 | * @returns {Promise} 37 | */ 38 | "ImportConn": () => { 39 | return window.go.main.App.ImportConn(); 40 | }, 41 | /** 42 | * LoadingConnInfo 43 | * @param {string} arg1 - Go Type: string 44 | * @param {string} arg2 - Go Type: string 45 | * @returns {Promise} - Go Type: string 46 | */ 47 | "LoadingConnInfo": (arg1, arg2) => { 48 | return window.go.main.App.LoadingConnInfo(arg1, arg2); 49 | }, 50 | /** 51 | * LoadingConnKey 52 | * @returns {Promise} - Go Type: string 53 | */ 54 | "LoadingConnKey": () => { 55 | return window.go.main.App.LoadingConnKey(); 56 | }, 57 | /** 58 | * LoadingDbResource 59 | * @param {string} arg1 - Go Type: string 60 | * @returns {Promise} - Go Type: string 61 | */ 62 | "LoadingDbResource": (arg1) => { 63 | return window.go.main.App.LoadingDbResource(arg1); 64 | }, 65 | /** 66 | * Ok 67 | * @param {string} arg1 - Go Type: string 68 | * @returns {Promise} - Go Type: string 69 | */ 70 | "Ok": (arg1) => { 71 | return window.go.main.App.Ok(arg1); 72 | }, 73 | /** 74 | * RedisDelKey 75 | * @param {string} arg1 - Go Type: string 76 | * @param {string} arg2 - Go Type: string 77 | * @param {string} arg3 - Go Type: string 78 | * @param {string} arg4 - Go Type: string 79 | * @returns {Promise} 80 | */ 81 | "RedisDelKey": (arg1, arg2, arg3, arg4) => { 82 | return window.go.main.App.RedisDelKey(arg1, arg2, arg3, arg4); 83 | }, 84 | /** 85 | * RedisGetData 86 | * @param {string} arg1 - Go Type: string 87 | * @param {string} arg2 - Go Type: string 88 | * @param {string} arg3 - Go Type: string 89 | * @param {string} arg4 - Go Type: string 90 | * @returns {Promise} - Go Type: string 91 | */ 92 | "RedisGetData": (arg1, arg2, arg3, arg4) => { 93 | return window.go.main.App.RedisGetData(arg1, arg2, arg3, arg4); 94 | }, 95 | /** 96 | * RedisReName 97 | * @param {string} arg1 - Go Type: string 98 | * @param {string} arg2 - Go Type: string 99 | * @param {string} arg3 - Go Type: string 100 | * @param {string} arg4 - Go Type: string 101 | * @param {string} arg5 - Go Type: string 102 | * @returns {Promise} 103 | */ 104 | "RedisReName": (arg1, arg2, arg3, arg4, arg5) => { 105 | return window.go.main.App.RedisReName(arg1, arg2, arg3, arg4, arg5); 106 | }, 107 | /** 108 | * RedisSaveStringValue 109 | * @param {string} arg1 - Go Type: string 110 | * @param {string} arg2 - Go Type: string 111 | * @param {string} arg3 - Go Type: string 112 | * @param {string} arg4 - Go Type: string 113 | * @param {string} arg5 - Go Type: string 114 | * @param {string} arg6 - Go Type: string 115 | * @returns {Promise} 116 | */ 117 | "RedisSaveStringValue": (arg1, arg2, arg3, arg4, arg5, arg6) => { 118 | return window.go.main.App.RedisSaveStringValue(arg1, arg2, arg3, arg4, arg5, arg6); 119 | }, 120 | /** 121 | * RedisUpTtl 122 | * @param {string} arg1 - Go Type: string 123 | * @param {string} arg2 - Go Type: string 124 | * @param {string} arg3 - Go Type: string 125 | * @param {string} arg4 - Go Type: string 126 | * @param {string} arg5 - Go Type: string 127 | * @returns {Promise} 128 | */ 129 | "RedisUpTtl": (arg1, arg2, arg3, arg4, arg5) => { 130 | return window.go.main.App.RedisUpTtl(arg1, arg2, arg3, arg4, arg5); 131 | }, 132 | /** 133 | * TestConnection 134 | * @param {string} arg1 - Go Type: string 135 | * @returns {Promise} 136 | */ 137 | "TestConnection": (arg1) => { 138 | return window.go.main.App.TestConnection(arg1); 139 | }, 140 | }, 141 | }, 142 | 143 | }; 144 | export default go; 145 | -------------------------------------------------------------------------------- /frontend/src/wailsjs/go/models.ts: -------------------------------------------------------------------------------- 1 | /* Do not change, this code is generated from Golang structs */ 2 | 3 | export {}; 4 | -------------------------------------------------------------------------------- /frontend/src/wailsjs/go/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "go", 3 | "version": "1.0.0", 4 | "description": "Package to wrap your bound go methods", 5 | "main": "bindings.js", 6 | "types": "bindings.d.ts", 7 | "scripts": {}, 8 | "author": "", 9 | "license": "ISC" 10 | } -------------------------------------------------------------------------------- /frontend/src/wailsjs/runtime/ipcdev.js: -------------------------------------------------------------------------------- 1 | (()=>{(function(){let n=function(e){for(var s=window[e.shift()];s&&e.length;)s=s[e.shift()];return s},o=n(["chrome","webview","postMessage"]),t=n(["webkit","messageHandlers","external","postMessage"]);if(!o&&!t){console.error("Unsupported Platform");return}o&&(window.WailsInvoke=e=>window.chrome.webview.postMessage(e)),t&&(window.WailsInvoke=e=>window.webkit.messageHandlers.external.postMessage(e))})();})(); 2 | -------------------------------------------------------------------------------- /frontend/src/wailsjs/runtime/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@wailsapp/runtime", 3 | "version": "2.0.0", 4 | "description": "Wails Javascript runtime library", 5 | "main": "runtime.js", 6 | "types": "runtime.d.ts", 7 | "scripts": { 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/wailsapp/wails.git" 12 | }, 13 | "keywords": [ 14 | "Wails", 15 | "Javascript", 16 | "Go" 17 | ], 18 | "author": "Lea Anthony ", 19 | "license": "MIT", 20 | "bugs": { 21 | "url": "https://github.com/wailsapp/wails/issues" 22 | }, 23 | "homepage": "https://github.com/wailsapp/wails#readme" 24 | } 25 | -------------------------------------------------------------------------------- /frontend/src/wailsjs/runtime/runtime.d.ts: -------------------------------------------------------------------------------- 1 | export interface Position { 2 | x: number; 3 | y: number; 4 | } 5 | 6 | export interface Size { 7 | w: number; 8 | h: number; 9 | } 10 | 11 | export interface runtime { 12 | EventsEmit(eventName: string, data?: any): void; 13 | 14 | EventsOn(eventName: string, callback: (data?: any) => void): void; 15 | 16 | EventsOnMultiple(eventName: string, callback: (data?: any) => void, maxCallbacks: number): void; 17 | 18 | EventsOnce(eventName: string, callback: (data?: any) => void): void; 19 | 20 | LogTrace(message: string): void; 21 | 22 | LogDebug(message: string): void; 23 | 24 | LogError(message: string): void; 25 | 26 | LogFatal(message: string): void; 27 | 28 | LogInfo(message: string): void; 29 | 30 | LogWarning(message: string): void; 31 | 32 | WindowReload(): void; 33 | 34 | WindowCenter(): void; 35 | 36 | WindowSetTitle(title: string): void; 37 | 38 | WindowFullscreen(): void; 39 | 40 | WindowUnfullscreen(): void; 41 | 42 | WindowSetSize(width: number, height: number): Promise; 43 | 44 | WindowGetSize(): Promise; 45 | 46 | WindowSetMaxSize(width: number, height: number): void; 47 | 48 | WindowSetMinSize(width: number, height: number): void; 49 | 50 | WindowSetPosition(x: number, y: number): void; 51 | 52 | WindowGetPosition(): Promise; 53 | 54 | WindowHide(): void; 55 | 56 | WindowShow(): void; 57 | 58 | WindowMaximise(): void; 59 | 60 | WindowToggleMaximise(): void; 61 | 62 | WindowUnmaximise(): void; 63 | 64 | WindowMinimise(): void; 65 | 66 | WindowUnminimise(): void; 67 | 68 | WindowSetRGBA(R: number, G: number, B: number, A: number): void; 69 | 70 | BrowserOpenURL(url: string): void; 71 | 72 | Quit(): void; 73 | } 74 | 75 | declare global { 76 | interface Window { 77 | runtime: runtime; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /frontend/src/wailsjs/runtime/runtime.js: -------------------------------------------------------------------------------- 1 | (()=>{var d=Object.defineProperty;var m=n=>d(n,"__esModule",{value:!0});var e=(n,i)=>{m(n);for(var o in i)d(n,o,{get:i[o],enumerable:!0})};var t={};e(t,{LogDebug:()=>W,LogError:()=>x,LogFatal:()=>s,LogInfo:()=>c,LogTrace:()=>p,LogWarning:()=>f});function p(n){window.runtime.LogTrace(n)}function W(n){window.runtime.LogDebug(n)}function c(n){window.runtime.LogInfo(n)}function f(n){window.runtime.LogWarning(n)}function x(n){window.runtime.LogError(n)}function s(n){window.runtime.LogFatal(n)}var w={};e(w,{EventsEmit:()=>S,EventsOn:()=>a,EventsOnMultiple:()=>l,EventsOnce:()=>g});function l(n,i,o){window.runtime.EventsOnMultiple(n,i,o)}function a(n,i){OnMultiple(n,i,-1)}function g(n,i){OnMultiple(n,i,1)}function S(n){let i=[n].slice.call(arguments);return window.runtime.EventsEmit.apply(null,i)}var r={};e(r,{WindowCenter:()=>M,WindowFullscreen:()=>v,WindowGetPosition:()=>B,WindowGetSize:()=>O,WindowHide:()=>F,WindowMaximise:()=>b,WindowMinimise:()=>C,WindowReload:()=>L,WindowSetMaxSize:()=>G,WindowSetMinSize:()=>R,WindowSetPosition:()=>T,WindowSetRGBA:()=>H,WindowSetSize:()=>U,WindowSetTitle:()=>E,WindowShow:()=>P,WindowToggleMaximise:()=>h,WindowUnfullscreen:()=>z,WindowUnmaximise:()=>A,WindowUnminimise:()=>D});function L(){window.runtime.WindowReload()}function M(){window.runtime.WindowCenter()}function E(n){window.runtime.WindowSetTitle(n)}function v(){window.runtime.WindowFullscreen()}function z(){window.runtime.WindowUnfullscreen()}function O(){window.runtime.WindowGetSize()}function U(n,i){window.runtime.WindowSetSize(n,i)}function G(n,i){window.runtime.WindowSetMaxSize(n,i)}function R(n,i){window.runtime.WindowSetMinSize(n,i)}function T(n,i){window.runtime.WindowSetPosition(n,i)}function B(){window.runtime.WindowGetPosition()}function F(){window.runtime.WindowHide()}function P(){window.runtime.WindowShow()}function b(){window.runtime.WindowMaximise()}function h(){window.runtime.WindowToggleMaximise()}function A(){window.runtime.WindowUnmaximise()}function C(){window.runtime.WindowMinimise()}function D(){window.runtime.WindowUnminimise()}function H(n){window.runtime.WindowSetRGBA(n)}var u={};e(u,{BrowserOpenURL:()=>I});function I(n){window.runtime.BrowserOpenURL(n)}function Q(){window.runtime.Quit()}var j={...t,...w,...r,...u,Quit:Q};})(); 2 | -------------------------------------------------------------------------------- /frontend/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vite"; 2 | import vue from "@vitejs/plugin-vue"; 3 | import path from "path"; 4 | 5 | // https://vitejs.dev/config/ 6 | export default defineConfig({ 7 | plugins: [vue()], 8 | resolve: { 9 | alias: { 10 | "@": path.resolve(__dirname, "src"), 11 | }, 12 | }, 13 | build: { 14 | outDir: "dist", 15 | rollupOptions: { 16 | output: { 17 | entryFileNames: `src/assets/[name].js`, 18 | chunkFileNames: `src/assets/[name].js`, 19 | assetFileNames: `src/assets/[name].[ext]`, 20 | }, 21 | }, 22 | }, 23 | }); 24 | -------------------------------------------------------------------------------- /frontend/wailsjs/runtime/ipcdev.js: -------------------------------------------------------------------------------- 1 | (()=>{(function(){let n=function(e){for(var s=window[e.shift()];s&&e.length;)s=s[e.shift()];return s},o=n(["chrome","webview","postMessage"]),t=n(["webkit","messageHandlers","external","postMessage"]);if(!o&&!t){console.error("Unsupported Platform");return}o&&(window.WailsInvoke=e=>window.chrome.webview.postMessage(e)),t&&(window.WailsInvoke=e=>window.webkit.messageHandlers.external.postMessage(e))})();})(); 2 | -------------------------------------------------------------------------------- /frontend/wailsjs/runtime/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@wailsapp/runtime", 3 | "version": "2.0.0", 4 | "description": "Wails Javascript runtime library", 5 | "main": "runtime.js", 6 | "types": "runtime.d.ts", 7 | "scripts": { 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/wailsapp/wails.git" 12 | }, 13 | "keywords": [ 14 | "Wails", 15 | "Javascript", 16 | "Go" 17 | ], 18 | "author": "Lea Anthony ", 19 | "license": "MIT", 20 | "bugs": { 21 | "url": "https://github.com/wailsapp/wails/issues" 22 | }, 23 | "homepage": "https://github.com/wailsapp/wails#readme" 24 | } 25 | -------------------------------------------------------------------------------- /frontend/wailsjs/runtime/runtime.d.ts: -------------------------------------------------------------------------------- 1 | export interface Position { 2 | x: number; 3 | y: number; 4 | } 5 | 6 | export interface Size { 7 | w: number; 8 | h: number; 9 | } 10 | 11 | export interface runtime { 12 | EventsEmit(eventName: string, data?: any): void; 13 | 14 | EventsOn(eventName: string, callback: (data?: any) => void): void; 15 | 16 | EventsOnMultiple(eventName: string, callback: (data?: any) => void, maxCallbacks: number): void; 17 | 18 | EventsOnce(eventName: string, callback: (data?: any) => void): void; 19 | 20 | LogTrace(message: string): void; 21 | 22 | LogDebug(message: string): void; 23 | 24 | LogError(message: string): void; 25 | 26 | LogFatal(message: string): void; 27 | 28 | LogInfo(message: string): void; 29 | 30 | LogWarning(message: string): void; 31 | 32 | WindowReload(): void; 33 | 34 | WindowCenter(): void; 35 | 36 | WindowSetTitle(title: string): void; 37 | 38 | WindowFullscreen(): void; 39 | 40 | WindowUnfullscreen(): void; 41 | 42 | WindowSetSize(width: number, height: number): Promise; 43 | 44 | WindowGetSize(): Promise; 45 | 46 | WindowSetMaxSize(width: number, height: number): void; 47 | 48 | WindowSetMinSize(width: number, height: number): void; 49 | 50 | WindowSetPosition(x: number, y: number): void; 51 | 52 | WindowGetPosition(): Promise; 53 | 54 | WindowHide(): void; 55 | 56 | WindowShow(): void; 57 | 58 | WindowMaximise(): void; 59 | 60 | WindowToggleMaximise(): void; 61 | 62 | WindowUnmaximise(): void; 63 | 64 | WindowMinimise(): void; 65 | 66 | WindowUnminimise(): void; 67 | 68 | WindowSetRGBA(R: number, G: number, B: number, A: number): void; 69 | 70 | BrowserOpenURL(url: string): void; 71 | 72 | Quit(): void; 73 | } 74 | 75 | declare global { 76 | interface Window { 77 | runtime: runtime; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /frontend/wailsjs/runtime/runtime.js: -------------------------------------------------------------------------------- 1 | (()=>{var d=Object.defineProperty;var m=n=>d(n,"__esModule",{value:!0});var e=(n,i)=>{m(n);for(var o in i)d(n,o,{get:i[o],enumerable:!0})};var t={};e(t,{LogDebug:()=>W,LogError:()=>x,LogFatal:()=>s,LogInfo:()=>c,LogTrace:()=>p,LogWarning:()=>f});function p(n){window.runtime.LogTrace(n)}function W(n){window.runtime.LogDebug(n)}function c(n){window.runtime.LogInfo(n)}function f(n){window.runtime.LogWarning(n)}function x(n){window.runtime.LogError(n)}function s(n){window.runtime.LogFatal(n)}var w={};e(w,{EventsEmit:()=>S,EventsOn:()=>a,EventsOnMultiple:()=>l,EventsOnce:()=>g});function l(n,i,o){window.runtime.EventsOnMultiple(n,i,o)}function a(n,i){OnMultiple(n,i,-1)}function g(n,i){OnMultiple(n,i,1)}function S(n){let i=[n].slice.call(arguments);return window.runtime.EventsEmit.apply(null,i)}var r={};e(r,{WindowCenter:()=>M,WindowFullscreen:()=>v,WindowGetPosition:()=>B,WindowGetSize:()=>O,WindowHide:()=>F,WindowMaximise:()=>b,WindowMinimise:()=>C,WindowReload:()=>L,WindowSetMaxSize:()=>G,WindowSetMinSize:()=>R,WindowSetPosition:()=>T,WindowSetRGBA:()=>H,WindowSetSize:()=>U,WindowSetTitle:()=>E,WindowShow:()=>P,WindowToggleMaximise:()=>h,WindowUnfullscreen:()=>z,WindowUnmaximise:()=>A,WindowUnminimise:()=>D});function L(){window.runtime.WindowReload()}function M(){window.runtime.WindowCenter()}function E(n){window.runtime.WindowSetTitle(n)}function v(){window.runtime.WindowFullscreen()}function z(){window.runtime.WindowUnfullscreen()}function O(){window.runtime.WindowGetSize()}function U(n,i){window.runtime.WindowSetSize(n,i)}function G(n,i){window.runtime.WindowSetMaxSize(n,i)}function R(n,i){window.runtime.WindowSetMinSize(n,i)}function T(n,i){window.runtime.WindowSetPosition(n,i)}function B(){window.runtime.WindowGetPosition()}function F(){window.runtime.WindowHide()}function P(){window.runtime.WindowShow()}function b(){window.runtime.WindowMaximise()}function h(){window.runtime.WindowToggleMaximise()}function A(){window.runtime.WindowUnmaximise()}function C(){window.runtime.WindowMinimise()}function D(){window.runtime.WindowUnminimise()}function H(n){window.runtime.WindowSetRGBA(n)}var u={};e(u,{BrowserOpenURL:()=>I});function I(n){window.runtime.BrowserOpenURL(n)}function Q(){window.runtime.Quit()}var j={...t,...w,...r,...u,Quit:Q};})(); 2 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module ToDb 2 | 3 | go 1.17 4 | 5 | require ( 6 | github.com/go-redis/redis/v8 v8.11.5 7 | github.com/tidwall/gjson v1.8.0 8 | github.com/wailsapp/wails/v2 v2.0.0-beta.34 9 | ) 10 | 11 | require ( 12 | github.com/andybalholm/brotli v1.0.2 // indirect 13 | github.com/cespare/xxhash/v2 v2.1.2 // indirect 14 | github.com/davecgh/go-spew v1.1.1 // indirect 15 | github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect 16 | github.com/fasthttp/websocket v0.0.0-20200320073529-1554a54587ab // indirect 17 | github.com/gabriel-vasile/mimetype v1.3.1 // indirect 18 | github.com/go-ole/go-ole v1.2.6 // indirect 19 | github.com/gofiber/fiber/v2 v2.17.0 // indirect 20 | github.com/gofiber/websocket/v2 v2.0.8 // indirect 21 | github.com/google/uuid v1.1.2 // indirect 22 | github.com/imdario/mergo v0.3.12 // indirect 23 | github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e // indirect 24 | github.com/klauspost/compress v1.12.2 // indirect 25 | github.com/leaanthony/go-ansi-parser v1.0.1 // indirect 26 | github.com/leaanthony/go-common-file-dialog v1.0.3 // indirect 27 | github.com/leaanthony/go-webview2 v1.0.3-0.20220314105146-f44268990abe // indirect 28 | github.com/leaanthony/gosod v1.0.3 // indirect 29 | github.com/leaanthony/slicer v1.5.0 // indirect 30 | github.com/leaanthony/typescriptify-golang-structs v0.1.7 // indirect 31 | github.com/leaanthony/winc v0.0.0-20220208061147-37b059b9dc3b // indirect 32 | github.com/pkg/browser v0.0.0-20210706143420-7d21f8c997e2 // indirect 33 | github.com/pkg/errors v0.9.1 // indirect 34 | github.com/savsgio/gotils v0.0.0-20200117113501-90175b0fbe3f // indirect 35 | github.com/tidwall/match v1.0.3 // indirect 36 | github.com/tidwall/pretty v1.1.0 // indirect 37 | github.com/tkrajina/go-reflector v0.5.5 // indirect 38 | github.com/valyala/bytebufferpool v1.0.0 // indirect 39 | github.com/valyala/fasthttp v1.28.0 // indirect 40 | github.com/valyala/tcplisten v1.0.0 // indirect 41 | golang.org/x/net v0.0.0-20210510120150-4163338589ed // indirect 42 | golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 // indirect 43 | ) 44 | 45 | // replace github.com/wailsapp/wails/v2 v2.0.0-beta.34 => /Users/symbol/go/pkg/mod/github.com/wailsapp/wails/v2@v2.0.0-beta.34 46 | -------------------------------------------------------------------------------- /jb_beam.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xingcxb/ToDb/e039f594c6d84c56122e5d4ee4bb0e67e403f56c/jb_beam.png -------------------------------------------------------------------------------- /kit/dialog.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: symbol 3 | * @Date: 2022-05-22 11:21:34 4 | * @LastEditors: symbol 5 | * @LastEditTime: 2022-05-28 17:00:39 6 | * @FilePath: \ToDb\kit\dialog.go 7 | * @Description: 对话框控制工具 8 | * 9 | * Copyright (c) 2022 by symbol, All Rights Reserved. 10 | */ 11 | 12 | package kit 13 | 14 | import ( 15 | "ToDb/common/consts" 16 | "context" 17 | 18 | "github.com/wailsapp/wails/v2/pkg/runtime" 19 | ) 20 | 21 | var ( 22 | insDiaLogKit = sDiaLogKit{} 23 | ) 24 | 25 | type sDiaLogKit struct { 26 | ctx context.Context 27 | } 28 | 29 | func DiaLogKit() *sDiaLogKit { 30 | return &insDiaLogKit 31 | } 32 | 33 | // DefaultDialog 显示默认的对话框 34 | func (d *sDiaLogKit) DefaultDialog(ctx context.Context, title, message string, icon []byte) { 35 | runtime.MessageDialog(ctx, runtime.MessageDialogOptions{ 36 | Type: func(title string) runtime.DialogType { 37 | if title == "错误" { 38 | return runtime.ErrorDialog 39 | } 40 | return runtime.InfoDialog 41 | }(title), 42 | Icon: icon, 43 | Title: title, 44 | Message: message, 45 | Buttons: []string{"确定"}, 46 | DefaultButton: "确定", 47 | }) 48 | } 49 | 50 | // AboutDialog 关于对话框 51 | func (d *sDiaLogKit) About(ctx context.Context, icon []byte) { 52 | runtime.MessageDialog(ctx, runtime.MessageDialogOptions{ 53 | Type: runtime.InfoDialog, 54 | Title: consts.AppName, 55 | Message: consts.Description, 56 | Icon: icon, 57 | DefaultButton: consts.BtnConfirmText, 58 | Buttons: []string{consts.BtnConfirmText}, 59 | }) 60 | } 61 | -------------------------------------------------------------------------------- /kit/os/file.go: -------------------------------------------------------------------------------- 1 | // package os 2 | // @Author: symbol 3 | // @Date: 2022-05-03 4 | // @LastEditors: symbol 5 | // @LastEditTime: 2022-05-03 18:06:31 6 | // @FilePath: \ToDb\lib\os\file.go 7 | // @Description: 针对操作系统文件/文件夹进行封装 8 | 9 | // Copyright (c) 2022 by symbol, All Rights Reserved. 10 | 11 | package os 12 | 13 | import ( 14 | "context" 15 | "io/fs" 16 | "io/ioutil" 17 | "os" 18 | "os/user" 19 | "strings" 20 | ) 21 | 22 | var ( 23 | insFile = sFile{} 24 | lastPath = ".ToDb" 25 | ) 26 | 27 | type sFile struct { 28 | ctx context.Context 29 | } 30 | 31 | func File() *sFile { 32 | return &insFile 33 | } 34 | 35 | // FormatOsPath 36 | // 基于系统替换路径中的分隔符 37 | // @param {[type]} ctx context.Context 38 | // @return string,error 39 | func (s *sFile) FormatOsPath(ctx context.Context, path string) string { 40 | return strings.ReplaceAll(path, "/", string(os.PathSeparator)) 41 | } 42 | 43 | // HomeDir 44 | // 获取系统当前使用的用户名 45 | // @param {[type]} ctx context.Context 46 | // @return string,error 47 | func (o *sFile) HomeDir(ctx context.Context) (string, error) { 48 | user, err := user.Current() 49 | if err != nil { 50 | return "", err 51 | } 52 | path := user.HomeDir + string(os.PathSeparator) + lastPath + string(os.PathSeparator) 53 | return path, nil 54 | } 55 | 56 | // CreateFile 创建文件夹或文件 57 | // @param {[type]} ctx context.Context [description] 58 | // @param {string} fileName 文件名 如果文件名不存在则创建文件夹 59 | // @return error 60 | func (o *sFile) CreateFile(ctx context.Context, fileName string) error { 61 | // 文件夹路径 62 | folderPath, err := o.HomeDir(ctx) 63 | if err != nil { 64 | return err 65 | } 66 | // 判断文件夹是否存在 67 | if !o.Exists(ctx, folderPath) { 68 | // 文件夹不存在,创建文件夹,并且对文件夹设置隐藏属性 69 | err = os.MkdirAll(folderPath, os.ModePerm) 70 | if err != nil { 71 | return err 72 | } 73 | } 74 | if fileName != "" { 75 | // 文件名如果存在,就创建文件 76 | filePath := folderPath + fileName + ".json" 77 | if !o.Exists(ctx, filePath) { 78 | // 创建文件 79 | file, err := os.Create(filePath) 80 | if err != nil { 81 | return err 82 | } 83 | defer file.Close() 84 | } 85 | } 86 | return nil 87 | } 88 | 89 | // Exists 文件或文件夹是否存在 90 | // @param {[type]} ctx context.Context [description] 91 | // @param {string} path 文件夹路径 92 | // @return bool true 存在 false 不存在 93 | func (o *sFile) Exists(ctx context.Context, path string) bool { 94 | _, err := os.Stat(path) 95 | if os.IsNotExist(err) { 96 | return false 97 | } 98 | return true 99 | } 100 | 101 | // SaveFile 保存信息到文件 102 | // @param {[type]} ctx context.Context [description] 103 | // @param {string} fileName 文件名 104 | // @param {string} content 文件内容 105 | // @return error 106 | func (o *sFile) SaveFile(ctx context.Context, fileName, content string) error { 107 | // 文件夹路径 108 | folderPath, err := o.HomeDir(ctx) 109 | if err != nil { 110 | return err 111 | } 112 | folderPath = folderPath + fileName + ".json" 113 | f, err := os.OpenFile(folderPath, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0666) 114 | if err != nil { 115 | return err 116 | } 117 | f.WriteString(content) 118 | defer f.Close() 119 | return nil 120 | } 121 | 122 | // ReadFiles 读取文件夹中的文件 123 | // @param {[type]} ctx context.Context [description] 124 | // @return []fs.FileInfo,error 125 | func (o *sFile) ReadFiles(ctx context.Context) ([]fs.FileInfo, error) { 126 | // 文件夹路径 127 | folderPath, err := o.HomeDir(ctx) 128 | if err != nil { 129 | return nil, err 130 | } 131 | f, err := ioutil.ReadDir(folderPath) 132 | if err != nil { 133 | return nil, err 134 | } 135 | for i, v := range f { 136 | // macOS下会存在这个隐藏文件,所以需要过滤掉 137 | if v.Name() == ".DS_Store" { 138 | f = append(f[:i], f[i+1:]...) 139 | } 140 | } 141 | return f, nil 142 | } 143 | 144 | // InputFile 导入文件 145 | // @param {[type]} ctx context.Context [description] 146 | // @return error 147 | func (o *sFile) InputFile(ctx context.Context) error { 148 | return nil 149 | } 150 | -------------------------------------------------------------------------------- /kit/os/os_test.go: -------------------------------------------------------------------------------- 1 | // package os 2 | // @Author: easymbol 3 | // @Date: 2022/5/3 4 | // @LastEditors: easymbol 5 | // @LastEditTime: 2022/5/3 22:23 6 | // @FilePath: 7 | // @Description: TODO 8 | 9 | // Copyright (c) 2022 by easymbol, All Rights Reserved. 10 | 11 | package os 12 | 13 | import ( 14 | "context" 15 | "fmt" 16 | "testing" 17 | ) 18 | 19 | func TestCreateFile(t *testing.T) { 20 | File().CreateFile(context.Background(), "test.txt") 21 | } 22 | 23 | func TestReadFiles(t *testing.T) { 24 | files, err := File().ReadFiles(context.Background()) 25 | if err != nil { 26 | t.Error(err) 27 | } 28 | fmt.Println(files) 29 | } 30 | -------------------------------------------------------------------------------- /kit/redis/redis_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: symbol 3 | * @Date: 2022-05-04 10:22:53 4 | * @LastEditors: symbol 5 | * @LastEditTime: 2022-05-20 17:54:11 6 | * @FilePath: /todb/kit/redis/redis_test.go 7 | * @Description: 8 | * 9 | * Copyright (c) 2022 by symbol, All Rights Reserved. 10 | */ 11 | package redisKit 12 | 13 | import ( 14 | "ToDb/kit" 15 | "context" 16 | "fmt" 17 | "testing" 18 | ) 19 | 20 | func TestConnect(t *testing.T) { 21 | Addr = "127.0.0.1" 22 | Port = "6379" 23 | Password = "123456" 24 | InitDb() 25 | fmt.Println(Redis().GetMainViewInfo(context.Background())) 26 | } 27 | 28 | func TestInfo(t *testing.T) { 29 | Addr = "127.0.0.1" 30 | Port = "6379" 31 | Password = "123456" 32 | InitDb() 33 | Redis().ChangeDb(context.Background(), 13) 34 | //fmt.Println(GetBaseAllInfo(context.Background())) 35 | v, _ := Redis().GetDbKeys(context.Background(), 0) 36 | fmt.Println(v) 37 | _v := kit.StrKit().PackageTree(v) 38 | fmt.Println(_v) 39 | } 40 | 41 | func TestDel(t *testing.T) { 42 | Addr = "127.0.0.1" 43 | Port = "6379" 44 | Password = "123456" 45 | InitDb() 46 | Redis().ChangeDb(context.Background(), 13) 47 | Redis().Del(context.Background(), "1111") 48 | } 49 | 50 | func TestStream(t *testing.T) { 51 | Addr = "127.0.0.1" 52 | Port = "6379" 53 | Password = "123456" 54 | InitDb() 55 | Redis().ChangeDb(context.Background(), 13) 56 | Redis().GetStreamValue(context.Background(), "1:2:stream") 57 | } 58 | 59 | func TestZSet(t *testing.T) { 60 | Addr = "127.0.0.1" 61 | Port = "6379" 62 | Password = "123456" 63 | InitDb() 64 | Redis().ChangeDb(context.Background(), 13) 65 | Redis().GetZSetCount(context.Background(), "1:2:zset") 66 | } 67 | -------------------------------------------------------------------------------- /kit/response.go: -------------------------------------------------------------------------------- 1 | package kit 2 | 3 | import "encoding/json" 4 | 5 | type JsonResponse struct { 6 | Code int `json:"code"` //错误码 7 | Message string `json:"message"` //提示信息 8 | Data interface{} `json:"data"` //返回数据 9 | } 10 | 11 | func (jr *JsonResponse) String() string { 12 | val, _ := json.Marshal(jr) 13 | return string(val) 14 | } 15 | -------------------------------------------------------------------------------- /kit/strKit.go: -------------------------------------------------------------------------------- 1 | package kit 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "strings" 7 | ) 8 | 9 | var ( 10 | insStrKit = sStrKit{} 11 | ) 12 | 13 | type sStrKit struct { 14 | ctx context.Context 15 | } 16 | 17 | func StrKit() *sStrKit { 18 | return &insStrKit 19 | } 20 | 21 | type Node struct { 22 | Label string `json:"label"` 23 | Key string `json:"key"` 24 | FullStr string `json:"fullStr"` 25 | Count int `json:"count"` 26 | Children []*Node `json:"children,omitempty"` 27 | } 28 | 29 | func (s *sStrKit) PackageTree(v []string) string { 30 | // 根据数组中的中的冒号数量进行排序,最多的冒号数量的元素放在最前面 31 | v = s.BubbleDescSort(v) 32 | 33 | // 声明切片存放树形数据 34 | var treeNode []Node 35 | for _, val := range v { 36 | var node Node 37 | sl := strings.SplitN(val, ":", 2) 38 | // 查找treeNode切片中是否已经存在当前key 39 | for _, v := range treeNode { 40 | var sb strings.Builder 41 | sb.WriteString(sl[0]) 42 | sb.WriteString(":*") 43 | if v.Key == sb.String() { 44 | node = v 45 | } 46 | } 47 | flag := node.Key == "" 48 | if flag { 49 | node.Label = sl[0] 50 | var sb strings.Builder 51 | sb.WriteString(sl[0]) 52 | sb.WriteString(":*") 53 | node.Key = sb.String() 54 | node.FullStr = val 55 | node.Count = 0 56 | } 57 | if len(sl) > 1 { 58 | s.GetChildren(sl[1], val, &node) 59 | if !flag { 60 | for i, v := range treeNode { 61 | if v.Key == node.Key { 62 | treeNode[i] = node 63 | } 64 | } 65 | } 66 | } 67 | if flag { 68 | treeNode = append(treeNode, node) 69 | } 70 | } 71 | res, err := json.Marshal(treeNode) 72 | if err != nil { 73 | panic(err) 74 | } 75 | return string(res) 76 | } 77 | 78 | func (s *sStrKit) GetChildren(nodeStr, fullStr string, parentNode *Node) { 79 | node := &Node{} 80 | sl := strings.SplitN(nodeStr, ":", 2) 81 | for _, v := range parentNode.Children { 82 | var sb strings.Builder 83 | sb.WriteString(sl[0]) 84 | sb.WriteString(":*") 85 | if len(sl) > 1 && v.Key == sb.String() { 86 | node = v 87 | } 88 | } 89 | flag := node.Key == "" 90 | if flag { 91 | node.Label = sl[0] 92 | var sb strings.Builder 93 | sb.WriteString(sl[0]) 94 | sb.WriteString(":*") 95 | node.Key = sb.String() 96 | node.FullStr = fullStr 97 | node.Count = 0 98 | } 99 | 100 | if len(sl) > 1 { 101 | s.GetChildren(sl[1], fullStr, node) 102 | } else { 103 | // 如果没有子节点说明是最终节点 104 | node.Key = fullStr 105 | } 106 | if flag { 107 | parentNode.Children = append(parentNode.Children, node) 108 | parentNode.Count = len(parentNode.Children) 109 | } 110 | } 111 | 112 | // BubbleDescSort 冒泡排序 倒序 113 | func (s *sStrKit) BubbleDescSort(values []string) []string { 114 | for i := 0; i < len(values)-1; i++ { 115 | for j := i + 1; j < len(values); j++ { 116 | if strings.Count(values[i], ":") < strings.Count(values[j], ":") { 117 | values[i], values[j] = values[j], values[i] 118 | } 119 | } 120 | } 121 | return values 122 | } 123 | 124 | // BubbleAscSort 冒泡排序 正序 125 | func (s *sStrKit) BubbleAscSort(values []string) []string { 126 | for i := 0; i < len(values)-1; i++ { 127 | for j := i + 1; j < len(values); j++ { 128 | if strings.Count(values[i], ":") > strings.Count(values[j], ":") { 129 | values[i], values[j] = values[j], values[i] 130 | } 131 | } 132 | } 133 | return values 134 | } 135 | 136 | // FirstUpper 字符串首字母大写 137 | func (s *sStrKit) FirstUpper(str string) string { 138 | if str == "" { 139 | return "" 140 | } 141 | return strings.ToUpper(str[:1]) + str[1:] 142 | } 143 | 144 | // FirstLower 字符串首字母小写 145 | func (s *sStrKit) FirstLower(str string) string { 146 | if str == "" { 147 | return "" 148 | } 149 | return strings.ToLower(str[:1]) + str[1:] 150 | } 151 | -------------------------------------------------------------------------------- /kit/update.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: symbol 3 | * @Date: 2022-05-28 23:13:40 4 | * @LastEditors: symbol 5 | * @LastEditTime: 2022-05-28 23:48:38 6 | * @FilePath: \ToDb\kit\update.go 7 | * @Description: 8 | * 9 | * Copyright (c) 2022 by symbol, All Rights Reserved. 10 | */ 11 | package kit 12 | 13 | import ( 14 | "context" 15 | "fmt" 16 | ) 17 | 18 | var ( 19 | insUpdate = sUpdate{} 20 | ) 21 | 22 | type sUpdate struct { 23 | ctx context.Context 24 | } 25 | 26 | func Update() *sUpdate { 27 | return &insUpdate 28 | } 29 | 30 | const ( 31 | githubUrl = "https://api.github.com/repos/xingcxb/todb/releases/latest" 32 | ) 33 | 34 | // 检查是否存在更新 35 | func (u *sUpdate) CheckUpdate() { 36 | //TODO 未发布版本暂缓实现 37 | fmt.Println(githubUrl) 38 | } 39 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: symbol 3 | * @Date: 2022-05-08 08:49:54 4 | * @LastEditors: symbol 5 | * @LastEditTime: 2022-05-23 22:44:39 6 | * @FilePath: \ToDb\main.go 7 | * @Description: 8 | * 9 | * Copyright (c) 2022 by symbol, All Rights Reserved. 10 | */ 11 | package main 12 | 13 | import ( 14 | "ToDb/common/consts" 15 | "embed" 16 | "log" 17 | goruntime "runtime" 18 | 19 | "github.com/wailsapp/wails/v2" 20 | "github.com/wailsapp/wails/v2/pkg/options" 21 | ) 22 | 23 | //go:embed frontend/dist 24 | var assets embed.FS 25 | 26 | //go:embed build/appicon.png 27 | var icon []byte 28 | 29 | func main() { 30 | // 判断系统如果是win需要增高窗口大小 31 | windowsHeight := 728 32 | osInfo := goruntime.GOOS 33 | if osInfo == "windows" { 34 | windowsHeight = 770 35 | } 36 | // Create an instance of the app structure 37 | // 创建一个App结构体实例 38 | app := NewApp() 39 | 40 | // Create application with options 41 | // 使用选项创建应用 42 | err := wails.Run(&options.App{ 43 | Title: consts.AppName, 44 | Width: 1342, 45 | Height: windowsHeight, 46 | DisableResize: true, 47 | Assets: assets, 48 | OnStartup: app.startup, 49 | OnDomReady: app.domReady, 50 | OnBeforeClose: app.beforeClose, 51 | OnShutdown: app.shutdown, 52 | WindowStartState: options.Normal, 53 | Bind: []interface{}{ 54 | app, 55 | }, 56 | }) 57 | 58 | if err != nil { 59 | log.Fatal(err) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /menu/macOSMenu.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: symbol 3 | * @Date: 2022-05-22 11:21:34 4 | * @LastEditors: symbol 5 | * @LastEditTime: 2022-05-28 23:12:52 6 | * @FilePath: \ToDb\menu\macOSMenu.go 7 | * @Description: 8 | * 9 | * Copyright (c) 2022 by symbol, All Rights Reserved. 10 | */ 11 | package menu 12 | 13 | import ( 14 | "ToDb/kit" 15 | "context" 16 | 17 | "github.com/wailsapp/wails/v2/pkg/menu" 18 | "github.com/wailsapp/wails/v2/pkg/menu/keys" 19 | "github.com/wailsapp/wails/v2/pkg/runtime" 20 | ) 21 | 22 | var ( 23 | insMacOSMenu = sMacOSMenu{} 24 | ) 25 | 26 | type sMacOSMenu struct { 27 | ctx context.Context 28 | } 29 | 30 | func MacOSMenu() *sMacOSMenu { 31 | return &insMacOSMenu 32 | } 33 | 34 | // About macOS菜单关于 35 | func (s *sMacOSMenu) About(ctx context.Context, icon []byte) *menu.MenuItem { 36 | //todo 由于目前不可变暂时使用默认 37 | //看了别人的的代码,可以考虑使用自定义的对话框来实现 38 | return menu.SubMenu("关于", 39 | menu.NewMenuFromItems( 40 | menu.Text("关于ToDb", nil, func(cd *menu.CallbackData) { 41 | kit.DiaLogKit().About(ctx, icon) 42 | }), 43 | menu.Separator(), 44 | menu.Text("退出", keys.CmdOrCtrl("Q"), func(cd *menu.CallbackData) { 45 | runtime.Quit(ctx) 46 | }), 47 | ), 48 | ) 49 | } 50 | 51 | // File macOs文件 52 | func (s *sMacOSMenu) File(ctx context.Context) *menu.MenuItem { 53 | return menu.SubMenu("文件", 54 | menu.NewMenuFromItems( 55 | menu.SubMenu("新建连接", 56 | menu.NewMenuFromItems( 57 | menu.Text("Redis...", keys.CmdOrCtrl("N"), func(data *menu.CallbackData) { 58 | runtime.EventsEmit(ctx, "createConn", "redis") 59 | }), 60 | // menu.Text("MySQL...", nil, func(data *menu.CallbackData) { 61 | // runtime.EventsEmit(ctx, "createConn", "mysql") 62 | // }), 63 | ), 64 | ), 65 | menu.Text("新建查询", nil, nil), 66 | menu.Separator(), 67 | menu.Text("导入连接...", keys.CmdOrCtrl("I"), func(data *menu.CallbackData) { 68 | runtime.EventsEmit(ctx, "importConn") 69 | }), 70 | menu.Text("导出连接...", nil, func(data *menu.CallbackData) { 71 | runtime.EventsEmit(ctx, "exportConn") 72 | }), 73 | ), 74 | ) 75 | } 76 | -------------------------------------------------------------------------------- /menu/menu.go: -------------------------------------------------------------------------------- 1 | package menu 2 | 3 | import ( 4 | "ToDb/kit" 5 | "context" 6 | goruntime "runtime" 7 | 8 | "github.com/wailsapp/wails/v2/pkg/menu" 9 | "github.com/wailsapp/wails/v2/pkg/menu/keys" 10 | "github.com/wailsapp/wails/v2/pkg/runtime" 11 | ) 12 | 13 | var ( 14 | osInfo = "" 15 | insMenu = sMenu{} 16 | ) 17 | 18 | type sMenu struct { 19 | ctx context.Context 20 | } 21 | 22 | func Menu() *sMenu { 23 | return &insMenu 24 | } 25 | 26 | // InitMenu 初始化菜单目录 27 | func InitMenu(ctx context.Context, icon []byte) { 28 | var appMenu *menu.Menu 29 | // 判断系统类型 30 | osInfo = goruntime.GOOS 31 | switch osInfo { 32 | case "windows": 33 | //Windows目录 34 | appMenu = windows(ctx, icon) 35 | case "darwin": 36 | //macos 37 | appMenu = macOs(ctx, icon) 38 | default: 39 | //linux 40 | kit.DiaLogKit().DefaultDialog(ctx, "错误", "暂不支持该系统", nil) 41 | } 42 | runtime.MenuSetApplicationMenu(ctx, appMenu) 43 | } 44 | 45 | // macOS菜单 46 | func macOs(ctx context.Context, icon []byte) *menu.Menu { 47 | return menu.NewMenuFromItems( 48 | MacOSMenu().About(ctx, icon), 49 | MacOSMenu().File(ctx), 50 | Menu().Edit(ctx), 51 | Menu().Find(ctx), 52 | Menu().Tools(ctx), 53 | Menu().Help(ctx, icon), 54 | ) 55 | } 56 | 57 | // windows菜单 58 | func windows(ctx context.Context, icon []byte) *menu.Menu { 59 | return menu.NewMenuFromItems( 60 | WinMenu().File(ctx), 61 | Menu().Edit(ctx), 62 | Menu().Find(ctx), 63 | Menu().Tools(ctx), 64 | Menu().Help(ctx, icon), 65 | ) 66 | } 67 | 68 | // Edit 编辑 69 | func (s *sMenu) Edit(ctx context.Context) *menu.MenuItem { 70 | return menu.SubMenu("编辑", 71 | menu.NewMenuFromItems( 72 | menu.Text("撤销", keys.CmdOrCtrl("Z"), nil), 73 | menu.Separator(), 74 | menu.Text("剪切", keys.CmdOrCtrl("X"), nil), 75 | menu.Text("复制", keys.CmdOrCtrl("C"), nil), 76 | menu.Text("粘贴", keys.CmdOrCtrl("V"), nil), 77 | ), 78 | ) 79 | } 80 | 81 | // Find 查找 82 | func (s *sMenu) Find(ctx context.Context) *menu.MenuItem { 83 | return menu.SubMenu("查找", 84 | menu.NewMenuFromItems( 85 | menu.Text("运行", nil, nil), 86 | menu.Text("停止", nil, nil), 87 | ), 88 | ) 89 | } 90 | 91 | // Tools 工具 92 | func (s *sMenu) Tools(ctx context.Context) *menu.MenuItem { 93 | return menu.SubMenu("工具", 94 | menu.NewMenuFromItems( 95 | menu.Text("历史日志", nil, nil), 96 | menu.Text("刷新", keys.CmdOrCtrl("R"), func(data *menu.CallbackData) { 97 | runtime.WindowReload(ctx) 98 | }), 99 | ), 100 | ) 101 | } 102 | 103 | // Help 帮助 104 | func (s *sMenu) Help(ctx context.Context, icon []byte) *menu.MenuItem { 105 | return menu.SubMenu("帮助", 106 | menu.NewMenuFromItems( 107 | menu.Text("帮助中心", nil, nil), 108 | menu.SubMenu("在线文档", 109 | menu.NewMenuFromItems( 110 | menu.Text("Redis", nil, func(_ *menu.CallbackData) { 111 | runtime.BrowserOpenURL(ctx, "http://doc.redisfans.com/") 112 | }), 113 | menu.Text("MySQL", nil, func(_ *menu.CallbackData) { 114 | runtime.BrowserOpenURL(ctx, "https://dev.mysql.com/doc/refman/8.0/en/") 115 | }), 116 | ), 117 | ), 118 | menu.Text("意见反馈", nil, nil), 119 | menu.Separator(), 120 | menu.Text("检查更新", nil, func(_ *menu.CallbackData) { 121 | kit.Update().CheckUpdate() 122 | }), 123 | menu.Separator(), 124 | menu.Text("关于", nil, func(cd *menu.CallbackData) { 125 | kit.DiaLogKit().About(ctx, icon) 126 | }), 127 | ), 128 | ) 129 | } 130 | -------------------------------------------------------------------------------- /menu/winMenu.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: symbol 3 | * @Date: 2022-05-22 11:21:34 4 | * @LastEditors: symbol 5 | * @LastEditTime: 2022-05-23 11:57:23 6 | * @FilePath: /todb/menu/winMenu.go 7 | * @Description: 8 | * 9 | * Copyright (c) 2022 by symbol, All Rights Reserved. 10 | */ 11 | package menu 12 | 13 | import ( 14 | "context" 15 | 16 | "github.com/wailsapp/wails/v2/pkg/menu" 17 | "github.com/wailsapp/wails/v2/pkg/menu/keys" 18 | "github.com/wailsapp/wails/v2/pkg/runtime" 19 | ) 20 | 21 | var ( 22 | insWinMenu = sWinMenu{} 23 | ) 24 | 25 | type sWinMenu struct { 26 | ctx context.Context 27 | } 28 | 29 | func WinMenu() *sWinMenu { 30 | return &insWinMenu 31 | } 32 | 33 | // File win文件 34 | func (s *sWinMenu) File(ctx context.Context) *menu.MenuItem { 35 | return menu.SubMenu("文件", 36 | menu.NewMenuFromItems( 37 | menu.SubMenu("新建连接", 38 | menu.NewMenuFromItems( 39 | menu.Text("Redis...", keys.CmdOrCtrl("N"), func(data *menu.CallbackData) { 40 | runtime.EventsEmit(ctx, "createConn", "redis") 41 | }), 42 | // menu.Text("MySQL...", nil, func(data *menu.CallbackData) { 43 | // runtime.EventsEmit(ctx, "createConn", "mysql") 44 | // }), 45 | ), 46 | ), 47 | menu.Text("新建查询", nil, nil), 48 | menu.Separator(), 49 | menu.Text("导入连接...", keys.CmdOrCtrl("I"), func(data *menu.CallbackData) { 50 | runtime.EventsEmit(ctx, "importConn") 51 | }), 52 | menu.Text("导出连接...", nil, func(data *menu.CallbackData) { 53 | runtime.EventsEmit(ctx, "exportConn") 54 | }), 55 | menu.Separator(), 56 | menu.Text("退出", keys.CmdOrCtrl("Q"), func(cd *menu.CallbackData) { 57 | runtime.Quit(ctx) 58 | }), 59 | ), 60 | ) 61 | } 62 | -------------------------------------------------------------------------------- /scripts/build-macos-arm.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | echo -e "Start running the script..." 4 | cd ../ 5 | 6 | echo -e "Start building the app for macos platform..." 7 | wails build --clean --platform darwin/arm64 8 | 9 | echo -e "End running the script!" 10 | -------------------------------------------------------------------------------- /scripts/build-macos-intel.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | echo -e "Start running the script..." 4 | cd ../ 5 | 6 | echo -e "Start building the app for macos platform..." 7 | wails build --clean --platform darwin 8 | 9 | echo -e "End running the script!" 10 | -------------------------------------------------------------------------------- /scripts/build-macos.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | echo -e "Start running the script..." 4 | cd ../ 5 | 6 | echo -e "Start building the app for macos platform..." 7 | wails build --clean --platform darwin/universal 8 | 9 | echo -e "End running the script!" 10 | -------------------------------------------------------------------------------- /scripts/build-windows.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | echo -e "Start running the script..." 4 | cd ../ 5 | 6 | echo -e "Start building the app for windows platform..." 7 | wails build --clean --platform windows/amd64 8 | 9 | echo -e "End running the script!" 10 | -------------------------------------------------------------------------------- /scripts/build.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | echo -e "Start running the script..." 4 | cd ../ 5 | 6 | echo -e "Start building the app..." 7 | wails build --clean 8 | 9 | echo -e "End running the script!" 10 | -------------------------------------------------------------------------------- /scripts/install-wails-cli.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | echo -e "Start running the script..." 4 | cd ../ 5 | 6 | echo -e "Current Go version: \c" 7 | go version 8 | 9 | echo -e "Install the Wails command line tool..." 10 | go install github.com/wailsapp/wails/v2/cmd/wails@latest 11 | 12 | echo -e "Successful installation!" 13 | 14 | echo -e "End running the script!" 15 | -------------------------------------------------------------------------------- /service/general.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: symbol 3 | * @Date: 2022-05-21 19:59:46 4 | * @LastEditors: symbol 5 | * @LastEditTime: 2022-05-28 14:53:21 6 | * @FilePath: \ToDb\service\general.go 7 | * @Description: 8 | * 9 | * Copyright (c) 2022 by symbol, All Rights Reserved. 10 | */ 11 | // package service 12 | // @Author: symbol 13 | // @Date: 2022-05-04 14 | // @LastEditors: symbol 15 | // @LastEditTime: 2022-05-04 18:06:31 16 | // @FilePath: \ToDb\service\general.go 17 | // @Description: 通用的通信模块 18 | 19 | // Copyright (c) 2022 by symbol, All Rights Reserved. 20 | 21 | package service 22 | 23 | import ( 24 | "ToDb/kit/os" 25 | "context" 26 | "io/ioutil" 27 | sysOs "os" 28 | "os/exec" 29 | "os/user" 30 | sysRuntime "runtime" 31 | 32 | "github.com/tidwall/gjson" 33 | "github.com/wailsapp/wails/v2/pkg/runtime" 34 | ) 35 | 36 | var ( 37 | insGeneral = sGeneral{} 38 | ) 39 | 40 | type sGeneral struct { 41 | ctx context.Context 42 | } 43 | 44 | func General() *sGeneral { 45 | return &insGeneral 46 | } 47 | 48 | // ImportConn 导入连接 49 | // @param {[type]} ctx context.Context [description] 50 | // @return error 51 | func (s *sGeneral) ImportConn(ctx context.Context) error { 52 | user, err := user.Current() 53 | if err != nil { 54 | return err 55 | } 56 | defaultPath := user.HomeDir + string(sysOs.PathSeparator) + "Downloads" 57 | // 获取到文件的路径和错误信息 58 | selection, err := runtime.OpenFileDialog(ctx, runtime.OpenDialogOptions{ 59 | // 默认对话框打开时显示的目录 60 | DefaultDirectory: defaultPath, 61 | // 对话框标题 62 | Title: "选择导入文件", 63 | // 默认文件名 64 | DefaultFilename: "localhost.json", 65 | // 文件过滤器列表 66 | Filters: []runtime.FileFilter{ 67 | { 68 | DisplayName: "File (*.json)", 69 | Pattern: "*.json", 70 | }, 71 | }, 72 | }) 73 | if err != nil { 74 | return err 75 | } 76 | // 读取文件 77 | bData, err := ioutil.ReadFile(selection) 78 | if err != nil { 79 | return err 80 | } 81 | alias := gjson.Get(string(bData), "alias").String() 82 | return os.File().SaveFile(ctx, alias, string(bData)) 83 | } 84 | 85 | // ExportConn 导出连接[由于需要第二个窗口,基础框架未实现故暂时未实现] 86 | // @param {[type]} ctx context.Context [description] 87 | // @return error 88 | func (s *sGeneral) ExportConn(ctx context.Context) error { 89 | homeDir, err := os.File().HomeDir(ctx) 90 | if err != nil { 91 | return err 92 | } 93 | // 打开文件资源管理器 94 | osName := sysRuntime.GOOS 95 | 96 | switch osName { 97 | case "windows": 98 | exec.Command("explorer", homeDir).Start() 99 | case "linux": 100 | exec.Command("xdg-open", homeDir).Start() 101 | case "darwin": 102 | exec.Command("open", homeDir).Start() 103 | } 104 | return nil 105 | } 106 | -------------------------------------------------------------------------------- /service/redis.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "ToDb/common/structs" 5 | "ToDb/kit" 6 | "ToDb/kit/os" 7 | redisKit "ToDb/kit/redis" 8 | "context" 9 | "encoding/json" 10 | "errors" 11 | "fmt" 12 | "io/ioutil" 13 | "net/http" 14 | "reflect" 15 | "strconv" 16 | "strings" 17 | 18 | "github.com/go-redis/redis/v8" 19 | 20 | "github.com/tidwall/gjson" 21 | ) 22 | 23 | var ( 24 | insRedis = sRedis{} 25 | ) 26 | 27 | type sRedis struct { 28 | ctx context.Context 29 | } 30 | 31 | func Redis() *sRedis { 32 | return &insRedis 33 | } 34 | 35 | // 检查参数 36 | func (s *sRedis) checkParameter(parameter map[string]gjson.Result) (int, string, bool) { 37 | //返回连接信息 38 | message := "连接成功" 39 | //状态码 40 | code := http.StatusOK 41 | //用于标记是否要继续匹配 42 | fail := false 43 | for k, v := range parameter { 44 | if k == "savePassword" || k == "username" { 45 | continue 46 | } 47 | if v.String() == "" { 48 | code = http.StatusBadRequest 49 | fieldV, _ := reflect.TypeOf(structs.ConnectionType{}).FieldByName(k) 50 | tag := fieldV.Tag.Get("tag") 51 | message = tag + "不能为空" 52 | fail = true 53 | break 54 | } 55 | } 56 | return code, message, fail 57 | } 58 | 59 | // RedisPing redis测试连接 60 | func (s *sRedis) RedisPing(connectionInfo string) (int, string) { 61 | //返回连接信息 62 | message := "连接成功" 63 | //状态码 64 | code := http.StatusOK 65 | //用于标记是否要继续匹配 66 | fail := false 67 | parameter := gjson.Parse(connectionInfo).Map() 68 | code, message, fail = s.checkParameter(parameter) 69 | 70 | info := structs.ConnectionType{ 71 | Alias: parameter["alias"].String(), 72 | HostURL: parameter["hostURL"].String(), 73 | Port: parameter["port"].String(), 74 | Username: parameter["username"].String(), 75 | Password: parameter["password"].String(), 76 | } 77 | 78 | if !fail { 79 | redisKit.Addr = info.HostURL 80 | redisKit.Port = info.Port 81 | redisKit.Username = info.Username 82 | redisKit.Password = info.Password 83 | redisKit.InitDb() 84 | err := redisKit.Redis().Ping(context.Background()) 85 | if err != nil { 86 | code = http.StatusBadRequest 87 | message = err.Error() 88 | } else { 89 | code = http.StatusOK 90 | message = "连接成功" 91 | } 92 | } 93 | return code, message 94 | } 95 | 96 | // Ok 确定按钮 97 | func (s *sRedis) Ok(ctx context.Context, connectionInfo string) (int, string) { 98 | //返回连接信息 99 | message := "连接成功" 100 | //状态码 101 | code := http.StatusOK 102 | parameter := gjson.Parse(connectionInfo).Map() 103 | code, message, _ = s.checkParameter(parameter) 104 | connType := parameter["connType"].String() 105 | connType = strings.ToLower(connType) 106 | info := structs.ConnectionType{ 107 | Type: connType, 108 | Alias: parameter["alias"].String(), 109 | HostURL: parameter["hostURL"].String(), 110 | Port: parameter["port"].String(), 111 | Username: parameter["username"].String(), 112 | Password: parameter["password"].String(), 113 | } 114 | if parameter["savePassword"].Bool() { 115 | // 创建文件或文件夹 116 | err := os.File().CreateFile(ctx, info.Alias) 117 | if err != nil { 118 | return code, message 119 | } 120 | value, _ := json.MarshalIndent(info, "", " ") 121 | err = os.File().SaveFile(ctx, info.Alias, string(value)) 122 | if err != nil { 123 | return 0, "保存失败" 124 | } 125 | } 126 | return code, message 127 | } 128 | 129 | // LoadingBaseHistoryInfo 加载已经存储的连接别名 130 | func (s *sRedis) LoadingBaseHistoryInfo(ctx context.Context) string { 131 | // 读取所有的文件 132 | files, _ := os.File().ReadFiles(ctx) 133 | datas := make([]structs.BaseTreeInfo, 0, 1) 134 | // 获取程序设定的基础路径 135 | homeDir, _ := os.File().HomeDir(ctx) 136 | // 循环组装数据,用于前端tree的显示 137 | for _, f := range files { 138 | fileName := f.Name() 139 | var filePath strings.Builder 140 | filePath.WriteString(homeDir) 141 | filePath.WriteString(fileName) 142 | // 读取文件内容 143 | valueByte, _ := ioutil.ReadFile(filePath.String()) 144 | // 提取类型 145 | t := gjson.Get(string(valueByte), "type").String() 146 | // 提取别名 147 | alias := gjson.Get(string(valueByte), "alias").String() 148 | bci := structs.BaseTreeInfo{ 149 | Title: alias, 150 | Label: alias, 151 | ConnType: t, 152 | IconPath: "", 153 | ConnFileAddr: filePath.String(), 154 | } 155 | datas = append(datas, bci) 156 | } 157 | jb, _ := json.Marshal(datas) 158 | return string(jb) 159 | } 160 | 161 | // LoadingHistoryInfo 加载已经存储的连接信息 162 | func (s *sRedis) LoadingHistoryInfo(fileName string) (int, string) { 163 | s.initRedis(fileName) 164 | err := redisKit.Redis().Ping(context.Background()) 165 | if err != nil { 166 | return http.StatusBadRequest, err.Error() 167 | } 168 | var data []structs.DbTreeInfo 169 | //redis则直接显示15个库 170 | for i := 0; i < 16; i++ { 171 | var dbName strings.Builder 172 | dbName.WriteString("db") 173 | dbName.WriteString(strconv.Itoa(i)) 174 | dbName.WriteString("(") 175 | dbName.WriteString(strconv.Itoa(redisKit.Redis().GetDbCount(context.Background(), i))) 176 | dbName.WriteString(")") 177 | data = append(data, structs.DbTreeInfo{ 178 | Label: dbName.String(), 179 | Title: dbName.String(), 180 | Key: strconv.Itoa(i), 181 | }) 182 | } 183 | 184 | vb, _ := json.Marshal(data) 185 | return http.StatusOK, string(vb) 186 | } 187 | 188 | // 连接redis 189 | func (s *sRedis) initRedis(fileName string) []byte { 190 | // 获取所有连接文件的路径 191 | homeDir, _ := os.File().HomeDir(context.Background()) 192 | var filePath strings.Builder 193 | filePath.WriteString(homeDir) 194 | filePath.WriteString(fileName) 195 | filePath.WriteString(".json") 196 | valueByte, _ := ioutil.ReadFile(filePath.String()) 197 | 198 | //优先初始化redis链接 199 | redisKit.Port = gjson.Get(string(valueByte), "port").String() 200 | redisKit.Username = gjson.Get(string(valueByte), "username").String() 201 | redisKit.Password = gjson.Get(string(valueByte), "password").String() 202 | redisKit.Addr = gjson.Get(string(valueByte), "hostURL").String() 203 | redisKit.InitDb() 204 | return valueByte 205 | } 206 | 207 | // LoadingDbResource 加载数据库资源消耗信息 208 | func (s *sRedis) LoadingDbResource(ctx context.Context, key string) string { 209 | s.initRedis(key) 210 | return redisKit.Redis().GetMainViewInfo(ctx) 211 | } 212 | 213 | // GetNodeData 获取节点数据 214 | func (s *sRedis) GetNodeData(ctx context.Context, connType, connName, nodeIdStr string) (string, error) { 215 | var value string 216 | if connType == "" || 217 | connName == "" { 218 | return value, errors.New("parameter is missing") 219 | } 220 | s.initRedis(connName) 221 | nodeId, _ := strconv.Atoi(nodeIdStr) 222 | redisKit.Redis().ChangeDb(ctx, nodeId) 223 | arr, err := redisKit.Redis().GetDbKeys(ctx, 0) 224 | if err != nil { 225 | return "", err 226 | } 227 | value = kit.StrKit().PackageTree(arr) 228 | return value, nil 229 | } 230 | 231 | // RedisGetData 通过key获取连接信息 232 | func (s *sRedis) RedisGetData(ctx context.Context, connType, connName, nodeIdStr, key string) (structs.GetValue, error) { 233 | var getValue structs.GetValue 234 | if connType == "" || 235 | connName == "" { 236 | return getValue, errors.New("parameter is missing") 237 | } 238 | s.initRedis(connName) 239 | nodeId, _ := strconv.Atoi(nodeIdStr) 240 | redisKit.Redis().ChangeDb(ctx, nodeId) 241 | // 获取数据类型 242 | valueType := redisKit.Redis().GetType(ctx, key) 243 | valueType = strings.ToLower(valueType) 244 | switch valueType { 245 | case "string": 246 | // 获取类型为string的数据 247 | v := redisKit.Redis().GetStrValue(ctx, key) 248 | getValue.Type = "string" 249 | getValue.Key = key 250 | getValue.Ttl = redisKit.Redis().GetTTL(ctx, key) 251 | getValue.Value = v 252 | getValue.Size = len(v) 253 | command := s.BuildCommand(key, "string", v) 254 | getValue.CommandStr = command 255 | return getValue, nil 256 | case "list": 257 | // 获取类型为list的数据 258 | v := redisKit.Redis().GetListValue(ctx, key) 259 | getValue.Type = "list" 260 | getValue.Key = key 261 | getValue.Ttl = redisKit.Redis().GetTTL(ctx, key) 262 | var listValues []structs.RedisList 263 | for i, vv := range v { 264 | listValues = append(listValues, structs.RedisList{ 265 | Id: i + 1, 266 | Value: vv, 267 | }) 268 | } 269 | getValue.Value = listValues 270 | command := s.BuildCommand(key, "list", v) 271 | getValue.CommandStr = command 272 | return getValue, nil 273 | case "set": 274 | // 获取类型为set的数据 275 | v := redisKit.Redis().GetSetValue(ctx, key) 276 | getValue.Type = "set" 277 | getValue.Key = key 278 | getValue.Ttl = redisKit.Redis().GetTTL(ctx, key) 279 | var setValues []structs.RedisList 280 | for i, vv := range v { 281 | setValues = append(setValues, structs.RedisList{ 282 | Id: i + 1, 283 | Value: vv, 284 | }) 285 | } 286 | getValue.Value = setValues 287 | command := s.BuildCommand(key, "set", v) 288 | getValue.CommandStr = command 289 | return getValue, nil 290 | case "hash": 291 | // 获取类型为hash的数据 292 | v := redisKit.Redis().GetHashValue(ctx, key) 293 | getValue.Type = "hash" 294 | getValue.Key = key 295 | getValue.Ttl = redisKit.Redis().GetTTL(ctx, key) 296 | var hashValues []structs.RedisHash 297 | var i = 1 298 | for k, vv := range v { 299 | hashValues = append(hashValues, structs.RedisHash{ 300 | Id: i, 301 | Key: k, 302 | Value: vv, 303 | }) 304 | i++ 305 | } 306 | getValue.Value = hashValues 307 | command := s.BuildCommand(key, "hash", v) 308 | getValue.CommandStr = command 309 | return getValue, nil 310 | case "stream": 311 | // 获取类型为stream的数据 312 | v := redisKit.Redis().GetStreamValue(ctx, key) 313 | getValue.Type = "stream" 314 | getValue.Key = key 315 | getValue.Ttl = redisKit.Redis().GetTTL(ctx, key) 316 | var streamValues []structs.RedisHash 317 | for i, vv := range v { 318 | _data, _ := json.Marshal(vv.Values) 319 | streamValues = append(streamValues, structs.RedisHash{ 320 | Id: i, 321 | Key: vv.ID, 322 | Value: string(_data), 323 | }) 324 | // streamValues = append(streamValues, vv) 325 | } 326 | getValue.Value = streamValues 327 | command := s.BuildCommand(key, "stream", v) 328 | getValue.CommandStr = command 329 | return getValue, nil 330 | case "zset": 331 | // 获取类型为zset的数据 332 | v := redisKit.Redis().GetZSetValue(ctx, key) 333 | getValue.Type = "zset" 334 | getValue.Key = key 335 | getValue.Ttl = redisKit.Redis().GetTTL(ctx, key) 336 | var zsetValues []structs.RedisHash 337 | var i = 1 338 | for mk, mv := range v { 339 | zsetValues = append(zsetValues, structs.RedisHash{ 340 | Id: i, 341 | Key: mk, 342 | Value: mv, 343 | }) 344 | i++ 345 | } 346 | getValue.Value = zsetValues 347 | command := s.BuildCommand(key, "zset", v) 348 | getValue.CommandStr = command 349 | return getValue, nil 350 | default: 351 | return getValue, errors.New("unknown error") 352 | } 353 | } 354 | 355 | // GetValueType 获取指定key的数据类型 356 | func (s *sRedis) GetValueType(ctx context.Context, connName, nodeIdStr, key string) (string, error) { 357 | if connName == "" { 358 | return "", errors.New("parameter is missing") 359 | } 360 | s.initRedis(connName) 361 | s.initRedis(connName) 362 | nodeId, _ := strconv.Atoi(nodeIdStr) 363 | redisKit.Redis().ChangeDb(ctx, nodeId) 364 | // 获取数据类型 365 | valueType := redisKit.Redis().GetType(ctx, key) 366 | return valueType, nil 367 | } 368 | 369 | // RedisReName 重命名key 370 | func (s *sRedis) RedisReName(ctx context.Context, connType, connName, nodeIdStr, oldKey, newKey string) string { 371 | if connType == "" || 372 | connName == "" { 373 | return "parameter is missing" 374 | } 375 | s.initRedis(connName) 376 | nodeId, _ := strconv.Atoi(nodeIdStr) 377 | redisKit.Redis().ChangeDb(ctx, nodeId) 378 | // 通过键获取值 379 | v := redisKit.Redis().RenName(ctx, oldKey, newKey) 380 | if v != nil { 381 | return v.Error() 382 | } 383 | return "success" 384 | } 385 | 386 | // RedisUpTtl 更新redis剩余时间 387 | func (s *sRedis) RedisUpTtl(ctx context.Context, connType, connName, nodeIdStr, key string, ttlStr string) string { 388 | //todo 当ttl=-1时会出现数据直接丢失的情况 389 | ttl, err := strconv.Atoi(ttlStr) 390 | if err != nil { 391 | return "ttl is not number" 392 | } 393 | if connType == "" || 394 | connName == "" { 395 | return "parameter is missing" 396 | } 397 | s.initRedis(connName) 398 | nodeId, _ := strconv.Atoi(nodeIdStr) 399 | redisKit.Redis().ChangeDb(ctx, nodeId) 400 | // 通过键获取值 401 | var v error 402 | if ttl == -1 { 403 | // 表示需要永久存储 404 | v = redisKit.Redis().UpPermanent(ctx, key) 405 | } else { 406 | v = redisKit.Redis().UpTtl(ctx, key, ttl) 407 | } 408 | if v != nil { 409 | return v.Error() 410 | } 411 | return "success" 412 | } 413 | 414 | // RedisDel 删除redis数据 415 | func (s *sRedis) RedisDel(ctx context.Context, connType, connName, nodeIdStr, key string) string { 416 | if connType == "" || 417 | connName == "" { 418 | return "parameter is missing" 419 | } 420 | s.initRedis(connName) 421 | nodeId, _ := strconv.Atoi(nodeIdStr) 422 | redisKit.Redis().ChangeDb(ctx, nodeId) 423 | // 通过键获取值 424 | v := redisKit.Redis().Del(ctx, key) 425 | if v == 0 { 426 | return "del error" 427 | } 428 | return "success" 429 | } 430 | 431 | // RedisUpdateStringValue 更新redis数据 432 | func (s *sRedis) RedisUpdateStringValue(ctx context.Context, connType, connName, nodeIdStr, key, value, ttlStr string) error { 433 | if connType == "" || 434 | connName == "" { 435 | return errors.New("parameter is missing") 436 | } 437 | s.initRedis(connName) 438 | nodeId, _ := strconv.Atoi(nodeIdStr) 439 | redisKit.Redis().ChangeDb(ctx, nodeId) 440 | // 通过键获取值 441 | ttl, _ := strconv.Atoi(ttlStr) 442 | err := redisKit.Redis().AddData(ctx, key, value, ttl) 443 | if err != nil { 444 | return err 445 | } 446 | return nil 447 | } 448 | 449 | // BuildCommand 构建命令 450 | func (s *sRedis) BuildCommand(key, keyType string, value interface{}) string { 451 | lowerCaseKeyType := strings.ToLower(keyType) 452 | var command strings.Builder 453 | switch lowerCaseKeyType { 454 | case "string": 455 | // 构建set命令 456 | // SET "1:2:34" "你好啊😂" 457 | command.WriteString("SET ") 458 | command.WriteString("\"") 459 | command.WriteString(key) 460 | command.WriteString("\" \"") 461 | command.WriteString(value.(string)) 462 | command.WriteString("\"") 463 | case "hash": 464 | // 构建hash命令 465 | //HMSET "1:2:hash" "New field" "New value" "123" "321" 466 | command.WriteString("HMSET ") 467 | command.WriteString("\"") 468 | command.WriteString(key) 469 | arr := value.(map[string]string) 470 | for k, v := range arr { 471 | command.WriteString("\" \"") 472 | command.WriteString(k) 473 | command.WriteString("\" \"") 474 | command.WriteString(v) 475 | } 476 | command.WriteString("\"") 477 | case "list": 478 | // 构建list命令 479 | // RPUSH "1:2:list" "New member" "12312213" "1231" "测试" 480 | // return "RPUSH " + key + " " + value 481 | command.WriteString("RPUSH ") 482 | command.WriteString("\"") 483 | command.WriteString(key) 484 | command.WriteString("\"") 485 | arr := value.([]string) 486 | for _, v := range arr { 487 | command.WriteString(" \"") 488 | command.WriteString(v) 489 | command.WriteString("\"") 490 | } 491 | case "set": 492 | // 构建set命令 493 | // SADD "1:2:set" "New member" "sdfsdf" 494 | // return "SADD " + key + " " + value 495 | command.WriteString("SADD ") 496 | command.WriteString("\"") 497 | command.WriteString(key) 498 | command.WriteString("\"") 499 | arr := value.([]string) 500 | for _, v := range arr { 501 | command.WriteString(" \"") 502 | command.WriteString(v) 503 | command.WriteString("\"") 504 | } 505 | case "zset": 506 | // 构建zset命令 507 | //ZADD "1:2:zset" 12 "321" 0 "New member" 508 | // return "ZADD " + key + " " + value 509 | command.WriteString("ZADD ") 510 | command.WriteString("\"") 511 | command.WriteString(key) 512 | command.WriteString("\" ") 513 | arr := value.(map[string]string) 514 | fmt.Println("=====>", arr) 515 | for k, v := range arr { 516 | command.WriteString(k) 517 | command.WriteString(" \"") 518 | command.WriteString(v) 519 | command.WriteString("\" ") 520 | } 521 | case "stream": 522 | // 构建stream命令 523 | // return "XADD " + key + " " + value 524 | command.WriteString("XADD ") 525 | command.WriteString("\"") 526 | command.WriteString(key) 527 | command.WriteString("\"") 528 | arr := value.([]redis.XMessage) 529 | for _, v := range arr { 530 | command.WriteString(v.ID) 531 | _val := v.Values 532 | for mk, mv := range _val { 533 | command.WriteString(" \"") 534 | command.WriteString(mk) 535 | command.WriteString("\" \"") 536 | command.WriteString(mv.(string)) 537 | command.WriteString("\"") 538 | } 539 | } 540 | default: 541 | return "" 542 | } 543 | return command.String() 544 | } 545 | -------------------------------------------------------------------------------- /todb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xingcxb/ToDb/e039f594c6d84c56122e5d4ee4bb0e67e403f56c/todb.png -------------------------------------------------------------------------------- /wails.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ToDb", 3 | "assetdir": "frontend/dist", 4 | "frontend:build": "npm run build", 5 | "frontend:install": "npm install", 6 | "frontend:dev": "", 7 | "wailsjsdir": "frontend/src", 8 | "version": "2", 9 | "Path": "", 10 | "BuildDir": "", 11 | "outputfilename": "wails_note.exe", 12 | "OutputType": "", 13 | "Platform": "", 14 | "Author": { 15 | "name": "Symbol", 16 | "email": "xingcxb@hotmail.com" 17 | }, 18 | "Info": { 19 | "companyName": "Symbol", 20 | "productName": "ToDb", 21 | "productVersion": "0.1.0", 22 | "copyright": "Copyright©️ 2022- Symbol. All Rights Reserved.", 23 | "comments": "又一个装模作样的数据库连接工具🙄️" 24 | }, 25 | "debounceMS": 100, 26 | "devserverurl": "http://localhost:34115", 27 | "appargs": "" 28 | } --------------------------------------------------------------------------------