├── myModel
├── Version.go
├── 自动更新模块_test.go
└── 自动更新模块.go
├── .github
├── releasesText.md
└── workflows
│ └── publish.yml
├── assets
└── image-20240807232923351.png
├── frontend
├── src
│ ├── assets
│ │ └── main.css
│ ├── components
│ │ ├── boxs
│ │ │ ├── td
│ │ │ │ ├── Progress
│ │ │ │ │ └── Progress.vue
│ │ │ │ ├── Switch
│ │ │ │ │ └── Switch.vue
│ │ │ │ ├── Button
│ │ │ │ │ └── Button.vue
│ │ │ │ ├── Link
│ │ │ │ │ └── Link.vue
│ │ │ │ └── Select
│ │ │ │ │ └── Select.vue
│ │ │ └── el
│ │ │ │ ├── Switch
│ │ │ │ └── Switch.vue
│ │ │ │ ├── Tooltip
│ │ │ │ └── Tooltip.vue
│ │ │ │ ├── Button
│ │ │ │ └── Button.vue
│ │ │ │ ├── ProgressBar
│ │ │ │ └── ProgressBar.vue
│ │ │ │ ├── FlexLayout
│ │ │ │ └── FlexLayout.vue
│ │ │ │ ├── TextEdit
│ │ │ │ └── TextEdit.vue
│ │ │ │ ├── RadioButton
│ │ │ │ └── RadioButton.vue
│ │ │ │ ├── CheckBox
│ │ │ │ └── CheckBox.vue
│ │ │ │ ├── Tabs
│ │ │ │ └── Tabs.vue
│ │ │ │ ├── Table
│ │ │ │ └── Table.vue
│ │ │ │ ├── Tree
│ │ │ │ └── Tree.vue
│ │ │ │ ├── ControlButton
│ │ │ │ └── ControlButton.vue
│ │ │ │ ├── Menu
│ │ │ │ └── Menu.vue
│ │ │ │ ├── Label
│ │ │ │ └── Label.vue
│ │ │ │ ├── CustomComponent
│ │ │ │ └── CustomComponent.vue
│ │ │ │ ├── TabsTW
│ │ │ │ └── TabsTW.vue
│ │ │ │ └── CommonLayout
│ │ │ │ └── CommonLayout.vue
│ │ └── RenderDesignComponentPreview.vue
│ ├── App.vue
│ ├── main.js
│ ├── Helper.js
│ ├── win
│ │ ├── __aux_code.js
│ │ ├── __load_data.js
│ │ ├── event.js
│ │ └── design.json
│ └── public.js
├── jsconfig.json
├── index.html
├── README.md
├── wailsjs
│ └── go
│ │ └── main
│ │ ├── App.d.ts
│ │ └── App.js
├── package.json
└── vite.config.js
├── template.json
├── wails.json
├── README.md
├── .gitignore
├── action
├── config.json
├── run_override_test.go
└── run_override.go
├── main.go
├── README.zh-Hans.md
├── app.go
├── go.mod
└── go.sum
/myModel/Version.go:
--------------------------------------------------------------------------------
1 | package myModel
2 |
3 | var Version = `v0.0.1`
4 |
--------------------------------------------------------------------------------
/.github/releasesText.md:
--------------------------------------------------------------------------------
1 | # overrideManager
2 |
3 | 奋斗了{{用了多少时间}},本次更新内容如下:
4 |
5 | {{最新发布信息}}
6 |
7 | {{变更内容}}
--------------------------------------------------------------------------------
/assets/image-20240807232923351.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/duolabmeng6/overrideManager/HEAD/assets/image-20240807232923351.png
--------------------------------------------------------------------------------
/frontend/src/assets/main.css:
--------------------------------------------------------------------------------
1 | html,
2 | body {
3 | margin: 0;
4 | padding: 0;
5 | }
6 | * {
7 | -webkit-user-select: none;
8 | }
--------------------------------------------------------------------------------
/frontend/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "baseUrl": ".",
4 | "paths": {
5 | "@/*": ["src/*"]
6 | }
7 | },
8 | "include": ["src/**/*"]
9 | }
10 |
--------------------------------------------------------------------------------
/template.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "wails-template-vue-go-easy",
3 | "shortname": "vue",
4 | "author": "duolabmeng6 <1715109585@qq.com>",
5 | "description": "Window visualization project",
6 | "helpurl": "https://github.com/duolabmeng6/wails-template-vue-go-easy"
7 | }
--------------------------------------------------------------------------------
/wails.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://wails.io/schemas/config.v2.json",
3 | "name": "overrideManager",
4 | "outputfilename": "overrideManager",
5 | "frontend:install": "npm install",
6 | "frontend:build": "npm run build",
7 | "frontend:dev:watcher": "npm run dev",
8 | "frontend:dev:serverUrl": "auto",
9 | "author": {
10 | "name": "“xxxxxxx”",
11 | "email": "xxxxxxx@qq.com"
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/frontend/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | myproject
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/frontend/README.md:
--------------------------------------------------------------------------------
1 | # Vue 3 + Vite
2 |
3 | This template should help get you started developing with Vue 3 in Vite. The template uses Vue 3 `
20 |
--------------------------------------------------------------------------------
/frontend/src/components/boxs/el/Switch/Switch.vue:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
12 |
13 |
26 |
--------------------------------------------------------------------------------
/frontend/src/components/boxs/el/Tooltip/Tooltip.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
12 |
13 |
14 |
15 |
28 |
--------------------------------------------------------------------------------
/frontend/src/components/boxs/el/Button/Button.vue:
--------------------------------------------------------------------------------
1 |
2 | {{ item.text }}
9 |
10 |
11 |
12 |
13 |
26 |
--------------------------------------------------------------------------------
/frontend/src/components/boxs/el/ProgressBar/ProgressBar.vue:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
13 |
14 |
27 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | build/bin
2 | node_modules
3 | frontend/dist
4 | .DS_Store
5 |
6 | # Logs
7 | logs
8 | *.log
9 | npm-debug.log*
10 | yarn-debug.log*
11 | yarn-error.log*
12 | pnpm-debug.log*
13 | lerna-debug.log*
14 |
15 | node_modules
16 | .DS_Store
17 | dist
18 | dist-ssr
19 | coverage
20 | *.local
21 |
22 | /cypress/videos/
23 | /cypress/screenshots/
24 |
25 | # Editor directories and files
26 | .vscode/*
27 | !.vscode/extensions.json
28 | .idea
29 | *.suo
30 | *.ntvs*
31 | *.njsproj
32 | *.sln
33 | *.sw?
34 | .vscode
35 | *.lockb
36 | package-lock.json
37 | package.json.md5
38 | /frontend/package.json.md5
39 | /frontend/package-lock.json
40 | frontend/wailsjs/runtime/*
41 | *.lockb
--------------------------------------------------------------------------------
/frontend/src/components/boxs/td/Switch/Switch.vue:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 |
12 |
29 |
--------------------------------------------------------------------------------
/frontend/src/components/boxs/td/Button/Button.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{ item.text }}
14 |
15 |
16 |
17 |
18 |
31 |
--------------------------------------------------------------------------------
/action/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "bind": "0.0.0.0:8181",
3 | "proxy_url": "",
4 | "timeout": 600,
5 | "codex_api_base": "http://127.0.0.1:5001/v1",
6 | "codex_api_key": "sk-6a05efdddb81434f9cb1fff4ee83fa04",
7 | "codex_api_organization": "",
8 | "codex_api_project": "",
9 | "codex_max_tokens": 500,
10 | "code_instruct_model": "deepseek-coder",
11 | "chat_api_base": "https://api.deepseek.com/v1",
12 | "chat_api_key": "sk-6a05efdddb81434f9cb1fff4ee83fa04",
13 | "chat_api_organization": "",
14 | "chat_api_project": "",
15 | "chat_max_tokens": 4096,
16 | "chat_model_default": "deepseek-chat",
17 | "chat_model_map": {
18 | },
19 | "chat_locale": "zh_CN",
20 | "auth_token": ""
21 | }
--------------------------------------------------------------------------------
/frontend/src/components/boxs/td/Link/Link.vue:
--------------------------------------------------------------------------------
1 |
2 |
15 |
16 |
17 |
18 |
31 |
--------------------------------------------------------------------------------
/frontend/src/components/boxs/el/FlexLayout/FlexLayout.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
11 |
12 |
13 |
14 |
15 |
16 |
29 |
--------------------------------------------------------------------------------
/frontend/src/components/boxs/el/TextEdit/TextEdit.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
12 |
13 |
14 |
15 |
28 |
29 |
36 |
--------------------------------------------------------------------------------
/frontend/src/components/boxs/td/Select/Select.vue:
--------------------------------------------------------------------------------
1 |
2 |
10 |
17 |
18 |
19 |
20 |
21 |
22 |
34 |
--------------------------------------------------------------------------------
/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "embed"
5 |
6 | "github.com/wailsapp/wails/v2"
7 | "github.com/wailsapp/wails/v2/pkg/options"
8 | "github.com/wailsapp/wails/v2/pkg/options/assetserver"
9 | )
10 |
11 | //go:embed all:frontend/dist
12 | var assets embed.FS
13 |
14 | func main() {
15 | // Create an instance of the app structure
16 | app := NewApp()
17 |
18 | // Create application with options
19 | err := wails.Run(&options.App{
20 | Title: "myproject",
21 | Width: 1024,
22 | Height: 768,
23 | StartHidden: true,
24 | AssetServer: &assetserver.Options{
25 | Assets: assets,
26 | },
27 | BackgroundColour: &options.RGBA{R: 255, G: 255, B: 255, A: 1},
28 | OnStartup: app.startup,
29 | Bind: []interface{}{
30 | app,
31 | },
32 | })
33 |
34 | if err != nil {
35 | println("Error:", err.Error())
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/frontend/wailsjs/go/main/App.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 E停止服务器() {
6 | return window['go']['main']['App']['E停止服务器']();
7 | }
8 |
9 | export function E写到文件(arg1, arg2) {
10 | return window['go']['main']['App']['E写到文件'](arg1, arg2);
11 | }
12 |
13 | export function E取运行目录() {
14 | return window['go']['main']['App']['E取运行目录']();
15 | }
16 |
17 | export function E启动服务器(arg1) {
18 | return window['go']['main']['App']['E启动服务器'](arg1);
19 | }
20 |
21 | export function E检查更新() {
22 | return window['go']['main']['App']['E检查更新']();
23 | }
24 |
25 | export function E读入文本(arg1) {
26 | return window['go']['main']['App']['E读入文本'](arg1);
27 | }
28 |
29 | export function GetVersion() {
30 | return window['go']['main']['App']['GetVersion']();
31 | }
32 |
--------------------------------------------------------------------------------
/frontend/src/components/boxs/el/RadioButton/RadioButton.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {{ v.label }}
11 |
12 |
13 |
14 | {{ v.label }}
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
42 |
--------------------------------------------------------------------------------
/frontend/src/components/boxs/el/CheckBox/CheckBox.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {{ v.label }}
11 |
12 |
13 |
14 | {{ v.label }}
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
42 |
--------------------------------------------------------------------------------
/frontend/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "eview",
3 | "version": "0.0.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "vite",
7 | "build": "vite build",
8 | "preview": "vite preview"
9 | },
10 | "dependencies": {
11 | "@guolao/vue-monaco-editor": "1.3.0",
12 | "@vitejs/plugin-vue-jsx": "^3.0.2",
13 | "element-plus": "^2.3.14",
14 | "monaco-editor": "0.38.0",
15 | "pinia": "^2.1.6",
16 | "pinyin-pro": "^3.16.3",
17 | "tdesign-icons-vue-next": "^0.2.2",
18 | "tdesign-vue-next": "^1.6.5",
19 | "uuid": "^9.0.1",
20 | "vue": "^3.3.4",
21 | "vue-i18n": "^9.6.1",
22 | "vue3-sfc-loader": "^0.8.4"
23 | },
24 | "devDependencies": {
25 | "@iconify/vue": "^4.1.1",
26 | "@vitejs/plugin-vue": "^4.2.3",
27 | "monaco-editor-webpack-plugin": "^7.1.0",
28 | "unplugin-auto-import": "^0.16.6",
29 | "unplugin-icons": "^0.16.6",
30 | "unplugin-vue-components": "^0.25.1",
31 | "vite": "^4.4.11"
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/frontend/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
10 |
11 |
12 |
42 |
--------------------------------------------------------------------------------
/frontend/src/main.js:
--------------------------------------------------------------------------------
1 | import {createApp} from 'vue'
2 | import {createPinia} from 'pinia'
3 | // import ElementPlus from 'element-plus'
4 | import 'element-plus/dist/index.css'
5 | // import TDesign from 'tdesign-vue-next'
6 | import 'tdesign-vue-next/es/style/index.css'
7 |
8 | import App from './App.vue'
9 | import Helper from "./Helper.js"
10 |
11 |
12 | import RenderDesignComponentPreview from "./components/RenderDesignComponentPreview.vue"
13 |
14 | const app = createApp(App)
15 |
16 | app.component('RenderDesignComponent', RenderDesignComponentPreview)
17 |
18 |
19 | const BoxComponentNames_el = Helper.registerBoxComponentNames(app, 'el', import.meta.glob('./components/boxs/el/**/*.vue', {eager: true}))
20 | const BoxComponentNames_td = Helper.registerBoxComponentNames(app, 'td', import.meta.glob('./components/boxs/td/**/*.vue', {eager: true}))
21 |
22 |
23 |
24 | app.use(createPinia())
25 | // app.use(TDesign)
26 | // app.use(ElementPlus)
27 | app.config.warnHandler = function (msg, vm, trace) {
28 | // 自定义处理警告的逻辑,或者什么都不做以屏蔽
29 | };
30 | app.mount('#app')
31 |
32 |
--------------------------------------------------------------------------------
/frontend/src/components/boxs/el/Tabs/Tabs.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
33 |
--------------------------------------------------------------------------------
/frontend/src/components/boxs/el/Table/Table.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
12 |
13 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/frontend/src/components/boxs/el/Tree/Tree.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
14 |
15 |
16 |
17 |
49 |
--------------------------------------------------------------------------------
/README.zh-Hans.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | [English](README.md) · [简体中文](README.zh-Hans.md)
13 |
14 |
15 |
16 |
17 |
18 | # 窗口设计师 GoEasyDesigner 的演示项目
19 |
20 | 用于运行窗口设计师设计好界面和程序
21 |
22 |
23 | 
24 |
25 |
26 | 创建项目
27 |
28 | ```
29 | wails init -n "go-easy-demo" -t https://github.com/duolabmeng6/wails-template-vue-go-easy
30 | ```
31 |
32 | 运行窗口
33 |
34 | ```
35 | cd go-easy-demo
36 | wails dev
37 | ```
38 |
39 |
40 | [下载 GoEasyDesigner 窗口设计师](https://github.com/duolabmeng6/GoEasyDesigner/releases)
41 |
42 | [ GoEasyDesigner 项目地址](https://github.com/duolabmeng6/GoEasyDesigner)
43 |
44 | # 在线体验窗口设计师
45 |
46 | 网页端仅提供基本的界面设计,如需更好编码体验,请下载客户端.
47 |
48 | 国内地址: https://go.kenhong.com/
49 |
50 | 国外地址: https://g.yx24.me
51 |
52 | 画好界面点保存,会下载2个文件 `design.json`,`__aux_code.js` 注意你的浏览器允许下载多个文件的权限
53 |
54 | 下载本项目的代码 复制 `go-easy-demo` 文件夹作为开发项目
55 |
56 | `go-easy-demo/frontend/src/win/design.json`
57 |
58 | `go-easy-demo/frontend/src/win/__aux_code.js`
59 |
60 | 运行项目 就可以看到你设计好的界面了.
61 |
62 | ```
63 | cd go-easy-demo
64 | wails dev
65 | ```
66 |
67 |
68 |
--------------------------------------------------------------------------------
/frontend/src/Helper.js:
--------------------------------------------------------------------------------
1 | function registerBoxComponentNames(app, uiName, meta) {
2 | let ComponentNames = []
3 | const componentsContext = meta;
4 |
5 | const keys = Object.keys(componentsContext).map((path) => {
6 | const name = path.split('/').pop().replace(/\.\w+$/, '');
7 | const module = componentsContext[path];
8 | //如果名称后面是 属性 或者是 Attr 就不要加入
9 | if (!(name.endsWith("属性") || name.endsWith("Attr"))) {
10 | ComponentNames.push(name)
11 | }
12 | // console.log("注册组件", uiName, name)
13 | app.component(uiName + name, module.default);
14 | // app.component(name, module.default);
15 | return name;
16 | });
17 |
18 | return ComponentNames;
19 |
20 | }
21 |
22 | function registerBoxComponentDefaultValue(app, uiName, meta) {
23 | let ComponentDefaultValue = {}
24 | const componentsContext = meta;
25 |
26 | const keys = Object.keys(componentsContext).map((path) => {
27 | const name = path.split('/').pop().replace(/\.\w+$/, '');
28 | const module = componentsContext[path];
29 | ComponentDefaultValue[name] = module.default;
30 | return name;
31 | });
32 |
33 | return ComponentDefaultValue;
34 | }
35 |
36 | export default {
37 | registerBoxComponentNames,
38 | registerBoxComponentDefaultValue
39 | }
--------------------------------------------------------------------------------
/action/run_override_test.go:
--------------------------------------------------------------------------------
1 | package action
2 |
3 | import (
4 | "bytes"
5 | "fmt"
6 | "github.com/duolabmeng6/goefun/ecore"
7 | "sync"
8 | "testing"
9 | "time"
10 | )
11 |
12 | func Test_run_override(t *testing.T) {
13 | var logBuffer bytes.Buffer
14 | // 输出捕获的日志
15 | var mu sync.Mutex
16 |
17 | configContent := ecore.E读入文本("/Users/ll/Desktop/2024/override2ui/action/config.json")
18 | //_, _ := Run_override(configContent)
19 | Run_override(configContent, &logBuffer)
20 | // 使用time.Ticker定期检查logBuffer
21 | ticker := time.NewTicker(100 * time.Millisecond)
22 | defer ticker.Stop()
23 |
24 | // 使用goroutine异步处理日志输出
25 | go func() {
26 | for range ticker.C {
27 | mu.Lock() // 锁定缓冲区,避免竞态条件
28 | if logBuffer.Len() > 0 {
29 | fmt.Print(logBuffer.String()) // 使用fmt.Print而不是println
30 | logBuffer.Reset()
31 | }
32 | mu.Unlock() // 解锁缓冲区
33 | }
34 | }()
35 |
36 | //println(ok, err.Error())
37 | //time.Sleep(time.Second * 1)
38 | //eh := ehttp.NewHttp()
39 | //text, err := eh.Get("http://127.0.0.1:8181/models")
40 | //println(text, err)
41 | //time.Sleep(time.Second * 2)
42 | //Stop_override()
43 | //
44 | //time.Sleep(time.Second * 3)
45 | //ok, err = Run_override(configContent)
46 | //println(ok, err)
47 | //time.Sleep(time.Second * 1)
48 | //text, err = eh.Get("http://127.0.0.1:8181/models")
49 | //println(text, err)
50 | time.Sleep(time.Second * 30)
51 | //Stop_override()
52 |
53 | }
54 |
--------------------------------------------------------------------------------
/frontend/src/components/boxs/el/ControlButton/ControlButton.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
11 |
14 |
15 |
16 |
17 |
43 |
--------------------------------------------------------------------------------
/frontend/src/win/__aux_code.js:
--------------------------------------------------------------------------------
1 |
2 | import designData from './design.json';
3 |
4 | function __aux_code() {
5 | return {
6 | Win : designData[0],
7 | 选择夹3 : designData[0].childComponents[0],
8 | ContentArea3 : designData[0].childComponents[0].childComponents[0],
9 | 编辑框1 : designData[0].childComponents[0].childComponents[0].childComponents[0],
10 | 按钮_启动服务 : designData[0].childComponents[0].childComponents[0].childComponents[1],
11 | 标签1 : designData[0].childComponents[0].childComponents[0].childComponents[2],
12 | 按钮_重置配置 : designData[0].childComponents[0].childComponents[0].childComponents[3],
13 | 按钮_保存配置 : designData[0].childComponents[0].childComponents[0].childComponents[4],
14 | ContentArea4 : designData[0].childComponents[0].childComponents[1],
15 | 按钮_检查更新 : designData[0].childComponents[0].childComponents[1].childComponents[0],
16 | ContentArea5 : designData[0].childComponents[0].childComponents[2],
17 | 编辑框2 : designData[0].childComponents[0].childComponents[2].childComponents[0],
18 | 标签2 : designData[0].childComponents[0].childComponents[2].childComponents[1],
19 | 标签3 : designData[0].childComponents[0].childComponents[2].childComponents[2],
20 | 按钮1 : designData[0].childComponents[0].childComponents[2].childComponents[3],
21 | ContentArea1 : designData[0].childComponents[0].childComponents[3],
22 | 编辑框3 : designData[0].childComponents[0].childComponents[3].childComponents[0],
23 |
24 | }
25 | }
26 | export default __aux_code
27 |
--------------------------------------------------------------------------------
/frontend/src/components/boxs/el/Menu/Menu.vue:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 | {{ menu.label }}
11 |
12 |
13 | {{ menu.label }}
14 |
15 |
16 | {{ menu.label }}
17 |
18 |
19 | {{ child.label }}
20 |
21 | {{ grandChild.label }}
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
43 |
--------------------------------------------------------------------------------
/frontend/src/components/boxs/el/Label/Label.vue:
--------------------------------------------------------------------------------
1 |
2 |
6 | {{ item.text}}
13 |
14 |
15 |
16 |
17 |
18 |
31 |
--------------------------------------------------------------------------------
/frontend/src/components/boxs/el/CustomComponent/CustomComponent.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "bytes"
5 | "changeme/action"
6 | "changeme/myModel"
7 | "context"
8 | "fmt"
9 | "github.com/duolabmeng6/goefun/ecore"
10 | "github.com/duolabmeng6/goefun/etool"
11 | "github.com/wailsapp/wails/v2/pkg/runtime"
12 | "sync"
13 | "time"
14 | )
15 |
16 | // App struct
17 | type App struct {
18 | ctx context.Context
19 | mu sync.Mutex
20 | ticker *time.Ticker
21 | logBuffer bytes.Buffer
22 | }
23 |
24 | // NewApp creates a new App application struct
25 | func NewApp() *App {
26 | return &App{}
27 | }
28 |
29 | // startup is called when the app starts. The context is saved
30 | // so we can call the runtime methods
31 | func (a *App) startup(ctx context.Context) {
32 | a.ctx = ctx
33 | a.ticker = time.NewTicker(1000 * time.Millisecond)
34 | //a.logBuffer = bytes.Buffer
35 | go func() {
36 | for range a.ticker.C {
37 | fmt.Println("ticker")
38 | a.mu.Lock() // 锁定缓冲区,避免竞态条件
39 | if a.logBuffer.Len() > 0 {
40 | fmt.Print(a.logBuffer.String()) // 使用fmt.Print而不是println
41 | jsonstring := etool.E到Json(map[string]string{
42 | "logs": a.logBuffer.String(),
43 | })
44 | runtime.EventsEmit(a.ctx, "logs", jsonstring)
45 | a.logBuffer.Reset()
46 | }
47 | a.mu.Unlock() // 解锁缓冲区
48 | }
49 | }()
50 |
51 | }
52 |
53 | func (a *App) GetVersion() string {
54 | println("GetVersion", myModel.Version)
55 | return myModel.Version
56 | }
57 | func (a *App) E检查更新() string {
58 | myModel.E检查更新()
59 | return "ok"
60 | }
61 |
62 | func (a *App) E启动服务器(配置内容 string) string {
63 | _, err := action.Run_override(配置内容, &a.logBuffer)
64 | if err != nil {
65 | return err.Error()
66 | }
67 | //defer a.ticker.Stop()
68 | jsonstring := etool.E到Json(map[string]string{
69 | "logs": "已启动服务器",
70 | })
71 | runtime.EventsEmit(a.ctx, "logs", jsonstring)
72 | return "启动成功"
73 | }
74 |
75 | func (a *App) E停止服务器() string {
76 | action.Stop_override()
77 | return "已停止"
78 | }
79 |
80 | func (a *App) E写到文件(文件路径 string, 写出数据 string) error {
81 | err := ecore.E写到文件(文件路径, []byte(写出数据))
82 | return err
83 | }
84 | func (a *App) E读入文本(文件路径 string) string {
85 | data := ecore.E读入文本(文件路径)
86 | return data
87 | }
88 | func (a *App) E取运行目录() string {
89 | data := ecore.E取运行目录()
90 | return data
91 | }
92 |
--------------------------------------------------------------------------------
/frontend/vite.config.js:
--------------------------------------------------------------------------------
1 | import {fileURLToPath, URL} from 'node:url'
2 |
3 | import {defineConfig} from 'vite'
4 | import vue from '@vitejs/plugin-vue'
5 |
6 | import AutoImport from 'unplugin-auto-import/vite'
7 | import Components from 'unplugin-vue-components/vite'
8 | import {ElementPlusResolver} from 'unplugin-vue-components/resolvers'
9 | import IconsResolver from 'unplugin-icons/resolver'
10 | import vueJsx from '@vitejs/plugin-vue-jsx'
11 |
12 | import { TDesignResolver } from 'unplugin-vue-components/resolvers';
13 |
14 | // https://vitejs.dev/config/
15 | export default defineConfig({
16 | server: {
17 | host: "0.0.0.0",
18 | hmr: {
19 | host: 'localhost',
20 | protocol: 'ws',
21 | }
22 | },
23 | publicDir: "public",
24 | plugins: [
25 | vue(),
26 | vueJsx(),
27 | AutoImport({
28 | imports: ['vue'],
29 | resolvers: [
30 | ElementPlusResolver(),
31 |
32 | // Auto import icon components
33 | // 自动导入图标组件
34 | IconsResolver({
35 | prefix: 'Icon',
36 | }),
37 | TDesignResolver({
38 | library: 'vue-next'
39 | })
40 | ],
41 | }),
42 | Components({
43 | resolvers: [
44 | // Auto register icon components
45 | // 自动注册图标组件
46 | IconsResolver({
47 | enabledCollections: ['ep'],
48 | }),
49 | // Auto register Element Plus components
50 | // 自动导入 Element Plus 组件
51 | ElementPlusResolver(),
52 | TDesignResolver({
53 | library: 'vue-next'
54 | })
55 | ],
56 | }),
57 |
58 | ],
59 | resolve: {
60 | alias: {
61 | '@': fileURLToPath(new URL('./src', import.meta.url))
62 | }
63 | },
64 | esbuild: {
65 | jsxFactory: 'h',
66 | jsxFragment: 'Fragment',
67 | // jsxInject: `import React from 'react'`,
68 | },
69 | optimizeDeps: {
70 | include: [
71 | './src/win/design.json',
72 | './src/win/__load_data.js',
73 | './src/win/__aux_code.js',
74 | './src/win/event.js',
75 | ]
76 | }
77 | })
78 |
--------------------------------------------------------------------------------
/frontend/src/win/__load_data.js:
--------------------------------------------------------------------------------
1 | import {ref} from 'vue'
2 | import {defineStore} from 'pinia'
3 | import * as systemFc from "../../wailsjs/runtime/runtime"; // 根据实际文件路径进行修改
4 | import {BindWindowEvent} from './event'
5 | import __aux_code from "./__aux_code";
6 | import designData from './design.json';
7 |
8 | export const __load_data = defineStore('window_data', {
9 | state: () => {
10 | return {
11 | list : ref(designData),
12 | comps : __aux_code(designData)
13 | }
14 | },
15 | actions: {
16 | async init() {
17 |
18 | BindWindowEvent(this, this.comps)
19 | try {
20 | if (this.comps.Win.hasOwnProperty("event_created")) {
21 | await this.WinCreated()
22 | }
23 | } catch (e) {
24 | console.log("WinCreated To be defined")
25 | }
26 |
27 | const dthis = this
28 |
29 | try {
30 | setTimeout(function () {
31 | systemFc.WindowShow()
32 | }, 200)
33 |
34 | if (dthis.comps.Win.width.includes('v') || dthis.comps.Win.width.includes('%')) {
35 | return;
36 | }
37 | if (dthis.comps.Win.height.includes('v') || dthis.comps.Win.height.includes('%')) {
38 | return;
39 | }
40 | systemFc.WindowSetSize(parseInt(dthis.comps.Win.width), parseInt(dthis.comps.Win.height))
41 | //Recalculate the width and height of the client area
42 | setTimeout(function () {
43 | var WidthFix = parseInt(dthis.comps.Win.width) - window.innerWidth
44 | var HeightFix = parseInt(dthis.comps.Win.height) - window.innerHeight
45 | systemFc.WindowSetSize(parseInt(dthis.comps.Win.width) + WidthFix, parseInt(dthis.comps.Win.height) + HeightFix)
46 | document.body.style.overflow = 'auto'
47 | }, 1)
48 | systemFc.WindowSetTitle(dthis.comps.Win.text)
49 | //Move to the center of the screen
50 | systemFc.WindowCenter()
51 | } catch (e) {
52 | console.error("Error initializing win size", e)
53 | }
54 |
55 | },
56 | handleAllEvents(el, e, item, callFuncName) {
57 | try {
58 | var dynamicFunction = undefined
59 | eval(`dynamicFunction = this.${callFuncName}`)
60 | dynamicFunction(e, item)
61 | } catch (e) {
62 | console.log("Function call error", callFuncName, "dynamicFunction", dynamicFunction, "e", e)
63 | }
64 | },
65 |
66 | },
67 | })
--------------------------------------------------------------------------------
/frontend/src/components/boxs/el/TabsTW/TabsTW.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
100 |
101 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module changeme
2 |
3 | go 1.21
4 |
5 | toolchain go1.21.1
6 |
7 | require (
8 | github.com/duolabmeng6/goefun v1.3.8
9 | github.com/gin-gonic/gin v1.9.1
10 | github.com/ncruces/zenity v0.10.13
11 | github.com/tidwall/gjson v1.14.4
12 | github.com/tidwall/sjson v1.1.7
13 | github.com/wailsapp/wails/v2 v2.9.1
14 | golang.org/x/net v0.25.0
15 | )
16 |
17 | require (
18 | github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible // indirect
19 | github.com/akavel/rsrc v0.10.2 // indirect
20 | github.com/axgle/mahonia v0.0.0-20180208002826-3358181d7394 // indirect
21 | github.com/bep/debounce v1.2.1 // indirect
22 | github.com/bytedance/sonic v1.9.1 // indirect
23 | github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
24 | github.com/dchest/jsmin v0.0.0-20220218165748-59f39799265f // indirect
25 | github.com/fsnotify/fsnotify v1.5.4 // indirect
26 | github.com/gabriel-vasile/mimetype v1.4.2 // indirect
27 | github.com/gin-contrib/sse v0.1.0 // indirect
28 | github.com/go-ole/go-ole v1.2.6 // indirect
29 | github.com/go-playground/locales v0.14.1 // indirect
30 | github.com/go-playground/universal-translator v0.18.1 // indirect
31 | github.com/go-playground/validator/v10 v10.14.0 // indirect
32 | github.com/goccy/go-json v0.10.2 // indirect
33 | github.com/godbus/dbus/v5 v5.1.0 // indirect
34 | github.com/gogf/gf/v2 v2.4.1 // indirect
35 | github.com/gogf/guuid v1.0.0 // indirect
36 | github.com/google/uuid v1.3.0 // indirect
37 | github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e // indirect
38 | github.com/joho/godotenv v1.5.1 // indirect
39 | github.com/josephspurrier/goversioninfo v1.4.0 // indirect
40 | github.com/json-iterator/go v1.1.12 // indirect
41 | github.com/klauspost/cpuid/v2 v2.2.4 // indirect
42 | github.com/kr/pretty v0.3.0 // indirect
43 | github.com/kr/text v0.2.0 // indirect
44 | github.com/labstack/echo/v4 v4.10.2 // indirect
45 | github.com/labstack/gommon v0.4.0 // indirect
46 | github.com/leaanthony/go-ansi-parser v1.6.0 // indirect
47 | github.com/leaanthony/gosod v1.0.3 // indirect
48 | github.com/leaanthony/slicer v1.6.0 // indirect
49 | github.com/leaanthony/u v1.1.0 // indirect
50 | github.com/leodido/go-urn v1.2.4 // indirect
51 | github.com/mattn/go-colorable v0.1.13 // indirect
52 | github.com/mattn/go-isatty v0.0.19 // indirect
53 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
54 | github.com/modern-go/reflect2 v1.0.2 // indirect
55 | github.com/pelletier/go-toml/v2 v2.0.8 // indirect
56 | github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect
57 | github.com/pkg/errors v0.9.1 // indirect
58 | github.com/randall77/makefat v0.0.0-20210315173500-7ddd0e42c844 // indirect
59 | github.com/rivo/uniseg v0.4.4 // indirect
60 | github.com/rogpeppe/go-internal v1.8.0 // indirect
61 | github.com/samber/lo v1.38.1 // indirect
62 | github.com/teris-io/shortid v0.0.0-20171029131806-771a37caa5cf // indirect
63 | github.com/tidwall/match v1.1.1 // indirect
64 | github.com/tidwall/pretty v1.2.0 // indirect
65 | github.com/tkrajina/go-reflector v0.5.6 // indirect
66 | github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
67 | github.com/ugorji/go/codec v1.2.11 // indirect
68 | github.com/valyala/bytebufferpool v1.0.0 // indirect
69 | github.com/valyala/fasttemplate v1.2.2 // indirect
70 | github.com/wailsapp/go-webview2 v1.0.10 // indirect
71 | github.com/wailsapp/mimetype v1.4.1 // indirect
72 | go.opentelemetry.io/otel v1.7.0 // indirect
73 | go.opentelemetry.io/otel/trace v1.7.0 // indirect
74 | golang.org/x/arch v0.3.0 // indirect
75 | golang.org/x/crypto v0.23.0 // indirect
76 | golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 // indirect
77 | golang.org/x/image v0.18.0 // indirect
78 | golang.org/x/sys v0.22.0 // indirect
79 | golang.org/x/text v0.16.0 // indirect
80 | google.golang.org/protobuf v1.30.0 // indirect
81 | gopkg.in/ini.v1 v1.67.0 // indirect
82 | gopkg.in/yaml.v3 v3.0.1 // indirect
83 | )
84 |
--------------------------------------------------------------------------------
/myModel/自动更新模块_test.go:
--------------------------------------------------------------------------------
1 | package myModel
2 |
3 | import (
4 | "fmt"
5 | "github.com/duolabmeng6/goefun/ecore"
6 | "github.com/ncruces/zenity"
7 | "os/user"
8 | "path/filepath"
9 | "testing"
10 | )
11 |
12 | func Test简单(t *testing.T) {
13 | E检查更新()
14 | }
15 | func TestE获取Github仓库Releases版本和更新内容(t *testing.T) {
16 | info := E获取Github仓库Releases版本和更新内容()
17 | ecore.E调试输出(info)
18 |
19 | }
20 |
21 | func Test下载(t *testing.T) {
22 | err := E下载带进度回调("https://github.com/duolabmeng6/GoEasyDesigner/releases/download/v0.2.4/GoEasyDesigner_MacOS.zip",
23 | "/Users/ll/Downloads/GoEasyDesigner_MacOS.zip", func(进度 float64) {
24 | // 进度回调函数
25 | fmt.Println("正在下载...", 进度)
26 | })
27 | if err != nil {
28 | fmt.Println("下载出错:", err)
29 | } else {
30 | fmt.Println("下载完成")
31 | }
32 |
33 | }
34 | func Test系统版本(t *testing.T) {
35 | fmt.Println("系统是否为Windows:", E系统是否为window系统())
36 | fmt.Println("系统是否为Linux:", E系统是否为linux系统())
37 | fmt.Println("系统是否为Mac:", E系统是否为mac系统())
38 | fmt.Println("是否为调试模式:", E是否为调试模式())
39 |
40 | }
41 |
42 | func Test解压缩(t *testing.T) {
43 | 压缩包的路径 := "/Users/ll/Downloads/GoEasyDesigner_MacOS.zip"
44 | //解压目录 := "/Users/ll/Desktop/goproject/v3fanyi/mymodel/testzip/"
45 | //允许解压路径前缀 := []string{"qoq.app/Contents/"}
46 | //println(zip解压2(压缩包的路径, 解压目录, 允许解压路径前缀))
47 | E更新自己MacOS应用(压缩包的路径, "GoEasyDesigner.app")
48 | }
49 | func Test更新流程MacOS(t *testing.T) {
50 | usr, err := user.Current()
51 | if err != nil {
52 | panic(err)
53 | }
54 | 下载文件夹路径 := filepath.Join(usr.HomeDir, "Downloads")
55 |
56 | println(下载文件夹路径)
57 | info := E获取Github仓库Releases版本和更新内容()
58 |
59 | err = E下载带进度回调(info.MacDownloadURL, 下载文件夹路径+"/mactest.zip", func(进度 float64) {
60 | // 进度回调函数
61 | fmt.Println("正在下载...", 进度)
62 | })
63 | if err != nil {
64 | fmt.Println("下载出错:", err)
65 | return
66 | }
67 |
68 | flag, s := E更新自己MacOS应用(下载文件夹路径+"/mactest.zip", "qoq.app")
69 | println(flag, s)
70 |
71 | }
72 |
73 | func Test更新流程Window(t *testing.T) {
74 | //
75 | usr, err := user.Current()
76 | if err != nil {
77 | panic(err)
78 | }
79 | 下载文件夹路径 := filepath.Join(usr.HomeDir, "Downloads")
80 |
81 | //println(下载文件夹路径)
82 | //info := E获取Github仓库Releases版本和更新内容()
83 | //
84 | //err = E下载带进度回调(info.WinDownloadURL, 下载文件夹路径+"/GoEasyDesigner.exe", func(进度 float64) {
85 | // // 进度回调函数
86 | // fmt.Println("正在下载...", 进度)
87 | //})
88 | //if err != nil {
89 | // fmt.Println("下载出错:", err)
90 | // return
91 | //}
92 |
93 | flag, s := E更新自己Window应用(下载文件夹路径 + "/GoEasyDesigner.exe")
94 | println(flag, s)
95 |
96 | }
97 |
98 | func Test整个流程(t *testing.T) {
99 |
100 | 下载文件夹路径 := E取用户下载文件夹路径()
101 | info := E获取Github仓库Releases版本和更新内容()
102 | println(info.MacDownloadURL)
103 | println(下载文件夹路径)
104 | if info.Version == Version {
105 | err := zenity.Info("当前已经是最新版本")
106 | if err != nil {
107 | return
108 | }
109 | return
110 | }
111 |
112 | err := zenity.Question("软件有新版本可用,是否更新?\n当前版本:"+
113 | Version+
114 | "\n最新版本:"+info.Version,
115 | zenity.Title("更新提示"),
116 | zenity.Icon(zenity.QuestionIcon),
117 | zenity.OKLabel("更新"),
118 | zenity.CancelLabel("取消"))
119 | ecore.E调试输出(err)
120 | println("更新", err)
121 | if err != nil {
122 | return
123 | }
124 | progress, _ := zenity.Progress(
125 | zenity.Title("软件更新"),
126 | zenity.MaxValue(100), // 设置最大进度值为100
127 | )
128 |
129 | progress.Text("正在下载...")
130 |
131 | err = E下载带进度回调(info.MacDownloadURL, 下载文件夹路径+"/"+应用名称+"_MacOS.zip", func(进度 float64) {
132 | fmt.Println("正在下载...", 进度)
133 | progress.Text("正在下载..." + fmt.Sprintf("%.2f", 进度) + "%")
134 | progress.Value(int(进度))
135 | })
136 | if err != nil {
137 | fmt.Println("下载出错:", err)
138 | zenity.Info("下载错误,检查你的网络")
139 | progress.Close()
140 | return
141 | }
142 | progress.Text("下载完成 即将完成更新")
143 | if progress.Close() != nil {
144 | fmt.Println("点击了取消")
145 | return
146 | }
147 | fmt.Println("下载完成了")
148 | flag, s := E更新自己MacOS应用(下载文件夹路径+"/"+应用名称+"_MacOS.zip", 应用名称+".app")
149 | println(flag, s)
150 | }
151 |
--------------------------------------------------------------------------------
/frontend/src/components/RenderDesignComponentPreview.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
19 |
20 |
21 |
23 |
24 |
25 |
26 |
27 |
37 | {{ item.name ? item.name : 'ContentArea' }}
38 |
39 |
40 |
42 |
43 |
44 | { onCustomEvent(n, v, item) }" />
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
110 |
111 |
--------------------------------------------------------------------------------
/frontend/src/win/event.js:
--------------------------------------------------------------------------------
1 | import * as systemFc from "../../wailsjs/runtime";
2 | import * as goFc from "../../wailsjs/go/main/App";
3 | import {__load_data} from './__load_data'
4 | import {ElMessage} from "element-plus";
5 |
6 | let odata = {
7 | "bind": "0.0.0.0:8181",
8 | "proxy_url": "",
9 | "timeout": 600,
10 | "codex_api_base": "https://api.deepseek.com/beta/v1",
11 | "codex_api_key": "sk-这里填写你的key",
12 | "codex_api_organization": "",
13 | "codex_api_project": "",
14 | "codex_max_tokens": 500,
15 | "code_instruct_model": "deepseek-coder",
16 | "chat_api_base": "https://api.deepseek.com/v1",
17 | "chat_api_key": "sk-这里填写你的key",
18 | "chat_api_organization": "",
19 | "chat_api_project": "",
20 | "chat_max_tokens": 4096,
21 | "chat_model_default": "deepseek-chat",
22 | "chat_model_map": {},
23 | "chat_locale": "zh_CN",
24 | "auth_token": ""
25 | }
26 | let letvscode = {
27 | "github.copilot.advanced": {
28 | "debug.overrideCAPIUrl": "http://127.0.0.1:8181/v1",
29 | "debug.overrideProxyUrl": "http://127.0.0.1:8181",
30 | "debug.chatOverrideProxyUrl": "http://127.0.0.1:8181/v1/chat/completions",
31 | "authProvider": "github-enterprise"
32 | },
33 | "github-enterprise.uri": "https://cocopilot.org",
34 | }
35 |
36 | export function BindWindowEvent() {
37 | const c = __load_data()
38 | let comps = c.comps
39 | c.WinCreated = async function () {
40 | console.log("Win创建完毕")
41 | comps.编辑框3.text = ""
42 | systemFc.EventsOn("logs", function (data) {
43 | let jsondata = JSON.parse(data);
44 | console.log("jsondata",jsondata)
45 | comps.编辑框3.text = comps.编辑框3.text + jsondata.logs;
46 | });
47 |
48 | comps.Win.text = "overrideManager " + await goFc.GetVersion();
49 | comps.选择夹3.value = "0"
50 |
51 | //这个odata的数据需要json格式化
52 | comps.编辑框1.text = JSON.stringify(odata, null, 4)
53 | comps.编辑框2.text = JSON.stringify(letvscode, null, 4)
54 |
55 | comps.标签2.text = `安装 VSCode Copilot
56 | Copilot 1.219.0
57 | Copilotchat 0.17.1
58 | 按 ctrl+shift+p 输入 settings 打开首选项 加入这些配置 重启vscode`
59 |
60 |
61 | let 配置文件目录 = await goFc.E取运行目录()
62 |
63 | let data = await goFc.E读入文本(配置文件目录 + "/my-config.conf")
64 | if (data !== "") {
65 | ElMessage.success('已读取配置和启动服务 配置文件路径:' + 配置文件目录 + "/my-config.conf");
66 |
67 | comps.编辑框1.text = data
68 | c.按钮_启动服务被单击()
69 | }
70 |
71 |
72 | }
73 |
74 | c.按钮_启动服务被单击 = async function () {
75 | if (comps.按钮_启动服务.text === "启动服务") {
76 | let 启动状态 = await goFc.E启动服务器(comps.编辑框1.text)
77 | console.log("启动状态", 启动状态)
78 | if (启动状态 == '启动成功') {
79 | comps.按钮_启动服务.text = "停止服务"
80 | ElMessage.success('已启动');
81 | } else {
82 | ElMessage.error(启动状态);
83 | }
84 | comps.标签1.text = 启动状态
85 | } else {
86 | comps.按钮_启动服务.text = "启动服务"
87 | await goFc.E停止服务器()
88 | comps.标签1.text = "已停止"
89 | ElMessage.success('已停止');
90 |
91 | }
92 |
93 |
94 | }
95 |
96 | c.按钮_重置配置被单击 = async function () {
97 | console.log("按钮_重置配置被单击")
98 | comps.编辑框1.text = JSON.stringify(odata, null, 4)
99 | ElMessage.success('已重置');
100 | }
101 |
102 | c.按钮_保存配置被单击 = async function () {
103 | console.log("按钮_保存配置被单击")
104 | let 配置文件目录 = await goFc.E取运行目录()
105 | await goFc.E写到文件(配置文件目录 + "/my-config.conf", comps.编辑框1.text)
106 |
107 | ElMessage.success('保存到' + 配置文件目录 + "/my-config.conf");
108 | }
109 |
110 | c.标签3被单击 = async function () {
111 | console.log("标签3被单击")
112 | systemFc.BrowserOpenURL("https://my.rongyiapi.com/post/rang-Copilot-yong-shang-DeepSeek-zong-yu-gao-ming-bai-le-vscode-he-JetBrainsIDE-du-neng-yong.html")
113 |
114 | }
115 |
116 | c.按钮1被单击 = async function () {
117 | systemFc.ClipboardSetText(comps.编辑框2.text)
118 | ElMessage.success('已复制');
119 |
120 | }
121 |
122 | c.按钮_检查更新被单击 = async function () {
123 | ElMessage.success('检查更新中');
124 | await goFc.E检查更新();
125 | }
126 | //Don't delete the event function flag
127 | }
--------------------------------------------------------------------------------
/.github/workflows/publish.yml:
--------------------------------------------------------------------------------
1 | name: 发布软件
2 |
3 | on:
4 | workflow_dispatch:
5 | push:
6 | paths-ignore:
7 | - '**/*.md'
8 | - '**/*.yml'
9 | - .gitignore
10 | - .editorconfig
11 |
12 | permissions: write-all # 给所有工作写权限
13 |
14 |
15 | jobs:
16 | jobs_v:
17 | name: 构建版本号和变更信息
18 | runs-on: ubuntu-latest
19 | outputs:
20 | version: ${{ steps.create_version.outputs.NewVersion }} # 版本号
21 | body: ${{ steps.create_body.outputs.Body }} # 版本变更内容
22 | steps:
23 | - uses: actions/checkout@v4
24 | with:
25 | submodules: recursive
26 | fetch-depth: 0
27 | - name: 检查是否 "发布"
28 | run: |
29 | latest_commit_message=$(git log -1 --pretty=%B)
30 | if [[ $latest_commit_message == *"发布"* ]]; then
31 | echo "找到发布关键字继续工作流"
32 | else
33 | echo "没有找到发布关键字停止工作流"
34 | exit 1 # 停止工作流程
35 | fi
36 | - name: 递增版本号
37 | id: create_version
38 | uses: duolabmeng6/action-autotag-python@master
39 | with:
40 | token: ${{ secrets.GITHUB_TOKEN }}
41 | - name: 获取更新日志
42 | id: create_body
43 | uses: duolabmeng6/action-Releases-log@main
44 | with:
45 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
46 | FILE: .github/releasesText.md
47 | KEYS: bug,改进,优化,新增,删除
48 | - name: 查看版本号和更新日志
49 | run: |
50 | echo ${{ format('version={0}', steps.create_version.outputs.NewVersion ) }}
51 | echo "${{ steps.create_body.outputs.Body }}"
52 |
53 | jobs_macos:
54 | # 暂停
55 | #if: false
56 | needs: [ jobs_v ]
57 | name: 构建macos软件
58 | runs-on: macos-12
59 | env:
60 | version: ${{ needs.jobs_v.outputs.version }}
61 | body: ${{ needs.jobs_v.outputs.Body }}
62 | steps:
63 | - name: Checkout code
64 | uses: actions/checkout@v3
65 | - name: 构建缓存
66 | uses: actions/cache@v3
67 | with:
68 | path: |
69 | /Users/runner/.npm
70 | key: ${{ runner.os }}-js-${{ hashFiles('**/package.json') }}
71 | restore-keys: |
72 | ${{ runner.os }}-js-
73 | - name: 查看文件
74 | run: |
75 | pwd
76 | ls -la
77 | echo "New Body: ${{ env.body }}"
78 |
79 | - name: 安装 go
80 | uses: actions/setup-go@v4
81 | with:
82 | go-version: '^1.21.0'
83 | cache-dependency-path: |
84 | **/go.sum
85 | **/go.mod
86 | go-version-file: 'go.mod'
87 | - run: go version
88 | - name: 安装 wails 工具
89 | run: |
90 | go install github.com/wailsapp/wails/v2/cmd/wails@latest
91 | wails
92 | - name: 生成 Version.go 文件
93 | run: |
94 | echo -e "package myModel\n\nvar Version = \"${version}\"" > myModel/Version.go
95 | cat myModel/Version.go
96 | - name: 编译
97 | run: |
98 | pwd
99 | wails build
100 | ls -la
101 | - name: 查看编译的文件保存
102 | run: |
103 | pwd
104 | ls -la
105 | mkdir -p /tmp/artifacts
106 | cp -r build/bin/overrideManager.app /tmp/artifacts/overrideManager.app
107 | cp -r build/dmg.sh /tmp/artifacts/dmg.sh
108 | chmod +x /tmp/artifacts/overrideManager.app/Contents/MacOS/overrideManager
109 | - name: 创建压缩包
110 | run: |
111 | cd /tmp/artifacts/
112 | ./dmg.sh
113 | zip -r ./overrideManager_MacOS.zip ./overrideManager.app
114 |
115 | - name: 上传产物
116 | uses: actions/upload-artifact@v3
117 | with:
118 | name: macos
119 | path: |
120 | /tmp/artifacts/*.zip
121 | /tmp/artifacts/*.dmg
122 |
123 |
124 | jobs_window:
125 | needs: jobs_v # 等待 jobs_v 任务完成才执行
126 | name: 构建window软件
127 | timeout-minutes: 20
128 | runs-on: windows-2022
129 | env:
130 | version: ${{ needs.jobs_v.outputs.version }}
131 | body: ${{ needs.jobs_v.outputs.Body }}
132 | steps:
133 | - uses: actions/checkout@v3
134 | with:
135 | submodules: recursive
136 | - name: 构建缓存
137 | uses: actions/cache@v3
138 | with:
139 | path: |
140 | C:\npm\cache
141 | key: ${{ runner.os }}-js-${{ hashFiles('**/package.json') }}
142 | restore-keys: |
143 | ${{ runner.os }}-js-
144 | - name: 读入环境信息
145 | run: |
146 | echo ${{ format('version {0}', env.version ) }} # 版本号
147 | - name: 安装 go
148 | uses: actions/setup-go@v4
149 | with:
150 | go-version: '^1.21.0'
151 | cache-dependency-path: |
152 | **/go.sum
153 | **/go.mod
154 | go-version-file: 'go.mod'
155 | - run: go version
156 | - name: 安装 UPX
157 | uses: crazy-max/ghaction-upx@v2
158 | with:
159 | install-only: true
160 | - name: 安装 wails 工具
161 | run: |
162 | go install github.com/wailsapp/wails/v2/cmd/wails@latest
163 | wails
164 | - name: 生成 Version.go 文件
165 | shell: bash
166 | run: |
167 | echo -e "package myModel\n\nvar Version = \"${version}\"" > myModel/Version.go
168 | cat myModel/Version.go
169 | - name: 编译exe
170 | run: |
171 | wails build -webview2 embed -upx
172 | ls
173 | - name: 上传产物
174 | uses: actions/upload-artifact@v3
175 | with:
176 | name: window
177 | path: |
178 | build/bin/*.exe
179 |
180 |
181 | jobs4:
182 | needs: [ jobs_v,jobs_macos,jobs_window ]
183 | name: 发布版本
184 | runs-on: ubuntu-latest
185 | env:
186 | version: ${{ needs.jobs_v.outputs.version }}
187 | body: ${{ needs.jobs_v.outputs.Body }}
188 | steps:
189 | - uses: actions/checkout@v3
190 | with:
191 | submodules: recursive
192 | fetch-depth: 0
193 | - name: 下载产物
194 | id: download
195 | uses: actions/download-artifact@v3
196 | with:
197 | path: ./
198 | - name: 读入环境信息
199 | run: |
200 | echo ${{ format('version {0}', env.version ) }}
201 | echo ${{steps.download.outputs.download-path}}
202 | ls -R
203 | - name: 发布文件
204 | uses: ncipollo/release-action@v1
205 | with:
206 | token: ${{ secrets.GITHUB_TOKEN }}
207 | allowUpdates: true # 覆盖文件
208 | #draft: true # 草稿 自己可见 版本号会保持一样 默认是自动发布 latest
209 | #prerelease: true # 预发布 别人可以看到 版本号会继续加
210 | tag: ${{ env.version }} # 版本号 v0.1.0
211 | body: ${{ env.body }}
212 | artifacts: "macos/*.zip,macos/*.dmg,window/*.exe,window/*.zip"
213 |
214 |
--------------------------------------------------------------------------------
/frontend/src/public.js:
--------------------------------------------------------------------------------
1 | const parseDimension = (value) => {
2 | //检查是否包含 vw vh % 等单位
3 | if (typeof value === 'string') {
4 | if(value === "inherit" || value === "auto"){
5 | return value;
6 | }
7 | if(value.includes('v') || value.includes('%')){
8 | return value;
9 | }
10 | }
11 | return `${parseInt(value)}px`;
12 | };
13 |
14 | const getItemStyle = (item) => {
15 | const style = {
16 | top: parseDimension(item.top),
17 | left: parseDimension(item.left),
18 | width: parseDimension(item.width),
19 | height: parseDimension(item.height),
20 | border: item.border,
21 | background: item.background,
22 | zIndex: item.层级,
23 | color: item.textCorlor,
24 | };
25 |
26 | return style;
27 | };
28 |
29 | const getItemStyle2 = (item) => {
30 | // console.log("??");
31 | let style = {
32 | top: parseDimension(item.top),
33 | left: parseDimension(item.left),
34 | width: parseDimension(item.width),
35 | height: parseDimension(item.height),
36 | };
37 | // const element窗口 = document.getElementById("窗口");
38 | // if (element窗口) {
39 | // const rect = element窗口.getBoundingClientRect();
40 | // // style.top = parseInt(style.top) + rect.top + "px";
41 | // // style.left = parseInt(style.left) + rect.left + "px";
42 | // }
43 |
44 | const designer = document.getElementById("designer");
45 | let rect_designer = {}
46 | if (designer) {
47 | rect_designer = designer.getBoundingClientRect();
48 | } else {
49 | return style
50 | }
51 |
52 | const element = document.querySelector(`[data-id="${item.data_id}"]`);
53 | if (element) {
54 | //获取元素在浏览器中的绝对位置
55 | const rect = element.getBoundingClientRect();
56 | style.top = `${rect.top - rect_designer.top}px`;
57 | style.left = `${rect.left - rect_designer.left}px`;
58 | style.width = `${rect.width}px`;
59 | style.height = `${rect.height}px`;
60 | } else {
61 | style.display = "none"
62 | return style;
63 | }
64 | if (style.top == "0px" && style.left == "0px" && style.width == "0px" && style.height == "0px") {
65 | style.display = "none"
66 | return style;
67 | }
68 | style.display = "block"
69 |
70 | return style;
71 | };
72 | const getItemStyleShape = (item) => {
73 | const style = {
74 | top: parseDimension(item.top),
75 | left: parseDimension(item.left),
76 | right: parseDimension(item.right),
77 | bottom: parseDimension(item.bottom),
78 | width: parseDimension(item.width),
79 | height: parseDimension(item.height),
80 | border: item.border,
81 | background: item.background,
82 | zIndex: item.层级,
83 | position: 'absolute',
84 | overflowY: item.overflowY || 'visible',
85 | overflowX: item.overflowX || 'visible'
86 | };
87 |
88 |
89 |
90 | if (item.position !== undefined) {
91 | style.position = item.position;
92 | }
93 | if (item.图片 !== undefined) {
94 | if (item.backgroundSize !== undefined) {
95 | style.backgroundSize = '100% 100%';
96 | }
97 | if (item.backgroundRepeat !== undefined) {
98 | style.backgroundRepeat = 'no-repeat';
99 | }
100 | if (item.backgroundPosition !== undefined) {
101 | style.backgroundPosition = 'center';
102 | }
103 | if (item.backgroundAttachment !== undefined) {
104 | style.backgroundAttachment = 'scroll';
105 | }
106 | style.backgroundImage = `url(${item.图片})`;
107 | style.backgroundSize = item.backgroundSize
108 | style.backgroundRepeat = item.backgroundRepeat
109 | style.backgroundPosition = item.backgroundPosition
110 | style.backgroundAttachment = item.backgroundAttachment
111 | }
112 | if (item.fontSize !== undefined) {
113 | item.fontSize = item.fontSize || 12;
114 | }
115 |
116 | return style;
117 | };
118 |
119 |
120 | /* 生成组件对应关系方便ide 提示 */
121 |
122 | function generateComponentCode(components, parentPath = '') {
123 | const codeLines = [];
124 | let k = 0;
125 | let codeLine;
126 | for (const component of components) {
127 | let componentName = component["名称"];
128 | const componentPath = `${parentPath}.子组件[${k}]`;
129 | const componentPath2 = `${parentPath}`;
130 | k = k + 1;
131 | // componentName 如果带有空格 删除空格 并且把后面1个字母大写
132 | if (componentName) {
133 | if (componentName.includes(" ")) {
134 | const arr = componentName.split(" ");
135 | let newName = "";
136 | for (let i = 0; i < arr.length; i++) {
137 | newName = newName + arr[i].charAt(0).toUpperCase() + arr[i].slice(1);
138 | }
139 | componentName = newName;
140 | }
141 | }
142 |
143 |
144 | codeLine = `组件.${componentName} = list[0]${componentPath}`;
145 | if (componentName == "" || componentName == undefined) {
146 | } else {
147 | codeLines.push(codeLine);
148 | }
149 |
150 | if (component["子组件"] && component["子组件"].length > 0) {
151 | const childCodeLines = generateComponentCode(component["子组件"], componentPath);
152 | codeLines.push(...childCodeLines);
153 | }
154 | }
155 |
156 | return codeLines;
157 | }
158 |
159 | function 生成辅助代码(obj) {
160 | const codeLines = generateComponentCode(obj);
161 | let code = " 组件.窗口 = list[0]\r\n"
162 | for (const codeLine of codeLines) {
163 | code = code + " " + codeLine + "\r\n"
164 | }
165 | let codeTpl = `
166 | function __辅助代码(list, 组件) {
167 | ${code}
168 | return 组件
169 | }
170 | export default __辅助代码
171 | `
172 | return codeTpl
173 | }
174 |
175 | // const jsonData = require('./design.json');
176 | // console.log(生成辅助代码(jsonData[0].子组件))
177 |
178 |
179 | function 取父目录(path) {
180 | // 将所有的反斜杠 \ 替换为斜杠 /
181 | var convertedPath = path.replace(/\\/g, '/');
182 |
183 | // 使用正则表达式获取父目录
184 | var parentDirectory = convertedPath.replace(/\/[^/]*$/, '');
185 |
186 | return parentDirectory;
187 | }
188 |
189 | function InsertCode(jscode, insertionCode) {
190 | // 获取函数名称 窗口.按钮3被点击 = function () { 截取 按钮3被点击
191 | const 函数名称 = insertionCode.substring(insertionCode.indexOf(".") + 1, insertionCode.indexOf("=")).trim();
192 |
193 | // 检查是否已经存在
194 | if (jscode.includes(函数名称)) {
195 | return jscode;
196 | }
197 |
198 | // Define the regular expression pattern
199 | const pattern = `(\\/\\/Don't delete the event function flag[^\\n]*)`;
200 |
201 | // Compile the regular expression
202 | const re = new RegExp(pattern);
203 |
204 | // Replace the matching pattern with the insertion code
205 | const result = jscode.replace(re, `${insertionCode}$1`);
206 |
207 | return result;
208 | }
209 |
210 | export {getItemStyle, getItemStyle2, getItemStyleShape, 生成辅助代码, 取父目录, InsertCode};
--------------------------------------------------------------------------------
/frontend/src/components/boxs/el/CommonLayout/CommonLayout.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
7 |
8 |
9 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
19 |
20 |
21 |
23 |
24 |
25 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
36 |
37 |
38 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
48 |
49 |
50 |
51 |
53 |
54 |
55 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
66 |
67 |
68 |
69 |
70 |
72 |
73 |
74 |
75 |
76 |
78 |
79 |
80 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
93 |
94 |
95 |
96 |
98 |
99 |
100 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
113 |
114 |
115 |
116 |
118 |
119 |
120 |
122 |
123 |
124 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
160 |
164 |
--------------------------------------------------------------------------------
/frontend/src/win/design.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "id": "1",
4 | "name": "Win",
5 | "componentName": "Window",
6 | "text": "verride Manager",
7 | "top": "0",
8 | "left": "0",
9 | "width": "700",
10 | "height": "600",
11 | "background": "rgba(0, 0, 0, 0.05)",
12 | "noPlace": false,
13 | "noDrag": true,
14 | "disable": false,
15 | "visible": true,
16 | "zIndex": 0,
17 | "childComponents": [
18 | {
19 | "top": "0",
20 | "left": "0",
21 | "width": "100%",
22 | "height": "100%",
23 | "value": "3",
24 | "noPlace": true,
25 | "visible": true,
26 | "disable": false,
27 | "zIndex": 0,
28 | "overflowY": "hidden",
29 | "overflowX": "hidden",
30 | "type": "border-card",
31 | "tagPosition": "top",
32 | "childComponents": [
33 | {
34 | "id": "id-65309",
35 | "name": "ContentArea3",
36 | "text": "override 配置",
37 | "componentName": "elContainer",
38 | "top": "0",
39 | "left": "0",
40 | "width": "100%",
41 | "height": "100%",
42 | "noPlace": false,
43 | "noDrag": true,
44 | "visible": true,
45 | "disable": false,
46 | "pid": "id-13078",
47 | "childComponents": [
48 | {
49 | "top": "13",
50 | "left": "17",
51 | "width": "669",
52 | "height": "466",
53 | "noPlace": true,
54 | "visible": true,
55 | "disable": false,
56 | "zIndex": 0,
57 | "size": "custom",
58 | "内容": "",
59 | "type": "textarea",
60 | "placeholder": "请输入..",
61 | "maxlength": -1,
62 | "id": "id-41279",
63 | "componentName": "elTextEdit",
64 | "componentRawName": "TextEdit",
65 | "name": "编辑框1",
66 | "text": "",
67 | "fid": "id-65309",
68 | "data_id": "6aa594"
69 | },
70 | {
71 | "top": 494,
72 | "left": 15,
73 | "width": "76",
74 | "height": "40",
75 | "noPlace": true,
76 | "buttonType": "default",
77 | "visible": true,
78 | "disable": false,
79 | "size": "custom",
80 | "zIndex": 0,
81 | "text": "启动服务",
82 | "id": "id-66742",
83 | "componentName": "elButton",
84 | "componentRawName": "Button",
85 | "name": "按钮_启动服务",
86 | "fid": "id-65309",
87 | "data_id": "802b92",
88 | "event_click": "按钮_启动服务被单击"
89 | },
90 | {
91 | "top": "491",
92 | "left": "199",
93 | "width": "327",
94 | "height": "40",
95 | "noPlace": true,
96 | "visible": true,
97 | "disable": false,
98 | "zIndex": 0,
99 | "size": "default",
100 | "truncated": false,
101 | "tag": "p",
102 | "tagPosition": "text-left-center",
103 | "corlor": "#606266",
104 | "text": "状态: 未启动",
105 | "id": "id-47778",
106 | "componentName": "elLabel",
107 | "componentRawName": "Label",
108 | "name": "标签1",
109 | "fid": "id-65309",
110 | "data_id": "d5505e"
111 | },
112 | {
113 | "top": 494,
114 | "left": 106,
115 | "width": "76",
116 | "height": "40",
117 | "noPlace": true,
118 | "buttonType": "default",
119 | "visible": true,
120 | "disable": false,
121 | "size": "custom",
122 | "zIndex": 0,
123 | "text": "重置配置",
124 | "id": "id-48794",
125 | "componentName": "elButton",
126 | "componentRawName": "Button",
127 | "name": "按钮_重置配置",
128 | "fid": "id-65309",
129 | "data_id": "33f75f",
130 | "event_click": "按钮_重置配置被单击"
131 | },
132 | {
133 | "top": 494,
134 | "left": 605,
135 | "width": "76",
136 | "height": "40",
137 | "noPlace": true,
138 | "buttonType": "default",
139 | "visible": true,
140 | "disable": false,
141 | "size": "custom",
142 | "zIndex": 0,
143 | "text": "保存配置",
144 | "id": "id-27078",
145 | "componentName": "elButton",
146 | "componentRawName": "Button",
147 | "name": "按钮_保存配置",
148 | "fid": "id-65309",
149 | "data_id": "11b6ed",
150 | "event_click": "按钮_保存配置被单击"
151 | }
152 | ],
153 | "data_id": "130ec7"
154 | },
155 | {
156 | "id": "id-20457",
157 | "name": "ContentArea4",
158 | "text": "软件设置",
159 | "componentName": "elContainer",
160 | "top": "0",
161 | "left": "0",
162 | "width": "100%",
163 | "height": "100%",
164 | "noPlace": false,
165 | "noDrag": true,
166 | "visible": true,
167 | "disable": false,
168 | "pid": "id-13078",
169 | "childComponents": [
170 | {
171 | "top": "26",
172 | "left": "40",
173 | "width": "76",
174 | "height": "40",
175 | "noPlace": true,
176 | "buttonType": "default",
177 | "visible": true,
178 | "disable": false,
179 | "size": "custom",
180 | "zIndex": 0,
181 | "text": "检查更新",
182 | "id": "id-21196",
183 | "componentName": "elButton",
184 | "componentRawName": "Button",
185 | "name": "按钮_检查更新",
186 | "fid": "id-20457",
187 | "data_id": "c572ac",
188 | "event_click": "按钮_检查更新被单击"
189 | }
190 | ],
191 | "data_id": "ab919b"
192 | },
193 | {
194 | "id": "id-49704",
195 | "name": "ContentArea5",
196 | "text": "vscode配置",
197 | "componentName": "elContainer",
198 | "top": "0",
199 | "left": "0",
200 | "width": "100%",
201 | "height": "100%",
202 | "noPlace": false,
203 | "noDrag": true,
204 | "visible": true,
205 | "disable": false,
206 | "pid": "id-13078",
207 | "childComponents": [
208 | {
209 | "top": "18",
210 | "left": "17",
211 | "width": "651",
212 | "height": "322",
213 | "noPlace": true,
214 | "visible": true,
215 | "disable": false,
216 | "zIndex": 0,
217 | "size": "custom",
218 | "内容": "",
219 | "type": "textarea",
220 | "placeholder": "请输入..",
221 | "maxlength": -1,
222 | "id": "id-54714",
223 | "componentName": "elTextEdit",
224 | "componentRawName": "TextEdit",
225 | "name": "编辑框2",
226 | "text": "编辑框2",
227 | "fid": "id-49704",
228 | "data_id": "e141df"
229 | },
230 | {
231 | "top": "358",
232 | "left": "21",
233 | "width": "526",
234 | "height": "123",
235 | "noPlace": true,
236 | "visible": true,
237 | "disable": false,
238 | "zIndex": 0,
239 | "size": "default",
240 | "truncated": false,
241 | "tag": "p",
242 | "tagPosition": "text-top-left",
243 | "corlor": "#606266",
244 | "text": "教程",
245 | "id": "id-39613",
246 | "componentName": "elLabel",
247 | "componentRawName": "Label",
248 | "name": "标签2",
249 | "fid": "id-49704",
250 | "data_id": "883479"
251 | },
252 | {
253 | "top": "488",
254 | "left": "17",
255 | "width": "258",
256 | "height": "40",
257 | "noPlace": true,
258 | "visible": true,
259 | "disable": false,
260 | "zIndex": 0,
261 | "size": "default",
262 | "truncated": false,
263 | "tag": "p",
264 | "tagPosition": "text-center",
265 | "corlor": "#606266",
266 | "text": "打开详细教程教程",
267 | "id": "id-79669",
268 | "componentName": "elLabel",
269 | "componentRawName": "Label",
270 | "name": "标签3",
271 | "fid": "id-49704",
272 | "data_id": "208c8b",
273 | "event_click": "标签3被单击"
274 | },
275 | {
276 | "top": "352",
277 | "left": "587",
278 | "width": "76",
279 | "height": "40",
280 | "noPlace": true,
281 | "buttonType": "default",
282 | "visible": true,
283 | "disable": false,
284 | "size": "custom",
285 | "zIndex": 0,
286 | "text": "复制配置",
287 | "id": "id-19657",
288 | "componentName": "elButton",
289 | "componentRawName": "Button",
290 | "name": "按钮1",
291 | "fid": "id-49704",
292 | "data_id": "05b11e",
293 | "event_click": "按钮1被单击"
294 | }
295 | ],
296 | "data_id": "430c6a"
297 | },
298 | {
299 | "id": "id-77320",
300 | "name": "ContentArea1",
301 | "text": "日志",
302 | "componentName": "elContainer",
303 | "top": "0",
304 | "left": "0",
305 | "width": "100%",
306 | "height": "100%",
307 | "noPlace": false,
308 | "noDrag": true,
309 | "visible": true,
310 | "disable": false,
311 | "pid": "id-13078",
312 | "childComponents": [
313 | {
314 | "top": "9",
315 | "left": "11",
316 | "width": "670",
317 | "height": "374",
318 | "noPlace": true,
319 | "visible": true,
320 | "disable": false,
321 | "zIndex": 0,
322 | "size": "custom",
323 | "内容": "",
324 | "type": "textarea",
325 | "placeholder": "请输入..",
326 | "maxlength": -1,
327 | "id": "id-34301",
328 | "componentName": "elTextEdit",
329 | "componentRawName": "TextEdit",
330 | "name": "编辑框3",
331 | "text": "编辑框3",
332 | "fid": "id-77320",
333 | "data_id": "b9dda5"
334 | }
335 | ],
336 | "data_id": "195f1e"
337 | }
338 | ],
339 | "id": "id-13078",
340 | "componentName": "elTabs",
341 | "componentRawName": "Tabs",
342 | "name": "选择夹3",
343 | "fid": "1",
344 | "data_id": "7f068e"
345 | }
346 | ],
347 | "data_id": "83546e",
348 | "event_created": "Win创建完毕"
349 | }
350 | ]
--------------------------------------------------------------------------------
/myModel/自动更新模块.go:
--------------------------------------------------------------------------------
1 | package myModel
2 |
3 | import (
4 | "archive/zip"
5 | "encoding/json"
6 | "fmt"
7 | "github.com/duolabmeng6/goefun/ecore"
8 | "github.com/ncruces/zenity"
9 | "io"
10 | "net/http"
11 | "os"
12 | "os/exec"
13 | "os/user"
14 | "path/filepath"
15 | "runtime"
16 | "strings"
17 | "time"
18 | )
19 |
20 | var 应用名称 = "overrideManager"
21 | var owner = "duolabmeng6" // GitHub 仓库的所有者
22 | var repo = "overrideManager" // GitHub 仓库的名称
23 |
24 | type GithubJSONData []struct {
25 | URL string `json:"url"`
26 | AssetsURL string `json:"assets_url"`
27 | UploadURL string `json:"upload_url"`
28 | HTMLURL string `json:"html_url"`
29 | ID int `json:"id"`
30 | Author struct {
31 | Login string `json:"login"`
32 | ID int `json:"id"`
33 | NodeID string `json:"node_id"`
34 | AvatarURL string `json:"avatar_url"`
35 | GravatarID string `json:"gravatar_id"`
36 | URL string `json:"url"`
37 | HTMLURL string `json:"html_url"`
38 | FollowersURL string `json:"followers_url"`
39 | FollowingURL string `json:"following_url"`
40 | GistsURL string `json:"gists_url"`
41 | StarredURL string `json:"starred_url"`
42 | SubscriptionsURL string `json:"subscriptions_url"`
43 | OrganizationsURL string `json:"organizations_url"`
44 | ReposURL string `json:"repos_url"`
45 | EventsURL string `json:"events_url"`
46 | ReceivedEventsURL string `json:"received_events_url"`
47 | Type string `json:"type"`
48 | SiteAdmin bool `json:"site_admin"`
49 | } `json:"author"`
50 | NodeID string `json:"node_id"`
51 | TagName string `json:"tag_name"`
52 | TargetCommitish string `json:"target_commitish"`
53 | Name string `json:"name"`
54 | Draft bool `json:"draft"`
55 | Prerelease bool `json:"prerelease"`
56 | CreatedAt time.Time `json:"created_at"`
57 | PublishedAt time.Time `json:"published_at"`
58 | Assets []struct {
59 | URL string `json:"url"`
60 | ID int `json:"id"`
61 | NodeID string `json:"node_id"`
62 | Name string `json:"name"`
63 | Label string `json:"label"`
64 | Uploader struct {
65 | Login string `json:"login"`
66 | ID int `json:"id"`
67 | NodeID string `json:"node_id"`
68 | AvatarURL string `json:"avatar_url"`
69 | GravatarID string `json:"gravatar_id"`
70 | URL string `json:"url"`
71 | HTMLURL string `json:"html_url"`
72 | FollowersURL string `json:"followers_url"`
73 | FollowingURL string `json:"following_url"`
74 | GistsURL string `json:"gists_url"`
75 | StarredURL string `json:"starred_url"`
76 | SubscriptionsURL string `json:"subscriptions_url"`
77 | OrganizationsURL string `json:"organizations_url"`
78 | ReposURL string `json:"repos_url"`
79 | EventsURL string `json:"events_url"`
80 | ReceivedEventsURL string `json:"received_events_url"`
81 | Type string `json:"type"`
82 | SiteAdmin bool `json:"site_admin"`
83 | } `json:"uploader"`
84 | ContentType string `json:"content_type"`
85 | State string `json:"state"`
86 | Size int `json:"size"`
87 | DownloadCount int `json:"download_count"`
88 | CreatedAt time.Time `json:"created_at"`
89 | UpdatedAt time.Time `json:"updated_at"`
90 | BrowserDownloadURL string `json:"browser_download_url"`
91 | } `json:"assets"`
92 | TarballURL string `json:"tarball_url"`
93 | ZipballURL string `json:"zipball_url"`
94 | Body string `json:"body"`
95 | }
96 | type ReleaseInfo struct {
97 | Version string `json:"版本号"`
98 | DownloadURLs []string `json:"下载地址列表"`
99 | MacDownloadURL string `json:"mac下载地址"`
100 | WinDownloadURL string `json:"win下载地址"`
101 | Changelog string `json:"更新内容"`
102 | ReleaseTime string `json:"发布时间"`
103 | }
104 |
105 | func E获取Github仓库Releases版本和更新内容() *ReleaseInfo {
106 | //owner := "duolabmeng6" // GitHub 仓库的所有者
107 | //repo := "GoEasyDesigner" // GitHub 仓库的名称
108 | var urls []string
109 | _url := fmt.Sprintf("https://api.github.com/repos/%s/%s/releases", owner, repo)
110 | urls = append(urls, _url)
111 | //_url = fmt.Sprintf("https://go.kenhong.com/releases_latest.json")
112 | //urls = append(urls, _url)
113 | // 访问urls中的内容 如果第一个访问失败了 就继续下一个
114 | var resp *http.Response
115 | var err error
116 | for _, url := range urls {
117 | resp, err = http.Get(url)
118 | if err == nil {
119 | if resp.StatusCode != http.StatusOK {
120 | fmt.Println("请求失败:", resp.Status, url)
121 | continue
122 | }
123 | break
124 | }
125 | fmt.Println("请求失败:", err)
126 | }
127 | defer resp.Body.Close()
128 |
129 | body, err := io.ReadAll(resp.Body)
130 | if err != nil {
131 | fmt.Println("读取响应失败:", err)
132 | return nil
133 | }
134 | //var err error
135 | //body := ecore.E读入文件("/Users/ll/Desktop/goproject/v3fanyi/mymodel/githubdata.json")
136 |
137 | var releases GithubJSONData
138 |
139 | err = json.Unmarshal(body, &releases)
140 | if err != nil {
141 | fmt.Println("解析响应失败:", err)
142 | return nil
143 | }
144 | if len(releases) <= 0 {
145 | return nil
146 | }
147 |
148 | //latestRelease := releases[0]
149 | //version := latestRelease.TagName
150 | //fmt.Println("最新的 Release 版本号:", version)
151 | //fmt.Println("最新的 Release 版本号:", latestRelease.Body)
152 | //// 循环获取 文件名和下载地址
153 | //for _, asset := range latestRelease.Assets {
154 | // fmt.Println(asset.Name, asset.BrowserDownloadURL)
155 | //}
156 |
157 | latestRelease := releases[0]
158 | version := latestRelease.TagName
159 | downloadURLs := make([]string, 0)
160 | macDownloadURL := ""
161 | winDownloadURL := ""
162 |
163 | // 获取文件名和下载地址
164 | for _, asset := range latestRelease.Assets {
165 | downloadURLs = append(downloadURLs, asset.BrowserDownloadURL)
166 | //查找文本是否包含 macos.zip 和 .exe 不区分大小写
167 | if strings.Contains(strings.ToLower(asset.Name), "macos.zip") {
168 | macDownloadURL = asset.BrowserDownloadURL
169 | }
170 | if strings.Contains(strings.ToLower(asset.Name), ".exe") {
171 | winDownloadURL = asset.BrowserDownloadURL
172 | }
173 |
174 | }
175 |
176 | releaseInfo := &ReleaseInfo{
177 | Version: version,
178 | DownloadURLs: downloadURLs,
179 | MacDownloadURL: "https://mirror.ghproxy.com/" + macDownloadURL,
180 | WinDownloadURL: "https://mirror.ghproxy.com/" + winDownloadURL,
181 | Changelog: latestRelease.Body,
182 | ReleaseTime: ecore.E到时间(latestRelease.CreatedAt.String()).E时间到文本("Y-m-d H:i:s"),
183 | }
184 |
185 | return releaseInfo
186 | }
187 |
188 | func E下载带进度回调(下载地址 string, 文件保存路径 string, fc func(进度 float64)) error {
189 | resp, err := http.Get(下载地址)
190 | if err != nil {
191 | return err
192 | }
193 | defer resp.Body.Close()
194 |
195 | out, err := os.Create(文件保存路径)
196 | if err != nil {
197 | return err
198 | }
199 | defer out.Close()
200 |
201 | 下载文件大小 := resp.ContentLength
202 | 已下载大小 := int64(0)
203 | buf := make([]byte, 1024)
204 |
205 | 进度Ticker := time.Tick(time.Millisecond * 50) //降低刷新的频率
206 |
207 | for {
208 | n, err := resp.Body.Read(buf)
209 | if n > 0 {
210 | _, err := out.Write(buf[:n])
211 | if err != nil {
212 | return err
213 | }
214 |
215 | 已下载大小 += int64(n)
216 | 进度 := float64(已下载大小) / float64(下载文件大小) * 100
217 |
218 | select {
219 | case <-进度Ticker:
220 | if fc != nil {
221 | fc(进度) // 调用进度回调函数
222 | }
223 | default:
224 | // 不触发进度回调
225 | }
226 | }
227 |
228 | if err != nil {
229 | if err != io.EOF {
230 | return err
231 | }
232 | break
233 | }
234 | }
235 | fc(100) // 调用进度回调函数
236 |
237 | return nil
238 | }
239 |
240 | func E系统是否为window系统() bool {
241 | return strings.ToLower(runtime.GOOS) == "windows"
242 | }
243 |
244 | func E系统是否为linux系统() bool {
245 | return strings.ToLower(runtime.GOOS) == "linux"
246 | }
247 |
248 | func E系统是否为mac系统() bool {
249 | return strings.ToLower(runtime.GOOS) == "darwin"
250 | }
251 |
252 | // E是否为调试模式
253 | // 判断当前的环境是否为编译后的程序
254 | // 真为调试模式
255 | // 假为编译后的程序
256 | func E是否为调试模式() bool {
257 | debugMode := false
258 | if os.Getenv("DEBUG") != "" {
259 | debugMode = true
260 | }
261 |
262 | return debugMode
263 | }
264 |
265 | func 取自身MacOs应用路径() string {
266 | // 如果不处于编译状态反馈空
267 | 编译后路径, err := os.Executable()
268 | //println("编译后路径", 编译后路径)
269 | if err != nil {
270 | return ""
271 | }
272 | // 调试模式下的路径
273 | //编译后路径 = "/Users/ll/Desktop/goproject/v3fanyi/bin/qoq.app/Contents/MacOS/qoq"
274 | // 编译后路径 取父目录
275 | 编译后路径 = 编译后路径[:strings.LastIndex(编译后路径, "/")]
276 |
277 | app目录 := 编译后路径[:strings.LastIndex(编译后路径, "/")]
278 | app目录 = app目录[:strings.LastIndex(app目录, "/")]
279 | 父目录名称 := 编译后路径[strings.LastIndex(编译后路径, "/")+1:]
280 | if 父目录名称 == "MacOS" {
281 | return app目录
282 | } else {
283 | return ""
284 | }
285 | }
286 |
287 | func E更新自己MacOS应用(资源压缩包 string, 应用名称 string) (bool, string) {
288 | //资源压缩包 = "/Users/ll/Downloads/GoEasyDesigner_MacOS.zip"
289 | //应用名称 = "GoEasyDesigner.app"
290 | //println("资源压缩包", 资源压缩包)
291 | //println("应用名称", 应用名称)
292 |
293 | MacOs应用路径 := 取自身MacOs应用路径()
294 | //fmt.Println("MacOs应用路径", MacOs应用路径)
295 | //MacOs应用路径 := "/Users/ll/Downloads/" + 应用名称
296 | if MacOs应用路径 != "" {
297 | app目录父目录 := MacOs应用路径[:strings.LastIndex(MacOs应用路径, "/")]
298 | fmt.Printf("资源压缩包 %s app目录父目录%s MacOs应用路径%s \r\n", 资源压缩包, app目录父目录, MacOs应用路径)
299 | if MacOs应用路径 != "" {
300 | 解压结果 := zip解压(资源压缩包, app目录父目录, []string{应用名称 + "/Contents/"})
301 | println("解压结果", 解压结果)
302 | // 解压完成后删除压缩包
303 | //os.Remove(资源压缩包)
304 | MacOs应用路径 = filepath.Join(app目录父目录, 应用名称)
305 | // QApplication.quit()
306 | 应用名称 = 应用名称[:strings.LastIndex(应用名称, ".")]
307 | 运行命令 := fmt.Sprintf("killall %s && open -n -a %s", 应用名称, MacOs应用路径)
308 | println("运行命令", 运行命令)
309 | cmd := exec.Command("sh", "-c", 运行命令)
310 | err := cmd.Run()
311 | if err != nil {
312 | fmt.Println(err)
313 | return false, "killall失败 请自己重启应用"
314 | }
315 | return true, MacOs应用路径
316 | }
317 | } else {
318 | fmt.Println("非MacOS编译环境")
319 | return false, "非MacOS编译环境"
320 | }
321 | fmt.Println("应用路径为空")
322 | return false, "应用路径为空"
323 | }
324 |
325 | func zip解压(压缩包的路径 string, 解压目录 string, 允许解压路径前缀 []string) bool {
326 | // 保持权限和软连接解压
327 | // 允许解压路径前缀 例如 ["my_app.app/Contents/"] 不填则全部解压
328 |
329 | 文件, err := zip.OpenReader(压缩包的路径)
330 | if err != nil {
331 | fmt.Println(err)
332 | return false
333 | }
334 | defer 文件.Close()
335 |
336 | for _, info := range 文件.File {
337 | // 检查目标文件路径是否在允许解压路径前缀中
338 | if len(允许解压路径前缀) > 0 {
339 | 允许解压 := false
340 | for _, 路径 := range 允许解压路径前缀 {
341 | if strings.HasPrefix(info.Name, 路径) {
342 | 允许解压 = true
343 | }
344 | }
345 | if !允许解压 {
346 | continue
347 | }
348 | }
349 |
350 | 文件名 := info.Name
351 |
352 | 目标文件路径 := filepath.Join(解压目录, 文件名)
353 |
354 | 权限 := info.Mode().Perm()
355 | if info.Mode()&os.ModeSymlink != 0 { // 检查是否为软连接
356 | 软连接位置, err := readLink(info)
357 | if err != nil {
358 | fmt.Println(err)
359 | continue
360 | }
361 | // 检查目标文件路径是否存在,如果存在就删除,防止创建失败
362 | if _, err := os.Lstat(目标文件路径); err == nil {
363 | os.Remove(目标文件路径)
364 | }
365 | err = os.Symlink(软连接位置, 目标文件路径)
366 | if err != nil {
367 | fmt.Println(err)
368 | }
369 | } else {
370 | // 删除文件重新解压
371 | if _, err := os.Lstat(目标文件路径); err == nil {
372 | // 检查是否为文件
373 | if !info.Mode().IsDir() {
374 | os.Remove(目标文件路径)
375 | }
376 | }
377 | err = extractFile(info, 目标文件路径)
378 | if err != nil {
379 | fmt.Println(err)
380 | }
381 |
382 | // 修改文件权限
383 | if _, err := os.Lstat(目标文件路径); err == nil {
384 | err = os.Chmod(目标文件路径, 权限)
385 | if err != nil {
386 | fmt.Println(err)
387 | }
388 | }
389 | }
390 | }
391 |
392 | return true
393 | }
394 |
395 | func readLink(info *zip.File) (string, error) {
396 | file, err := info.Open()
397 | if err != nil {
398 | return "", err
399 | }
400 | defer file.Close()
401 |
402 | linkName := make([]byte, info.FileInfo().Size())
403 | _, err = io.ReadFull(file, linkName)
404 | if err != nil {
405 | return "", err
406 | }
407 |
408 | return string(linkName), nil
409 | }
410 | func extractFile(info *zip.File, 目标文件路径 string) error {
411 | file, err := info.Open()
412 | if err != nil {
413 | return err
414 | }
415 | defer file.Close()
416 |
417 | if info.FileInfo().IsDir() {
418 | err = os.MkdirAll(目标文件路径, info.Mode())
419 | if err != nil {
420 | return err
421 | }
422 | } else {
423 | writer, err := os.OpenFile(目标文件路径, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, info.Mode())
424 | if err != nil {
425 | return err
426 | }
427 | defer writer.Close()
428 |
429 | _, err = io.Copy(writer, file)
430 | if err != nil {
431 | return err
432 | }
433 | }
434 |
435 | return nil
436 | }
437 |
438 | func E取用户下载文件夹路径() string {
439 | usr, err := user.Current()
440 | if err != nil {
441 | fmt.Println("pull:", err)
442 | return ""
443 | }
444 | 下载文件夹路径 := filepath.Join(usr.HomeDir, "Downloads")
445 | return 下载文件夹路径
446 | }
447 |
448 | func 取自身路径Window() (string, error) {
449 | exe, err := os.Executable()
450 | if err != nil {
451 | return "", err
452 | }
453 | return exe, nil
454 | }
455 |
456 | func E更新自己Window应用(exe资源文件路径 string) (bool, string) {
457 | // window更新方法
458 | 自身路径Window, _ := 取自身路径Window()
459 |
460 | //文件名 := filepath.Base(自身路径Window)
461 | // 检查文件是否存在
462 | 旧的文件名 := 自身路径Window + ".old.bak"
463 | if _, err := os.Stat(旧的文件名); err == nil {
464 | // 删除文件
465 | os.Remove(旧的文件名)
466 | }
467 |
468 | os.Rename(自身路径Window, 旧的文件名)
469 | os.Rename(exe资源文件路径, 自身路径Window)
470 |
471 | // 结束自身运行然后重启自己
472 | cmd := exec.Command(自身路径Window)
473 | cmd.Args = append(cmd.Args, os.Args[1:]...)
474 | cmd.Start()
475 |
476 | os.Exit(0) // 此处退出当前进程
477 | return true, ""
478 | }
479 |
480 | func E检查更新_Mac() {
481 |
482 | 下载文件夹路径 := E取用户下载文件夹路径()
483 | info := E获取Github仓库Releases版本和更新内容()
484 | if info == nil {
485 | err := zenity.Info("检查更新失败")
486 | if err != nil {
487 | return
488 | }
489 | return
490 | }
491 | println(info.MacDownloadURL)
492 | println(下载文件夹路径)
493 | if info.Version == Version {
494 | err := zenity.Info("当前已经是最新版本")
495 | if err != nil {
496 | return
497 | }
498 | return
499 | }
500 |
501 | err := zenity.Question("软件有新版本可用,是否更新?\n当前版本:"+
502 | Version+
503 | "\n最新版本:"+info.Version,
504 | zenity.Title("更新提示"),
505 | zenity.Icon(zenity.QuestionIcon),
506 | zenity.OKLabel("更新"),
507 | zenity.CancelLabel("取消"))
508 | ecore.E调试输出(err)
509 | println("更新", err)
510 | if err != nil {
511 | return
512 | }
513 | progress, _ := zenity.Progress(
514 | zenity.Title("软件更新"),
515 | zenity.MaxValue(100), // 设置最大进度值为100
516 | )
517 |
518 | progress.Text("正在下载...")
519 |
520 | err = E下载带进度回调(info.MacDownloadURL, 下载文件夹路径+"/"+应用名称+"_MacOS.zip", func(进度 float64) {
521 | fmt.Println("正在下载...", 进度)
522 | progress.Text("正在下载..." + fmt.Sprintf("%.2f", 进度) + "%")
523 | progress.Value(int(进度))
524 | })
525 | if err != nil {
526 | fmt.Println("下载出错:", err)
527 | zenity.Info("下载错误,检查你的网络")
528 | progress.Close()
529 | return
530 | }
531 | progress.Text("下载完成 即将完成更新")
532 | if progress.Close() != nil {
533 | fmt.Println("点击了取消")
534 | return
535 | }
536 | fmt.Println("下载完成了")
537 | flag, s := E更新自己MacOS应用(下载文件夹路径+"/"+应用名称+"_MacOS.zip", 应用名称+".app")
538 | println(flag, s)
539 | }
540 |
541 | func E检查更新_window() {
542 |
543 | 下载文件夹路径 := E取用户下载文件夹路径()
544 | info := E获取Github仓库Releases版本和更新内容()
545 | if info == nil {
546 | zenity.Info("网络原因无法获取更新信息")
547 | return
548 | }
549 | if info.Version == Version {
550 | err := zenity.Info("当前已经是最新版本")
551 | if err != nil {
552 | return
553 | }
554 | return
555 | }
556 |
557 | err := zenity.Question("软件有新版本可用,是否更新?\n当前版本:"+
558 | Version+
559 | "\n最新版本:"+info.Version,
560 | zenity.Title("更新提示"),
561 | zenity.Icon(zenity.QuestionIcon),
562 | zenity.OKLabel("更新"),
563 | zenity.CancelLabel("取消"))
564 | ecore.E调试输出(err)
565 | println("更新", err)
566 | if err != nil {
567 | return
568 | }
569 | progress, _ := zenity.Progress(
570 | zenity.Title("软件更新"),
571 | zenity.MaxValue(100), // 设置最大进度值为100
572 | )
573 |
574 | progress.Text("正在下载...")
575 |
576 | err = E下载带进度回调(info.WinDownloadURL, 下载文件夹路径+"/"+应用名称+".exe", func(进度 float64) {
577 | fmt.Println("正在下载...", 进度)
578 | progress.Text("正在下载..." + fmt.Sprintf("%.2f", 进度) + "%")
579 | progress.Value(int(进度))
580 | })
581 | if err != nil {
582 | fmt.Println("下载出错:", err)
583 | zenity.Info("下载错误,检查你的网络")
584 | progress.Close()
585 | return
586 | }
587 | progress.Text("下载完成 即将完成更新")
588 | if progress.Close() != nil {
589 | fmt.Println("点击了取消")
590 | return
591 | }
592 | fmt.Println("下载完成了")
593 | flag, s := E更新自己Window应用(下载文件夹路径 + "/" + 应用名称 + ".exe")
594 | println(flag, s)
595 | }
596 | func E是否为window系统() bool {
597 | return runtime.GOOS == "windows"
598 | }
599 |
600 | func E是否为macOS系统() bool {
601 | return runtime.GOOS == "darwin"
602 | }
603 |
604 | func E是否为UbuntuLinux系统() bool {
605 | return runtime.GOOS == "linux"
606 | }
607 |
608 | func E检查更新() {
609 | if E是否为macOS系统() {
610 | E检查更新_Mac()
611 | }
612 | if E是否为window系统() {
613 | E检查更新_window()
614 | }
615 | }
616 |
--------------------------------------------------------------------------------
/action/run_override.go:
--------------------------------------------------------------------------------
1 | package action
2 |
3 | import (
4 | "bytes"
5 | "context"
6 | "encoding/json"
7 | "errors"
8 | "fmt"
9 | "github.com/duolabmeng6/goefun/ecore"
10 | "io"
11 | "log"
12 | "net"
13 | "net/http"
14 | "net/url"
15 | "os"
16 | "reflect"
17 | "strconv"
18 | "strings"
19 | "time"
20 |
21 | "github.com/gin-gonic/gin"
22 | "github.com/tidwall/gjson"
23 | "github.com/tidwall/sjson"
24 | "golang.org/x/net/http2"
25 | )
26 |
27 | const DefaultInstructModel = "gpt-3.5-turbo-instruct"
28 |
29 | const StableCodeModelPrefix = "stable-code"
30 |
31 | const DeepSeekCoderModel = "deepseek-coder"
32 |
33 | type config struct {
34 | Bind string `json:"bind"`
35 | ProxyUrl string `json:"proxy_url"`
36 | Timeout int `json:"timeout"`
37 | CodexApiBase string `json:"codex_api_base"`
38 | CodexApiKey string `json:"codex_api_key"`
39 | CodexApiOrganization string `json:"codex_api_organization"`
40 | CodexApiProject string `json:"codex_api_project"`
41 | CodexMaxTokens int `json:"codex_max_tokens"`
42 | CodeInstructModel string `json:"code_instruct_model"`
43 | ChatApiBase string `json:"chat_api_base"`
44 | ChatApiKey string `json:"chat_api_key"`
45 | ChatApiOrganization string `json:"chat_api_organization"`
46 | ChatApiProject string `json:"chat_api_project"`
47 | ChatMaxTokens int `json:"chat_max_tokens"`
48 | ChatModelDefault string `json:"chat_model_default"`
49 | ChatModelMap map[string]string `json:"chat_model_map"`
50 | ChatLocale string `json:"chat_locale"`
51 | AuthToken string `json:"auth_token"`
52 | }
53 |
54 | func readConfig(content string) *config {
55 | //content, err := os.ReadFile("config.json")
56 | //if nil != err {
57 | // log.Fatal(err)
58 | //}
59 | var err error
60 |
61 | _cfg := &config{}
62 | err = json.Unmarshal([]byte(content), &_cfg)
63 | if nil != err {
64 | log.Fatal(err)
65 | }
66 |
67 | v := reflect.ValueOf(_cfg).Elem()
68 | t := v.Type()
69 |
70 | for i := 0; i < v.NumField(); i++ {
71 | field := v.Field(i)
72 | tag := t.Field(i).Tag.Get("json")
73 | if tag == "" {
74 | continue
75 | }
76 |
77 | value, exists := os.LookupEnv("OVERRIDE_" + strings.ToUpper(tag))
78 | if !exists {
79 | continue
80 | }
81 |
82 | switch field.Kind() {
83 | case reflect.String:
84 | field.SetString(value)
85 | case reflect.Bool:
86 | if boolValue, err := strconv.ParseBool(value); err == nil {
87 | field.SetBool(boolValue)
88 | }
89 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
90 | if intValue, err := strconv.ParseInt(value, 10, 64); err == nil {
91 | field.SetInt(intValue)
92 | }
93 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
94 | if uintValue, err := strconv.ParseUint(value, 10, 64); err == nil {
95 | field.SetUint(uintValue)
96 | }
97 | case reflect.Float32, reflect.Float64:
98 | if floatValue, err := strconv.ParseFloat(value, field.Type().Bits()); err == nil {
99 | field.SetFloat(floatValue)
100 | }
101 | }
102 | }
103 | if _cfg.CodeInstructModel == "" {
104 | _cfg.CodeInstructModel = DefaultInstructModel
105 | }
106 |
107 | if _cfg.CodexMaxTokens == 0 {
108 | _cfg.CodexMaxTokens = 500
109 | }
110 |
111 | if _cfg.ChatMaxTokens == 0 {
112 | _cfg.ChatMaxTokens = 4096
113 | }
114 |
115 | return _cfg
116 | }
117 |
118 | func getClient(cfg *config) (*http.Client, error) {
119 | transport := &http.Transport{
120 | ForceAttemptHTTP2: true,
121 | DisableKeepAlives: false,
122 | }
123 |
124 | err := http2.ConfigureTransport(transport)
125 | if nil != err {
126 | return nil, err
127 | }
128 |
129 | if "" != cfg.ProxyUrl {
130 | proxyUrl, err := url.Parse(cfg.ProxyUrl)
131 | if nil != err {
132 | return nil, err
133 | }
134 |
135 | transport.Proxy = http.ProxyURL(proxyUrl)
136 | }
137 |
138 | client := &http.Client{
139 | Transport: transport,
140 | Timeout: time.Duration(cfg.Timeout) * time.Second,
141 | }
142 |
143 | return client, nil
144 | }
145 |
146 | func abortCodex(c *gin.Context, status int) {
147 | c.Header("Content-Type", "text/event-stream")
148 |
149 | c.String(status, "data: [DONE]\n")
150 | c.Abort()
151 | }
152 |
153 | func closeIO(c io.Closer) {
154 | err := c.Close()
155 | if nil != err {
156 | log.Println(err)
157 | }
158 | }
159 |
160 | type ProxyService struct {
161 | cfg *config
162 | client *http.Client
163 | }
164 |
165 | func NewProxyService(cfg *config) (*ProxyService, error) {
166 | client, err := getClient(cfg)
167 | if nil != err {
168 | return nil, err
169 | }
170 |
171 | return &ProxyService{
172 | cfg: cfg,
173 | client: client,
174 | }, nil
175 | }
176 | func AuthMiddleware(authToken string) gin.HandlerFunc {
177 | return func(c *gin.Context) {
178 | token := c.Param("token")
179 | if token != authToken {
180 | c.JSON(http.StatusUnauthorized, gin.H{"error": "Unauthorized"})
181 | c.Abort()
182 | return
183 | }
184 | c.Next()
185 | }
186 | }
187 |
188 | func (s *ProxyService) InitRoutes(e *gin.Engine) {
189 | e.GET("/_ping", s.pong)
190 | e.GET("/models", s.models)
191 | e.GET("/v1/models", s.models)
192 | authToken := s.cfg.AuthToken // replace with your dynamic value as needed
193 | if authToken != "" {
194 | // 鉴权
195 | v1 := e.Group("/:token/v1/", AuthMiddleware(authToken))
196 | {
197 | v1.POST("/chat/completions", s.completions)
198 | v1.POST("/engines/copilot-codex/completions", s.codeCompletions)
199 |
200 | v1.POST("/v1/chat/completions", s.completions)
201 | v1.POST("/v1/engines/copilot-codex/completions", s.codeCompletions)
202 | }
203 | } else {
204 | e.POST("/v1/chat/completions", s.completions)
205 | e.POST("/v1/engines/copilot-codex/completions", s.codeCompletions)
206 |
207 | e.POST("/v1/v1/chat/completions", s.completions)
208 | e.POST("/v1/v1/engines/copilot-codex/completions", s.codeCompletions)
209 | }
210 | }
211 |
212 | type Pong struct {
213 | Now int `json:"now"`
214 | Status string `json:"status"`
215 | Ns1 string `json:"ns1"`
216 | }
217 |
218 | func (s *ProxyService) pong(c *gin.Context) {
219 | c.JSON(http.StatusOK, Pong{
220 | Now: time.Now().Second(),
221 | Status: "ok",
222 | Ns1: "200 OK",
223 | })
224 | }
225 |
226 | func (s *ProxyService) models(c *gin.Context) {
227 | c.JSON(http.StatusOK, gin.H{
228 | "data": []gin.H{
229 | {
230 | "capabilities": gin.H{
231 | "family": "gpt-3.5-turbo",
232 | "object": "model_capabilities",
233 | "type": "chat",
234 | },
235 | "id": "gpt-3.5-turbo",
236 | "name": "GPT 3.5 Turbo",
237 | "object": "model",
238 | "version": "gpt-3.5-turbo-0613",
239 | },
240 | {
241 | "capabilities": gin.H{
242 | "family": "gpt-3.5-turbo",
243 | "object": "model_capabilities",
244 | "type": "chat",
245 | },
246 | "id": "gpt-3.5-turbo-0613",
247 | "name": "GPT 3.5 Turbo (2023-06-13)",
248 | "object": "model",
249 | "version": "gpt-3.5-turbo-0613",
250 | },
251 | {
252 | "capabilities": gin.H{
253 | "family": "gpt-4",
254 | "object": "model_capabilities",
255 | "type": "chat",
256 | },
257 | "id": "gpt-4",
258 | "name": "GPT 4",
259 | "object": "model",
260 | "version": "gpt-4-0613",
261 | },
262 | {
263 | "capabilities": gin.H{
264 | "family": "gpt-4",
265 | "object": "model_capabilities",
266 | "type": "chat",
267 | },
268 | "id": "gpt-4-0613",
269 | "name": "GPT 4 (2023-06-13)",
270 | "object": "model",
271 | "version": "gpt-4-0613",
272 | },
273 | {
274 | "capabilities": gin.H{
275 | "family": "gpt-4-turbo",
276 | "object": "model_capabilities",
277 | "type": "chat",
278 | },
279 | "id": "gpt-4-0125-preview",
280 | "name": "GPT 4 Turbo (2024-01-25 Preview)",
281 | "object": "model",
282 | "version": "gpt-4-0125-preview",
283 | },
284 | {
285 | "capabilities": gin.H{
286 | "family": "text-embedding-ada-002",
287 | "object": "model_capabilities",
288 | "type": "embeddings",
289 | },
290 | "id": "text-embedding-ada-002",
291 | "name": "Embedding V2 Ada",
292 | "object": "model",
293 | "version": "text-embedding-ada-002",
294 | },
295 | {
296 | "capabilities": gin.H{
297 | "family": "text-embedding-ada-002",
298 | "object": "model_capabilities",
299 | "type": "embeddings",
300 | },
301 | "id": "text-embedding-ada-002-index",
302 | "name": "Embedding V2 Ada (Index)",
303 | "object": "model",
304 | "version": "text-embedding-ada-002",
305 | },
306 | {
307 | "capabilities": gin.H{
308 | "family": "text-embedding-3-small",
309 | "object": "model_capabilities",
310 | "type": "embeddings",
311 | },
312 | "id": "text-embedding-3-small",
313 | "name": "Embedding V3 small",
314 | "object": "model",
315 | "version": "text-embedding-3-small",
316 | },
317 | {
318 | "capabilities": gin.H{
319 | "family": "text-embedding-3-small",
320 | "object": "model_capabilities",
321 | "type": "embeddings",
322 | },
323 | "id": "text-embedding-3-small-inference",
324 | "name": "Embedding V3 small (Inference)",
325 | "object": "model",
326 | "version": "text-embedding-3-small",
327 | },
328 | },
329 | "object": "list",
330 | })
331 | }
332 |
333 | func (s *ProxyService) completions(c *gin.Context) {
334 | ctx := c.Request.Context()
335 |
336 | body, err := io.ReadAll(c.Request.Body)
337 | if nil != err {
338 | c.AbortWithStatus(http.StatusBadRequest)
339 | return
340 | }
341 |
342 | model := gjson.GetBytes(body, "model").String()
343 | if mapped, ok := s.cfg.ChatModelMap[model]; ok {
344 | model = mapped
345 | } else {
346 | model = s.cfg.ChatModelDefault
347 | }
348 | body, _ = sjson.SetBytes(body, "model", model)
349 |
350 | if !gjson.GetBytes(body, "function_call").Exists() {
351 | messages := gjson.GetBytes(body, "messages").Array()
352 | lastIndex := len(messages) - 1
353 | if !strings.Contains(messages[lastIndex].Get("content").String(), "Respond in the following locale") {
354 | locale := s.cfg.ChatLocale
355 | if locale == "" {
356 | locale = "zh_CN"
357 | }
358 | body, _ = sjson.SetBytes(body, "messages."+strconv.Itoa(lastIndex)+".content", messages[lastIndex].Get("content").String()+"Respond in the following locale: "+locale+".")
359 | }
360 | }
361 |
362 | body, _ = sjson.DeleteBytes(body, "intent")
363 | body, _ = sjson.DeleteBytes(body, "intent_threshold")
364 | body, _ = sjson.DeleteBytes(body, "intent_content")
365 |
366 | if int(gjson.GetBytes(body, "max_tokens").Int()) > s.cfg.ChatMaxTokens {
367 | body, _ = sjson.SetBytes(body, "max_tokens", s.cfg.ChatMaxTokens)
368 | }
369 |
370 | proxyUrl := s.cfg.ChatApiBase + "/chat/completions"
371 | req, err := http.NewRequestWithContext(ctx, http.MethodPost, proxyUrl, io.NopCloser(bytes.NewBuffer(body)))
372 | if nil != err {
373 | c.AbortWithStatus(http.StatusInternalServerError)
374 | return
375 | }
376 |
377 | req.Header.Set("Content-Type", "application/json")
378 | req.Header.Set("Authorization", "Bearer "+s.cfg.ChatApiKey)
379 | if "" != s.cfg.ChatApiOrganization {
380 | req.Header.Set("OpenAI-Organization", s.cfg.ChatApiOrganization)
381 | }
382 | if "" != s.cfg.ChatApiProject {
383 | req.Header.Set("OpenAI-Project", s.cfg.ChatApiProject)
384 | }
385 |
386 | resp, err := s.client.Do(req)
387 | if nil != err {
388 | if errors.Is(err, context.Canceled) {
389 | c.AbortWithStatus(http.StatusRequestTimeout)
390 | return
391 | }
392 |
393 | log.Println("request conversation failed:", err.Error())
394 | c.AbortWithStatus(http.StatusInternalServerError)
395 | return
396 | }
397 | defer closeIO(resp.Body)
398 |
399 | if resp.StatusCode != http.StatusOK { // log
400 | body, _ := io.ReadAll(resp.Body)
401 | log.Println("request completions failed:", string(body))
402 |
403 | resp.Body = io.NopCloser(bytes.NewBuffer(body))
404 | }
405 |
406 | c.Status(resp.StatusCode)
407 |
408 | contentType := resp.Header.Get("Content-Type")
409 | if "" != contentType {
410 | c.Header("Content-Type", contentType)
411 | }
412 |
413 | _, _ = io.Copy(c.Writer, resp.Body)
414 | }
415 |
416 | func (s *ProxyService) codeCompletions(c *gin.Context) {
417 | ctx := c.Request.Context()
418 |
419 | time.Sleep(200 * time.Millisecond)
420 | if ctx.Err() != nil {
421 | abortCodex(c, http.StatusRequestTimeout)
422 | return
423 | }
424 |
425 | body, err := io.ReadAll(c.Request.Body)
426 | if nil != err {
427 | abortCodex(c, http.StatusBadRequest)
428 | return
429 | }
430 |
431 | body = ConstructRequestBody(body, s.cfg)
432 |
433 | proxyUrl := s.cfg.CodexApiBase + "/completions"
434 | req, err := http.NewRequestWithContext(ctx, http.MethodPost, proxyUrl, io.NopCloser(bytes.NewBuffer(body)))
435 | if nil != err {
436 | abortCodex(c, http.StatusInternalServerError)
437 | return
438 | }
439 |
440 | req.Header.Set("Content-Type", "application/json")
441 | req.Header.Set("Authorization", "Bearer "+s.cfg.CodexApiKey)
442 | if "" != s.cfg.CodexApiOrganization {
443 | req.Header.Set("OpenAI-Organization", s.cfg.CodexApiOrganization)
444 | }
445 | if "" != s.cfg.CodexApiProject {
446 | req.Header.Set("OpenAI-Project", s.cfg.CodexApiProject)
447 | }
448 |
449 | resp, err := s.client.Do(req)
450 | if nil != err {
451 | if errors.Is(err, context.Canceled) {
452 | abortCodex(c, http.StatusRequestTimeout)
453 | return
454 | }
455 |
456 | log.Println("request completions failed:", err.Error())
457 | abortCodex(c, http.StatusInternalServerError)
458 | return
459 | }
460 | defer closeIO(resp.Body)
461 |
462 | if resp.StatusCode != http.StatusOK {
463 | body, _ := io.ReadAll(resp.Body)
464 | log.Println("request completions failed:", string(body))
465 |
466 | abortCodex(c, resp.StatusCode)
467 | return
468 | }
469 |
470 | c.Status(resp.StatusCode)
471 |
472 | contentType := resp.Header.Get("Content-Type")
473 | if "" != contentType {
474 | c.Header("Content-Type", contentType)
475 | }
476 |
477 | _, _ = io.Copy(c.Writer, resp.Body)
478 | }
479 |
480 | func ConstructRequestBody(body []byte, cfg *config) []byte {
481 | body, _ = sjson.DeleteBytes(body, "extra")
482 | body, _ = sjson.DeleteBytes(body, "nwo")
483 | body, _ = sjson.SetBytes(body, "model", cfg.CodeInstructModel)
484 |
485 | if int(gjson.GetBytes(body, "max_tokens").Int()) > cfg.CodexMaxTokens {
486 | body, _ = sjson.SetBytes(body, "max_tokens", cfg.CodexMaxTokens)
487 | }
488 |
489 | if strings.Contains(cfg.CodeInstructModel, StableCodeModelPrefix) {
490 | return constructWithStableCodeModel(body)
491 | } else if strings.HasPrefix(cfg.CodeInstructModel, DeepSeekCoderModel) {
492 | if gjson.GetBytes(body, "n").Int() > 1 {
493 | body, _ = sjson.SetBytes(body, "n", 1)
494 | }
495 | }
496 |
497 | if strings.HasSuffix(cfg.ChatApiBase, "chat") {
498 | // @Todo constructWithChatModel
499 | // 如果code base以chat结尾则构建chatModel,暂时没有好的prompt
500 | }
501 |
502 | return body
503 | }
504 |
505 | func constructWithStableCodeModel(body []byte) []byte {
506 | suffix := gjson.GetBytes(body, "suffix")
507 | prompt := gjson.GetBytes(body, "prompt")
508 | content := fmt.Sprintf("%s%s", prompt, suffix)
509 |
510 | // 创建新的 JSON 对象并添加到 body 中
511 | messages := []map[string]string{
512 | {
513 | "role": "user",
514 | "content": content,
515 | },
516 | }
517 | return constructWithChatModel(body, messages)
518 | }
519 |
520 | func constructWithChatModel(body []byte, messages interface{}) []byte {
521 |
522 | body, _ = sjson.SetBytes(body, "messages", messages)
523 |
524 | // fmt.Printf("Request Body: %s\n", body)
525 | // 2. 将转义的字符替换回原来的字符
526 | jsonStr := string(body)
527 | jsonStr = strings.ReplaceAll(jsonStr, "\\u003c", "<")
528 | jsonStr = strings.ReplaceAll(jsonStr, "\\u003e", ">")
529 | return []byte(jsonStr)
530 | }
531 |
532 | var server *http.Server
533 |
534 | func isPortInUse(port int) bool {
535 | conn, err := net.Listen("tcp", fmt.Sprintf(":%d", port))
536 | if err != nil {
537 | return true
538 | }
539 | conn.Close()
540 | return false
541 | }
542 | func Run_override(configContent string, logWriter *bytes.Buffer) (bool, error) {
543 | cfg := readConfig(configContent)
544 | // 检查端口是否已被占用
545 | port := ecore.StrCut(cfg.Bind, ":$")
546 | if isPortInUse(int(ecore.E到整数(port))) {
547 | return false, fmt.Errorf("端口 %s 已在使用中", port)
548 | }
549 |
550 | gin.SetMode(gin.ReleaseMode)
551 | gin.DefaultWriter = logWriter
552 | r := gin.Default()
553 |
554 | proxyService, err := NewProxyService(cfg)
555 | if nil != err {
556 | log.Fatal(err)
557 | return false, err
558 | }
559 |
560 | proxyService.InitRoutes(r)
561 |
562 | server = &http.Server{
563 | Addr: cfg.Bind,
564 | Handler: r,
565 | }
566 | //0.0.0.0:8181
567 | // 2024/08/08 18:51:06 listen: listen tcp 0.0.0.0:8181: bind: address already in use 我的程序为什么挂了 可以不挂吗
568 | go func() {
569 | if err := server.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
570 | log.Fatalf("listen: %s\n", err)
571 | }
572 | }()
573 |
574 | return true, nil
575 | }
576 |
577 | func Stop_override() {
578 | log.Println("Server is shutting down...")
579 |
580 | ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
581 | defer cancel()
582 |
583 | if err := server.Shutdown(ctx); err != nil {
584 | log.Fatal("Server forced to shutdown:", err)
585 | }
586 |
587 | log.Println("Server exiting")
588 | }
589 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
2 | github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I=
3 | github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
4 | github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible h1:1G1pk05UrOh0NlF1oeaaix1x8XzrfjIDK47TY0Zehcw=
5 | github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
6 | github.com/akavel/rsrc v0.10.2 h1:Zxm8V5eI1hW4gGaYsJQUhxpjkENuG91ki8B4zCrvEsw=
7 | github.com/akavel/rsrc v0.10.2/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c=
8 | github.com/axgle/mahonia v0.0.0-20180208002826-3358181d7394 h1:OYA+5W64v3OgClL+IrOD63t4i/RW7RqrAVl9LTZ9UqQ=
9 | github.com/axgle/mahonia v0.0.0-20180208002826-3358181d7394/go.mod h1:Q8n74mJTIgjX4RBBcHnJ05h//6/k6foqmgE45jTQtxg=
10 | github.com/bep/debounce v1.2.1 h1:v67fRdBA9UQu2NhLFXrSg0Brw7CexQekrBwDMM8bzeY=
11 | github.com/bep/debounce v1.2.1/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3ISbCJ0=
12 | github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
13 | github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s=
14 | github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
15 | github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
16 | github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams=
17 | github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
18 | github.com/clbanning/mxj v1.8.4 h1:HuhwZtbyvyOw+3Z1AowPkU87JkJUSv751ELWaiTpj8I=
19 | github.com/clbanning/mxj v1.8.4/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng=
20 | github.com/clbanning/mxj/v2 v2.5.5 h1:oT81vUeEiQQ/DcHbzSytRngP6Ky9O+L+0Bw0zSJag9E=
21 | github.com/clbanning/mxj/v2 v2.5.5/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s=
22 | github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
23 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
24 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
25 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
26 | github.com/dchest/jsmin v0.0.0-20220218165748-59f39799265f h1:OGqDDftRTwrvUoL6pOG7rYTmWsTCvyEWFsMjg+HcOaA=
27 | github.com/dchest/jsmin v0.0.0-20220218165748-59f39799265f/go.mod h1:Dv9D0NUlAsaQcGQZa5kc5mqR9ua72SmA8VXi4cd+cBw=
28 | github.com/duolabmeng6/goefun v1.3.8 h1:eAeiAs4YBoK9xn5/SHuJVb6kxQIEOTOehdtboGONowM=
29 | github.com/duolabmeng6/goefun v1.3.8/go.mod h1:mg175FgUQ0tWPSfuobtIIaL7el8ZOYyLmJJi28dXOTg=
30 | github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
31 | github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
32 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
33 | github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI=
34 | github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
35 | github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
36 | github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
37 | github.com/ganlvtech/go-exportable-cookiejar v0.0.0-20181231191301-34af33d7487e h1:zF8BCigEVAtW07yhpupU1FQ8DEzINaJ6HVfPUQY/uqY=
38 | github.com/ganlvtech/go-exportable-cookiejar v0.0.0-20181231191301-34af33d7487e/go.mod h1:EdkZC5tfJxfNZYtycwAG0adNOJRpGUyIhLcG6DvhyU0=
39 | github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
40 | github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
41 | github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
42 | github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
43 | github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
44 | github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=
45 | github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
46 | github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
47 | github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
48 | github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
49 | github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
50 | github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
51 | github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
52 | github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
53 | github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
54 | github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
55 | github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
56 | github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js=
57 | github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
58 | github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
59 | github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
60 | github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
61 | github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
62 | github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
63 | github.com/gogf/gf v1.12.3 h1:71arUxK76Fr8tlbt3NcVKbWHnn0PBrLoAj8Fk06CmPM=
64 | github.com/gogf/gf v1.12.3/go.mod h1:toXfwdoKLjTZXhaQL3c5o4EZuzsFABratQvGyu5A/6w=
65 | github.com/gogf/gf/v2 v2.4.1 h1:snsuvDhNFiRoAuWBbKfIIng0KyMaRA87Qr03GLir5j8=
66 | github.com/gogf/gf/v2 v2.4.1/go.mod h1:tsbmtwcAl2chcYoq/fP9W2FZf06aw4i89X34nbSHo9Y=
67 | github.com/gogf/guuid v1.0.0 h1:tUB0VKMcFmbIrteVjRAEvoxXX7vQNOA6DMf1ub9xli8=
68 | github.com/gogf/guuid v1.0.0/go.mod h1:weuAJZ9kkvXmvYgQKitT0RdX0l+94D0eFf5NoJxDRFI=
69 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
70 | github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
71 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
72 | github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
73 | github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
74 | github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
75 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
76 | github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
77 | github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
78 | github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
79 | github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
80 | github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
81 | github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
82 | github.com/gqcn/structs v1.1.1 h1:cyzGRwfmn3d1d54fwW3KUNyG9QxR0ldIeqwFGeBt638=
83 | github.com/gqcn/structs v1.1.1/go.mod h1:/aBhTBSsKQ2Ec9pbnYdGphtdWXHFn4KrCL0fXM/Adok=
84 | github.com/grokify/html-strip-tags-go v0.0.0-20190921062105-daaa06bf1aaf/go.mod h1:2Su6romC5/1VXOQMaWL2yb618ARB8iVo6/DR99A6d78=
85 | github.com/grokify/html-strip-tags-go v0.0.1 h1:0fThFwLbW7P/kOiTBs03FsJSV9RM2M/Q/MOnCQxKMo0=
86 | github.com/grokify/html-strip-tags-go v0.0.1/go.mod h1:2Su6romC5/1VXOQMaWL2yb618ARB8iVo6/DR99A6d78=
87 | github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e h1:Q3+PugElBCf4PFpxhErSzU3/PY5sFL5Z6rfv4AbGAck=
88 | github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e/go.mod h1:alcuEEnZsY1WQsagKhZDsoPCRoOijYqhZvPwLG0kzVs=
89 | github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
90 | github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
91 | github.com/josephspurrier/goversioninfo v1.4.0 h1:Puhl12NSHUSALHSuzYwPYQkqa2E1+7SrtAPJorKK0C8=
92 | github.com/josephspurrier/goversioninfo v1.4.0/go.mod h1:JWzv5rKQr+MmW+LvM412ToT/IkYDZjaclF2pKDss8IY=
93 | github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
94 | github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
95 | github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
96 | github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk=
97 | github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
98 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
99 | github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
100 | github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
101 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
102 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
103 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
104 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
105 | github.com/labstack/echo/v4 v4.10.2 h1:n1jAhnq/elIFTHr1EYpiYtyKgx4RW9ccVgkqByZaN2M=
106 | github.com/labstack/echo/v4 v4.10.2/go.mod h1:OEyqf2//K1DFdE57vw2DRgWY0M7s65IVQO2FzvI4J5k=
107 | github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8=
108 | github.com/labstack/gommon v0.4.0/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM=
109 | github.com/leaanthony/debme v1.2.1 h1:9Tgwf+kjcrbMQ4WnPcEIUcQuIZYqdWftzZkBr+i/oOc=
110 | github.com/leaanthony/debme v1.2.1/go.mod h1:3V+sCm5tYAgQymvSOfYQ5Xx2JCr+OXiD9Jkw3otUjiA=
111 | github.com/leaanthony/go-ansi-parser v1.6.0 h1:T8TuMhFB6TUMIUm0oRrSbgJudTFw9csT3ZK09w0t4Pg=
112 | github.com/leaanthony/go-ansi-parser v1.6.0/go.mod h1:+vva/2y4alzVmmIEpk9QDhA7vLC5zKDTRwfZGOp3IWU=
113 | github.com/leaanthony/gosod v1.0.3 h1:Fnt+/B6NjQOVuCWOKYRREZnjGyvg+mEhd1nkkA04aTQ=
114 | github.com/leaanthony/gosod v1.0.3/go.mod h1:BJ2J+oHsQIyIQpnLPjnqFGTMnOZXDbvWtRCSG7jGxs4=
115 | github.com/leaanthony/slicer v1.5.0/go.mod h1:FwrApmf8gOrpzEWM2J/9Lh79tyq8KTX5AzRtwV7m4AY=
116 | github.com/leaanthony/slicer v1.6.0 h1:1RFP5uiPJvT93TAHi+ipd3NACobkW53yUiBqZheE/Js=
117 | github.com/leaanthony/slicer v1.6.0/go.mod h1:o/Iz29g7LN0GqH3aMjWAe90381nyZlDNquK+mtH2Fj8=
118 | github.com/leaanthony/u v1.1.0 h1:2n0d2BwPVXSUq5yhe8lJPHdxevE2qK5G99PMStMZMaI=
119 | github.com/leaanthony/u v1.1.0/go.mod h1:9+o6hejoRljvZ3BzdYlVL0JYCwtnAsVuN9pVTQcaRfI=
120 | github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
121 | github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
122 | github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo=
123 | github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
124 | github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE=
125 | github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU=
126 | github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
127 | github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
128 | github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
129 | github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
130 | github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
131 | github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
132 | github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
133 | github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
134 | github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
135 | github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
136 | github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
137 | github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
138 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
139 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
140 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
141 | github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
142 | github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
143 | github.com/ncruces/zenity v0.10.13 h1:0Gd/EdjjEQIhrFaJ05Q5ZvyjlcjnorlZpdzgUzqQIH0=
144 | github.com/ncruces/zenity v0.10.13/go.mod h1:UyAUPSjHm1hOdeZa3Lrh/zmItyGq+iH5AP6xSCx8CFM=
145 | github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
146 | github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
147 | github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
148 | github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ=
149 | github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4=
150 | github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU=
151 | github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI=
152 | github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
153 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
154 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
155 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
156 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
157 | github.com/randall77/makefat v0.0.0-20210315173500-7ddd0e42c844 h1:GranzK4hv1/pqTIhMTXt2X8MmMOuH3hMeUR0o9SP5yc=
158 | github.com/randall77/makefat v0.0.0-20210315173500-7ddd0e42c844/go.mod h1:T1TLSfyWVBRXVGzWd0o9BI4kfoO9InEgfQe4NV3mLz8=
159 | github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
160 | github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
161 | github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
162 | github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
163 | github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
164 | github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
165 | github.com/samber/lo v1.38.1 h1:j2XEAqXKb09Am4ebOg31SpvzUTTs6EN3VfgeLUhPdXM=
166 | github.com/samber/lo v1.38.1/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA=
167 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
168 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
169 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
170 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
171 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
172 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
173 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
174 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
175 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
176 | github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
177 | github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
178 | github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
179 | github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
180 | github.com/teris-io/shortid v0.0.0-20171029131806-771a37caa5cf h1:Z2X3Os7oRzpdJ75iPqWZc0HeJWFYNCvKsfpQwFpRNTA=
181 | github.com/teris-io/shortid v0.0.0-20171029131806-771a37caa5cf/go.mod h1:M8agBzgqHIhgj7wEn9/0hJUZcrvt9VY+Ln+S1I5Mha0=
182 | github.com/tidwall/gjson v1.8.0/go.mod h1:5/xDoumyyDNerp2U36lyolv46b3uF/9Bu6OfyQ9GImk=
183 | github.com/tidwall/gjson v1.14.4 h1:uo0p8EbA09J7RQaflQ1aBRffTR7xedD2bcIVSYxLnkM=
184 | github.com/tidwall/gjson v1.14.4/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
185 | github.com/tidwall/match v1.0.3/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
186 | github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
187 | github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
188 | github.com/tidwall/pretty v1.1.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
189 | github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
190 | github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
191 | github.com/tidwall/sjson v1.1.7 h1:sgVPwu/yygHJ2m1pJDLgGM/h+1F5odx5Q9ljG3imRm8=
192 | github.com/tidwall/sjson v1.1.7/go.mod h1:w/yG+ezBeTdUxiKs5NcPicO9diP38nk96QBAbIIGeFs=
193 | github.com/tkrajina/go-reflector v0.5.6 h1:hKQ0gyocG7vgMD2M3dRlYN6WBBOmdoOzJ6njQSepKdE=
194 | github.com/tkrajina/go-reflector v0.5.6/go.mod h1:ECbqLgccecY5kPmPmXg1MrHW585yMcDkVl6IvJe64T4=
195 | github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
196 | github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
197 | github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
198 | github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
199 | github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
200 | github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
201 | github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
202 | github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
203 | github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
204 | github.com/wailsapp/go-webview2 v1.0.10 h1:PP5Hug6pnQEAhfRzLCoOh2jJaPdrqeRgJKZhyYyDV/w=
205 | github.com/wailsapp/go-webview2 v1.0.10/go.mod h1:Uk2BePfCRzttBBjFrBmqKGJd41P6QIHeV9kTgIeOZNo=
206 | github.com/wailsapp/mimetype v1.4.1 h1:pQN9ycO7uo4vsUUuPeHEYoUkLVkaRntMnHJxVwYhwHs=
207 | github.com/wailsapp/mimetype v1.4.1/go.mod h1:9aV5k31bBOv5z6u+QP8TltzvNGJPmNJD4XlAL3U+j3o=
208 | github.com/wailsapp/wails/v2 v2.9.1 h1:irsXnoQrCpeKzKTYZ2SUVlRRyeMR6I0vCO9Q1cvlEdc=
209 | github.com/wailsapp/wails/v2 v2.9.1/go.mod h1:7maJV2h+Egl11Ak8QZN/jlGLj2wg05bsQS+ywJPT0gI=
210 | github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
211 | go.opentelemetry.io/otel v1.7.0 h1:Z2lA3Tdch0iDcrhJXDIlC94XE+bxok1F9B+4Lz/lGsM=
212 | go.opentelemetry.io/otel v1.7.0/go.mod h1:5BdUoMIz5WEs0vt0CUEMtSSaTSHBBVwrhnz7+nrD5xk=
213 | go.opentelemetry.io/otel/sdk v1.7.0 h1:4OmStpcKVOfvDOgCt7UriAPtKolwIhxpnSNI/yK+1B0=
214 | go.opentelemetry.io/otel/sdk v1.7.0/go.mod h1:uTEOTwaqIVuTGiJN7ii13Ibp75wJmYUDe374q6cZwUU=
215 | go.opentelemetry.io/otel/trace v1.7.0 h1:O37Iogk1lEkMRXewVtZ1BBTVn5JEp8GrJvP92bJqC6o=
216 | go.opentelemetry.io/otel/trace v1.7.0/go.mod h1:fzLSB9nqR2eXzxPXb2JW9IKE+ScyXA48yyE4TNvoHqU=
217 | go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
218 | go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
219 | golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
220 | golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k=
221 | golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
222 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
223 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
224 | golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
225 | golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
226 | golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 h1:k/i9J1pBpvlfR+9QsetwPyERsqu1GIbi967PQMq3Ivc=
227 | golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w=
228 | golang.org/x/image v0.18.0 h1:jGzIakQa/ZXI1I0Fxvaa9W7yP25TqT6cHIHn+6CqvSQ=
229 | golang.org/x/image v0.18.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E=
230 | golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
231 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
232 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
233 | golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
234 | golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
235 | golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
236 | golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
237 | golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
238 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
239 | golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
240 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
241 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
242 | golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
243 | golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
244 | golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
245 | golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
246 | golang.org/x/sys v0.0.0-20200810151505-1b9f1253b3ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
247 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
248 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
249 | golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
250 | golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
251 | golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
252 | golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
253 | golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
254 | golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
255 | golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
256 | golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
257 | golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
258 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
259 | golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
260 | golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
261 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
262 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
263 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
264 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
265 | golang.org/x/text v0.3.8-0.20211105212822-18b340fc7af2/go.mod h1:EFNZuWvGYxIRUEX+K8UmCFwYmZjqcrnq15ZuVldZkZ0=
266 | golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
267 | golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
268 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
269 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
270 | golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
271 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
272 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
273 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
274 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
275 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
276 | google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
277 | google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
278 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
279 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
280 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
281 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
282 | gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
283 | gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
284 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
285 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
286 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
287 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
288 | rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
289 |
--------------------------------------------------------------------------------