tam thoi
This commit is contained in:
@@ -113,6 +113,7 @@ type App struct {
|
|||||||
monitoringTornDown bool
|
monitoringTornDown bool
|
||||||
chatUnread int
|
chatUnread int
|
||||||
replyStaffID uint
|
replyStaffID uint
|
||||||
|
fetchAppsMu sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
type LocalStats struct {
|
type LocalStats struct {
|
||||||
@@ -299,6 +300,7 @@ func (a *App) startLocalServer() {
|
|||||||
go a.refreshStudentData()
|
go a.refreshStudentData()
|
||||||
|
|
||||||
// Chuyển hướng WebView về trang dashboard của app bằng cách reload app assets
|
// Chuyển hướng WebView về trang dashboard của app bằng cách reload app assets
|
||||||
|
guard.SuppressFor(5 * time.Second)
|
||||||
runtime.WindowReloadApp(a.ctx)
|
runtime.WindowReloadApp(a.ctx)
|
||||||
w.Write([]byte("ok"))
|
w.Write([]byte("ok"))
|
||||||
} else {
|
} else {
|
||||||
@@ -330,7 +332,10 @@ func (a *App) startLocalServer() {
|
|||||||
w.Header().Set("Access-Control-Allow-Methods", "GET, OPTIONS")
|
w.Header().Set("Access-Control-Allow-Methods", "GET, OPTIONS")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
go runtime.WindowReloadApp(a.ctx)
|
go func() {
|
||||||
|
guard.SuppressFor(5 * time.Second)
|
||||||
|
runtime.WindowReloadApp(a.ctx)
|
||||||
|
}()
|
||||||
w.Write([]byte("ok"))
|
w.Write([]byte("ok"))
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -495,6 +500,7 @@ func (a *App) NavigateToLogin() {
|
|||||||
a.mu.Lock()
|
a.mu.Lock()
|
||||||
a.expectingLogin = true
|
a.expectingLogin = true
|
||||||
a.mu.Unlock()
|
a.mu.Unlock()
|
||||||
|
guard.SuppressFor(5 * time.Second)
|
||||||
runtime.WindowExecJS(a.ctx, "window.location.href = 'https://portal.rikkei.edu.vn/dangnhap'")
|
runtime.WindowExecJS(a.ctx, "window.location.href = 'https://portal.rikkei.edu.vn/dangnhap'")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -519,6 +525,7 @@ func (a *App) Logout() {
|
|||||||
a.disconnectWS()
|
a.disconnectWS()
|
||||||
a.stopScreenshotStream()
|
a.stopScreenshotStream()
|
||||||
|
|
||||||
|
guard.SuppressFor(5 * time.Second)
|
||||||
runtime.WindowReloadApp(a.ctx)
|
runtime.WindowReloadApp(a.ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -912,6 +919,9 @@ func (a *App) rejectUnauthorizedWifi(ssid, bssid string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) fetchAllowedApps(classId int64) {
|
func (a *App) fetchAllowedApps(classId int64) {
|
||||||
|
a.fetchAppsMu.Lock()
|
||||||
|
defer a.fetchAppsMu.Unlock()
|
||||||
|
|
||||||
a.mu.Lock()
|
a.mu.Lock()
|
||||||
studentID := int64(0)
|
studentID := int64(0)
|
||||||
if a.student != nil {
|
if a.student != nil {
|
||||||
@@ -935,6 +945,12 @@ func (a *App) fetchAllowedApps(classId int64) {
|
|||||||
}
|
}
|
||||||
if err := json.NewDecoder(resp.Body).Decode(&res); err == nil {
|
if err := json.NewDecoder(resp.Body).Decode(&res); err == nil {
|
||||||
if res.Exit {
|
if res.Exit {
|
||||||
|
a.mu.Lock()
|
||||||
|
stillExam := a.dashboard.MonitorMode == "exam"
|
||||||
|
a.mu.Unlock()
|
||||||
|
if stillExam {
|
||||||
|
return
|
||||||
|
}
|
||||||
reason := res.Reason
|
reason := res.Reason
|
||||||
if reason == "" {
|
if reason == "" {
|
||||||
reason = "Không đáp ứng điều kiện học tập (chưa cấu hình ứng dụng được phép hoặc ngoài giờ học)."
|
reason = "Không đáp ứng điều kiện học tập (chưa cấu hình ứng dụng được phép hoặc ngoài giờ học)."
|
||||||
@@ -984,6 +1000,7 @@ func (a *App) fetchStudentStatus(studentID int64) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
a.mu.Lock()
|
a.mu.Lock()
|
||||||
|
prevMode := a.dashboard.MonitorMode
|
||||||
a.dashboard = res
|
a.dashboard = res
|
||||||
if res.MonitorLabel != "" {
|
if res.MonitorLabel != "" {
|
||||||
a.statusMsg = res.MonitorLabel
|
a.statusMsg = res.MonitorLabel
|
||||||
@@ -998,6 +1015,10 @@ func (a *App) fetchStudentStatus(studentID int64) {
|
|||||||
a.statusMsg = res.MonitorLabel
|
a.statusMsg = res.MonitorLabel
|
||||||
}
|
}
|
||||||
a.mu.Unlock()
|
a.mu.Unlock()
|
||||||
|
|
||||||
|
if prevMode != res.MonitorMode {
|
||||||
|
guard.SuppressFor(4 * time.Second)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) connectWS() {
|
func (a *App) connectWS() {
|
||||||
@@ -1292,6 +1313,7 @@ func (a *App) examStudentContext() (int64, *StudentExamSnapshot, error) {
|
|||||||
|
|
||||||
// ReturnToDashboard quay lại giao diện chính của app (giữ cookie WebView2).
|
// ReturnToDashboard quay lại giao diện chính của app (giữ cookie WebView2).
|
||||||
func (a *App) ReturnToDashboard() {
|
func (a *App) ReturnToDashboard() {
|
||||||
|
guard.SuppressFor(5 * time.Second)
|
||||||
runtime.WindowReloadApp(a.ctx)
|
runtime.WindowReloadApp(a.ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1315,10 +1337,12 @@ func (a *App) openExamWebView(targetURL, title string) error {
|
|||||||
url.QueryEscape(targetURL),
|
url.QueryEscape(targetURL),
|
||||||
url.QueryEscape(title),
|
url.QueryEscape(title),
|
||||||
)
|
)
|
||||||
|
guard.SuppressFor(5 * time.Second)
|
||||||
runtime.WindowExecJS(a.ctx, fmt.Sprintf("window.location.href = %q", wrapper))
|
runtime.WindowExecJS(a.ctx, fmt.Sprintf("window.location.href = %q", wrapper))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
// Trang thi bên ngoài: mở trực tiếp trong WebView (first-party cookies → giữ phiên đăng nhập).
|
// Trang thi bên ngoài: mở trực tiếp trong WebView (first-party cookies → giữ phiên đăng nhập).
|
||||||
|
guard.SuppressFor(5 * time.Second)
|
||||||
runtime.WindowExecJS(a.ctx, fmt.Sprintf("window.location.href = %q", targetURL))
|
runtime.WindowExecJS(a.ctx, fmt.Sprintf("window.location.href = %q", targetURL))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -1481,6 +1505,7 @@ func uniqueFilePath(path string) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func openFolderInExplorer(filePath string) {
|
func openFolderInExplorer(filePath string) {
|
||||||
|
guard.SuppressFor(5 * time.Second)
|
||||||
dir := filepath.Dir(filePath)
|
dir := filepath.Dir(filePath)
|
||||||
_ = exec.Command("explorer", dir).Start()
|
_ = exec.Command("explorer", dir).Start()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -249,6 +249,7 @@ var systemAllowed = map[string]bool{
|
|||||||
"powershell.exe": true,
|
"powershell.exe": true,
|
||||||
"conhost.exe": true,
|
"conhost.exe": true,
|
||||||
"client.exe": true, // App Wails của ta
|
"client.exe": true, // App Wails của ta
|
||||||
|
"simple_care_v1.0.exe": true,
|
||||||
"wails.exe": true,
|
"wails.exe": true,
|
||||||
"msedgewebview2.exe": true, // WebView2 runtime của Wails
|
"msedgewebview2.exe": true, // WebView2 runtime của Wails
|
||||||
"code.exe": true, // VSCode
|
"code.exe": true, // VSCode
|
||||||
|
|||||||
@@ -2,5 +2,8 @@
|
|||||||
|
|
||||||
package guard
|
package guard
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
func Start(onViolation func(reason string)) {}
|
func Start(onViolation func(reason string)) {}
|
||||||
func Stop() {}
|
func Stop() {}
|
||||||
|
func SuppressFor(_ time.Duration) {}
|
||||||
|
|||||||
@@ -72,15 +72,36 @@ type msg struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
guardOnce sync.Once
|
guardOnce sync.Once
|
||||||
guardViolation func(string)
|
guardViolation func(string)
|
||||||
guardStop chan struct{}
|
guardStop chan struct{}
|
||||||
guardBaselineUser string
|
guardBaselineUser string
|
||||||
guardBaselineSession uint32
|
guardBaselineSession uint32
|
||||||
desktopHook uintptr
|
desktopHook uintptr
|
||||||
guardClassAtom uint16
|
guardClassAtom uint16
|
||||||
|
suppressMu sync.Mutex
|
||||||
|
suppressViolationsUntil time.Time
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// SuppressFor tạm không thoát app khi WebView/Explorer chuyển màn hình nội bộ.
|
||||||
|
func SuppressFor(d time.Duration) {
|
||||||
|
if d <= 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
suppressMu.Lock()
|
||||||
|
next := time.Now().Add(d)
|
||||||
|
if next.After(suppressViolationsUntil) {
|
||||||
|
suppressViolationsUntil = next
|
||||||
|
}
|
||||||
|
suppressMu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func violationsSuppressed() bool {
|
||||||
|
suppressMu.Lock()
|
||||||
|
defer suppressMu.Unlock()
|
||||||
|
return time.Now().Before(suppressViolationsUntil)
|
||||||
|
}
|
||||||
|
|
||||||
// Start giám sát môi trường Windows — vi phạm thì gọi onViolation (đổi user, đa màn hình, đổi desktop ảo).
|
// Start giám sát môi trường Windows — vi phạm thì gọi onViolation (đổi user, đa màn hình, đổi desktop ảo).
|
||||||
func Start(onViolation func(reason string)) {
|
func Start(onViolation func(reason string)) {
|
||||||
guardOnce.Do(func() {
|
guardOnce.Do(func() {
|
||||||
@@ -122,7 +143,7 @@ func pollLoop() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func checkEnvironment() {
|
func checkEnvironment() {
|
||||||
if guardViolation == nil {
|
if guardViolation == nil || violationsSuppressed() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if n := monitorCount(); n > 1 {
|
if n := monitorCount(); n > 1 {
|
||||||
@@ -141,6 +162,10 @@ func checkEnvironment() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func triggerViolation(reason string) {
|
func triggerViolation(reason string) {
|
||||||
|
if violationsSuppressed() {
|
||||||
|
log.Printf("[GUARD] suppressed: %s", reason)
|
||||||
|
return
|
||||||
|
}
|
||||||
if guardViolation != nil {
|
if guardViolation != nil {
|
||||||
guardViolation(reason)
|
guardViolation(reason)
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
server/uploads/exams/5/papers/5/main.pdf
Normal file
BIN
server/uploads/exams/5/papers/5/main.pdf
Normal file
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 909 KiB |
BIN
server/uploads/exams/5/papers/5/resources/horse_icon.png
Normal file
BIN
server/uploads/exams/5/papers/5/resources/horse_icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
BIN
server/uploads/exams/5/papers/5/resources/main (1).pdf
Normal file
BIN
server/uploads/exams/5/papers/5/resources/main (1).pdf
Normal file
Binary file not shown.
Reference in New Issue
Block a user