├── apps ├── controllers │ ├── setting.go │ ├── files_test.go │ ├── help.go │ ├── z_inject_gen.go │ ├── files.go │ └── binlog.go ├── orm │ ├── sqlite_test.go │ ├── sql_logs.go │ ├── sqlite.go │ ├── files_logs_orm.go │ └── binlog_orm.go ├── ctx │ ├── z_ctx.go │ ├── ctx.go │ ├── log_test.go │ └── log.go ├── mysql │ ├── binlog_test.go │ ├── sqlite_test.go │ ├── sqlite.go │ └── binlog.go ├── bind.go ├── app.go └── datas │ └── paths.go ├── frontend ├── env.d.ts ├── public │ └── favicon.ico ├── src │ ├── store │ │ └── db.ts │ ├── assets │ │ ├── base.css │ │ ├── logo.svg │ │ └── main.css │ ├── main.ts │ ├── App.vue │ ├── layouts │ │ ├── index.vue │ │ ├── HeaderView.vue │ │ └── LeftMenu.vue │ ├── views │ │ ├── AboutView.vue │ │ ├── HomeView.vue │ │ ├── db │ │ │ ├── DbList.vue │ │ │ └── DbDetails.vue │ │ ├── BinlogView.vue │ │ └── binlog │ │ │ └── LogsList.vue │ ├── router │ │ └── index.ts │ └── component │ │ └── icons │ │ ├── About.vue │ │ ├── Github.vue │ │ ├── Add.vue │ │ ├── Binlog.vue │ │ └── Setting.vue ├── tsconfig.json ├── wailsjs │ ├── go │ │ └── controllers │ │ │ ├── Help.d.ts │ │ │ ├── Help.js │ │ │ ├── Binlog.d.ts │ │ │ ├── Binlog.js │ │ │ ├── Files.d.ts │ │ │ └── Files.js │ └── runtime │ │ ├── package.json │ │ ├── runtime.js │ │ └── runtime.d.ts ├── index.html ├── tsconfig.app.json ├── tsconfig.node.json ├── vite.config.ts ├── .gitignore └── package.json ├── Makefile ├── scripts ├── build.sh ├── build-macos-intel.sh ├── build-macos-arm.sh ├── build-macos.sh ├── build-windows.sh └── install-wails-cli.sh ├── wails.json ├── .gitignore ├── README.md ├── NEXTSTEPS.md ├── main.go ├── go.mod ├── .github └── workflows │ ├── release-windows.yaml │ ├── release-macos.yaml │ └── release-linux.yaml └── go.sum /apps/controllers/setting.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | -------------------------------------------------------------------------------- /frontend/env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | dev: 2 | wails dev 3 | 4 | generate: 5 | wails generate -------------------------------------------------------------------------------- /frontend/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ctfang/mysql-binlog-ui/main/frontend/public/favicon.ico -------------------------------------------------------------------------------- /frontend/src/store/db.ts: -------------------------------------------------------------------------------- 1 | import { reactive } from "vue"; 2 | 3 | export const db = reactive({ 4 | ID: 0, 5 | }); 6 | -------------------------------------------------------------------------------- /frontend/src/assets/base.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --main-header: 38px; 3 | --main-height: calc(100vh - var(--main-header)); 4 | } 5 | -------------------------------------------------------------------------------- /apps/orm/sqlite_test.go: -------------------------------------------------------------------------------- 1 | package orm 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestGetDB(t *testing.T) { 8 | GetDB() 9 | } 10 | -------------------------------------------------------------------------------- /apps/ctx/z_ctx.go: -------------------------------------------------------------------------------- 1 | package ctx 2 | 3 | import "context" 4 | 5 | var ctx context.Context 6 | 7 | func SetCtx(c context.Context) { 8 | ctx = c 9 | } 10 | -------------------------------------------------------------------------------- /apps/mysql/binlog_test.go: -------------------------------------------------------------------------------- 1 | package mysql 2 | 3 | import "testing" 4 | 5 | func TestSaveToSqlite(t *testing.T) { 6 | SaveToSqlite("/Users/lvluo/Downloads/Q-sh-mhhy-prod-farm_binlog_mysqlbin.014384") 7 | } 8 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /apps/bind.go: -------------------------------------------------------------------------------- 1 | package apps 2 | 3 | import ( 4 | "changeme/apps/controllers" 5 | ) 6 | 7 | func (a *App) GetBind() []interface{} { 8 | got := controllers.GetAllProvider() 9 | 10 | return got 11 | } 12 | -------------------------------------------------------------------------------- /frontend/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "files": [], 3 | "references": [ 4 | { 5 | "path": "./tsconfig.node.json" 6 | }, 7 | { 8 | "path": "./tsconfig.app.json" 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /apps/ctx/ctx.go: -------------------------------------------------------------------------------- 1 | package ctx 2 | 3 | import "github.com/wailsapp/wails/v2/pkg/runtime" 4 | 5 | func OpenFileDialog(dialogOptions runtime.OpenDialogOptions) (string, error) { 6 | return runtime.OpenFileDialog(ctx, dialogOptions) 7 | } 8 | -------------------------------------------------------------------------------- /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-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.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 | -------------------------------------------------------------------------------- /frontend/wailsjs/go/controllers/Help.d.ts: -------------------------------------------------------------------------------- 1 | // Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL 2 | // This file is automatically generated. DO NOT EDIT 3 | 4 | export function Exit():Promise; 5 | 6 | export function OpenURL(arg1:string):Promise; 7 | -------------------------------------------------------------------------------- /apps/orm/sql_logs.go: -------------------------------------------------------------------------------- 1 | package orm 2 | 3 | import ( 4 | "changeme/apps/ctx" 5 | "gorm.io/gorm/logger" 6 | ) 7 | 8 | // NewSqlLog skip 需要屏蔽的sql 9 | func NewSqlLog(config logger.Config) logger.Interface { 10 | got := &ctx.Log{ 11 | Config: config, 12 | } 13 | 14 | return got 15 | } 16 | -------------------------------------------------------------------------------- /frontend/src/assets/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /apps/controllers/files_test.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | "github.com/go-home-admin/home/bootstrap/utils" 5 | "testing" 6 | ) 7 | 8 | func TestFiles_GetTitleList(t *testing.T) { 9 | f := &Files{} 10 | gotGot, gotCount := f.GetTitleList(1, 11) 11 | 12 | utils.Dump(gotGot, gotCount) 13 | } 14 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /wails.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://wails.io/schemas/config.v2.json", 3 | "name": "mysql-binlog-ui", 4 | "outputfilename": "mysqlBinlogUI", 5 | "frontend:build": "npm run build", 6 | "frontend:dev:watcher": "npm run dev", 7 | "frontend:dev:serverUrl": "auto", 8 | "author": { 9 | "name": "yuanzhao", 10 | "email": "2206582181@qq.com" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /frontend/src/main.ts: -------------------------------------------------------------------------------- 1 | import "./assets/main.css"; 2 | 3 | import { createApp, ref, provide } from "vue"; 4 | import App from "./App.vue"; 5 | import ElementPlus from "element-plus"; 6 | import "element-plus/dist/index.css"; 7 | import router from "./router"; 8 | 9 | const app = createApp(App); 10 | 11 | app.use(router); 12 | app.use(ElementPlus); 13 | 14 | app.mount("#app"); 15 | -------------------------------------------------------------------------------- /frontend/wailsjs/go/controllers/Help.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | // Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL 3 | // This file is automatically generated. DO NOT EDIT 4 | 5 | export function Exit() { 6 | return window['go']['controllers']['Help']['Exit'](); 7 | } 8 | 9 | export function OpenURL(arg1) { 10 | return window['go']['controllers']['Help']['OpenURL'](arg1); 11 | } 12 | -------------------------------------------------------------------------------- /frontend/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite App 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /frontend/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@vue/tsconfig/tsconfig.dom.json", 3 | "include": ["env.d.ts", "src/**/*", "src/**/*.vue"], 4 | "exclude": ["src/**/__tests__/*"], 5 | "compilerOptions": { 6 | "composite": true, 7 | "noEmit": true, 8 | "baseUrl": ".", 9 | "paths": { 10 | "@/*": ["./src/*", "./wailsjs/*"], 11 | "@wailsjs/*": ["./wailsjs/*"] 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /frontend/wailsjs/go/controllers/Binlog.d.ts: -------------------------------------------------------------------------------- 1 | // Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL 2 | // This file is automatically generated. DO NOT EDIT 3 | import {controllers} from '../models'; 4 | import {orm} from '../models'; 5 | 6 | export function GetDetailsList(arg1:number,arg2:controllers.SearchBinlog):Promise; 7 | 8 | export function GetTitleList(arg1:number):Promise>; 9 | -------------------------------------------------------------------------------- /frontend/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tsconfig/node18/tsconfig.json", 3 | "include": [ 4 | "vite.config.*", 5 | "vitest.config.*", 6 | "cypress.config.*", 7 | "nightwatch.conf.*", 8 | "playwright.config.*" 9 | ], 10 | "compilerOptions": { 11 | "composite": true, 12 | "noEmit": true, 13 | "module": "ESNext", 14 | "moduleResolution": "Bundler", 15 | "types": ["node"] 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /frontend/wailsjs/go/controllers/Binlog.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | // Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL 3 | // This file is automatically generated. DO NOT EDIT 4 | 5 | export function GetDetailsList(arg1, arg2) { 6 | return window['go']['controllers']['Binlog']['GetDetailsList'](arg1, arg2); 7 | } 8 | 9 | export function GetTitleList(arg1) { 10 | return window['go']['controllers']['Binlog']['GetTitleList'](arg1); 11 | } 12 | -------------------------------------------------------------------------------- /frontend/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { fileURLToPath, URL } from 'node:url' 2 | 3 | import { defineConfig } from 'vite' 4 | import vue from '@vitejs/plugin-vue' 5 | 6 | // https://vitejs.dev/config/ 7 | export default defineConfig({ 8 | plugins: [vue()], 9 | resolve: { 10 | alias: { 11 | '@': fileURLToPath(new URL('./src', import.meta.url)), 12 | '@wailsjs': fileURLToPath(new URL('./wailsjs', import.meta.url)) 13 | } 14 | } 15 | }) 16 | -------------------------------------------------------------------------------- /frontend/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | .DS_Store 12 | dist 13 | dist-ssr 14 | coverage 15 | *.local 16 | 17 | /cypress/videos/ 18 | /cypress/screenshots/ 19 | 20 | # Editor directories and files 21 | .vscode/* 22 | !.vscode/extensions.json 23 | .idea 24 | *.suo 25 | *.ntvs* 26 | *.njsproj 27 | *.sln 28 | *.sw? 29 | 30 | *.tsbuildinfo 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | build/bin 4 | *.log 5 | npm-debug.log* 6 | yarn-debug.log* 7 | yarn-error.log* 8 | pnpm-debug.log* 9 | lerna-debug.log* 10 | 11 | node_modules 12 | .DS_Store 13 | dist 14 | dist-ssr 15 | coverage 16 | *.local 17 | 18 | /cypress/videos/ 19 | /cypress/screenshots/ 20 | 21 | # Editor directories and files 22 | .vscode/* 23 | !.vscode/extensions.json 24 | .idea 25 | *.suo 26 | *.ntvs* 27 | *.njsproj 28 | *.sln 29 | *.sw? 30 | 31 | *.tsbuildinfo 32 | -------------------------------------------------------------------------------- /frontend/src/App.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 25 | -------------------------------------------------------------------------------- /apps/ctx/log_test.go: -------------------------------------------------------------------------------- 1 | package ctx 2 | 3 | import ( 4 | "errors" 5 | "testing" 6 | ) 7 | 8 | func TestLogError(t *testing.T) { 9 | type args struct { 10 | msg string 11 | arr []interface{} 12 | } 13 | tests := []struct { 14 | name string 15 | args args 16 | }{ 17 | { 18 | name: "test", 19 | args: args{ 20 | msg: "test", 21 | arr: []interface{}{1, 2, errors.New("err = 3")}, 22 | }, 23 | }, 24 | } 25 | for _, tt := range tests { 26 | t.Run(tt.name, func(t *testing.T) { 27 | LogError(tt.args.msg, tt.args.arr...) 28 | }) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## About 2 | 3 | 读取原始的 MySQL Binlog文件后,生成更加可读的结构化文件。 解析内容后,可以在 UI 上直接便捷的查看。 4 | 也可以通过生成的 sqlite 数据库文件,直接在数据库客户端上查看。 5 | 6 | ### Prerequisites 7 | 8 | * Go (latest version) 9 | * Node.js >= 16 10 | * NPM >= 9 11 | 12 | ### Compile and run 13 | [CONTRIBUTING.md](..%2F..%2Ftiny-rdm%2F.github%2FCONTRIBUTING.md) 14 | ```bash 15 | wails dev 16 | ``` 17 | 18 | ### 按照固定版本 Wails CLI v2.8.0 19 | go install github.com/wailsapp/wails/v2/cmd/wails@v2.8.0 20 | 21 | 22 | wails build -platform windows/amd64 23 | wails build -platform darwin/universal 24 | wails build -platform linux/amd64 -------------------------------------------------------------------------------- /frontend/wailsjs/go/controllers/Files.d.ts: -------------------------------------------------------------------------------- 1 | // Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL 2 | // This file is automatically generated. DO NOT EDIT 3 | import {controllers} from '../models'; 4 | 5 | export function ClearAllData():Promise; 6 | 7 | export function DeleteFile(arg1:number):Promise; 8 | 9 | export function GetDecodeRowCount():Promise; 10 | 11 | export function GetSystemFile():Promise; 12 | 13 | export function GetTitleList(arg1:number,arg2:number):Promise; 14 | 15 | export function SaveToSqlite(arg1:string):Promise; 16 | -------------------------------------------------------------------------------- /frontend/src/layouts/index.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /apps/controllers/help.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | "os" 5 | "os/exec" 6 | "runtime" 7 | ) 8 | 9 | // Help @Bean 10 | type Help struct{} 11 | 12 | // OpenURL 在默认浏览器中打开给定的URL 13 | func (h *Help) OpenURL(url string) error { 14 | var cmd string 15 | var args []string 16 | 17 | switch runtime.GOOS { 18 | case "windows": 19 | cmd = "cmd" 20 | args = []string{"/c", "start", url} 21 | case "darwin": 22 | cmd = "open" 23 | args = []string{url} 24 | default: // "linux", "freebsd", "openbsd", "netbsd" 25 | cmd = "xdg-open" 26 | args = []string{url} 27 | } 28 | 29 | return exec.Command(cmd, args...).Start() 30 | } 31 | 32 | func (h *Help) Exit() { 33 | os.Exit(0) 34 | } 35 | -------------------------------------------------------------------------------- /apps/mysql/sqlite_test.go: -------------------------------------------------------------------------------- 1 | package mysql 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | ) 7 | 8 | func Test_getSqliteDBName(t *testing.T) { 9 | type args struct { 10 | binlogFilePath string 11 | } 12 | tests := []struct { 13 | name string 14 | args args 15 | want string 16 | want2 string 17 | }{ 18 | { 19 | name: "腾讯云格式", 20 | args: args{"test/Q-sh-mhhy-prod-farm_binlog_mysqlbin.014384"}, 21 | want: "014384", 22 | want2: "Q-sh-mhhy-prod-farm-binlog-mysqlbin", 23 | }, 24 | } 25 | for _, tt := range tests { 26 | t.Run(tt.name, func(t *testing.T) { 27 | _, table := getSqliteDBName(tt.args.binlogFilePath) 28 | if !reflect.DeepEqual(table, tt.want) { 29 | t.Errorf("getSqliteDBName() got = %v, want %v", table, tt.want) 30 | } 31 | }) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /frontend/wailsjs/go/controllers/Files.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | // Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL 3 | // This file is automatically generated. DO NOT EDIT 4 | 5 | export function ClearAllData() { 6 | return window['go']['controllers']['Files']['ClearAllData'](); 7 | } 8 | 9 | export function DeleteFile(arg1) { 10 | return window['go']['controllers']['Files']['DeleteFile'](arg1); 11 | } 12 | 13 | export function GetDecodeRowCount() { 14 | return window['go']['controllers']['Files']['GetDecodeRowCount'](); 15 | } 16 | 17 | export function GetSystemFile() { 18 | return window['go']['controllers']['Files']['GetSystemFile'](); 19 | } 20 | 21 | export function GetTitleList(arg1, arg2) { 22 | return window['go']['controllers']['Files']['GetTitleList'](arg1, arg2); 23 | } 24 | 25 | export function SaveToSqlite(arg1) { 26 | return window['go']['controllers']['Files']['SaveToSqlite'](arg1); 27 | } 28 | -------------------------------------------------------------------------------- /apps/controllers/z_inject_gen.go: -------------------------------------------------------------------------------- 1 | // gen for home toolset 2 | package controllers 3 | 4 | import ( 5 | providers "github.com/go-home-admin/home/bootstrap/providers" 6 | ) 7 | 8 | var _BinlogSingle *Binlog 9 | var _FilesSingle *Files 10 | var _HelpSingle *Help 11 | 12 | func GetAllProvider() []interface{} { 13 | return []interface{}{ 14 | NewBinlog(), 15 | NewFiles(), 16 | NewHelp(), 17 | } 18 | } 19 | 20 | func NewBinlog() *Binlog { 21 | if _BinlogSingle == nil { 22 | _BinlogSingle = &Binlog{} 23 | providers.AfterProvider(_BinlogSingle, "") 24 | } 25 | return _BinlogSingle 26 | } 27 | func NewFiles() *Files { 28 | if _FilesSingle == nil { 29 | _FilesSingle = &Files{} 30 | providers.AfterProvider(_FilesSingle, "") 31 | } 32 | return _FilesSingle 33 | } 34 | func NewHelp() *Help { 35 | if _HelpSingle == nil { 36 | _HelpSingle = &Help{} 37 | providers.AfterProvider(_HelpSingle, "") 38 | } 39 | return _HelpSingle 40 | } 41 | -------------------------------------------------------------------------------- /apps/app.go: -------------------------------------------------------------------------------- 1 | package apps 2 | 3 | import ( 4 | "changeme/apps/ctx" 5 | "changeme/apps/orm" 6 | "context" 7 | ) 8 | 9 | // App struct 10 | type App struct { 11 | ctx context.Context 12 | } 13 | 14 | // NewApp creates a new App application struct 15 | func NewApp() *App { 16 | return &App{} 17 | } 18 | 19 | // StartUp is called at application startup 20 | func (a *App) StartUp(c context.Context) { 21 | ctx.SetCtx(c) 22 | a.ctx = c 23 | } 24 | 25 | func (a App) DomReady(ctx context.Context) { 26 | // Add your action here 27 | } 28 | 29 | func (a *App) BeforeClose(ctx context.Context) (prevent bool) { 30 | return false 31 | } 32 | 33 | func (a *App) Shutdown(ctx context.Context) { 34 | if orm.DB != nil { 35 | db, err := orm.DB.DB() 36 | if err == nil { 37 | db.Close() 38 | } 39 | } 40 | 41 | if orm.BinlogDBMap != nil { 42 | for _, dbTemp := range orm.BinlogDBMap { 43 | db, err := dbTemp.DB() 44 | if err == nil { 45 | db.Close() 46 | } 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /frontend/src/views/AboutView.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /frontend/src/router/index.ts: -------------------------------------------------------------------------------- 1 | import { createRouter, createWebHashHistory } from "vue-router"; 2 | import About from "@/component/icons/About.vue"; 3 | import BinlogIcon from "@/component/icons/Binlog.vue"; 4 | import AddIcon from "@/component/icons/Add.vue"; 5 | 6 | export const AppMenu = [ 7 | { 8 | path: "/", 9 | name: "home", 10 | component: () => import("../views/HomeView.vue"), 11 | meta: { 12 | title: "Home", 13 | icon: BinlogIcon, 14 | }, 15 | }, 16 | { 17 | path: "/binlogs", 18 | name: "binlogs", 19 | component: () => import("../views/BinlogView.vue"), 20 | meta: { 21 | title: "Logs", 22 | icon: AddIcon, 23 | }, 24 | }, 25 | { 26 | path: "/about", 27 | name: "about", 28 | component: () => import("../views/AboutView.vue"), 29 | meta: { 30 | title: "About", 31 | icon: About, 32 | }, 33 | }, 34 | ]; 35 | 36 | const router = createRouter({ 37 | history: createWebHashHistory(import.meta.env.BASE_URL), 38 | routes: AppMenu, 39 | }); 40 | 41 | export default router; 42 | -------------------------------------------------------------------------------- /frontend/src/views/HomeView.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 28 | 29 | 51 | -------------------------------------------------------------------------------- /apps/orm/sqlite.go: -------------------------------------------------------------------------------- 1 | package orm 2 | 3 | import ( 4 | "changeme/apps/ctx" 5 | "changeme/apps/datas" 6 | "github.com/sirupsen/logrus" 7 | "gorm.io/driver/sqlite" 8 | "gorm.io/gorm" 9 | "gorm.io/gorm/logger" 10 | "os" 11 | "path" 12 | ) 13 | 14 | var DB *gorm.DB 15 | 16 | // GetDB 单例 17 | func GetDB() *gorm.DB { 18 | if DB != nil { 19 | return DB 20 | } 21 | DB = NewDB(datas.GetPath("/system") + ".db") 22 | return DB 23 | } 24 | 25 | // NewDB 每次都是新的 26 | func NewDB(dbPath string) *gorm.DB { 27 | savePath := path.Dir(dbPath) 28 | savePath = datas.ToPath(savePath) 29 | if err := os.MkdirAll(savePath, 0755); err != nil { 30 | return nil 31 | } 32 | dbPath = datas.ToPath(dbPath) 33 | db, err := gorm.Open(sqlite.Open(dbPath), &gorm.Config{ 34 | Logger: NewSqlLog(logger.Config{ 35 | SlowThreshold: 0, 36 | LogLevel: logger.LogLevel(logrus.DebugLevel), 37 | Colorful: true, 38 | }), 39 | }) 40 | if err != nil { 41 | ctx.LogError("new DB Open err = ", err) 42 | return nil 43 | } 44 | 45 | err = db.AutoMigrate(&UploadLogs{}) 46 | if err != nil { 47 | ctx.LogError("new DB AutoMigrate err = ", err) 48 | return nil 49 | } 50 | return db 51 | } 52 | -------------------------------------------------------------------------------- /frontend/src/assets/main.css: -------------------------------------------------------------------------------- 1 | @import "./base.css"; 2 | 3 | html { 4 | height: 100vh; 5 | } 6 | 7 | body { 8 | height: 100%; 9 | padding: 0px; 10 | margin: 0px; 11 | background: #dee0df; 12 | } 13 | 14 | #app { 15 | height: 100%; 16 | } 17 | 18 | [v-cloak] { 19 | display: none; 20 | } 21 | 22 | .layout-header { 23 | position: absolute; 24 | background-color: #f0f0f0; 25 | box-shadow: 0 1px 3px hsla(0, 0%, 13%, 0.1); 26 | height: var(--main-header); 27 | left: 0px; 28 | width: 100%; 29 | z-index: 3000; 30 | } 31 | 32 | .layout-body { 33 | position: absolute; 34 | top: var(--main-header); 35 | width: 100%; 36 | } 37 | 38 | .layout-sidebar { 39 | width: 60px; 40 | height: var(--main-height); 41 | float: left; 42 | background-color: #f2f2f2; 43 | box-shadow: 0 1px 3px hsla(0, 0%, 7%, 0.1); 44 | } 45 | 46 | .layout-main { 47 | width: calc(100% - 60px); 48 | float: left; 49 | } 50 | 51 | .main { 52 | width: 100%; 53 | float: left; 54 | } 55 | 56 | .app-container { 57 | box-shadow: 0 1px 3px hsla(0, 0%, 7%, 0.1); 58 | height: calc(var(--main-height) - 20px); 59 | padding: 10px; 60 | background-color: #ffffff; 61 | box-sizing: border-box; 62 | margin: 10px 10px 0 10px; 63 | } 64 | -------------------------------------------------------------------------------- /frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "go-gui", 3 | "version": "0.0.0", 4 | "private": true, 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "run-p type-check \"build-only {@}\" --", 9 | "preview": "vite preview", 10 | "build-only": "vite build", 11 | "type-check": "vue-tsc --build --force", 12 | "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore", 13 | "format": "prettier --write src/" 14 | }, 15 | "dependencies": { 16 | "@element-plus/icons-vue": "^2.3.1", 17 | "element-plus": "^2.4.4", 18 | "vue": "^3.3.11", 19 | "vue-router": "^4.2.5" 20 | }, 21 | "devDependencies": { 22 | "@rushstack/eslint-patch": "^1.3.3", 23 | "@tsconfig/node18": "^18.2.2", 24 | "@types/node": "^18.19.3", 25 | "@vitejs/plugin-vue": "^4.5.2", 26 | "@vue/eslint-config-prettier": "^8.0.0", 27 | "@vue/eslint-config-typescript": "^12.0.0", 28 | "@vue/tsconfig": "^0.5.0", 29 | "eslint": "^8.49.0", 30 | "eslint-plugin-vue": "^9.17.0", 31 | "npm-run-all2": "^6.1.1", 32 | "prettier": "^3.0.3", 33 | "typescript": "~5.3.0", 34 | "vite": "^5.0.10", 35 | "vue-tsc": "^1.8.25" 36 | } 37 | ,"author":"yuanzhao"} -------------------------------------------------------------------------------- /frontend/src/component/icons/About.vue: -------------------------------------------------------------------------------- 1 | 22 | -------------------------------------------------------------------------------- /frontend/src/views/db/DbList.vue: -------------------------------------------------------------------------------- 1 | 28 | 29 | 42 | 43 | 60 | å 61 | -------------------------------------------------------------------------------- /NEXTSTEPS.md: -------------------------------------------------------------------------------- 1 | # Next Steps 2 | 3 | Congratulations on generating your template! 4 | 5 | ## Completing your template 6 | 7 | The next steps to complete the template are: 8 | 9 | 1. Complete the fields in the `template.json` file. 10 | - It is really important to ensure `helpurl` is valid as this is where users of the template will be directed for help. 11 | 2. Update `README.md`. 12 | 3. Edit `wails.json` and ensure all fields are correct, especially: 13 | - `wailsjsdir` - path to generate wailsjs modules 14 | - `frontend:install` - The command to install your frontend dependencies 15 | - `frontend:build` - The command to build your frontend 16 | 4. Remove any `public` or `dist` directories. 17 | 5. Delete this file. 18 | 19 | ## Testing your template 20 | 21 | You can test your template by running this command: 22 | 23 | `wails init -n test -t /Users/lvluo/Desktop/github.com/ctfang/go-gui-template` 24 | 25 | ### Checklist 26 | 27 | Once generated, do the following tests: 28 | - Change into the new project directory and run `wails build`. A working binary should be generated in the `build/bin` project directory. 29 | - Run `wails dev`. This will compile your app and run it. 30 | - You should be able to see your application running on http://localhost:34115/ 31 | 32 | ## Publishing your template 33 | 34 | You can publish a template to a git repository and use it as follows: 35 | 36 | `wails init -name test -t https://your/git/url` 37 | 38 | EG: 39 | 40 | `wails init -name test -t https://github.com/leaanthony/testtemplate` 41 | 42 | -------------------------------------------------------------------------------- /frontend/src/layouts/HeaderView.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 23 | 24 | 67 | -------------------------------------------------------------------------------- /frontend/src/component/icons/Github.vue: -------------------------------------------------------------------------------- 1 | 19 | -------------------------------------------------------------------------------- /frontend/src/component/icons/Add.vue: -------------------------------------------------------------------------------- 1 | 18 | -------------------------------------------------------------------------------- /apps/mysql/sqlite.go: -------------------------------------------------------------------------------- 1 | package mysql 2 | 3 | import ( 4 | "changeme/apps/ctx" 5 | "changeme/apps/datas" 6 | "fmt" 7 | "gorm.io/driver/sqlite" 8 | "gorm.io/gorm" 9 | "gorm.io/gorm/logger" 10 | "log" 11 | "os" 12 | "path" 13 | "strings" 14 | ) 15 | 16 | func getSqliteDBName(binlogFilePath string) (dbPath, table string) { 17 | table = "logs" 18 | 19 | info := path.Base(binlogFilePath) 20 | dbPath = strings.ReplaceAll(info, "_", "-") 21 | switch strings.Count(dbPath, ".") { 22 | case 1: 23 | arr := strings.Split(info, ".") 24 | dbPath = arr[0] 25 | table = arr[1] 26 | } 27 | 28 | gotPath := datas.GetSqlitePath(dbPath) 29 | gotPath = datas.ToPath(gotPath) 30 | 31 | return gotPath, "binlog_" + table 32 | } 33 | 34 | // GetDB 链接指定 db 必须手动关闭 35 | func GetDB(dbPath, table string) (*gorm.DB, error) { 36 | savePath := path.Dir(dbPath) 37 | savePath = datas.ToPath(savePath) 38 | if err := os.MkdirAll(savePath, 0755); err != nil { 39 | return nil, err 40 | } 41 | 42 | newLogger := logger.New( 43 | log.New(os.Stdout, "\r\n", log.LstdFlags), // 自定义输出,这里使用标准输出 44 | logger.Config{ 45 | LogLevel: logger.Warn, // 日志级别,你可以设置为logger.Silent以禁用日志 46 | }, 47 | ) 48 | dbPath = datas.ToPath(dbPath) 49 | db, err := gorm.Open(sqlite.Open(dbPath+".db"), &gorm.Config{ 50 | Logger: newLogger, 51 | }) 52 | if err != nil { 53 | if err != nil { 54 | return nil, err 55 | } 56 | } 57 | ctx.LogDebug("写入 sqlite = " + dbPath) 58 | create(db, table) 59 | return db, err 60 | } 61 | 62 | func create(db *gorm.DB, table string) { 63 | ret := db.Exec(`CREATE TABLE IF NOT EXISTS ` + table + ` ( 64 | "id" INTEGER PRIMARY KEY AUTOINCREMENT, 65 | "schema" TEXT NOT NULL, 66 | "tables" TEXT NOT NULL, 67 | "timestamp" TIMESTAMP NOT NULL, 68 | "event" TEXT NOT NULL, 69 | "row_1" TEXT NOT NULL, 70 | "row_2" TEXT 71 | );`) 72 | 73 | if ret.Error != nil { 74 | fmt.Println(ret.Error.Error()) 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /apps/datas/paths.go: -------------------------------------------------------------------------------- 1 | package datas 2 | 3 | import ( 4 | "os" 5 | "path" 6 | "path/filepath" 7 | "runtime" 8 | "strings" 9 | ) 10 | 11 | func ToPath(savePath string) string { 12 | if runtime.GOOS != "windows" { 13 | return savePath 14 | } 15 | savePath = strings.ReplaceAll(savePath, "\\", "/") 16 | savePath = savePath[:3] + strings.ReplaceAll(savePath[3:], ":", "/") 17 | savePath = strings.ReplaceAll(savePath, "//", "/") 18 | savePath = strings.ReplaceAll(savePath, "-", "_") 19 | 20 | return savePath 21 | } 22 | 23 | func GetDataDir(appName string) (string, error) { 24 | var dataDir string 25 | 26 | switch runtime.GOOS { 27 | case "windows": 28 | // 在 Windows 上使用 APPDATA 环境变量 29 | appData := os.Getenv("APPDATA") 30 | if appData == "" { 31 | return "", os.ErrNotExist 32 | } 33 | dataDir = filepath.Join(appData, appName) 34 | 35 | case "darwin": 36 | // 在 macOS 上使用 HOME 环境变量并追加通用路径 37 | home := os.Getenv("HOME") 38 | if home == "" { 39 | return "", os.ErrNotExist 40 | } 41 | dataDir = filepath.Join(home, "Library", "Application Support", appName) 42 | 43 | default: 44 | // 对于其他操作系统,可以返回一个错误或提供另一种路径 45 | return "", os.ErrNotExist 46 | } 47 | 48 | return dataDir, nil 49 | } 50 | 51 | // GetPath 根据系统类型, 返回对应保存数据的目录 52 | func GetPath(filePath string) string { 53 | dir, _ := GetDataDir("mysql-binlog-ui/" + filePath) 54 | dir = strings.ReplaceAll(dir, "//", "/") 55 | savePath := path.Dir(dir) 56 | if savePath == "." { 57 | dir = strings.ReplaceAll(dir, "\\", "/") 58 | } 59 | return dir 60 | } 61 | 62 | // GetSqlitePath 根据系统类型, 返回对应保存数据的目录 63 | func GetSqlitePath(path string) string { 64 | return GetPath("/sqlite/" + path) 65 | } 66 | 67 | // GetFileSizeMB 返回文件大小,单位为MB 68 | func GetFileSizeMB(filePath string) float64 { 69 | file, err := os.Open(filePath) 70 | if err != nil { 71 | return 0 72 | } 73 | defer file.Close() 74 | 75 | fileInfo, err := file.Stat() 76 | if err != nil { 77 | return 0 78 | } 79 | 80 | // 获取文件大小,并转换为MB 81 | fileSizeMB := float64(fileInfo.Size()) / 1024 / 1024 82 | return fileSizeMB 83 | } 84 | -------------------------------------------------------------------------------- /apps/controllers/files.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | "changeme/apps/ctx" 5 | "changeme/apps/datas" 6 | "changeme/apps/mysql" 7 | "changeme/apps/orm" 8 | "github.com/wailsapp/wails/v2/pkg/runtime" 9 | "os" 10 | ) 11 | 12 | // Files @Bean 13 | type Files struct{} 14 | 15 | func (f *Files) file() (string, error) { 16 | return ctx.OpenFileDialog(runtime.OpenDialogOptions{ 17 | DefaultDirectory: "", 18 | DefaultFilename: "", 19 | Title: "选中 binlog 原文件", 20 | Filters: nil, 21 | ShowHiddenFiles: false, 22 | CanCreateDirectories: false, 23 | ResolvesAliases: false, 24 | TreatPackagesAsDirectories: false, 25 | }) 26 | } 27 | 28 | func (f *Files) GetSystemFile() string { 29 | path, err := f.file() 30 | if err != nil { 31 | ctx.LogError("file err = " + err.Error()) 32 | err.Error() 33 | } 34 | return path 35 | } 36 | 37 | func (f *Files) SaveToSqlite(path string) string { 38 | err := mysql.SaveToSqlite(path) 39 | if err != nil { 40 | return err.Error() 41 | } 42 | return "" 43 | } 44 | 45 | func (f *Files) GetDecodeRowCount() uint64 { 46 | return mysql.DecodeRowCount 47 | } 48 | 49 | func (f *Files) ClearAllData() { 50 | path, _ := datas.GetDataDir("mysql-binlog-ui/") 51 | path = datas.ToPath(path) 52 | 53 | // 删除所有目录 54 | os.RemoveAll(path) 55 | } 56 | 57 | type TitleData struct { 58 | List []*orm.UploadLogs 59 | Total int64 60 | } 61 | 62 | // GetTitleList 获取标题列表 63 | func (f *Files) GetTitleList(page int, limit int) TitleData { 64 | got, count := orm.NewOrmUploadLogs().Paginate(page, limit) 65 | 66 | for _, logs := range got { 67 | logs.FileSize = float64(int(logs.FileSize)) 68 | } 69 | 70 | return TitleData{ 71 | List: got, 72 | Total: count, 73 | } 74 | } 75 | 76 | func (f *Files) DeleteFile(ID int64) { 77 | file, ok := orm.NewOrmUploadLogs().WhereId(ID).First() 78 | if ok { 79 | orm.NewOrmUploadLogs().WhereId(ID).Delete() 80 | 81 | err := os.Remove(file.Database) 82 | if err != nil { 83 | ctx.LogDebug("del err = ", err) 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /frontend/src/component/icons/Binlog.vue: -------------------------------------------------------------------------------- 1 | 22 | -------------------------------------------------------------------------------- /apps/controllers/binlog.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | "changeme/apps/ctx" 5 | "changeme/apps/orm" 6 | "errors" 7 | "time" 8 | ) 9 | 10 | // Binlog @Bean 11 | type Binlog struct{} 12 | 13 | func (b Binlog) GetTitleList(id int64) ([]*orm.UploadLogs, error) { 14 | data, ok := orm.NewOrmUploadLogs().WhereId(id).First() 15 | if !ok { 16 | return nil, errors.New("data not is exist") 17 | } 18 | 19 | dbList := orm.NewOrmUploadLogs().WhereDatabase(data.Database).Limit(100).Order("id DESC").Get() 20 | return dbList, nil 21 | } 22 | 23 | type SearchBinlog struct { 24 | Table string 25 | Event string 26 | Text string 27 | Date []string 28 | 29 | Page int 30 | Limit int 31 | } 32 | 33 | type DetailsList struct { 34 | List []*orm.Binlog 35 | Total int64 36 | } 37 | 38 | func (b Binlog) GetDetailsList(id int64, search *SearchBinlog) (*DetailsList, error) { 39 | data, ok := orm.NewOrmUploadLogs().WhereId(id).First() 40 | if !ok { 41 | return nil, errors.New("data not is exist") 42 | } 43 | 44 | database, table := data.Database, data.Table 45 | model := orm.NewOrmBinlog(database, table).Order("id DESC").Order("id DESC") 46 | if search.Table != "" { 47 | model.Where("tables = ?", search.Table) 48 | } 49 | if search.Event != "" { 50 | model.Where("event = ?", search.Event) 51 | } 52 | if search.Text != "" { 53 | model.Where("(row_1 like ? or row_2 like ?)", "%"+search.Text+"%", "%"+search.Text+"%") 54 | } 55 | 56 | if search.Date != nil && len(search.Date) == 2 { 57 | if search.Date[0] != "" && search.Date[1] != "" { 58 | start, err := ParseISODateTime(search.Date[0]) 59 | if err != nil { 60 | ctx.LogError("start date err := ", err) 61 | } 62 | end, err := ParseISODateTime(search.Date[1]) 63 | if err != nil { 64 | ctx.LogError("end date err := ", err) 65 | } 66 | 67 | model.Where("(timestamp >= ? and timestamp <= ?)", start, end) 68 | } 69 | } 70 | 71 | list, count := model.Paginate(search.Page, search.Limit) 72 | 73 | return &DetailsList{ 74 | List: list, 75 | Total: count, 76 | }, nil 77 | } 78 | 79 | // ParseISODateTime 将 ISO 8601 格式的时间字符串转换为 time.Time 对象 80 | func ParseISODateTime(dateTimeStr string) (time.Time, error) { 81 | layout := "2006-01-02T15:04:05.000Z" 82 | return time.Parse(layout, dateTimeStr) 83 | } 84 | -------------------------------------------------------------------------------- /frontend/src/layouts/LeftMenu.vue: -------------------------------------------------------------------------------- 1 | 25 | 26 | 52 | 53 | 89 | -------------------------------------------------------------------------------- /apps/ctx/log.go: -------------------------------------------------------------------------------- 1 | package ctx 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "fmt" 7 | "github.com/wailsapp/wails/v2/pkg/runtime" 8 | "gorm.io/gorm/logger" 9 | "gorm.io/gorm/utils" 10 | "time" 11 | ) 12 | 13 | func LogDebug(msg string, arr ...interface{}) { 14 | if ctx == nil { 15 | fmt.Println(msg + fmt.Sprint(arr...)) 16 | return 17 | } 18 | if len(arr) > 0 { 19 | msg = msg + fmt.Sprint(arr...) 20 | } 21 | runtime.LogDebug(ctx, msg) 22 | } 23 | 24 | func LogInfo(msg string, arr ...interface{}) { 25 | if ctx == nil { 26 | fmt.Println(msg + fmt.Sprint(arr...)) 27 | return 28 | } 29 | if len(arr) > 0 { 30 | msg = msg + fmt.Sprint(arr...) 31 | } 32 | runtime.LogInfo(ctx, msg) 33 | } 34 | 35 | func LogWarn(msg string, arr ...interface{}) { 36 | if ctx == nil { 37 | fmt.Println(msg + fmt.Sprint(arr...)) 38 | return 39 | } 40 | if len(arr) > 0 { 41 | msg = msg + fmt.Sprint(arr...) 42 | } 43 | runtime.LogWarning(ctx, msg) 44 | } 45 | 46 | func LogError(msg string, arr ...interface{}) { 47 | if ctx == nil { 48 | fmt.Println(msg + fmt.Sprint(arr...)) 49 | return 50 | } 51 | if len(arr) > 0 { 52 | msg = msg + fmt.Sprint(arr...) 53 | } 54 | runtime.LogError(ctx, msg) 55 | } 56 | 57 | type Log struct { 58 | logger.Config 59 | } 60 | 61 | func (l *Log) LogMode(level logger.LogLevel) logger.Interface { 62 | l.LogLevel = level 63 | return l 64 | } 65 | 66 | func (l *Log) Info(ctx2 context.Context, s string, i ...interface{}) { 67 | if l.LogLevel >= logger.Info { 68 | LogInfo(s, append([]interface{}{utils.FileWithLineNum()}, i...)) 69 | } 70 | } 71 | 72 | func (l *Log) Warn(ctx2 context.Context, s string, i ...interface{}) { 73 | if l.LogLevel >= logger.Warn { 74 | LogWarn(s, append([]interface{}{utils.FileWithLineNum()}, i...)) 75 | } 76 | } 77 | 78 | func (l *Log) Error(ctx2 context.Context, s string, i ...interface{}) { 79 | if l.LogLevel >= logger.Error { 80 | LogError(s, append([]interface{}{utils.FileWithLineNum()}, i...)) 81 | } 82 | } 83 | 84 | func (l *Log) Trace(ctx context.Context, begin time.Time, fc func() (sql string, rowsAffected int64), err error) { 85 | if l.LogLevel <= logger.Silent { 86 | return 87 | } 88 | switch { 89 | case err != nil && !errors.Is(err, logger.ErrRecordNotFound): 90 | sql, _ := fc() 91 | LogDebug(fmt.Sprintf("%s\n%s", err.Error(), sql)) 92 | case l.LogLevel >= logger.Info: 93 | sql, _ := fc() 94 | LogDebug(sql) 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "changeme/apps" 5 | "embed" 6 | "github.com/wailsapp/wails/v2" 7 | "github.com/wailsapp/wails/v2/pkg/logger" 8 | "github.com/wailsapp/wails/v2/pkg/menu" 9 | "github.com/wailsapp/wails/v2/pkg/options" 10 | "github.com/wailsapp/wails/v2/pkg/options/assetserver" 11 | "github.com/wailsapp/wails/v2/pkg/options/mac" 12 | "github.com/wailsapp/wails/v2/pkg/options/windows" 13 | "log" 14 | "runtime" 15 | ) 16 | 17 | //go:embed all:frontend/dist 18 | var assets embed.FS 19 | 20 | //go:embed build/appicon.png 21 | var icon []byte 22 | 23 | func main() { 24 | // Create an instance of the app structure 25 | app := apps.NewApp() 26 | 27 | // menu 28 | appMenu := menu.NewMenu() 29 | if runtime.GOOS == "darwin" { 30 | appMenu.Append(menu.AppMenu()) 31 | appMenu.Append(menu.EditMenu()) 32 | appMenu.Append(menu.WindowMenu()) 33 | } 34 | 35 | // Create application with options 36 | err := wails.Run(&options.App{ 37 | Title: "mysql-binlog", 38 | Width: 1280, 39 | Height: 768, 40 | MinWidth: 1024, 41 | MinHeight: 768, 42 | DisableResize: false, 43 | Fullscreen: false, 44 | Frameless: runtime.GOOS != "darwin", 45 | StartHidden: false, 46 | HideWindowOnClose: false, 47 | BackgroundColour: options.NewRGBA(27, 38, 54, 0), 48 | AssetServer: &assetserver.Options{ 49 | Assets: assets, 50 | }, 51 | Menu: appMenu, 52 | EnableDefaultContextMenu: true, 53 | Logger: nil, 54 | LogLevel: logger.DEBUG, 55 | OnStartup: app.StartUp, 56 | OnDomReady: app.DomReady, 57 | OnBeforeClose: app.BeforeClose, 58 | OnShutdown: app.Shutdown, 59 | WindowStartState: options.Normal, 60 | Bind: app.GetBind(), 61 | // Windows platform specific options 62 | Windows: &windows.Options{ 63 | WebviewIsTransparent: true, 64 | WindowIsTranslucent: true, 65 | DisableFramelessWindowDecorations: true, 66 | }, 67 | // Mac platform specific options 68 | Mac: &mac.Options{ 69 | TitleBar: mac.TitleBarHiddenInset(), 70 | WebviewIsTransparent: false, 71 | WindowIsTranslucent: true, 72 | About: &mac.AboutInfo{ 73 | Title: "mysql-binlog-ui", 74 | Message: "一个mysql binlog解析工具", 75 | Icon: icon, 76 | }, 77 | }, 78 | }) 79 | 80 | if err != nil { 81 | log.Fatal(err) 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /frontend/src/views/BinlogView.vue: -------------------------------------------------------------------------------- 1 | 67 | 68 | 100 | 101 | 106 | -------------------------------------------------------------------------------- /frontend/src/component/icons/Setting.vue: -------------------------------------------------------------------------------- 1 | 19 | -------------------------------------------------------------------------------- /frontend/src/views/binlog/LogsList.vue: -------------------------------------------------------------------------------- 1 | 63 | 64 | 119 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module changeme 2 | 3 | go 1.22.3 4 | 5 | require ( 6 | github.com/go-home-admin/home v0.5.18 7 | github.com/go-mysql-org/go-mysql v1.9.0 8 | github.com/sirupsen/logrus v1.9.3 9 | github.com/wailsapp/wails/v2 v2.8.0 10 | gorm.io/driver/sqlite v1.5.6 11 | gorm.io/gorm v1.25.11 12 | ) 13 | 14 | require ( 15 | github.com/Masterminds/semver v1.5.0 // indirect 16 | github.com/bep/debounce v1.2.1 // indirect 17 | github.com/bytedance/sonic v1.9.1 // indirect 18 | github.com/cespare/xxhash/v2 v2.1.2 // indirect 19 | github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect 20 | github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect 21 | github.com/gabriel-vasile/mimetype v1.4.2 // indirect 22 | github.com/gin-contrib/sse v0.1.0 // indirect 23 | github.com/gin-gonic/gin v1.9.1 // indirect 24 | github.com/go-ole/go-ole v1.2.6 // indirect 25 | github.com/go-playground/locales v0.14.1 // indirect 26 | github.com/go-playground/universal-translator v0.18.1 // indirect 27 | github.com/go-playground/validator/v10 v10.14.0 // indirect 28 | github.com/go-redis/redis/v8 v8.11.5 // indirect 29 | github.com/go-sql-driver/mysql v1.7.1 // indirect 30 | github.com/goccy/go-json v0.10.2 // indirect 31 | github.com/godbus/dbus/v5 v5.1.0 // indirect 32 | github.com/google/uuid v1.3.0 // indirect 33 | github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e // indirect 34 | github.com/jinzhu/inflection v1.0.0 // indirect 35 | github.com/jinzhu/now v1.1.5 // indirect 36 | github.com/joho/godotenv v1.4.0 // indirect 37 | github.com/json-iterator/go v1.1.12 // indirect 38 | github.com/klauspost/compress v1.17.8 // indirect 39 | github.com/klauspost/cpuid/v2 v2.2.4 // indirect 40 | github.com/kr/pretty v0.3.1 // indirect 41 | github.com/kr/text v0.2.0 // indirect 42 | github.com/labstack/echo/v4 v4.10.2 // indirect 43 | github.com/labstack/gommon v0.4.0 // indirect 44 | github.com/leaanthony/go-ansi-parser v1.6.0 // indirect 45 | github.com/leaanthony/gosod v1.0.3 // indirect 46 | github.com/leaanthony/slicer v1.6.0 // indirect 47 | github.com/leaanthony/u v1.1.0 // indirect 48 | github.com/leodido/go-urn v1.2.4 // indirect 49 | github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible // indirect 50 | github.com/lestrrat-go/strftime v1.0.6 // indirect 51 | github.com/mattn/go-colorable v0.1.13 // indirect 52 | github.com/mattn/go-isatty v0.0.19 // indirect 53 | github.com/mattn/go-sqlite3 v1.14.22 // indirect 54 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 55 | github.com/modern-go/reflect2 v1.0.2 // indirect 56 | github.com/pelletier/go-toml/v2 v2.0.8 // indirect 57 | github.com/pingcap/errors v0.11.5-0.20221009092201-b66cddb77c32 // indirect 58 | github.com/pingcap/log v1.1.1-0.20230317032135-a0d097d16e22 // indirect 59 | github.com/pingcap/tidb/pkg/parser v0.0.0-20231103042308-035ad5ccbe67 // indirect 60 | github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect 61 | github.com/pkg/errors v0.9.1 // indirect 62 | github.com/rivo/uniseg v0.4.4 // indirect 63 | github.com/rogpeppe/go-internal v1.10.0 // indirect 64 | github.com/samber/lo v1.38.1 // indirect 65 | github.com/shopspring/decimal v1.2.0 // indirect 66 | github.com/siddontang/go v0.0.0-20180604090527-bdc77568d726 // indirect 67 | github.com/siddontang/go-log v0.0.0-20180807004314-8d05993dda07 // indirect 68 | github.com/tkrajina/go-reflector v0.5.6 // indirect 69 | github.com/twitchyliquid64/golang-asm v0.15.1 // indirect 70 | github.com/ugorji/go/codec v1.2.11 // indirect 71 | github.com/valyala/bytebufferpool v1.0.0 // indirect 72 | github.com/valyala/fasttemplate v1.2.2 // indirect 73 | github.com/wailsapp/go-webview2 v1.0.10 // indirect 74 | github.com/wailsapp/mimetype v1.4.1 // indirect 75 | go.uber.org/atomic v1.11.0 // indirect 76 | go.uber.org/multierr v1.11.0 // indirect 77 | go.uber.org/zap v1.26.0 // indirect 78 | golang.org/x/arch v0.3.0 // indirect 79 | golang.org/x/crypto v0.23.0 // indirect 80 | golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect 81 | golang.org/x/net v0.25.0 // indirect 82 | golang.org/x/sys v0.20.0 // indirect 83 | golang.org/x/text v0.15.0 // indirect 84 | google.golang.org/protobuf v1.30.0 // indirect 85 | gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect 86 | gopkg.in/yaml.v2 v2.4.0 // indirect 87 | gopkg.in/yaml.v3 v3.0.1 // indirect 88 | gorm.io/driver/mysql v1.3.2 // indirect 89 | ) 90 | -------------------------------------------------------------------------------- /apps/mysql/binlog.go: -------------------------------------------------------------------------------- 1 | package mysql 2 | 3 | import ( 4 | "changeme/apps/ctx" 5 | "changeme/apps/datas" 6 | "changeme/apps/orm" 7 | "encoding/json" 8 | "errors" 9 | "fmt" 10 | "github.com/go-mysql-org/go-mysql/replication" 11 | "gorm.io/gorm" 12 | "os" 13 | "sync" 14 | "time" 15 | ) 16 | 17 | var logsChan = make(chan *BinlogData, 1000) 18 | var logsChanStatus = make(chan bool) 19 | 20 | // BinlogData represents the structure of the "binlog_data" table. 21 | type BinlogData struct { 22 | ID int64 `gorm:"column:id"` 23 | Schema string `gorm:"column:schema"` 24 | Table string `gorm:"column:tables"` 25 | Timestamp time.Time `gorm:"column:timestamp"` 26 | Event string `gorm:"column:event"` 27 | Row1 string `gorm:"column:row_1"` 28 | Row2 string `gorm:"column:row_2"` 29 | } 30 | 31 | var lock sync.Mutex 32 | 33 | var DecodeRowCount uint64 = 0 34 | 35 | func SaveToSqlite(binlogFilePath string) error { 36 | if !lock.TryLock() { 37 | return errors.New("有数据在解析中") 38 | } 39 | defer lock.Unlock() 40 | 41 | DecodeRowCount = 0 42 | go watchLogsChan(binlogFilePath) 43 | defer func() { 44 | logsChanStatus <- true 45 | }() 46 | 47 | var data *BinlogData 48 | 49 | f := func(e *replication.BinlogEvent) error { 50 | // 处理不同类型的事件 51 | switch ee := e.Event.(type) { 52 | case *replication.RotateEvent: 53 | fmt.Println("RotateEvent:", ee.NextLogName, ee.Position) 54 | case *replication.RowsEvent: 55 | data = &BinlogData{ 56 | Schema: string(ee.Table.Schema), 57 | Table: string(ee.Table.Table), 58 | Timestamp: time.Unix(int64(e.Header.Timestamp), 0), 59 | } 60 | switch e.Header.EventType { 61 | case replication.WRITE_ROWS_EVENTv1, replication.WRITE_ROWS_EVENTv2: 62 | data.Event = "insert" 63 | if len(ee.Rows) == 1 { 64 | data.Event = "insert" 65 | } else { 66 | data.Event = "inserts" 67 | } 68 | r1, _ := json.Marshal(ee.Rows) 69 | data.Row1 = string(r1) 70 | case replication.UPDATE_ROWS_EVENTv1, replication.UPDATE_ROWS_EVENTv2: 71 | data.Event = "update" 72 | if len(ee.Rows) == 2 { 73 | r1, _ := json.Marshal(ee.Rows[0]) 74 | r2, _ := json.Marshal(ee.Rows[1]) 75 | data.Row1 = string(r1) 76 | data.Row2 = string(r2) 77 | } 78 | case replication.DELETE_ROWS_EVENTv1, replication.DELETE_ROWS_EVENTv2: 79 | if len(ee.Rows) == 1 { 80 | data.Event = "delete" 81 | } else { 82 | data.Event = "deletes" 83 | } 84 | r1, _ := json.Marshal(ee.Rows) 85 | data.Row1 = string(r1) 86 | } 87 | 88 | DecodeRowCount++ 89 | // 插入数据 90 | logsChan <- data 91 | case *replication.QueryEvent: 92 | case *replication.FormatDescriptionEvent: // 用来描述这些数字代表的事件类型的具体格式和定义 93 | case *replication.PreviousGTIDsEvent: // 记录上一个GTID的信息。GTID是一个全局唯一的事务标识符,用于在复制环境中准确地追踪数据的复制进度 94 | case *replication.GTIDEvent: // GTIDEvent会在每个事务开始时记录,并包含当前事务的GTID信息,以便在复制过程中进行数据同步 95 | case *replication.TableMapEvent: // TableMapEvent用于提供每个表的结构信息 96 | case *replication.XIDEvent: // XIDEvent用于标识这个事务的提交点 97 | //ee.Dump(os.Stdout) 98 | default: 99 | ee.Dump(os.Stdout) 100 | } 101 | return nil 102 | } 103 | // 创建一个BinlogReader来读取binlog事件 104 | r := replication.NewBinlogParser() 105 | err := r.ParseFile(binlogFilePath, 0, f) 106 | if err != nil { 107 | ctx.LogError("ParseFile err = " + err.Error()) 108 | return err 109 | } 110 | 111 | return nil 112 | } 113 | 114 | func watchLogsChan(binlogFilePath string) { 115 | dbPath, table := getSqliteDBName(binlogFilePath) 116 | db, err := GetDB(dbPath, table) 117 | if err != nil { 118 | ctx.LogError("watchLogsChan db err = " + err.Error() + " path = " + binlogFilePath) 119 | return 120 | } 121 | defer func() { 122 | dbInstance, _ := db.DB() 123 | _ = dbInstance.Close() 124 | }() 125 | 126 | logData := &orm.UploadLogs{ 127 | Database: dbPath + ".db", 128 | Table: table, 129 | File: binlogFilePath, 130 | FileSize: datas.GetFileSizeMB(binlogFilePath), 131 | Timestamp: time.Now(), 132 | } 133 | err = orm.NewOrmUploadLogs().Insert(logData) 134 | if err != nil { 135 | ctx.LogError("写入 logs 失败, err = ", err) 136 | return 137 | } 138 | 139 | insertDatas := make([]*BinlogData, 0) 140 | insertCount := 0 141 | 142 | loop := true 143 | for loop { 144 | select { 145 | case data := <-logsChan: 146 | insertDatas = append(insertDatas, data) 147 | insertCount++ 148 | if insertCount >= 50 { 149 | insertCount = 0 150 | addToSqlite(db, table, insertDatas) 151 | insertDatas = make([]*BinlogData, 0) 152 | } 153 | case <-logsChanStatus: 154 | insertCount = 0 155 | addToSqlite(db, table, insertDatas) 156 | ctx.LogDebug("完成 " + binlogFilePath) 157 | loop = false 158 | break 159 | } 160 | } 161 | 162 | logData.Status = 1 163 | logData.Msg = "运行完成" 164 | orm.NewOrmUploadLogs().WhereId(logData.ID).Updates(logData) 165 | } 166 | 167 | func addToSqlite(db *gorm.DB, table string, data []*BinlogData) { 168 | ret := db.Table(table).CreateInBatches(data, 100) 169 | if ret.Error != nil { 170 | ctx.LogError("addToSqlite err = " + ret.Error.Error()) 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /.github/workflows/release-windows.yaml: -------------------------------------------------------------------------------- 1 | name: Release Windows App 2 | 3 | on: 4 | release: 5 | types: [ published ] 6 | workflow_dispatch: 7 | inputs: 8 | tag: 9 | description: 'Version tag' 10 | required: true 11 | default: '1.0.0' 12 | 13 | jobs: 14 | release: 15 | name: Release Windows App 16 | runs-on: windows-latest 17 | strategy: 18 | matrix: 19 | platform: 20 | - windows/amd64 21 | - windows/arm64 22 | steps: 23 | - name: Checkout source code 24 | uses: actions/checkout@v3 25 | 26 | - name: Normalise platform tag 27 | id: normalise_platform 28 | shell: bash 29 | run: | 30 | tag=$(echo ${{ matrix.platform }} | sed -e 's/\//_/g' -e 's/amd64/x64/g') 31 | echo "tag=$tag" >> "$GITHUB_OUTPUT" 32 | 33 | - name: Normalise platform name 34 | id: normalise_platform_name 35 | shell: bash 36 | run: | 37 | pname=$(echo "${{ matrix.platform }}" | sed 's/windows\///g') 38 | echo "pname=$pname" >> "$GITHUB_OUTPUT" 39 | 40 | - name: Normalise version tag 41 | id: normalise_version 42 | shell: bash 43 | run: | 44 | if [ "${{ github.event.release.tag_name }}" == "" ]; then 45 | version=$(echo ${{ github.event.inputs.tag }} | sed -e 's/v//g') 46 | echo "version=$version" >> "$GITHUB_OUTPUT" 47 | else 48 | version=$(echo ${{ github.event.release.tag_name }} | sed -e 's/v//g') 49 | echo "version=$version" >> "$GITHUB_OUTPUT" 50 | fi 51 | 52 | - name: Setup Go 53 | uses: actions/setup-go@v4 54 | with: 55 | go-version: stable 56 | 57 | - name: Install chocolatey 58 | uses: crazy-max/ghaction-chocolatey@v2 59 | with: 60 | args: install nsis jq 61 | 62 | - name: Install wails 63 | shell: bash 64 | run: go install github.com/wailsapp/wails/v2/cmd/wails@v2.8.0 65 | 66 | - name: Setup Node 67 | uses: actions/setup-node@v3 68 | with: 69 | node-version: 20 70 | 71 | - name: Build frontend assets 72 | shell: bash 73 | run: | 74 | npm install -g npm@9 75 | jq '.info.productVersion = "${{ steps.normalise_version.outputs.version }}"' wails.json > tmp.json 76 | mv tmp.json wails.json 77 | cd frontend 78 | jq '.version = "${{ steps.normalise_version.outputs.version }}"' package.json > tmp.json 79 | mv tmp.json package.json 80 | npm install 81 | 82 | - name: Build Windows portable app 83 | shell: bash 84 | run: | 85 | CGO_ENABLED=1 wails build -clean -platform ${{ matrix.platform }} 86 | 87 | - name: Compress portable binary 88 | working-directory: ./build/bin 89 | run: Compress-Archive "mysqlBinlogUI.exe" "mysqlBinlogUI_Portable_${{ steps.normalise_version.outputs.version }}_${{ steps.normalise_platform.outputs.tag }}.zip" 90 | 91 | - name: Upload release asset (Portable) 92 | uses: softprops/action-gh-release@v1 93 | with: 94 | tag_name: v${{ steps.normalise_version.outputs.version }} 95 | files: ./build/bin/mysqlBinlogUI_Portable_${{ steps.normalise_version.outputs.version }}_${{ steps.normalise_platform.outputs.tag }}.zip 96 | token: ${{ secrets.GITHUB_TOKEN }} 97 | 98 | - name: Build Windows NSIS installer 99 | shell: bash 100 | run: | 101 | CGO_ENABLED=1 wails build -clean -platform ${{ matrix.platform }} \ 102 | -nsis -webview2 embed \ 103 | -ldflags "-X main.version=v${{ steps.normalise_version.outputs.version }}" 104 | 105 | - name: Codesign Windows NSIS installer 106 | working-directory: ./build/bin 107 | run: | 108 | echo "Creating certificate file" 109 | New-Item -ItemType directory -Path certificate 110 | Set-Content -Path certificate\certificate.txt -Value '${{ secrets.WIN_SIGNING_CERT }}' 111 | certutil -decode certificate\certificate.txt certificate\certificate.pfx 112 | echo "Signing mysqlBinlogUI installer" 113 | & 'C:/Program Files (x86)/Windows Kits/10/bin/10.0.17763.0/x86/signtool.exe' sign /fd sha256 /tr http://ts.ssl.com /f certificate\certificate.pfx /p '${{ secrets.WIN_SIGNING_CERT_PASSWORD }}' mysqlBinlogUI-${{ steps.normalise_platform_name.outputs.pname }}-installer.exe 114 | 115 | - name: Rename installer 116 | working-directory: ./build/bin 117 | run: Rename-Item -Path "mysqlBinlogUI-${{ steps.normalise_platform_name.outputs.pname }}-installer.exe" -NewName "mysqlBinlogUI_Setup_${{ steps.normalise_version.outputs.version }}_${{ steps.normalise_platform.outputs.tag }}.exe" 118 | 119 | - name: Upload release asset (Installer) 120 | uses: softprops/action-gh-release@v1 121 | with: 122 | tag_name: v${{ steps.normalise_version.outputs.version }} 123 | files: ./build/bin/mysqlBinlogUI_Setup_${{ steps.normalise_version.outputs.version }}_${{ steps.normalise_platform.outputs.tag }}.exe 124 | token: ${{ secrets.GITHUB_TOKEN }} 125 | -------------------------------------------------------------------------------- /frontend/wailsjs/runtime/runtime.js: -------------------------------------------------------------------------------- 1 | /* 2 | _ __ _ __ 3 | | | / /___ _(_) /____ 4 | | | /| / / __ `/ / / ___/ 5 | | |/ |/ / /_/ / / (__ ) 6 | |__/|__/\__,_/_/_/____/ 7 | The electron alternative for Go 8 | (c) Lea Anthony 2019-present 9 | */ 10 | 11 | export function LogPrint(message) { 12 | window.runtime.LogPrint(message); 13 | } 14 | 15 | export function LogTrace(message) { 16 | window.runtime.LogTrace(message); 17 | } 18 | 19 | export function LogDebug(message) { 20 | window.runtime.LogDebug(message); 21 | } 22 | 23 | export function LogInfo(message) { 24 | window.runtime.LogInfo(message); 25 | } 26 | 27 | export function LogWarning(message) { 28 | window.runtime.LogWarning(message); 29 | } 30 | 31 | export function LogError(message) { 32 | window.runtime.LogError(message); 33 | } 34 | 35 | export function LogFatal(message) { 36 | window.runtime.LogFatal(message); 37 | } 38 | 39 | export function EventsOnMultiple(eventName, callback, maxCallbacks) { 40 | return window.runtime.EventsOnMultiple(eventName, callback, maxCallbacks); 41 | } 42 | 43 | export function EventsOn(eventName, callback) { 44 | return EventsOnMultiple(eventName, callback, -1); 45 | } 46 | 47 | export function EventsOff(eventName, ...additionalEventNames) { 48 | return window.runtime.EventsOff(eventName, ...additionalEventNames); 49 | } 50 | 51 | export function EventsOnce(eventName, callback) { 52 | return EventsOnMultiple(eventName, callback, 1); 53 | } 54 | 55 | export function EventsEmit(eventName) { 56 | let args = [eventName].slice.call(arguments); 57 | return window.runtime.EventsEmit.apply(null, args); 58 | } 59 | 60 | export function WindowReload() { 61 | window.runtime.WindowReload(); 62 | } 63 | 64 | export function WindowReloadApp() { 65 | window.runtime.WindowReloadApp(); 66 | } 67 | 68 | export function WindowSetAlwaysOnTop(b) { 69 | window.runtime.WindowSetAlwaysOnTop(b); 70 | } 71 | 72 | export function WindowSetSystemDefaultTheme() { 73 | window.runtime.WindowSetSystemDefaultTheme(); 74 | } 75 | 76 | export function WindowSetLightTheme() { 77 | window.runtime.WindowSetLightTheme(); 78 | } 79 | 80 | export function WindowSetDarkTheme() { 81 | window.runtime.WindowSetDarkTheme(); 82 | } 83 | 84 | export function WindowCenter() { 85 | window.runtime.WindowCenter(); 86 | } 87 | 88 | export function WindowSetTitle(title) { 89 | window.runtime.WindowSetTitle(title); 90 | } 91 | 92 | export function WindowFullscreen() { 93 | window.runtime.WindowFullscreen(); 94 | } 95 | 96 | export function WindowUnfullscreen() { 97 | window.runtime.WindowUnfullscreen(); 98 | } 99 | 100 | export function WindowIsFullscreen() { 101 | return window.runtime.WindowIsFullscreen(); 102 | } 103 | 104 | export function WindowGetSize() { 105 | return window.runtime.WindowGetSize(); 106 | } 107 | 108 | export function WindowSetSize(width, height) { 109 | window.runtime.WindowSetSize(width, height); 110 | } 111 | 112 | export function WindowSetMaxSize(width, height) { 113 | window.runtime.WindowSetMaxSize(width, height); 114 | } 115 | 116 | export function WindowSetMinSize(width, height) { 117 | window.runtime.WindowSetMinSize(width, height); 118 | } 119 | 120 | export function WindowSetPosition(x, y) { 121 | window.runtime.WindowSetPosition(x, y); 122 | } 123 | 124 | export function WindowGetPosition() { 125 | return window.runtime.WindowGetPosition(); 126 | } 127 | 128 | export function WindowHide() { 129 | window.runtime.WindowHide(); 130 | } 131 | 132 | export function WindowShow() { 133 | window.runtime.WindowShow(); 134 | } 135 | 136 | export function WindowMaximise() { 137 | window.runtime.WindowMaximise(); 138 | } 139 | 140 | export function WindowToggleMaximise() { 141 | window.runtime.WindowToggleMaximise(); 142 | } 143 | 144 | export function WindowUnmaximise() { 145 | window.runtime.WindowUnmaximise(); 146 | } 147 | 148 | export function WindowIsMaximised() { 149 | return window.runtime.WindowIsMaximised(); 150 | } 151 | 152 | export function WindowMinimise() { 153 | window.runtime.WindowMinimise(); 154 | } 155 | 156 | export function WindowUnminimise() { 157 | window.runtime.WindowUnminimise(); 158 | } 159 | 160 | export function WindowSetBackgroundColour(R, G, B, A) { 161 | window.runtime.WindowSetBackgroundColour(R, G, B, A); 162 | } 163 | 164 | export function ScreenGetAll() { 165 | return window.runtime.ScreenGetAll(); 166 | } 167 | 168 | export function WindowIsMinimised() { 169 | return window.runtime.WindowIsMinimised(); 170 | } 171 | 172 | export function WindowIsNormal() { 173 | return window.runtime.WindowIsNormal(); 174 | } 175 | 176 | export function BrowserOpenURL(url) { 177 | window.runtime.BrowserOpenURL(url); 178 | } 179 | 180 | export function Environment() { 181 | return window.runtime.Environment(); 182 | } 183 | 184 | export function Quit() { 185 | window.runtime.Quit(); 186 | } 187 | 188 | export function Hide() { 189 | window.runtime.Hide(); 190 | } 191 | 192 | export function Show() { 193 | window.runtime.Show(); 194 | } 195 | 196 | export function ClipboardGetText() { 197 | return window.runtime.ClipboardGetText(); 198 | } 199 | 200 | export function ClipboardSetText(text) { 201 | return window.runtime.ClipboardSetText(text); 202 | } -------------------------------------------------------------------------------- /.github/workflows/release-macos.yaml: -------------------------------------------------------------------------------- 1 | name: Release macOS App 2 | 3 | on: 4 | release: 5 | types: [ published ] 6 | workflow_dispatch: 7 | inputs: 8 | tag: 9 | description: 'Version tag' 10 | required: true 11 | default: '1.0.0' 12 | 13 | jobs: 14 | release: 15 | name: Release macOS App 16 | runs-on: macos-latest # We can cross compile but need to be on macOS to notarise 17 | strategy: 18 | matrix: 19 | platform: 20 | - darwin/amd64 21 | - darwin/arm64 22 | # - darwin/universal 23 | steps: 24 | - name: Checkout source code 25 | uses: actions/checkout@v3 26 | 27 | - name: Normalise platform tag 28 | id: normalise_platform 29 | shell: bash 30 | run: | 31 | tag=$(echo ${{ matrix.platform }} | sed -e 's/\//_/g' -e 's/darwin/mac/g' -e 's/amd64/intel/g') 32 | echo "tag=$tag" >> "$GITHUB_OUTPUT" 33 | 34 | - name: Normalise version tag 35 | id: normalise_version 36 | shell: bash 37 | run: | 38 | if [ "${{ github.event.release.tag_name }}" == "" ]; then 39 | version=$(echo ${{ github.event.inputs.tag }} | sed -e 's/v//g') 40 | echo "version=$version" >> "$GITHUB_OUTPUT" 41 | else 42 | version=$(echo ${{ github.event.release.tag_name }} | sed -e 's/v//g') 43 | echo "version=$version" >> "$GITHUB_OUTPUT" 44 | fi 45 | 46 | - name: Setup Go 47 | uses: actions/setup-go@v4 48 | with: 49 | go-version: stable 50 | 51 | # - name: Install gon for macOS notarisation 52 | # shell: bash 53 | # run: wget https://github.com/mitchellh/gon/releases/download/v0.2.5/gon_macos.zip && unzip gon_macos.zip && mv gon /usr/local/bin 54 | # 55 | # - name: Import code signing certificate from Github Secrets 56 | # uses: Apple-Actions/import-codesign-certs@v1 57 | # with: 58 | # p12-file-base64: ${{ secrets.APPLE_DEVELOPER_CERTIFICATE_P12_BASE64 }} 59 | # p12-password: ${{ secrets.APPLE_DEVELOPER_CERTIFICATE_PASSWORD }} 60 | 61 | - name: Install wails 62 | shell: bash 63 | run: go install github.com/wailsapp/wails/v2/cmd/wails@v2.8.0 64 | 65 | - name: Setup Node 66 | uses: actions/setup-node@v3 67 | with: 68 | node-version: 20 69 | 70 | - name: Build frontend assets 71 | shell: bash 72 | run: | 73 | npm install -g npm@9 74 | jq '.info.productVersion = "${{ steps.normalise_version.outputs.version }}"' wails.json > tmp.json 75 | mv tmp.json wails.json 76 | cd frontend 77 | jq '.version = "${{ steps.normalise_version.outputs.version }}"' package.json > tmp.json 78 | mv tmp.json package.json 79 | npm install 80 | 81 | - name: Build wails app for macOS 82 | shell: bash 83 | run: | 84 | CGO_ENABLED=1 wails build -platform ${{ matrix.platform }} \ 85 | -ldflags "-X main.version=${{ steps.normalise_version.outputs.version }} -X main.gaMeasurementID=${{ secrets.GA_MEASUREMENT_ID }} -X main.gaSecretKey=${{ secrets.MAC_GA_SECRET }}" 86 | 87 | # - name: Notarise macOS app + create dmg 88 | # shell: bash 89 | # run: gon -log-level=info gon.config.json 90 | # env: 91 | # AC_USERNAME: ${{ secrets.AC_USERNAME }} 92 | # AC_PASSWORD: ${{ secrets.AC_PASSWORD }} 93 | 94 | - name: Checkout create-image 95 | uses: actions/checkout@v2 96 | with: 97 | repository: create-dmg/create-dmg 98 | path: ./build/create-dmg 99 | ref: master 100 | 101 | - name: Build macOS DMG 102 | shell: bash 103 | working-directory: ./build 104 | run: | 105 | mv bin/mysqlBinlogUI.app "bin/Mysql Binlog UI.app" 106 | ./create-dmg/create-dmg \ 107 | --no-internet-enable \ 108 | --volname "Mysql Binlog UI" \ 109 | --volicon "bin/Mysql Binlog UI.app/Contents/Resources/iconfile.icns" \ 110 | --background "dmg/background.tiff" \ 111 | --text-size 12 \ 112 | --window-pos 400 400 \ 113 | --window-size 660 450 \ 114 | --icon-size 80 \ 115 | --icon "Mysql Binlog UI.app" 180 180 \ 116 | --hide-extension "Mysql Binlog UI.app" \ 117 | --app-drop-link 480 180 \ 118 | --add-file "Repair" "dmg/fix-app" 230 290 \ 119 | --add-file "损坏修复" "dmg/fix-app_zh" 430 290 \ 120 | "bin/mysqlBinlogUI-${{ steps.normalise_platform.outputs.tag }}.dmg" \ 121 | "bin" 122 | 123 | - name: Rename dmg 124 | working-directory: ./build/bin 125 | run: mv "mysqlBinlogUI-${{ steps.normalise_platform.outputs.tag }}.dmg" "mysqlBinlogUI_${{ steps.normalise_version.outputs.version }}_${{ steps.normalise_platform.outputs.tag }}.dmg" 126 | 127 | - name: Upload release asset (DMG Package) 128 | uses: softprops/action-gh-release@v1 129 | with: 130 | tag_name: v${{ steps.normalise_version.outputs.version }} 131 | files: ./build/bin/mysqlBinlogUI_${{ steps.normalise_version.outputs.version }}_${{ steps.normalise_platform.outputs.tag }}.dmg 132 | token: ${{ secrets.GITHUB_TOKEN }} 133 | -------------------------------------------------------------------------------- /frontend/src/views/db/DbDetails.vue: -------------------------------------------------------------------------------- 1 | 82 | 83 | 180 | 181 | 214 | å 215 | -------------------------------------------------------------------------------- /.github/workflows/release-linux.yaml: -------------------------------------------------------------------------------- 1 | name: Release Linux App 2 | 3 | on: 4 | release: 5 | types: [ published ] 6 | workflow_dispatch: 7 | inputs: 8 | tag: 9 | description: 'Version tag' 10 | required: true 11 | default: '1.0.0' 12 | 13 | jobs: 14 | release: 15 | name: Release Linux App 16 | runs-on: ubuntu-20.04 17 | strategy: 18 | matrix: 19 | platform: 20 | - linux/amd64 21 | 22 | steps: 23 | - name: Checkout source code 24 | uses: actions/checkout@v3 25 | 26 | - name: Normalise platform tag 27 | id: normalise_platform 28 | shell: bash 29 | run: | 30 | tag=$(echo ${{ matrix.platform }} | sed -e 's/\//_/g') 31 | echo "tag=$tag" >> "$GITHUB_OUTPUT" 32 | 33 | - name: Normalise platform arch 34 | id: normalise_platform_arch 35 | run: | 36 | if [ "${{ matrix.platform }}" == "linux/amd64" ]; then 37 | echo "arch=x86_64" >> "$GITHUB_OUTPUT" 38 | elif [ "${{ matrix.platform }}" == "linux/aarch64" ]; then 39 | echo "arch=aarch64" >> "$GITHUB_OUTPUT" 40 | fi 41 | 42 | - name: Normalise version tag 43 | id: normalise_version 44 | shell: bash 45 | run: | 46 | if [ "${{ github.event.release.tag_name }}" == "" ]; then 47 | version=$(echo ${{ github.event.inputs.tag }} | sed -e 's/v//g') 48 | echo "version=$version" >> "$GITHUB_OUTPUT" 49 | else 50 | version=$(echo ${{ github.event.release.tag_name }} | sed -e 's/v//g') 51 | echo "version=$version" >> "$GITHUB_OUTPUT" 52 | fi 53 | 54 | - name: Setup Go 55 | uses: actions/setup-go@v4 56 | with: 57 | go-version: stable 58 | 59 | - name: Install wails 60 | shell: bash 61 | run: go install github.com/wailsapp/wails/v2/cmd/wails@v2.8.0 62 | 63 | - name: Install Ubuntu prerequisites 64 | shell: bash 65 | run: | 66 | sudo apt-get update 67 | sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.0-dev libfuse-dev libfuse2 68 | 69 | - name: Setup Node 70 | uses: actions/setup-node@v3 71 | with: 72 | node-version: 20 73 | 74 | - name: Build frontend assets 75 | shell: bash 76 | run: | 77 | npm install -g npm@9 78 | jq '.info.productVersion = "${{ steps.normalise_version.outputs.version }}"' wails.json > tmp.json 79 | mv tmp.json wails.json 80 | cd frontend 81 | jq '.version = "${{ steps.normalise_version.outputs.version }}"' package.json > tmp.json 82 | mv tmp.json package.json 83 | npm install 84 | 85 | - name: Build wails app for Linux 86 | shell: bash 87 | run: | 88 | CGO_ENABLED=1 wails build -platform ${{ matrix.platform }} \ 89 | -ldflags "-X main.version=v${{ steps.normalise_version.outputs.version }} -X main.gaMeasurementID=${{ secrets.GA_MEASUREMENT_ID }} -X main.gaSecretKey=${{ secrets.LINUX_GA_SECRET }}" \ 90 | -o mysql-binlog 91 | 92 | - name: Setup control template 93 | shell: bash 94 | run: | 95 | content=$(cat build/linux/mysql-binlog_0.0.0_amd64/DEBIAN/control) 96 | content=$(echo "$content" | sed -e "s/{{.Name}}/$(jq -r '.name' wails.json)/g") 97 | content=$(echo "$content" | sed -e "s/{{.Info.ProductVersion}}/$(jq -r '.info.productVersion' wails.json)/g") 98 | content=$(echo "$content" | sed -e "s/{{.Author.Name}}/$(jq -r '.author.name' wails.json)/g") 99 | content=$(echo "$content" | sed -e "s/{{.Author.Email}}/$(jq -r '.author.email' wails.json)/g") 100 | content=$(echo "$content" | sed -e "s/{{.Info.Comments}}/$(jq -r '.info.comments' wails.json)/g") 101 | echo $content 102 | echo "$content" > build/linux/mysql-binlog_0.0.0_amd64/DEBIAN/control 103 | 104 | - name: Setup app template 105 | shell: bash 106 | run: | 107 | content=$(cat build/linux/mysql-binlog_0.0.0_amd64/usr/share/applications/mysql-binlog.desktop) 108 | content=$(echo "$content" | sed -e "s/{{.Info.ProductName}}/$(jq -r '.info.productName' wails.json)/g") 109 | content=$(echo "$content" | sed -e "s/{{.Info.Comments}}/$(jq -r '.info.comments' wails.json)/g") 110 | echo $content 111 | echo "$content" > build/linux/mysql-binlog_0.0.0_amd64/usr/share/applications/mysql-binlog.desktop 112 | 113 | - name: Package up deb file 114 | shell: bash 115 | run: | 116 | mv build/bin/mysql-binlog build/linux/mysql-binlog_0.0.0_amd64/usr/local/bin/ 117 | cd build/linux 118 | mv mysql-binlog_0.0.0_amd64 "mysql-binlog_${{ steps.normalise_version.outputs.version }}_amd64" 119 | sed -i 's/0.0.0/${{ steps.normalise_version.outputs.version }}/g' "mysql-binlog_${{ steps.normalise_version.outputs.version }}_amd64/DEBIAN/control" 120 | dpkg-deb --build -Zxz "mysql-binlog_${{ steps.normalise_version.outputs.version }}_amd64" 121 | 122 | - name: Package up appimage file 123 | run: | 124 | curl https://github.com/linuxdeploy/linuxdeploy/releases/download/1-alpha-20240109-1/linuxdeploy-${{ steps.normalise_platform_arch.outputs.arch }}.AppImage \ 125 | -o linuxdeploy \ 126 | -L 127 | chmod u+x linuxdeploy 128 | 129 | ./linuxdeploy --appdir AppDir 130 | 131 | pushd AppDir 132 | # Copy WebKit files. 133 | find /usr/lib* -name WebKitNetworkProcess -exec mkdir -p $(dirname '{}') \; -exec cp --parents '{}' "." \; || true 134 | find /usr/lib* -name WebKitWebProcess -exec mkdir -p $(dirname '{}') \; -exec cp --parents '{}' "." \; || true 135 | find /usr/lib* -name libwebkit2gtkinjectedbundle.so -exec mkdir -p $(dirname '{}') \; -exec cp --parents '{}' "." \; || true 136 | popd 137 | 138 | 139 | mkdir -p AppDir/usr/share/icons/hicolor/512x512/apps 140 | build_dir="build/linux/mysql-binlog_${{ steps.normalise_version.outputs.version }}_amd64" 141 | 142 | cp -r $build_dir/usr/share/icons/hicolor/512x512/apps/mysql-binlog.png AppDir/usr/share/icons/hicolor/512x512/apps/ 143 | cp $build_dir/usr/local/bin/mysql-binlog AppDir/usr/bin/ 144 | 145 | 146 | sed -i 's#/usr/local/bin/mysql-binlog#mysql-binlog#g' $build_dir/usr/share/applications/mysql-binlog.desktop 147 | 148 | curl -o linuxdeploy-plugin-gtk.sh "https://raw.githubusercontent.com/tauri-apps/linuxdeploy-plugin-gtk/master/linuxdeploy-plugin-gtk.sh" 149 | 150 | sed -i '/XDG_DATA_DIRS/a export WEBKIT_DISABLE_COMPOSITING_MODE=1' linuxdeploy-plugin-gtk.sh 151 | chmod +x linuxdeploy-plugin-gtk.sh 152 | 153 | curl -o AppDir/AppRun https://github.com/AppImage/AppImageKit/releases/download/continuous/AppRun-${{ steps.normalise_platform_arch.outputs.arch }} -L 154 | 155 | ./linuxdeploy --appdir AppDir \ 156 | --output=appimage \ 157 | --plugin=gtk \ 158 | -e $build_dir/usr/local/bin/mysql-binlog \ 159 | -d $build_dir/usr/share/applications/mysql-binlog.desktop 160 | 161 | - name: Rename deb 162 | working-directory: ./build/linux 163 | run: mv "mysql-binlog_${{ steps.normalise_version.outputs.version }}_amd64.deb" "mysql-binlog_${{ steps.normalise_version.outputs.version }}_${{ steps.normalise_platform.outputs.tag }}.deb" 164 | 165 | - name: Rename appimage 166 | run: mv mysql_binlog-${{ steps.normalise_platform_arch.outputs.arch }}.AppImage "mysql-binlog_${{ steps.normalise_version.outputs.version }}_${{ steps.normalise_platform.outputs.tag }}.AppImage" 167 | 168 | - name: Upload release asset 169 | uses: softprops/action-gh-release@v1 170 | with: 171 | tag_name: v${{ steps.normalise_version.outputs.version }} 172 | files: | 173 | ./build/linux/mysql-binlog_${{ steps.normalise_version.outputs.version }}_${{ steps.normalise_platform.outputs.tag }}.deb 174 | mysql-binlog_${{ steps.normalise_version.outputs.version }}_${{ steps.normalise_platform.outputs.tag }}.AppImage 175 | token: ${{ secrets.GITHUB_TOKEN }} 176 | -------------------------------------------------------------------------------- /apps/orm/files_logs_orm.go: -------------------------------------------------------------------------------- 1 | package orm 2 | 3 | import ( 4 | "database/sql" 5 | "github.com/sirupsen/logrus" 6 | "gorm.io/gorm" 7 | "time" 8 | ) 9 | 10 | type UploadLogs struct { 11 | ID int64 `gorm:"column:id"` 12 | Database string `gorm:"column:database"` 13 | Table string `gorm:"column:table"` 14 | File string `gorm:"column:file"` 15 | FileSize float64 `gorm:"column:file_size"` 16 | Status int64 `gorm:"column:status"` 17 | Msg string `gorm:"column:msg"` 18 | Timestamp time.Time `gorm:"column:timestamp"` 19 | } 20 | 21 | type OrmUploadLogs struct { 22 | db *gorm.DB 23 | } 24 | 25 | func NewOrmUploadLogs() *OrmUploadLogs { 26 | return &OrmUploadLogs{db: GetDB().Model(&UploadLogs{})} 27 | } 28 | 29 | func (receiver *UploadLogs) TableName() string { 30 | return "upload_logs" 31 | } 32 | 33 | func (orm *OrmUploadLogs) TableName() string { 34 | return "upload_logs" 35 | } 36 | 37 | func (orm *OrmUploadLogs) GetDB() *gorm.DB { 38 | return orm.db 39 | } 40 | 41 | func (orm *OrmUploadLogs) GetTableInfo() interface{} { 42 | return &UploadLogs{} 43 | } 44 | 45 | // Create insert the value into database 46 | func (orm *OrmUploadLogs) Create(value interface{}) *gorm.DB { 47 | return orm.db.Create(value) 48 | } 49 | 50 | // CreateInBatches insert the value in batches into database 51 | func (orm *OrmUploadLogs) CreateInBatches(value interface{}, batchSize int) *gorm.DB { 52 | return orm.db.CreateInBatches(value, batchSize) 53 | } 54 | 55 | // Save update value in database, if the value doesn't have primary key, will insert it 56 | func (orm *OrmUploadLogs) Save(value interface{}) *gorm.DB { 57 | return orm.db.Save(value) 58 | } 59 | 60 | func (orm *OrmUploadLogs) Row() *sql.Row { 61 | return orm.db.Row() 62 | } 63 | 64 | func (orm *OrmUploadLogs) Rows() (*sql.Rows, error) { 65 | return orm.db.Rows() 66 | } 67 | 68 | // Scan scan value to a struct 69 | func (orm *OrmUploadLogs) Scan(dest interface{}) *gorm.DB { 70 | return orm.db.Scan(dest) 71 | } 72 | 73 | func (orm *OrmUploadLogs) ScanRows(rows *sql.Rows, dest interface{}) error { 74 | return orm.db.ScanRows(rows, dest) 75 | } 76 | 77 | // Connection use a db conn to execute Multiple commands,this conn will put conn pool after it is executed. 78 | func (orm *OrmUploadLogs) Connection(fc func(tx *gorm.DB) error) (err error) { 79 | return orm.db.Connection(fc) 80 | } 81 | 82 | // Transaction start a transaction as a block, return error will rollback, otherwise to commit. 83 | func (orm *OrmUploadLogs) Transaction(fc func(tx *gorm.DB) error, opts ...*sql.TxOptions) (err error) { 84 | return orm.db.Transaction(fc, opts...) 85 | } 86 | 87 | // Begin begins a transaction 88 | func (orm *OrmUploadLogs) Begin(opts ...*sql.TxOptions) *gorm.DB { 89 | return orm.db.Begin(opts...) 90 | } 91 | 92 | // Commit commit a transaction 93 | func (orm *OrmUploadLogs) Commit() *gorm.DB { 94 | return orm.db.Commit() 95 | } 96 | 97 | // Rollback rollback a transaction 98 | func (orm *OrmUploadLogs) Rollback() *gorm.DB { 99 | return orm.db.Rollback() 100 | } 101 | 102 | func (orm *OrmUploadLogs) SavePoint(name string) *gorm.DB { 103 | return orm.db.SavePoint(name) 104 | } 105 | 106 | func (orm *OrmUploadLogs) RollbackTo(name string) *gorm.DB { 107 | return orm.db.RollbackTo(name) 108 | } 109 | 110 | // Exec execute raw sql 111 | func (orm *OrmUploadLogs) Exec(sql string, values ...interface{}) *gorm.DB { 112 | return orm.db.Exec(sql, values...) 113 | } 114 | 115 | // Exists 检索对象是否存在 116 | func (orm *OrmUploadLogs) Exists() (bool, error) { 117 | dest := &struct { 118 | H int `json:"h"` 119 | }{} 120 | db := orm.db.Select("1 as h").Limit(1).Find(dest) 121 | return dest.H == 1, db.Error 122 | } 123 | 124 | func (orm *OrmUploadLogs) Unscoped() *OrmUploadLogs { 125 | orm.db.Unscoped() 126 | return orm 127 | } 128 | 129 | // ------------ 以下是单表独有的函数, 便捷字段条件, Laravel风格操作 --------- 130 | 131 | func (orm *OrmUploadLogs) Insert(row *UploadLogs) error { 132 | return orm.db.Create(row).Error 133 | } 134 | 135 | func (orm *OrmUploadLogs) Inserts(rows []*UploadLogs) *gorm.DB { 136 | return orm.db.Create(rows) 137 | } 138 | 139 | func (orm *OrmUploadLogs) Order(value interface{}) *OrmUploadLogs { 140 | orm.db.Order(value) 141 | return orm 142 | } 143 | 144 | func (orm *OrmUploadLogs) Group(name string) *OrmUploadLogs { 145 | orm.db.Group(name) 146 | return orm 147 | } 148 | 149 | func (orm *OrmUploadLogs) Limit(limit int) *OrmUploadLogs { 150 | orm.db.Limit(limit) 151 | return orm 152 | } 153 | 154 | func (orm *OrmUploadLogs) Offset(offset int) *OrmUploadLogs { 155 | orm.db.Offset(offset) 156 | return orm 157 | } 158 | 159 | // 直接查询列表, 如果需要条数, 使用Find() 160 | func (orm *OrmUploadLogs) Get() []*UploadLogs { 161 | got, _ := orm.Find() 162 | return got 163 | } 164 | 165 | // Pluck used to query single column from a model as a map 166 | // 167 | // var ages []int64 168 | // db.Model(&users).Pluck("age", &ages) 169 | func (orm *OrmUploadLogs) Pluck(column string, dest interface{}) *gorm.DB { 170 | return orm.db.Pluck(column, dest) 171 | } 172 | 173 | // Delete 有条件删除 174 | func (orm *OrmUploadLogs) Delete(conds ...interface{}) *gorm.DB { 175 | return orm.db.Delete(&UploadLogs{}, conds...) 176 | } 177 | 178 | // DeleteAll 删除所有 179 | func (orm *OrmUploadLogs) DeleteAll() *gorm.DB { 180 | return orm.db.Exec("DELETE FROM common") 181 | } 182 | 183 | func (orm *OrmUploadLogs) Count() int64 { 184 | var count int64 185 | orm.db.Count(&count) 186 | return count 187 | } 188 | 189 | // First 检索单个对象 190 | func (orm *OrmUploadLogs) First(conds ...interface{}) (*UploadLogs, bool) { 191 | dest := &UploadLogs{} 192 | db := orm.db.Limit(1).Find(dest, conds...) 193 | return dest, db.RowsAffected == 1 194 | } 195 | 196 | // Take return a record that match given conditions, the order will depend on the database implementation 197 | func (orm *OrmUploadLogs) Take(conds ...interface{}) (*UploadLogs, int64) { 198 | dest := &UploadLogs{} 199 | db := orm.db.Take(dest, conds...) 200 | return dest, db.RowsAffected 201 | } 202 | 203 | // Last find last record that match given conditions, order by primary key 204 | func (orm *OrmUploadLogs) Last(conds ...interface{}) (*UploadLogs, int64) { 205 | dest := &UploadLogs{} 206 | db := orm.db.Last(dest, conds...) 207 | return dest, db.RowsAffected 208 | } 209 | func (orm *OrmUploadLogs) Find(conds ...interface{}) ([]*UploadLogs, int64) { 210 | list := make([]*UploadLogs, 0) 211 | tx := orm.db.Find(&list, conds...) 212 | if tx.Error != nil { 213 | logrus.Error(tx.Error) 214 | } 215 | return list, tx.RowsAffected 216 | } 217 | 218 | // Paginate 分页 219 | func (orm *OrmUploadLogs) Paginate(page int, limit int) ([]*UploadLogs, int64) { 220 | var total int64 221 | list := make([]*UploadLogs, 0) 222 | orm.db.Count(&total) 223 | if total > 0 { 224 | if page == 0 { 225 | page = 1 226 | } 227 | 228 | offset := (page - 1) * limit 229 | tx := orm.db.Offset(offset).Limit(limit).Find(&list) 230 | if tx.Error != nil { 231 | logrus.Error(tx.Error) 232 | } 233 | } 234 | 235 | return list, total 236 | } 237 | 238 | // FindInBatches find records in batches 239 | func (orm *OrmUploadLogs) FindInBatches(dest interface{}, batchSize int, fc func(tx *gorm.DB, batch int) error) *gorm.DB { 240 | return orm.db.FindInBatches(dest, batchSize, fc) 241 | } 242 | 243 | // FirstOrInit gets the first matched record or initialize a new instance with given conditions (only works with struct or map conditions) 244 | func (orm *OrmUploadLogs) FirstOrInit(dest *UploadLogs, conds ...interface{}) (*UploadLogs, *gorm.DB) { 245 | return dest, orm.db.FirstOrInit(dest, conds...) 246 | } 247 | 248 | // FirstOrCreate gets the first matched record or create a new one with given conditions (only works with struct, map conditions) 249 | func (orm *OrmUploadLogs) FirstOrCreate(dest interface{}, conds ...interface{}) *gorm.DB { 250 | return orm.db.FirstOrCreate(dest, conds...) 251 | } 252 | 253 | // Update update attributes with callbacks, refer: https://gorm.io/docs/update.html#Update-Changed-Fields 254 | func (orm *OrmUploadLogs) Update(column string, value interface{}) *gorm.DB { 255 | return orm.db.Update(column, value) 256 | } 257 | 258 | // Updates update attributes with callbacks, refer: https://gorm.io/docs/update.html#Update-Changed-Fields 259 | func (orm *OrmUploadLogs) Updates(values interface{}) *gorm.DB { 260 | return orm.db.Updates(values) 261 | } 262 | 263 | func (orm *OrmUploadLogs) UpdateColumn(column string, value interface{}) *gorm.DB { 264 | return orm.db.UpdateColumn(column, value) 265 | } 266 | 267 | func (orm *OrmUploadLogs) UpdateColumns(values interface{}) *gorm.DB { 268 | return orm.db.UpdateColumns(values) 269 | } 270 | 271 | func (orm *OrmUploadLogs) Where(query interface{}, args ...interface{}) *OrmUploadLogs { 272 | orm.db.Where(query, args...) 273 | return orm 274 | } 275 | 276 | func (orm *OrmUploadLogs) Select(query interface{}, args ...interface{}) *OrmUploadLogs { 277 | orm.db.Select(query, args...) 278 | return orm 279 | } 280 | 281 | func (orm *OrmUploadLogs) Sum(field string) int64 { 282 | type result struct { 283 | S int64 `json:"s"` 284 | } 285 | ret := result{} 286 | orm.db.Select("SUM(`" + field + "`) AS s").Scan(&ret) 287 | return ret.S 288 | } 289 | 290 | func (orm *OrmUploadLogs) And(fuc func(orm *OrmUploadLogs)) *OrmUploadLogs { 291 | ormAnd := NewOrmUploadLogs() 292 | fuc(ormAnd) 293 | orm.db.Where(ormAnd.db) 294 | return orm 295 | } 296 | 297 | func (orm *OrmUploadLogs) Or(fuc func(orm *OrmUploadLogs)) *OrmUploadLogs { 298 | ormOr := NewOrmUploadLogs() 299 | fuc(ormOr) 300 | orm.db.Or(ormOr.db) 301 | return orm 302 | } 303 | 304 | func (orm *OrmUploadLogs) WhereId(val int64) *OrmUploadLogs { 305 | orm.db.Where("`id` = ?", val) 306 | return orm 307 | } 308 | 309 | func (orm *OrmUploadLogs) WhereIdIn(val []int64) *OrmUploadLogs { 310 | orm.db.Where("`id` IN ?", val) 311 | return orm 312 | } 313 | func (orm *OrmUploadLogs) WhereIdGt(val int64) *OrmUploadLogs { 314 | orm.db.Where("`id` > ?", val) 315 | return orm 316 | } 317 | func (orm *OrmUploadLogs) WhereIdGte(val int64) *OrmUploadLogs { 318 | orm.db.Where("`id` >= ?", val) 319 | return orm 320 | } 321 | func (orm *OrmUploadLogs) WhereIdLt(val int64) *OrmUploadLogs { 322 | orm.db.Where("`id` < ?", val) 323 | return orm 324 | } 325 | func (orm *OrmUploadLogs) WhereIdLte(val int64) *OrmUploadLogs { 326 | orm.db.Where("`id` <= ?", val) 327 | return orm 328 | } 329 | 330 | func (orm *OrmUploadLogs) WhereDatabase(val string) *OrmUploadLogs { 331 | orm.db.Where("`database` = ?", val) 332 | return orm 333 | } 334 | -------------------------------------------------------------------------------- /apps/orm/binlog_orm.go: -------------------------------------------------------------------------------- 1 | package orm 2 | 3 | import ( 4 | "changeme/apps/ctx" 5 | "database/sql" 6 | "github.com/sirupsen/logrus" 7 | "gorm.io/gorm" 8 | "time" 9 | ) 10 | 11 | type Binlog struct { 12 | ID int64 `gorm:"column:id"` 13 | Schema string `gorm:"column:schema"` 14 | Table string `gorm:"column:tables"` 15 | Timestamp time.Time `gorm:"column:timestamp"` 16 | Event string `gorm:"column:event"` 17 | Row1 string `gorm:"column:row_1"` 18 | Row2 string `gorm:"column:row_2"` 19 | 20 | table string 21 | } 22 | 23 | type OrmBinlog struct { 24 | db *gorm.DB 25 | 26 | database string 27 | table string 28 | } 29 | 30 | var BinlogDBMap = make(map[string]*gorm.DB) 31 | 32 | func NewOrmBinlog(database, table string) *OrmBinlog { 33 | var db *gorm.DB 34 | var ok bool 35 | if db, ok = BinlogDBMap[database]; !ok { 36 | db = NewDB(database) 37 | if db == nil { 38 | return nil 39 | } 40 | BinlogDBMap[database] = db 41 | } 42 | 43 | ctx.LogError(database, table) 44 | 45 | return &OrmBinlog{ 46 | database: database, 47 | db: db.Model( 48 | &Binlog{table: table}, 49 | ).Table(table), 50 | table: table, 51 | } 52 | } 53 | 54 | func (receiver *Binlog) TableName() string { 55 | return receiver.table 56 | } 57 | 58 | func (orm *OrmBinlog) TableName() string { 59 | return orm.table 60 | } 61 | 62 | func (orm *OrmBinlog) GetDB() *gorm.DB { 63 | return orm.db 64 | } 65 | 66 | func (orm *OrmBinlog) GetTableInfo() interface{} { 67 | return &Binlog{table: orm.table} 68 | } 69 | 70 | // Create insert the value into database 71 | func (orm *OrmBinlog) Create(value interface{}) *gorm.DB { 72 | return orm.db.Create(value) 73 | } 74 | 75 | // CreateInBatches insert the value in batches into database 76 | func (orm *OrmBinlog) CreateInBatches(value interface{}, batchSize int) *gorm.DB { 77 | return orm.db.CreateInBatches(value, batchSize) 78 | } 79 | 80 | // Save update value in database, if the value doesn't have primary key, will insert it 81 | func (orm *OrmBinlog) Save(value interface{}) *gorm.DB { 82 | return orm.db.Save(value) 83 | } 84 | 85 | func (orm *OrmBinlog) Row() *sql.Row { 86 | return orm.db.Row() 87 | } 88 | 89 | func (orm *OrmBinlog) Rows() (*sql.Rows, error) { 90 | return orm.db.Rows() 91 | } 92 | 93 | // Scan scan value to a struct 94 | func (orm *OrmBinlog) Scan(dest interface{}) *gorm.DB { 95 | return orm.db.Scan(dest) 96 | } 97 | 98 | func (orm *OrmBinlog) ScanRows(rows *sql.Rows, dest interface{}) error { 99 | return orm.db.ScanRows(rows, dest) 100 | } 101 | 102 | // Connection use a db conn to execute Multiple commands,this conn will put conn pool after it is executed. 103 | func (orm *OrmBinlog) Connection(fc func(tx *gorm.DB) error) (err error) { 104 | return orm.db.Connection(fc) 105 | } 106 | 107 | // Transaction start a transaction as a block, return error will rollback, otherwise to commit. 108 | func (orm *OrmBinlog) Transaction(fc func(tx *gorm.DB) error, opts ...*sql.TxOptions) (err error) { 109 | return orm.db.Transaction(fc, opts...) 110 | } 111 | 112 | // Begin begins a transaction 113 | func (orm *OrmBinlog) Begin(opts ...*sql.TxOptions) *gorm.DB { 114 | return orm.db.Begin(opts...) 115 | } 116 | 117 | // Commit commit a transaction 118 | func (orm *OrmBinlog) Commit() *gorm.DB { 119 | return orm.db.Commit() 120 | } 121 | 122 | // Rollback rollback a transaction 123 | func (orm *OrmBinlog) Rollback() *gorm.DB { 124 | return orm.db.Rollback() 125 | } 126 | 127 | func (orm *OrmBinlog) SavePoint(name string) *gorm.DB { 128 | return orm.db.SavePoint(name) 129 | } 130 | 131 | func (orm *OrmBinlog) RollbackTo(name string) *gorm.DB { 132 | return orm.db.RollbackTo(name) 133 | } 134 | 135 | // Exec execute raw sql 136 | func (orm *OrmBinlog) Exec(sql string, values ...interface{}) *gorm.DB { 137 | return orm.db.Exec(sql, values...) 138 | } 139 | 140 | // Exists 检索对象是否存在 141 | func (orm *OrmBinlog) Exists() (bool, error) { 142 | dest := &struct { 143 | H int `json:"h"` 144 | }{} 145 | db := orm.db.Select("1 as h").Limit(1).Find(dest) 146 | return dest.H == 1, db.Error 147 | } 148 | 149 | func (orm *OrmBinlog) Unscoped() *OrmBinlog { 150 | orm.db.Unscoped() 151 | return orm 152 | } 153 | 154 | // ------------ 以下是单表独有的函数, 便捷字段条件, Laravel风格操作 --------- 155 | 156 | func (orm *OrmBinlog) Insert(row *Binlog) error { 157 | return orm.db.Create(row).Error 158 | } 159 | 160 | func (orm *OrmBinlog) Inserts(rows []*Binlog) *gorm.DB { 161 | return orm.db.Create(rows) 162 | } 163 | 164 | func (orm *OrmBinlog) Order(value interface{}) *OrmBinlog { 165 | orm.db.Order(value) 166 | return orm 167 | } 168 | 169 | func (orm *OrmBinlog) Group(name string) *OrmBinlog { 170 | orm.db.Group(name) 171 | return orm 172 | } 173 | 174 | func (orm *OrmBinlog) Limit(limit int) *OrmBinlog { 175 | orm.db.Limit(limit) 176 | return orm 177 | } 178 | 179 | func (orm *OrmBinlog) Offset(offset int) *OrmBinlog { 180 | orm.db.Offset(offset) 181 | return orm 182 | } 183 | 184 | // 直接查询列表, 如果需要条数, 使用Find() 185 | func (orm *OrmBinlog) Get() []*Binlog { 186 | got, _ := orm.Find() 187 | return got 188 | } 189 | 190 | // Pluck used to query single column from a model as a map 191 | // 192 | // var ages []int64 193 | // db.Model(&users).Pluck("age", &ages) 194 | func (orm *OrmBinlog) Pluck(column string, dest interface{}) *gorm.DB { 195 | return orm.db.Pluck(column, dest) 196 | } 197 | 198 | // Delete 有条件删除 199 | func (orm *OrmBinlog) Delete(conds ...interface{}) *gorm.DB { 200 | return orm.db.Delete(&Binlog{}, conds...) 201 | } 202 | 203 | // DeleteAll 删除所有 204 | func (orm *OrmBinlog) DeleteAll() *gorm.DB { 205 | return orm.db.Exec("DELETE FROM common") 206 | } 207 | 208 | func (orm *OrmBinlog) Count() int64 { 209 | var count int64 210 | orm.db.Count(&count) 211 | return count 212 | } 213 | 214 | // First 检索单个对象 215 | func (orm *OrmBinlog) First(conds ...interface{}) (*Binlog, bool) { 216 | dest := &Binlog{table: orm.table} 217 | db := orm.db.Limit(1).Find(dest, conds...) 218 | return dest, db.RowsAffected == 1 219 | } 220 | 221 | // Take return a record that match given conditions, the order will depend on the database implementation 222 | func (orm *OrmBinlog) Take(conds ...interface{}) (*Binlog, int64) { 223 | dest := &Binlog{table: orm.table} 224 | db := orm.db.Take(dest, conds...) 225 | return dest, db.RowsAffected 226 | } 227 | 228 | // Last find last record that match given conditions, order by primary key 229 | func (orm *OrmBinlog) Last(conds ...interface{}) (*Binlog, int64) { 230 | dest := &Binlog{table: orm.table} 231 | db := orm.db.Last(dest, conds...) 232 | return dest, db.RowsAffected 233 | } 234 | func (orm *OrmBinlog) Find(conds ...interface{}) ([]*Binlog, int64) { 235 | list := make([]*Binlog, 0) 236 | tx := orm.db.Find(&list, conds...) 237 | if tx.Error != nil { 238 | logrus.Error(tx.Error) 239 | } 240 | return list, tx.RowsAffected 241 | } 242 | 243 | // Paginate 分页 244 | func (orm *OrmBinlog) Paginate(page int, limit int) ([]*Binlog, int64) { 245 | var total int64 246 | list := make([]*Binlog, 0) 247 | orm.db.Count(&total) 248 | if total > 0 { 249 | if page == 0 { 250 | page = 1 251 | } 252 | 253 | offset := (page - 1) * limit 254 | tx := orm.db.Offset(offset).Limit(limit).Find(&list) 255 | if tx.Error != nil { 256 | logrus.Error(tx.Error) 257 | } 258 | } 259 | 260 | return list, total 261 | } 262 | 263 | // FindInBatches find records in batches 264 | func (orm *OrmBinlog) FindInBatches(dest interface{}, batchSize int, fc func(tx *gorm.DB, batch int) error) *gorm.DB { 265 | return orm.db.FindInBatches(dest, batchSize, fc) 266 | } 267 | 268 | // FirstOrInit gets the first matched record or initialize a new instance with given conditions (only works with struct or map conditions) 269 | func (orm *OrmBinlog) FirstOrInit(dest *Binlog, conds ...interface{}) (*Binlog, *gorm.DB) { 270 | return dest, orm.db.FirstOrInit(dest, conds...) 271 | } 272 | 273 | // FirstOrCreate gets the first matched record or create a new one with given conditions (only works with struct, map conditions) 274 | func (orm *OrmBinlog) FirstOrCreate(dest interface{}, conds ...interface{}) *gorm.DB { 275 | return orm.db.FirstOrCreate(dest, conds...) 276 | } 277 | 278 | // Update update attributes with callbacks, refer: https://gorm.io/docs/update.html#Update-Changed-Fields 279 | func (orm *OrmBinlog) Update(column string, value interface{}) *gorm.DB { 280 | return orm.db.Update(column, value) 281 | } 282 | 283 | // Updates update attributes with callbacks, refer: https://gorm.io/docs/update.html#Update-Changed-Fields 284 | func (orm *OrmBinlog) Updates(values interface{}) *gorm.DB { 285 | return orm.db.Updates(values) 286 | } 287 | 288 | func (orm *OrmBinlog) UpdateColumn(column string, value interface{}) *gorm.DB { 289 | return orm.db.UpdateColumn(column, value) 290 | } 291 | 292 | func (orm *OrmBinlog) UpdateColumns(values interface{}) *gorm.DB { 293 | return orm.db.UpdateColumns(values) 294 | } 295 | 296 | func (orm *OrmBinlog) Where(query interface{}, args ...interface{}) *OrmBinlog { 297 | orm.db.Where(query, args...) 298 | return orm 299 | } 300 | 301 | func (orm *OrmBinlog) Select(query interface{}, args ...interface{}) *OrmBinlog { 302 | orm.db.Select(query, args...) 303 | return orm 304 | } 305 | 306 | func (orm *OrmBinlog) Sum(field string) int64 { 307 | type result struct { 308 | S int64 `json:"s"` 309 | } 310 | ret := result{} 311 | orm.db.Select("SUM(`" + field + "`) AS s").Scan(&ret) 312 | return ret.S 313 | } 314 | 315 | func (orm *OrmBinlog) And(fuc func(orm *OrmBinlog)) *OrmBinlog { 316 | ormAnd := NewOrmBinlog(orm.database, orm.table) 317 | fuc(ormAnd) 318 | orm.db.Where(ormAnd.db) 319 | return orm 320 | } 321 | 322 | func (orm *OrmBinlog) Or(fuc func(orm *OrmBinlog)) *OrmBinlog { 323 | ormOr := NewOrmBinlog(orm.database, orm.table) 324 | fuc(ormOr) 325 | orm.db.Or(ormOr.db) 326 | return orm 327 | } 328 | 329 | func (orm *OrmBinlog) WhereId(val int64) *OrmBinlog { 330 | orm.db.Where("`id` = ?", val) 331 | return orm 332 | } 333 | 334 | func (orm *OrmBinlog) WhereIdIn(val []int64) *OrmBinlog { 335 | orm.db.Where("`id` IN ?", val) 336 | return orm 337 | } 338 | func (orm *OrmBinlog) WhereIdGt(val int64) *OrmBinlog { 339 | orm.db.Where("`id` > ?", val) 340 | return orm 341 | } 342 | func (orm *OrmBinlog) WhereIdGte(val int64) *OrmBinlog { 343 | orm.db.Where("`id` >= ?", val) 344 | return orm 345 | } 346 | func (orm *OrmBinlog) WhereIdLt(val int64) *OrmBinlog { 347 | orm.db.Where("`id` < ?", val) 348 | return orm 349 | } 350 | func (orm *OrmBinlog) WhereIdLte(val int64) *OrmBinlog { 351 | orm.db.Where("`id` <= ?", val) 352 | return orm 353 | } 354 | -------------------------------------------------------------------------------- /frontend/wailsjs/runtime/runtime.d.ts: -------------------------------------------------------------------------------- 1 | /* 2 | _ __ _ __ 3 | | | / /___ _(_) /____ 4 | | | /| / / __ `/ / / ___/ 5 | | |/ |/ / /_/ / / (__ ) 6 | |__/|__/\__,_/_/_/____/ 7 | The electron alternative for Go 8 | (c) Lea Anthony 2019-present 9 | */ 10 | 11 | export interface Position { 12 | x: number; 13 | y: number; 14 | } 15 | 16 | export interface Size { 17 | w: number; 18 | h: number; 19 | } 20 | 21 | export interface Screen { 22 | isCurrent: boolean; 23 | isPrimary: boolean; 24 | width : number 25 | height : number 26 | } 27 | 28 | // Environment information such as platform, buildtype, ... 29 | export interface EnvironmentInfo { 30 | buildType: string; 31 | platform: string; 32 | arch: string; 33 | } 34 | 35 | // [EventsEmit](https://wails.io/docs/reference/runtime/events#eventsemit) 36 | // emits the given event. Optional data may be passed with the event. 37 | // This will trigger any event listeners. 38 | export function EventsEmit(eventName: string, ...data: any): void; 39 | 40 | // [EventsOn](https://wails.io/docs/reference/runtime/events#eventson) sets up a listener for the given event name. 41 | export function EventsOn(eventName: string, callback: (...data: any) => void): () => void; 42 | 43 | // [EventsOnMultiple](https://wails.io/docs/reference/runtime/events#eventsonmultiple) 44 | // sets up a listener for the given event name, but will only trigger a given number times. 45 | export function EventsOnMultiple(eventName: string, callback: (...data: any) => void, maxCallbacks: number): () => void; 46 | 47 | // [EventsOnce](https://wails.io/docs/reference/runtime/events#eventsonce) 48 | // sets up a listener for the given event name, but will only trigger once. 49 | export function EventsOnce(eventName: string, callback: (...data: any) => void): () => void; 50 | 51 | // [EventsOff](https://wails.io/docs/reference/runtime/events#eventsoff) 52 | // unregisters the listener for the given event name. 53 | export function EventsOff(eventName: string, ...additionalEventNames: string[]): void; 54 | 55 | // [EventsOffAll](https://wails.io/docs/reference/runtime/events#eventsoffall) 56 | // unregisters all listeners. 57 | export function EventsOffAll(): void; 58 | 59 | // [LogPrint](https://wails.io/docs/reference/runtime/log#logprint) 60 | // logs the given message as a raw message 61 | export function LogPrint(message: string): void; 62 | 63 | // [LogTrace](https://wails.io/docs/reference/runtime/log#logtrace) 64 | // logs the given message at the `trace` log level. 65 | export function LogTrace(message: string): void; 66 | 67 | // [LogDebug](https://wails.io/docs/reference/runtime/log#logdebug) 68 | // logs the given message at the `debug` log level. 69 | export function LogDebug(message: string): void; 70 | 71 | // [LogError](https://wails.io/docs/reference/runtime/log#logerror) 72 | // logs the given message at the `error` log level. 73 | export function LogError(message: string): void; 74 | 75 | // [LogFatal](https://wails.io/docs/reference/runtime/log#logfatal) 76 | // logs the given message at the `fatal` log level. 77 | // The application will quit after calling this method. 78 | export function LogFatal(message: string): void; 79 | 80 | // [LogInfo](https://wails.io/docs/reference/runtime/log#loginfo) 81 | // logs the given message at the `info` log level. 82 | export function LogInfo(message: string): void; 83 | 84 | // [LogWarning](https://wails.io/docs/reference/runtime/log#logwarning) 85 | // logs the given message at the `warning` log level. 86 | export function LogWarning(message: string): void; 87 | 88 | // [WindowReload](https://wails.io/docs/reference/runtime/window#windowreload) 89 | // Forces a reload by the main application as well as connected browsers. 90 | export function WindowReload(): void; 91 | 92 | // [WindowReloadApp](https://wails.io/docs/reference/runtime/window#windowreloadapp) 93 | // Reloads the application frontend. 94 | export function WindowReloadApp(): void; 95 | 96 | // [WindowSetAlwaysOnTop](https://wails.io/docs/reference/runtime/window#windowsetalwaysontop) 97 | // Sets the window AlwaysOnTop or not on top. 98 | export function WindowSetAlwaysOnTop(b: boolean): void; 99 | 100 | // [WindowSetSystemDefaultTheme](https://wails.io/docs/next/reference/runtime/window#windowsetsystemdefaulttheme) 101 | // *Windows only* 102 | // Sets window theme to system default (dark/light). 103 | export function WindowSetSystemDefaultTheme(): void; 104 | 105 | // [WindowSetLightTheme](https://wails.io/docs/next/reference/runtime/window#windowsetlighttheme) 106 | // *Windows only* 107 | // Sets window to light theme. 108 | export function WindowSetLightTheme(): void; 109 | 110 | // [WindowSetDarkTheme](https://wails.io/docs/next/reference/runtime/window#windowsetdarktheme) 111 | // *Windows only* 112 | // Sets window to dark theme. 113 | export function WindowSetDarkTheme(): void; 114 | 115 | // [WindowCenter](https://wails.io/docs/reference/runtime/window#windowcenter) 116 | // Centers the window on the monitor the window is currently on. 117 | export function WindowCenter(): void; 118 | 119 | // [WindowSetTitle](https://wails.io/docs/reference/runtime/window#windowsettitle) 120 | // Sets the text in the window title bar. 121 | export function WindowSetTitle(title: string): void; 122 | 123 | // [WindowFullscreen](https://wails.io/docs/reference/runtime/window#windowfullscreen) 124 | // Makes the window full screen. 125 | export function WindowFullscreen(): void; 126 | 127 | // [WindowUnfullscreen](https://wails.io/docs/reference/runtime/window#windowunfullscreen) 128 | // Restores the previous window dimensions and position prior to full screen. 129 | export function WindowUnfullscreen(): void; 130 | 131 | // [WindowIsFullscreen](https://wails.io/docs/reference/runtime/window#windowisfullscreen) 132 | // Returns the state of the window, i.e. whether the window is in full screen mode or not. 133 | export function WindowIsFullscreen(): Promise; 134 | 135 | // [WindowSetSize](https://wails.io/docs/reference/runtime/window#windowsetsize) 136 | // Sets the width and height of the window. 137 | export function WindowSetSize(width: number, height: number): Promise; 138 | 139 | // [WindowGetSize](https://wails.io/docs/reference/runtime/window#windowgetsize) 140 | // Gets the width and height of the window. 141 | export function WindowGetSize(): Promise; 142 | 143 | // [WindowSetMaxSize](https://wails.io/docs/reference/runtime/window#windowsetmaxsize) 144 | // Sets the maximum window size. Will resize the window if the window is currently larger than the given dimensions. 145 | // Setting a size of 0,0 will disable this constraint. 146 | export function WindowSetMaxSize(width: number, height: number): void; 147 | 148 | // [WindowSetMinSize](https://wails.io/docs/reference/runtime/window#windowsetminsize) 149 | // Sets the minimum window size. Will resize the window if the window is currently smaller than the given dimensions. 150 | // Setting a size of 0,0 will disable this constraint. 151 | export function WindowSetMinSize(width: number, height: number): void; 152 | 153 | // [WindowSetPosition](https://wails.io/docs/reference/runtime/window#windowsetposition) 154 | // Sets the window position relative to the monitor the window is currently on. 155 | export function WindowSetPosition(x: number, y: number): void; 156 | 157 | // [WindowGetPosition](https://wails.io/docs/reference/runtime/window#windowgetposition) 158 | // Gets the window position relative to the monitor the window is currently on. 159 | export function WindowGetPosition(): Promise; 160 | 161 | // [WindowHide](https://wails.io/docs/reference/runtime/window#windowhide) 162 | // Hides the window. 163 | export function WindowHide(): void; 164 | 165 | // [WindowShow](https://wails.io/docs/reference/runtime/window#windowshow) 166 | // Shows the window, if it is currently hidden. 167 | export function WindowShow(): void; 168 | 169 | // [WindowMaximise](https://wails.io/docs/reference/runtime/window#windowmaximise) 170 | // Maximises the window to fill the screen. 171 | export function WindowMaximise(): void; 172 | 173 | // [WindowToggleMaximise](https://wails.io/docs/reference/runtime/window#windowtogglemaximise) 174 | // Toggles between Maximised and UnMaximised. 175 | export function WindowToggleMaximise(): void; 176 | 177 | // [WindowUnmaximise](https://wails.io/docs/reference/runtime/window#windowunmaximise) 178 | // Restores the window to the dimensions and position prior to maximising. 179 | export function WindowUnmaximise(): void; 180 | 181 | // [WindowIsMaximised](https://wails.io/docs/reference/runtime/window#windowismaximised) 182 | // Returns the state of the window, i.e. whether the window is maximised or not. 183 | export function WindowIsMaximised(): Promise; 184 | 185 | // [WindowMinimise](https://wails.io/docs/reference/runtime/window#windowminimise) 186 | // Minimises the window. 187 | export function WindowMinimise(): void; 188 | 189 | // [WindowUnminimise](https://wails.io/docs/reference/runtime/window#windowunminimise) 190 | // Restores the window to the dimensions and position prior to minimising. 191 | export function WindowUnminimise(): void; 192 | 193 | // [WindowIsMinimised](https://wails.io/docs/reference/runtime/window#windowisminimised) 194 | // Returns the state of the window, i.e. whether the window is minimised or not. 195 | export function WindowIsMinimised(): Promise; 196 | 197 | // [WindowIsNormal](https://wails.io/docs/reference/runtime/window#windowisnormal) 198 | // Returns the state of the window, i.e. whether the window is normal or not. 199 | export function WindowIsNormal(): Promise; 200 | 201 | // [WindowSetBackgroundColour](https://wails.io/docs/reference/runtime/window#windowsetbackgroundcolour) 202 | // Sets the background colour of the window to the given RGBA colour definition. This colour will show through for all transparent pixels. 203 | export function WindowSetBackgroundColour(R: number, G: number, B: number, A: number): void; 204 | 205 | // [ScreenGetAll](https://wails.io/docs/reference/runtime/window#screengetall) 206 | // Gets the all screens. Call this anew each time you want to refresh data from the underlying windowing system. 207 | export function ScreenGetAll(): Promise; 208 | 209 | // [BrowserOpenURL](https://wails.io/docs/reference/runtime/browser#browseropenurl) 210 | // Opens the given URL in the system browser. 211 | export function BrowserOpenURL(url: string): void; 212 | 213 | // [Environment](https://wails.io/docs/reference/runtime/intro#environment) 214 | // Returns information about the environment 215 | export function Environment(): Promise; 216 | 217 | // [Quit](https://wails.io/docs/reference/runtime/intro#quit) 218 | // Quits the application. 219 | export function Quit(): void; 220 | 221 | // [Hide](https://wails.io/docs/reference/runtime/intro#hide) 222 | // Hides the application. 223 | export function Hide(): void; 224 | 225 | // [Show](https://wails.io/docs/reference/runtime/intro#show) 226 | // Shows the application. 227 | export function Show(): void; 228 | 229 | // [ClipboardGetText](https://wails.io/docs/reference/runtime/clipboard#clipboardgettext) 230 | // Returns the current text stored on clipboard 231 | export function ClipboardGetText(): Promise; 232 | 233 | // [ClipboardSetText](https://wails.io/docs/reference/runtime/clipboard#clipboardsettext) 234 | // Sets a text on the clipboard 235 | export function ClipboardSetText(text: string): Promise; 236 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 2 | github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= 3 | github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= 4 | github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= 5 | github.com/bep/debounce v1.2.1 h1:v67fRdBA9UQu2NhLFXrSg0Brw7CexQekrBwDMM8bzeY= 6 | github.com/bep/debounce v1.2.1/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3ISbCJ0= 7 | github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= 8 | github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s= 9 | github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= 10 | github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= 11 | github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 12 | github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= 13 | github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams= 14 | github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= 15 | github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 16 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 17 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 18 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 19 | github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= 20 | github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= 21 | github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= 22 | github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= 23 | github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= 24 | github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= 25 | github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= 26 | github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= 27 | github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= 28 | github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= 29 | github.com/go-home-admin/home v0.5.18 h1:5KHD5bmGjuw5eiSfkr7U0Zmbnnf7cnOnysF0Zmd5Jmc= 30 | github.com/go-home-admin/home v0.5.18/go.mod h1:Nka+2wKuKlu3xY5NKOZJw7g4RZCsioVT4KnVrO76MOY= 31 | github.com/go-mysql-org/go-mysql v1.9.0 h1:2YniuBkyD+Ll8HWfZcaJ3JtibUohZTjwbb27ZWhYdOA= 32 | github.com/go-mysql-org/go-mysql v1.9.0/go.mod h1:+SgFgTlqjqOQoMc98n9oyUWEgn2KkOL1VmXDoq2ONOs= 33 | github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= 34 | github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= 35 | github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= 36 | github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= 37 | github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= 38 | github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= 39 | github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= 40 | github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= 41 | github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js= 42 | github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= 43 | github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI= 44 | github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= 45 | github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= 46 | github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI= 47 | github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= 48 | github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= 49 | github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= 50 | github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= 51 | github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= 52 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 53 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 54 | github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= 55 | github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 56 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 57 | github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= 58 | github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 59 | github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e h1:Q3+PugElBCf4PFpxhErSzU3/PY5sFL5Z6rfv4AbGAck= 60 | github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e/go.mod h1:alcuEEnZsY1WQsagKhZDsoPCRoOijYqhZvPwLG0kzVs= 61 | github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= 62 | github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= 63 | github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= 64 | github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= 65 | github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= 66 | github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg= 67 | github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= 68 | github.com/jonboulle/clockwork v0.3.0 h1:9BSCMi8C+0qdApAp4auwX0RkLGUjs956h0EkuQymUhg= 69 | github.com/jonboulle/clockwork v0.3.0/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= 70 | github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= 71 | github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= 72 | github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU= 73 | github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= 74 | github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= 75 | github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk= 76 | github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= 77 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 78 | github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= 79 | github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= 80 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 81 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 82 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 83 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 84 | github.com/labstack/echo/v4 v4.10.2 h1:n1jAhnq/elIFTHr1EYpiYtyKgx4RW9ccVgkqByZaN2M= 85 | github.com/labstack/echo/v4 v4.10.2/go.mod h1:OEyqf2//K1DFdE57vw2DRgWY0M7s65IVQO2FzvI4J5k= 86 | github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8= 87 | github.com/labstack/gommon v0.4.0/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM= 88 | github.com/leaanthony/debme v1.2.1 h1:9Tgwf+kjcrbMQ4WnPcEIUcQuIZYqdWftzZkBr+i/oOc= 89 | github.com/leaanthony/debme v1.2.1/go.mod h1:3V+sCm5tYAgQymvSOfYQ5Xx2JCr+OXiD9Jkw3otUjiA= 90 | github.com/leaanthony/go-ansi-parser v1.6.0 h1:T8TuMhFB6TUMIUm0oRrSbgJudTFw9csT3ZK09w0t4Pg= 91 | github.com/leaanthony/go-ansi-parser v1.6.0/go.mod h1:+vva/2y4alzVmmIEpk9QDhA7vLC5zKDTRwfZGOp3IWU= 92 | github.com/leaanthony/gosod v1.0.3 h1:Fnt+/B6NjQOVuCWOKYRREZnjGyvg+mEhd1nkkA04aTQ= 93 | github.com/leaanthony/gosod v1.0.3/go.mod h1:BJ2J+oHsQIyIQpnLPjnqFGTMnOZXDbvWtRCSG7jGxs4= 94 | github.com/leaanthony/slicer v1.5.0/go.mod h1:FwrApmf8gOrpzEWM2J/9Lh79tyq8KTX5AzRtwV7m4AY= 95 | github.com/leaanthony/slicer v1.6.0 h1:1RFP5uiPJvT93TAHi+ipd3NACobkW53yUiBqZheE/Js= 96 | github.com/leaanthony/slicer v1.6.0/go.mod h1:o/Iz29g7LN0GqH3aMjWAe90381nyZlDNquK+mtH2Fj8= 97 | github.com/leaanthony/u v1.1.0 h1:2n0d2BwPVXSUq5yhe8lJPHdxevE2qK5G99PMStMZMaI= 98 | github.com/leaanthony/u v1.1.0/go.mod h1:9+o6hejoRljvZ3BzdYlVL0JYCwtnAsVuN9pVTQcaRfI= 99 | github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= 100 | github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= 101 | github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc h1:RKf14vYWi2ttpEmkA4aQ3j4u9dStX2t4M8UM6qqNsG8= 102 | github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc/go.mod h1:kopuH9ugFRkIXf3YoqHKyrJ9YfUFsckUU9S7B+XP+is= 103 | github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible h1:Y6sqxHMyB1D2YSzWkLibYKgg+SwmyFU9dF2hn6MdTj4= 104 | github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible/go.mod h1:ZQnN8lSECaebrkQytbHj4xNgtg8CR7RYXnPok8e0EHA= 105 | github.com/lestrrat-go/strftime v1.0.6 h1:CFGsDEt1pOpFNU+TJB0nhz9jl+K0hZSLE205AhTIGQQ= 106 | github.com/lestrrat-go/strftime v1.0.6/go.mod h1:f7jQKgV5nnJpYgdEasS+/y7EsTb8ykN2z68n3TtcTaw= 107 | github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE= 108 | github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU= 109 | github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= 110 | github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= 111 | github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= 112 | github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= 113 | github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= 114 | github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= 115 | github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 116 | github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= 117 | github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= 118 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 119 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= 120 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 121 | github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= 122 | github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= 123 | github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= 124 | github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= 125 | github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= 126 | github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= 127 | github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE= 128 | github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs= 129 | github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= 130 | github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= 131 | github.com/pingcap/errors v0.11.0/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= 132 | github.com/pingcap/errors v0.11.5-0.20221009092201-b66cddb77c32 h1:m5ZsBa5o/0CkzZXfXLaThzKuR85SnHHetqBCpzQ30h8= 133 | github.com/pingcap/errors v0.11.5-0.20221009092201-b66cddb77c32/go.mod h1:X2r9ueLEUZgtx2cIogM0v4Zj5uvvzhuuiu7Pn8HzMPg= 134 | github.com/pingcap/log v1.1.1-0.20230317032135-a0d097d16e22 h1:2SOzvGvE8beiC1Y4g9Onkvu6UmuBBOeWRGQEjJaT/JY= 135 | github.com/pingcap/log v1.1.1-0.20230317032135-a0d097d16e22/go.mod h1:DWQW5jICDR7UJh4HtxXSM20Churx4CQL0fwL/SoOSA4= 136 | github.com/pingcap/tidb/pkg/parser v0.0.0-20231103042308-035ad5ccbe67 h1:m0RZ583HjzG3NweDi4xAcK54NBBPJh+zXp5Fp60dHtw= 137 | github.com/pingcap/tidb/pkg/parser v0.0.0-20231103042308-035ad5ccbe67/go.mod h1:yRkiqLFwIqibYg2P7h4bclHjHcJiIFRLKhGRyBcKYus= 138 | github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU= 139 | github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= 140 | github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= 141 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 142 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 143 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 144 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 145 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 146 | github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= 147 | github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= 148 | github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= 149 | github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= 150 | github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= 151 | github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= 152 | github.com/samber/lo v1.38.1 h1:j2XEAqXKb09Am4ebOg31SpvzUTTs6EN3VfgeLUhPdXM= 153 | github.com/samber/lo v1.38.1/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA= 154 | github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= 155 | github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= 156 | github.com/siddontang/go v0.0.0-20180604090527-bdc77568d726 h1:xT+JlYxNGqyT+XcU8iUrN18JYed2TvG9yN5ULG2jATM= 157 | github.com/siddontang/go v0.0.0-20180604090527-bdc77568d726/go.mod h1:3yhqj7WBBfRhbBlzyOC3gUxftwsU0u8gqevxwIHQpMw= 158 | github.com/siddontang/go-log v0.0.0-20180807004314-8d05993dda07 h1:oI+RNwuC9jF2g2lP0u0cVEEZrc/AYBCuFdvwrLWM/6Q= 159 | github.com/siddontang/go-log v0.0.0-20180807004314-8d05993dda07/go.mod h1:yFdBgwXP24JziuRl2NMUahT7nGLNOKi1SIiFxMttVD4= 160 | github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= 161 | github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= 162 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 163 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 164 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 165 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 166 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 167 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 168 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 169 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 170 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 171 | github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 172 | github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 173 | github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= 174 | github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 175 | github.com/tkrajina/go-reflector v0.5.6 h1:hKQ0gyocG7vgMD2M3dRlYN6WBBOmdoOzJ6njQSepKdE= 176 | github.com/tkrajina/go-reflector v0.5.6/go.mod h1:ECbqLgccecY5kPmPmXg1MrHW585yMcDkVl6IvJe64T4= 177 | github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= 178 | github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= 179 | github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= 180 | github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= 181 | github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= 182 | github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= 183 | github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= 184 | github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= 185 | github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= 186 | github.com/wailsapp/go-webview2 v1.0.10 h1:PP5Hug6pnQEAhfRzLCoOh2jJaPdrqeRgJKZhyYyDV/w= 187 | github.com/wailsapp/go-webview2 v1.0.10/go.mod h1:Uk2BePfCRzttBBjFrBmqKGJd41P6QIHeV9kTgIeOZNo= 188 | github.com/wailsapp/mimetype v1.4.1 h1:pQN9ycO7uo4vsUUuPeHEYoUkLVkaRntMnHJxVwYhwHs= 189 | github.com/wailsapp/mimetype v1.4.1/go.mod h1:9aV5k31bBOv5z6u+QP8TltzvNGJPmNJD4XlAL3U+j3o= 190 | github.com/wailsapp/wails/v2 v2.8.0 h1:b2NNn99uGPiN6P5bDsnPwOJZWtAOUhNLv7Vl+YxMTr4= 191 | github.com/wailsapp/wails/v2 v2.8.0/go.mod h1:EFUGWkUX3KofO4fmKR/GmsLy3HhPH7NbyOEaMt8lBF0= 192 | go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= 193 | go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= 194 | go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= 195 | go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= 196 | go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= 197 | go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= 198 | go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= 199 | go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= 200 | go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= 201 | go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= 202 | go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= 203 | go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= 204 | go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= 205 | go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= 206 | go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= 207 | golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= 208 | golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k= 209 | golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= 210 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 211 | golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= 212 | golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= 213 | golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= 214 | golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= 215 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 216 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 217 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 218 | golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 219 | golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= 220 | golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= 221 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 222 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 223 | golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 224 | golang.org/x/sys v0.0.0-20200810151505-1b9f1253b3ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 225 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 226 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 227 | golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 228 | golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 229 | golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 230 | golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 231 | golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 232 | golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 233 | golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 234 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 235 | golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= 236 | golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 237 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 238 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 239 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 240 | golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= 241 | golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= 242 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 243 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 244 | golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 245 | golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 246 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 247 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 248 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 249 | google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= 250 | google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= 251 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 252 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= 253 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 254 | gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= 255 | gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= 256 | gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= 257 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= 258 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= 259 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 260 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 261 | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= 262 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 263 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 264 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 265 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 266 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 267 | gorm.io/driver/mysql v1.3.2 h1:QJryWiqQ91EvZ0jZL48NOpdlPdMjdip1hQ8bTgo4H7I= 268 | gorm.io/driver/mysql v1.3.2/go.mod h1:ChK6AHbHgDCFZyJp0F+BmVGb06PSIoh9uVYKAlRbb2U= 269 | gorm.io/driver/sqlite v1.5.6 h1:fO/X46qn5NUEEOZtnjJRWRzZMe8nqJiQ9E+0hi+hKQE= 270 | gorm.io/driver/sqlite v1.5.6/go.mod h1:U+J8craQU6Fzkcvu8oLeAQmi50TkwPEhHDEjQZXDah4= 271 | gorm.io/gorm v1.23.1/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk= 272 | gorm.io/gorm v1.25.11 h1:/Wfyg1B/je1hnDx3sMkX+gAlxrlZpn6X0BXRlwXlvHg= 273 | gorm.io/gorm v1.25.11/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ= 274 | rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= 275 | --------------------------------------------------------------------------------