add macos

This commit is contained in:
2026-07-01 10:06:31 +07:00
parent 248d3a6722
commit b702287cfb
10 changed files with 148 additions and 47 deletions

BIN
.DS_Store vendored

Binary file not shown.

BIN
client/.DS_Store vendored

Binary file not shown.

View File

@@ -124,9 +124,10 @@ type App struct {
lastSyncTime time.Time lastSyncTime time.Time
isStreamingSc bool isStreamingSc bool
streamScStop chan struct{} streamScStop chan struct{}
statusMsg string statusMsg string
expectingLogin bool expectingLogin bool
backendOnline bool needsClearPortalStorage bool
backendOnline bool
dashboard StudentDashboardSnapshot dashboard StudentDashboardSnapshot
wifiEnforce bool wifiEnforce bool
acceptedWifi map[string]bool acceptedWifi map[string]bool
@@ -209,6 +210,9 @@ func (a *App) startup(ctx context.Context) {
a.loadSession() a.loadSession()
a.loadStats() a.loadStats()
// Request Location Access (macOS)
winapi.RequestLocationAccess()
// Register blocker callbacks // Register blocker callbacks
blocker.Instance.OnBlocked = func(procName string, title string) { blocker.Instance.OnBlocked = func(procName string, title string) {
go a.reportBlockedApp(procName, title) go a.reportBlockedApp(procName, title)
@@ -534,7 +538,8 @@ func (a *App) NavigateToLogin() {
func (a *App) Logout() { func (a *App) Logout() {
a.mu.Lock() a.mu.Lock()
a.student = nil a.student = nil
a.expectingLogin = false a.expectingLogin = true
a.needsClearPortalStorage = true
a.mu.Unlock() a.mu.Unlock()
_ = os.Remove(a.sessionPath) _ = os.Remove(a.sessionPath)
@@ -552,7 +557,7 @@ func (a *App) Logout() {
a.stopScreenshotStream() a.stopScreenshotStream()
guard.SuppressFor(5 * time.Second) guard.SuppressFor(5 * time.Second)
runtime.WindowReloadApp(a.ctx) runtime.WindowExecJS(a.ctx, "window.location.href = 'https://portal.rikkei.edu.vn/dangnhap'")
} }
// GetStats trả về Wifi, thời gian online/offline và trạng thái giám sát // GetStats trả về Wifi, thời gian online/offline và trạng thái giám sát
@@ -622,9 +627,23 @@ func (a *App) authStorageScanner() {
a.mu.Lock() a.mu.Lock()
expecting := a.expectingLogin expecting := a.expectingLogin
needsClear := a.needsClearPortalStorage
loggedIn := a.student != nil loggedIn := a.student != nil
a.mu.Unlock() a.mu.Unlock()
if needsClear && expecting {
runtime.WindowExecJS(a.ctx, `
try {
localStorage.removeItem("student");
localStorage.removeItem("token");
} catch(e) {}
`)
a.mu.Lock()
a.needsClearPortalStorage = false
a.mu.Unlock()
continue
}
if loggedIn || !expecting { if loggedIn || !expecting {
continue continue
} }
@@ -661,8 +680,16 @@ func (a *App) runMonitorTick() {
wifi := winapi.GetWifiConnection() wifi := winapi.GetWifiConnection()
a.mu.Lock() a.mu.Lock()
a.wifiSSID = wifi.SSID if strings.Contains(strings.ToLower(wifi.SSID), "redacted") {
a.wifiBSSID = wifi.BSSID a.wifiSSID = "Chưa cấp quyền Vị Trí (macOS)"
} else {
a.wifiSSID = wifi.SSID
}
if strings.Contains(strings.ToLower(wifi.BSSID), "redacted") {
a.wifiBSSID = "Chưa cấp quyền Vị Trí (macOS)"
} else {
a.wifiBSSID = wifi.BSSID
}
a.mu.Unlock() a.mu.Unlock()
backendOnline := a.pingBackend() backendOnline := a.pingBackend()
@@ -732,8 +759,16 @@ func (a *App) refreshNetworkStatus() {
wifi := winapi.GetWifiConnection() wifi := winapi.GetWifiConnection()
backendOnline := a.pingBackend() backendOnline := a.pingBackend()
a.mu.Lock() a.mu.Lock()
a.wifiSSID = wifi.SSID if strings.Contains(strings.ToLower(wifi.SSID), "redacted") {
a.wifiBSSID = wifi.BSSID a.wifiSSID = "Chưa cấp quyền Vị Trí (macOS)"
} else {
a.wifiSSID = wifi.SSID
}
if strings.Contains(strings.ToLower(wifi.BSSID), "redacted") {
a.wifiBSSID = "Chưa cấp quyền Vị Trí (macOS)"
} else {
a.wifiBSSID = wifi.BSSID
}
a.backendOnline = backendOnline a.backendOnline = backendOnline
a.mu.Unlock() a.mu.Unlock()
if backendOnline { if backendOnline {
@@ -923,6 +958,10 @@ func (a *App) isWifiAllowed(ssid, bssid string) bool {
if !enforce || len(allowed) == 0 { if !enforce || len(allowed) == 0 {
return true return true
} }
// Bypass Wi-Fi check if it's redacted by macOS privacy controls
if strings.Contains(strings.ToLower(ssid), "redacted") || strings.Contains(strings.ToLower(bssid), "redacted") {
return true
}
bKey := winapi.BSSIDKey(bssid) bKey := winapi.BSSIDKey(bssid)
if bKey == "" || len(bKey) != 12 { if bKey == "" || len(bKey) != 12 {
return false return false

BIN
client/build/.DS_Store vendored

Binary file not shown.

View File

@@ -64,5 +64,7 @@
<key>NSAllowsLocalNetworking</key> <key>NSAllowsLocalNetworking</key>
<true/> <true/>
</dict> </dict>
<key>NSLocationWhenInUseUsageDescription</key>
<string>Ứng dụng cần quyền vị trí để xác thực mạng Wi-Fi phòng thi.</string>
</dict> </dict>
</plist> </plist>

View File

@@ -59,5 +59,7 @@
{{end}} {{end}}
</array> </array>
{{end}} {{end}}
<key>NSLocationWhenInUseUsageDescription</key>
<string>Ứng dụng cần quyền vị trí để xác thực mạng Wi-Fi phòng thi.</string>
</dict> </dict>
</plist> </plist>

View File

@@ -2,45 +2,52 @@
package winapi package winapi
/*
#cgo LDFLAGS: -framework CoreWLAN -framework CoreLocation -framework Foundation
#include <stdlib.h>
typedef struct {
char* ssid;
char* bssid;
} CWifiInfo;
void RequestLocationPermission();
CWifiInfo GetCurrentWifiInfo();
*/
import "C"
import ( import (
"bytes" "unsafe"
"os/exec"
"strings"
) )
// GetWifiConnection đọc SSID và BSSID từ ipconfig (macOS) // RequestLocationAccess requests Location permission on macOS
func GetWifiConnection() WifiConnection { func RequestLocationAccess() {
// Find Wi-Fi interface dynamically C.RequestLocationPermission()
wifiInterface := "en0" }
cmdPort := exec.Command("networksetup", "-listallhardwareports")
if outPort, err := cmdPort.Output(); err == nil { // GetWifiConnection đọc SSID và BSSID từ CoreWLAN (macOS)
lines := strings.Split(string(outPort), "\n") func GetWifiConnection() WifiConnection {
for i := 0; i < len(lines)-1; i++ { info := C.GetCurrentWifiInfo()
if strings.Contains(lines[i], "Hardware Port: Wi-Fi") { defer func() {
next := strings.TrimSpace(lines[i+1]) if info.ssid != nil {
if strings.HasPrefix(next, "Device:") { C.free(unsafe.Pointer(info.ssid))
wifiInterface = strings.TrimSpace(strings.TrimPrefix(next, "Device:")) }
break if info.bssid != nil {
} C.free(unsafe.Pointer(info.bssid))
} }
} }()
}
ssid := ""
cmd := exec.Command("ipconfig", "getsummary", wifiInterface) if info.ssid != nil {
var out bytes.Buffer ssid = C.GoString(info.ssid)
cmd.Stdout = &out }
if err := cmd.Run(); err != nil {
return WifiConnection{} bssid := ""
} if info.bssid != nil {
bssid = C.GoString(info.bssid)
var conn WifiConnection }
for _, line := range strings.Split(out.String(), "\n") {
trimmed := strings.TrimSpace(line) return WifiConnection{
if strings.HasPrefix(trimmed, "SSID :") { SSID: ssid,
conn.SSID = strings.TrimSpace(strings.TrimPrefix(trimmed, "SSID :")) BSSID: normalizeMAC(bssid),
} else if strings.HasPrefix(trimmed, "BSSID :") { }
conn.BSSID = normalizeMAC(strings.TrimSpace(strings.TrimPrefix(trimmed, "BSSID :")))
}
}
return conn
} }

View File

@@ -0,0 +1,45 @@
#import <CoreWLAN/CoreWLAN.h>
#import <CoreLocation/CoreLocation.h>
#import <Foundation/Foundation.h>
static CLLocationManager *locationManager = nil;
void RequestLocationPermission() {
dispatch_async(dispatch_get_main_queue(), ^{
if (locationManager == nil) {
locationManager = [[CLLocationManager alloc] init];
}
if (@available(macOS 10.15, *)) {
[locationManager requestWhenInUseAuthorization];
}
});
}
typedef struct {
char* ssid;
char* bssid;
} CWifiInfo;
CWifiInfo GetCurrentWifiInfo() {
CWifiInfo info;
info.ssid = NULL;
info.bssid = NULL;
@autoreleasepool {
CWWiFiClient *client = [CWWiFiClient sharedWiFiClient];
if (client != nil) {
CWInterface *interface = [client interface];
if (interface != nil) {
NSString *ssidStr = [interface ssid];
NSString *bssidStr = [interface bssid];
if (ssidStr != nil) {
info.ssid = strdup([ssidStr UTF8String]);
}
if (bssidStr != nil) {
info.bssid = strdup([bssidStr UTF8String]);
}
}
}
}
return info;
}

View File

@@ -6,3 +6,6 @@ package winapi
func GetWifiConnection() WifiConnection { func GetWifiConnection() WifiConnection {
return WifiConnection{} return WifiConnection{}
} }
// RequestLocationAccess requests Location permission (stub for other systems)
func RequestLocationAccess() {}

View File

@@ -38,3 +38,6 @@ func GetWifiConnection() WifiConnection {
} }
return conn return conn
} }
// RequestLocationAccess requests Location permission (stub for Windows)
func RequestLocationAccess() {}