mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-07-12 16:02:48 +08:00
- 新增 web-server 运行模式与浏览器端运行时桥接 - 支持管理员密码、会话策略、Google Authenticator 与恢复码 - 设置中心补充浏览器访问认证状态与改密入口 - 优化初始化向导,未启用 2FA 时跳过验证器步骤 Refs #618
177 lines
5.0 KiB
Go
177 lines
5.0 KiB
Go
package main
|
||
|
||
import (
|
||
"context"
|
||
"fmt"
|
||
"os"
|
||
"runtime"
|
||
"runtime/debug"
|
||
"strings"
|
||
|
||
aiservice "GoNavi-Wails/internal/ai/service"
|
||
"GoNavi-Wails/internal/app"
|
||
"GoNavi-Wails/internal/logger"
|
||
"GoNavi-Wails/internal/mcpserver"
|
||
"GoNavi-Wails/internal/webserver"
|
||
|
||
"github.com/wailsapp/wails/v2"
|
||
"github.com/wailsapp/wails/v2/pkg/menu"
|
||
"github.com/wailsapp/wails/v2/pkg/menu/keys"
|
||
"github.com/wailsapp/wails/v2/pkg/options"
|
||
"github.com/wailsapp/wails/v2/pkg/options/assetserver"
|
||
"github.com/wailsapp/wails/v2/pkg/options/mac"
|
||
"github.com/wailsapp/wails/v2/pkg/options/windows"
|
||
wailsRuntime "github.com/wailsapp/wails/v2/pkg/runtime"
|
||
)
|
||
|
||
const nativeSelectCurrentLineEvent = "gonavi:native-select-current-line"
|
||
|
||
func main() {
|
||
// 大结果集导出(88W+ 行)时,JSON 编解码会产生 5-8 倍内存副本,
|
||
// Go 默认 GOGC=100 下堆翻倍才触发 GC,叠加 Windows MADV_FREE 不归还 RSS,
|
||
// 会导致 RSS 单调爬升到峰值后不下降。这里收紧到 50,让 GC 更早触发。
|
||
// 代价是 CPU 开销略增,但导出/导入场景属 I/O 密集型,GC 开销可忽略。
|
||
debug.SetGCPercent(50)
|
||
|
||
if runSpecialMode(os.Args[1:]) {
|
||
return
|
||
}
|
||
|
||
// Create an instance of the app structure
|
||
application := app.NewApp()
|
||
aiService := aiservice.NewService()
|
||
lowMemoryMode := isLowMemoryMode()
|
||
var runtimeCtx context.Context
|
||
backgroundColour := &options.RGBA{R: 0, G: 0, B: 0, A: 0}
|
||
windowsBackdrop := windows.Acrylic
|
||
if lowMemoryMode {
|
||
backgroundColour = &options.RGBA{R: 255, G: 255, B: 255, A: 255}
|
||
windowsBackdrop = windows.None
|
||
}
|
||
var appMenu *menu.Menu
|
||
if strings.EqualFold(strings.TrimSpace(runtime.GOOS), "darwin") {
|
||
appMenu = buildMacApplicationMenu(func() {
|
||
if runtimeCtx == nil {
|
||
return
|
||
}
|
||
wailsRuntime.EventsEmit(runtimeCtx, nativeSelectCurrentLineEvent)
|
||
}, true)
|
||
}
|
||
|
||
// Create application with options
|
||
err := wails.Run(&options.App{
|
||
Title: "GoNavi",
|
||
Width: 1024,
|
||
Height: 768,
|
||
MinWidth: 900,
|
||
MinHeight: 600,
|
||
Frameless: true,
|
||
AssetServer: &assetserver.Options{
|
||
Assets: assets,
|
||
},
|
||
BackgroundColour: backgroundColour,
|
||
Menu: appMenu,
|
||
OnStartup: func(ctx context.Context) {
|
||
runtimeCtx = ctx
|
||
app.InitializeLifecycle(application, ctx)
|
||
aiservice.InitializeLifecycle(aiService, ctx)
|
||
},
|
||
OnShutdown: func(ctx context.Context) {
|
||
aiService.Shutdown()
|
||
application.Shutdown()
|
||
},
|
||
OnBeforeClose: app.NewBeforeCloseHandler(application),
|
||
Bind: []interface{}{
|
||
application,
|
||
aiService,
|
||
},
|
||
Windows: &windows.Options{
|
||
WebviewIsTransparent: !lowMemoryMode,
|
||
WindowIsTranslucent: !lowMemoryMode,
|
||
BackdropType: windowsBackdrop,
|
||
DisableWindowIcon: false,
|
||
DisableFramelessWindowDecorations: false,
|
||
WebviewUserDataPath: resolveWindowsWebviewUserDataPath(),
|
||
},
|
||
Mac: &mac.Options{
|
||
WebviewIsTransparent: true,
|
||
WindowIsTranslucent: true,
|
||
},
|
||
})
|
||
|
||
if err != nil {
|
||
logger.Error(err, "应用启动失败")
|
||
}
|
||
}
|
||
|
||
func buildMacApplicationMenu(onNativeSelectCurrentLine func(), frameless bool) *menu.Menu {
|
||
result := menu.NewMenuFromItems(
|
||
menu.AppMenu(),
|
||
menu.EditMenu(),
|
||
)
|
||
if !frameless {
|
||
result.Append(menu.WindowMenu())
|
||
}
|
||
queryEditorMenu := result.AddSubmenu("SQL")
|
||
queryEditorMenu.AddText("Copy Current Line", keys.CmdOrCtrl("e"), func(_ *menu.CallbackData) {
|
||
if onNativeSelectCurrentLine != nil {
|
||
onNativeSelectCurrentLine()
|
||
}
|
||
})
|
||
return result
|
||
}
|
||
|
||
func runSpecialMode(args []string) bool {
|
||
if len(args) == 0 {
|
||
return false
|
||
}
|
||
|
||
mode := strings.ToLower(strings.TrimSpace(args[0]))
|
||
switch mode {
|
||
case "mcp-server", "--mcp-server":
|
||
if err := runMCPServerMode(context.Background(), args[1:]); err != nil {
|
||
logger.Error(err, "GoNavi MCP Server 退出")
|
||
}
|
||
return true
|
||
case "web-server", "--web-server":
|
||
if err := webserver.Run(context.Background(), assets, args[1:]); err != nil {
|
||
logger.Error(err, "GoNavi Web Server 退出")
|
||
}
|
||
return true
|
||
default:
|
||
return false
|
||
}
|
||
}
|
||
|
||
func runMCPServerMode(ctx context.Context, args []string) error {
|
||
if len(args) == 0 {
|
||
return mcpserver.RunAppStdioServer(ctx)
|
||
}
|
||
|
||
mode := strings.ToLower(strings.TrimSpace(args[0]))
|
||
switch mode {
|
||
case "stdio", "--stdio":
|
||
return mcpserver.RunAppStdioServer(ctx)
|
||
case "http", "--http", "streamable-http", "--streamable-http":
|
||
options, err := mcpserver.ParseHTTPServerOptions(args[1:])
|
||
if err != nil {
|
||
return err
|
||
}
|
||
logger.Infof("GoNavi MCP Streamable HTTP Server 启动:addr=%s path=%s schemaOnly=%v", options.Addr, options.Path, options.SchemaOnly)
|
||
return mcpserver.RunAppStreamableHTTPServer(ctx, options)
|
||
case "remote-config", "--remote-config":
|
||
return mcpserver.WriteRemoteMCPClientConfig(os.Stdout, args[1:])
|
||
default:
|
||
return fmt.Errorf("未知 MCP server 模式: %s(支持 stdio/http/remote-config)", args[0])
|
||
}
|
||
}
|
||
|
||
func isLowMemoryMode() bool {
|
||
switch strings.ToLower(strings.TrimSpace(os.Getenv("GONAVI_LOW_MEMORY_MODE"))) {
|
||
case "1", "true", "yes", "on":
|
||
return true
|
||
default:
|
||
return false
|
||
}
|
||
}
|