add window guard
This commit is contained in:
176
client/app.go
176
client/app.go
@@ -19,6 +19,7 @@ import (
|
||||
"time"
|
||||
|
||||
"client/internal/blocker"
|
||||
"client/internal/guard"
|
||||
"client/internal/screen"
|
||||
"client/internal/winapi"
|
||||
|
||||
@@ -100,7 +101,9 @@ type App struct {
|
||||
dashboard StudentDashboardSnapshot
|
||||
wifiEnforce bool
|
||||
acceptedWifi map[string]bool
|
||||
wifiRejected bool
|
||||
wifiRejected bool
|
||||
quitDialogShown bool
|
||||
monitoringTornDown bool
|
||||
}
|
||||
|
||||
type LocalStats struct {
|
||||
@@ -173,6 +176,9 @@ func (a *App) startup(ctx context.Context) {
|
||||
})
|
||||
}
|
||||
|
||||
// Giám sát môi trường Windows: đa màn hình, desktop ảo, đổi user
|
||||
guard.Start(a.handleGuardViolation)
|
||||
|
||||
// Khởi chạy HTTP server nhận callback login thành công
|
||||
a.startLocalServer()
|
||||
|
||||
@@ -183,6 +189,53 @@ func (a *App) startup(ctx context.Context) {
|
||||
go a.authStorageScanner()
|
||||
}
|
||||
|
||||
func (a *App) handleGuardViolation(reason string) {
|
||||
a.showQuitDialog("Vi phạm giám sát", reason)
|
||||
}
|
||||
|
||||
// tearDownBeforeQuit ngắt mọi kênh giám sát ngay — trước khi hiện dialog (tránh treo OK để duy trì kết nối).
|
||||
func (a *App) tearDownBeforeQuit() {
|
||||
a.mu.Lock()
|
||||
if a.monitoringTornDown {
|
||||
a.mu.Unlock()
|
||||
return
|
||||
}
|
||||
a.monitoringTornDown = true
|
||||
a.mu.Unlock()
|
||||
|
||||
blocker.Instance.Stop()
|
||||
a.stopScreenshotStream()
|
||||
runtime.EventsEmit(a.ctx, "stop_webcam_stream")
|
||||
a.disconnectWS()
|
||||
guard.Stop()
|
||||
}
|
||||
|
||||
func (a *App) isMonitoringActive() bool {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
return !a.monitoringTornDown
|
||||
}
|
||||
|
||||
// showQuitDialog — ngắt giám sát trước, hiện cảnh báo, sinh viên bấm OK rồi app mới thoát.
|
||||
func (a *App) showQuitDialog(title, message string) {
|
||||
a.mu.Lock()
|
||||
if a.quitDialogShown {
|
||||
a.mu.Unlock()
|
||||
return
|
||||
}
|
||||
a.quitDialogShown = true
|
||||
a.mu.Unlock()
|
||||
|
||||
a.tearDownBeforeQuit()
|
||||
|
||||
_, _ = runtime.MessageDialog(a.ctx, runtime.MessageDialogOptions{
|
||||
Type: runtime.WarningDialog,
|
||||
Title: title,
|
||||
Message: message + "\n\nNhấn OK để đóng ứng dụng.",
|
||||
})
|
||||
runtime.Quit(a.ctx)
|
||||
}
|
||||
|
||||
// startLocalServer khởi chạy server lắng nghe callback nhận thông tin sinh viên từ webview
|
||||
func (a *App) startLocalServer() {
|
||||
mux := http.NewServeMux()
|
||||
@@ -224,6 +277,7 @@ func (a *App) startLocalServer() {
|
||||
go a.fetchAllowedApps(st.SystemID)
|
||||
go a.fetchStudentStatus(st.StudentID)
|
||||
go a.fetchWifiPolicy()
|
||||
go a.refreshNetworkStatus()
|
||||
|
||||
// Chuyển hướng WebView về trang dashboard của app bằng cách reload app assets
|
||||
runtime.WindowReloadApp(a.ctx)
|
||||
@@ -264,6 +318,7 @@ func (a *App) loadSession() {
|
||||
go a.fetchAllowedApps(s.SystemID)
|
||||
go a.fetchStudentStatus(s.StudentID)
|
||||
go a.fetchWifiPolicy()
|
||||
go a.refreshNetworkStatus()
|
||||
go a.syncProfileToServer(&s)
|
||||
}
|
||||
}
|
||||
@@ -433,6 +488,9 @@ func (a *App) GetStats() map[string]any {
|
||||
|
||||
// SendWebcamFrame truyền webcam frame từ JS frontend lên máy chủ qua WS
|
||||
func (a *App) SendWebcamFrame(frameBase64 string) {
|
||||
if !a.isMonitoringActive() {
|
||||
return
|
||||
}
|
||||
a.mu.Lock()
|
||||
conn := a.wsConn
|
||||
a.mu.Unlock()
|
||||
@@ -480,50 +538,69 @@ func (a *App) monitorLoop() {
|
||||
ticker := time.NewTicker(10 * time.Second)
|
||||
defer ticker.Stop()
|
||||
|
||||
a.runMonitorTick()
|
||||
|
||||
for {
|
||||
<-ticker.C
|
||||
a.runMonitorTick()
|
||||
}
|
||||
}
|
||||
|
||||
if !a.CheckLoginStatus() {
|
||||
continue
|
||||
}
|
||||
func (a *App) runMonitorTick() {
|
||||
if !a.isMonitoringActive() || !a.CheckLoginStatus() {
|
||||
return
|
||||
}
|
||||
|
||||
wifi := winapi.GetWifiConnection()
|
||||
a.mu.Lock()
|
||||
a.wifiSSID = wifi.SSID
|
||||
a.wifiBSSID = wifi.BSSID
|
||||
a.mu.Unlock()
|
||||
wifi := winapi.GetWifiConnection()
|
||||
a.mu.Lock()
|
||||
a.wifiSSID = wifi.SSID
|
||||
a.wifiBSSID = wifi.BSSID
|
||||
a.mu.Unlock()
|
||||
|
||||
backendOnline := a.pingBackend()
|
||||
backendOnline := a.pingBackend()
|
||||
|
||||
if backendOnline && wifi.SSID != "" && wifi.BSSID != "" {
|
||||
go a.reportWifi(wifi.SSID, wifi.BSSID)
|
||||
a.fetchWifiPolicy()
|
||||
if !a.isWifiAllowed(wifi.SSID, wifi.BSSID) {
|
||||
a.rejectUnauthorizedWifi(wifi.SSID, wifi.BSSID)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
a.mu.Lock()
|
||||
a.backendOnline = backendOnline
|
||||
if backendOnline {
|
||||
a.onlineSecs += 10
|
||||
a.unsyncedOn += 10
|
||||
} else {
|
||||
a.offlineSecs += 10
|
||||
a.unsyncedOff += 10
|
||||
}
|
||||
a.mu.Unlock()
|
||||
|
||||
a.saveStats()
|
||||
|
||||
if backendOnline {
|
||||
a.connectWS()
|
||||
a.syncLogsToServer()
|
||||
} else {
|
||||
a.disconnectWS()
|
||||
if backendOnline && wifi.SSID != "" && wifi.BSSID != "" {
|
||||
go a.reportWifi(wifi.SSID, wifi.BSSID)
|
||||
a.fetchWifiPolicy()
|
||||
if !a.isWifiAllowed(wifi.SSID, wifi.BSSID) {
|
||||
a.rejectUnauthorizedWifi(wifi.SSID, wifi.BSSID)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
a.mu.Lock()
|
||||
a.backendOnline = backendOnline
|
||||
if backendOnline {
|
||||
a.onlineSecs += 10
|
||||
a.unsyncedOn += 10
|
||||
} else {
|
||||
a.offlineSecs += 10
|
||||
a.unsyncedOff += 10
|
||||
}
|
||||
a.mu.Unlock()
|
||||
|
||||
a.saveStats()
|
||||
|
||||
if backendOnline {
|
||||
a.connectWS()
|
||||
a.syncLogsToServer()
|
||||
} else {
|
||||
a.disconnectWS()
|
||||
}
|
||||
}
|
||||
|
||||
// refreshNetworkStatus — đọc WiFi + ping server ngay (không cộng thời gian online/offline).
|
||||
func (a *App) refreshNetworkStatus() {
|
||||
if !a.isMonitoringActive() || !a.CheckLoginStatus() {
|
||||
return
|
||||
}
|
||||
wifi := winapi.GetWifiConnection()
|
||||
backendOnline := a.pingBackend()
|
||||
a.mu.Lock()
|
||||
a.wifiSSID = wifi.SSID
|
||||
a.wifiBSSID = wifi.BSSID
|
||||
a.backendOnline = backendOnline
|
||||
a.mu.Unlock()
|
||||
}
|
||||
|
||||
func (a *App) pingBackend() bool {
|
||||
@@ -537,6 +614,9 @@ func (a *App) pingBackend() bool {
|
||||
}
|
||||
|
||||
func (a *App) syncLogsToServer() {
|
||||
if !a.isMonitoringActive() {
|
||||
return
|
||||
}
|
||||
a.mu.Lock()
|
||||
student := a.student
|
||||
unsyncedOn := a.unsyncedOn
|
||||
@@ -580,6 +660,9 @@ func (a *App) syncLogsToServer() {
|
||||
}
|
||||
|
||||
func (a *App) reportBlockedApp(procName string, title string) {
|
||||
if !a.isMonitoringActive() {
|
||||
return
|
||||
}
|
||||
a.mu.Lock()
|
||||
student := a.student
|
||||
a.mu.Unlock()
|
||||
@@ -620,6 +703,9 @@ func (a *App) reportBlockedApp(procName string, title string) {
|
||||
}
|
||||
|
||||
func (a *App) reportWifi(ssid, bssid string) {
|
||||
if !a.isMonitoringActive() {
|
||||
return
|
||||
}
|
||||
a.mu.Lock()
|
||||
student := a.student
|
||||
a.mu.Unlock()
|
||||
@@ -709,15 +795,10 @@ func (a *App) rejectUnauthorizedWifi(ssid, bssid string) {
|
||||
a.wifiRejected = true
|
||||
a.mu.Unlock()
|
||||
|
||||
blocker.Instance.Stop()
|
||||
a.disconnectWS()
|
||||
|
||||
_, _ = runtime.MessageDialog(a.ctx, runtime.MessageDialogOptions{
|
||||
Type: runtime.WarningDialog,
|
||||
Title: "WiFi không được phép",
|
||||
Message: fmt.Sprintf("Điểm phát WiFi \"%s\" (%s) không được phép.\n\nCó thể là mạng giả mạo (hotspot trùng tên). Hãy kết nối đúng WiFi của trường rồi mở lại ứng dụng.", ssid, bssid),
|
||||
})
|
||||
runtime.Quit(a.ctx)
|
||||
a.showQuitDialog(
|
||||
"WiFi không được phép",
|
||||
fmt.Sprintf("Điểm phát WiFi \"%s\" (%s) không được phép.\n\nCó thể là mạng giả mạo (hotspot trùng tên). Hãy kết nối đúng WiFi của trường rồi mở lại ứng dụng.", ssid, bssid),
|
||||
)
|
||||
}
|
||||
|
||||
func (a *App) fetchAllowedApps(classId int64) {
|
||||
@@ -807,6 +888,9 @@ func (a *App) fetchStudentStatus(studentID int64) {
|
||||
}
|
||||
|
||||
func (a *App) connectWS() {
|
||||
if !a.isMonitoringActive() {
|
||||
return
|
||||
}
|
||||
a.mu.Lock()
|
||||
if a.wsConnected || a.student == nil {
|
||||
a.mu.Unlock()
|
||||
|
||||
Reference in New Issue
Block a user