Repository navigation
Expand file tree
/
Copy pathapp.go
More file actions
127 lines (108 loc) · 2.95 KB
/
Copy pathapp.go
File metadata and controls
127 lines (108 loc) · 2.95 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
package main
import (
"context"
"fmt"
"os"
"os/signal"
"sync"
"syscall"
"time"
"github.com/wailsapp/wails/v3/pkg/application"
"changeme/internal/core"
"changeme/internal/storage"
"changeme/internal/sysproxy"
"changeme/internal/tun"
)
// AppService provides the primary backend services exposed to the Wails v3 frontend.
type AppService struct {
ctx context.Context
mu sync.RWMutex
store *storage.Store
xrayMgr *core.XrayManager
tunMgr *tun.TunManager
connected bool
activeProfileID string
activeMode string
app *application.App
watchCtx context.Context
watchCancel context.CancelFunc
}
// NewAppService initializes a new AppService with storage and engine managers.
func NewAppService() *AppService {
store, _ := storage.NewStore()
return &AppService{
store: store,
xrayMgr: core.NewXrayManager(),
tunMgr: tun.NewTunManager(),
}
}
// ServiceStartup initializes the service upon Wails application startup.
func (a *AppService) ServiceStartup(ctx context.Context, options application.ServiceOptions) error {
a.ctx = ctx
a.app = application.Get()
a.log("Application started (Wails v3)")
// Clean up any lingering proxy settings or orphaned routes from previous sessions
_ = sysproxy.ClearProxy()
_ = a.tunMgr.CleanupStaleRoutes()
// Ensure wintun.dll is available on disk
_ = a.tunMgr.EnsureWintunDLL()
// Apply auto-start configuration if enabled
initialSettings := a.store.GetSettings()
if initialSettings.AutoStart && a.app != nil && a.app.Autostart != nil {
_ = a.app.Autostart.Enable()
}
go a.listenForKillSignals()
go a.statsLoop()
return nil
}
// ServiceShutdown performs graceful cleanup of proxy and TUN routes upon exit.
func (a *AppService) ServiceShutdown() error {
a.log("Shutting down - cleaning up proxies and TUN routes")
_ = a.Disconnect()
return nil
}
func (a *AppService) listenForKillSignals() {
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
defer signal.Stop(c)
select {
case <-c:
_ = sysproxy.ClearProxy()
_ = a.tunMgr.CleanupStaleRoutes()
case <-a.ctx.Done():
return
}
}
func (a *AppService) log(format string, args ...any) {
msg := fmt.Sprintf("[%s] %s", time.Now().Format("15:04:05"), fmt.Sprintf(format, args...))
if a.app != nil {
a.app.Event.Emit("log:new", msg)
}
}
func (a *AppService) statsLoop() {
ticker := time.NewTicker(1 * time.Second)
defer ticker.Stop()
for {
select {
case <-a.ctx.Done():
return
case <-ticker.C:
a.mu.RLock()
isConnected := a.connected
a.mu.RUnlock()
if !isConnected {
continue
}
// Don't query gRPC or wake up WebView2 JS thread if window is hidden in tray
if a.app != nil {
if w, ok := a.app.Window.GetByName("main"); ok && !w.IsVisible() {
continue
}
}
stats, err := a.xrayMgr.QueryTrafficStats(10807)
if err == nil && a.app != nil {
a.app.Event.Emit("traffic:update", stats)
}
}
}
}