package main import ( "embed" "flag" "log" "os" "path/filepath" "runtime" "github.com/wailsapp/wails/v2" "github.com/wailsapp/wails/v2/pkg/options" "github.com/wailsapp/wails/v2/pkg/options/assetserver" ) //go:embed frontend/dist var assets embed.FS func main() { apiURL := flag.String("api", "https://vpn.em-sysadmin.xyz", "API server URL") dataDir := flag.String("data", defaultDataDir(), "data directory") flag.Parse() // Ensure data dir exists os.MkdirAll(*dataDir, 0o755) // Setup file logging so we always have diagnostics logPath := filepath.Join(*dataDir, "vpnem.log") logFile, err := os.OpenFile(logPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0o644) if err == nil { log.SetOutput(logFile) defer logFile.Close() } log.Printf("vpnem starting, data=%s, api=%s, os=%s", *dataDir, *apiURL, runtime.GOOS) // Kill previous instances of vpnem and sing-box killPrevious() // Check wintun.dll on Windows if runtime.GOOS == "windows" { exe, _ := os.Executable() exeDir := filepath.Dir(exe) wintunPaths := []string{ filepath.Join(exeDir, "wintun.dll"), filepath.Join(*dataDir, "wintun.dll"), "wintun.dll", } found := false for _, p := range wintunPaths { if _, err := os.Stat(p); err == nil { log.Printf("wintun.dll found: %s", p) found = true break } } if !found { log.Printf("WARNING: wintun.dll not found! TUN will fail. Searched: %v", wintunPaths) } } app := NewApp(*dataDir, *apiURL) log.Println("starting Wails UI") if err := wails.Run(&options.App{ Title: "vpnem", Width: 480, Height: 600, MinWidth: 400, MinHeight: 500, AssetServer: &assetserver.Options{ Assets: assets, }, OnStartup: app.startup, OnShutdown: app.shutdown, Bind: []interface{}{ app, }, }); err != nil { log.Printf("FATAL wails error: %v", err) // On Windows, also write to a visible error file if runtime.GOOS == "windows" { errPath := filepath.Join(*dataDir, "ERROR.txt") os.WriteFile(errPath, []byte("vpnem failed to start:\n"+err.Error()+"\n\nCheck vpnem.log for details.\n"), 0o644) } os.Exit(1) } } func defaultDataDir() string { if runtime.GOOS == "windows" { return `C:\ProgramData\vpnem` } home, _ := os.UserHomeDir() return filepath.Join(home, ".local", "share", "vpnem") }