This commit is contained in:
@@ -12,15 +12,151 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var systemAllowed = map[string]bool{
|
var systemAllowed = map[string]bool{
|
||||||
"finder": true,
|
// ── Core macOS Desktop Infrastructure ─────────────────────────────────────
|
||||||
"terminal": true,
|
"finder": true, // macOS file manager / desktop
|
||||||
|
"dock": true, // macOS Dock — kill = dock disappears
|
||||||
|
"windowserver": true, // WindowServer — kill = instant logout
|
||||||
|
"loginwindow": true, // Login/session manager — kill = logout
|
||||||
|
"systemuiserver": true, // Menu bar icons (volume, wifi, battery, clock)
|
||||||
|
"controlcenter": true, // macOS Control Center (Monterey+)
|
||||||
|
"notificationcenter": true, // Notification Center
|
||||||
|
"spotlight": true, // Spotlight search
|
||||||
|
"launchpad": true,
|
||||||
|
"mission control": true,
|
||||||
|
"exposé": true,
|
||||||
|
"universalaccessd": true,
|
||||||
|
"accessibilityuiagent": true, // Accessibility helper
|
||||||
|
|
||||||
|
// ── Input Methods & Language (critical — kill = can't type) ───────────────
|
||||||
|
"inputmethodkit": true,
|
||||||
|
"ibus": true,
|
||||||
|
"hiragana kakomi input": true,
|
||||||
|
"kinput2": true,
|
||||||
|
"squirrel": true, // Rime input method
|
||||||
|
"scim": true,
|
||||||
|
"kotoeri": true, // Japanese IME
|
||||||
|
"pinyin - simplified": true, // macOS Chinese Pinyin
|
||||||
|
"zhuyin - traditional": true,
|
||||||
|
"vietnamese": true, // macOS built-in Vietnamese IME
|
||||||
|
"abc": true, // macOS ABC keyboard input
|
||||||
|
|
||||||
|
// ── Security / Keychain / Authentication ──────────────────────────────────
|
||||||
|
"securityagent": true, // macOS security agent — kill breaks sudo GUI, Keychain prompts
|
||||||
|
"keychain": true, // Keychain access
|
||||||
|
"keychainservicesagent": true,
|
||||||
|
"trustd": true,
|
||||||
|
"opendirectoryd": true,
|
||||||
|
"authorizationhost": true, // Authorization host — UAC equivalent
|
||||||
|
"securityd": true,
|
||||||
|
"coreauthenticationd": true,
|
||||||
|
"biometricd": true,
|
||||||
|
"touchidd": true,
|
||||||
|
|
||||||
|
// ── Audio / Media ─────────────────────────────────────────────────────────
|
||||||
|
"coreaudiod": true, // Core Audio daemon — kill = no sound
|
||||||
|
"audioundockhelper": true,
|
||||||
|
"audio midi setup": true,
|
||||||
|
"noiseremoval": true,
|
||||||
|
|
||||||
|
// ── Networking / VPN ──────────────────────────────────────────────────────
|
||||||
|
"networkd": true,
|
||||||
|
"nesessionmanager": true, // Network Extension — kill drops VPN
|
||||||
|
"scutil": true,
|
||||||
|
"configd": true,
|
||||||
|
"mDNSResponder": true, // Bonjour DNS
|
||||||
|
|
||||||
|
// ── Spotlight / File Indexing ──────────────────────────────────────────────
|
||||||
|
"mds": true, // Spotlight metadata server
|
||||||
|
"mds_stores": true,
|
||||||
|
"mdworker": true, // prefix match covers mdworker_shared
|
||||||
|
"mdworker_shared": true,
|
||||||
|
|
||||||
|
// ── iCloud / Apple Services ───────────────────────────────────────────────
|
||||||
|
"bird": true, // iCloud Drive daemon
|
||||||
|
"cloudd": true,
|
||||||
|
"com.apple.icloud": true, // prefix
|
||||||
|
"cloudphotod": true,
|
||||||
|
"nsurlsessiond": true,
|
||||||
|
|
||||||
|
// ── System Preferences / Settings ─────────────────────────────────────────
|
||||||
|
"system preferences": true, // macOS System Preferences (pre-Ventura)
|
||||||
|
"system settings": true, // macOS System Settings (Ventura+)
|
||||||
|
"software update": true,
|
||||||
|
"app store": true,
|
||||||
|
|
||||||
|
// ── Screen / Display ──────────────────────────────────────────────────────
|
||||||
|
"screensaver engine": true, // Screensaver
|
||||||
|
"com.apple.screensaver": true,
|
||||||
|
"colorsyncd": true,
|
||||||
|
"colorsync utility": true,
|
||||||
|
"nightshift": true,
|
||||||
|
"display menu": true,
|
||||||
|
|
||||||
|
// ── Clipboard / Pasteboard ────────────────────────────────────────────────
|
||||||
|
"pboard": true, // Pasteboard daemon — kill breaks copy/paste
|
||||||
|
|
||||||
|
// ── Printing ─────────────────────────────────────────────────────────────
|
||||||
|
"printingproxy": true,
|
||||||
|
"cupsd": true,
|
||||||
|
|
||||||
|
// ── Crash Reporting / Diagnostics ─────────────────────────────────────────
|
||||||
|
"crashreporter": true,
|
||||||
|
"diagnosticsd": true,
|
||||||
|
"spindump": true,
|
||||||
|
"reportmemoryexception": true,
|
||||||
|
|
||||||
|
// ── Webkit / App subprocesses ─────────────────────────────────────────────
|
||||||
|
"webkit": true, // prefix
|
||||||
|
"com.apple.webkit": true, // prefix
|
||||||
|
"com.apple.webkit.networking": true,
|
||||||
|
|
||||||
|
// ── Remote support ────────────────────────────────────────────────────────
|
||||||
|
"applescriptkit": true,
|
||||||
|
"applescript runner": true,
|
||||||
|
"rustdesk": true,
|
||||||
|
"anydesk": true,
|
||||||
|
"teamviewer": true,
|
||||||
|
"screen sharing": true,
|
||||||
|
"screensharingd": true, // macOS Screen Sharing
|
||||||
|
|
||||||
|
// ── Terminals ─────────────────────────────────────────────────────────────
|
||||||
|
"terminal": true, // macOS Terminal
|
||||||
"iterm": true,
|
"iterm": true,
|
||||||
"iterm2": true,
|
"iterm2": true,
|
||||||
|
"wezterm": true,
|
||||||
|
"kitty": true,
|
||||||
|
"alacritty": true,
|
||||||
|
"hyper": true,
|
||||||
|
|
||||||
|
// ── Shells ────────────────────────────────────────────────────────────────
|
||||||
"bash": true,
|
"bash": true,
|
||||||
"zsh": true,
|
"zsh": true,
|
||||||
"sh": true,
|
"sh": true,
|
||||||
|
"fish": true,
|
||||||
|
|
||||||
|
// ── AppleScript / Automation ──────────────────────────────────────────────
|
||||||
|
"system events": true, // AppleScript System Events (used by our blocker itself)
|
||||||
|
"osascript": true, // AppleScript runner (used by our getVisibleProcesses)
|
||||||
|
|
||||||
|
// ── Git & Credential Helpers ──────────────────────────────────────────────
|
||||||
|
"git": true,
|
||||||
|
"git-credential-manager": true,
|
||||||
|
"git-credential-osxkeychain": true,
|
||||||
|
"github desktop": true,
|
||||||
|
"sourcetree": true,
|
||||||
|
"fork": true,
|
||||||
|
|
||||||
|
// ── Docker ───────────────────────────────────────────────────────────────
|
||||||
|
"docker": true,
|
||||||
|
"docker desktop": true,
|
||||||
|
"com.docker": true, // prefix
|
||||||
|
|
||||||
|
// ── Our app + IDE/dev tools ────────────────────────────────────────────────
|
||||||
|
"client": true,
|
||||||
|
"simple_care_v1.0": true,
|
||||||
|
"simple_care": true,
|
||||||
"wails": true,
|
"wails": true,
|
||||||
"code": true,
|
"code": true, // VSCode
|
||||||
"cursor": true,
|
"cursor": true,
|
||||||
"windsurf": true,
|
"windsurf": true,
|
||||||
"goland": true,
|
"goland": true,
|
||||||
@@ -29,17 +165,10 @@ var systemAllowed = map[string]bool{
|
|||||||
"webstorm": true,
|
"webstorm": true,
|
||||||
"pycharm": true,
|
"pycharm": true,
|
||||||
"rider": true,
|
"rider": true,
|
||||||
"studio": true,
|
"studio": true, // Android Studio
|
||||||
"eclipse": true,
|
"eclipse": true,
|
||||||
"sublime_text": true,
|
"sublime text": true,
|
||||||
"git": true,
|
|
||||||
"docker": true,
|
|
||||||
"system events": true,
|
|
||||||
"osascript": true,
|
|
||||||
"client": true,
|
|
||||||
"simple_care_v1.0": true,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type ProcessInfo struct {
|
type ProcessInfo struct {
|
||||||
Name string
|
Name string
|
||||||
BundleID string
|
BundleID string
|
||||||
|
|||||||
@@ -12,15 +12,207 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var systemAllowed = map[string]bool{
|
var systemAllowed = map[string]bool{
|
||||||
|
// ── Display servers / Compositors ─────────────────────────────────────────
|
||||||
"gnome-shell": true,
|
"gnome-shell": true,
|
||||||
"mutter": true,
|
"mutter": true,
|
||||||
|
"mutter-x11-fram": true, // prefix match too
|
||||||
|
"xwayland": true,
|
||||||
|
"xorg": true,
|
||||||
|
"Xorg": true,
|
||||||
|
"x11": true,
|
||||||
|
"kwin_wayland": true, // KDE compositor
|
||||||
|
"kwin_x11": true,
|
||||||
|
"plasmashell": true, // KDE Plasma shell
|
||||||
|
"hyprland": true, // Hyprland Wayland compositor
|
||||||
|
"sway": true, // Sway compositor
|
||||||
|
"wayfire": true, // Wayfire compositor
|
||||||
|
"river": true, // River compositor
|
||||||
|
"labwc": true, // LabWC compositor
|
||||||
|
"openbox": true,
|
||||||
|
"i3": true,
|
||||||
|
"i3bar": true,
|
||||||
|
"awesome": true,
|
||||||
|
"bspwm": true,
|
||||||
|
"xfwm4": true,
|
||||||
|
"marco": true, // MATE wm
|
||||||
|
"compiz": true,
|
||||||
|
"picom": true,
|
||||||
|
"compton": true,
|
||||||
|
|
||||||
|
// ── Wayland session tools ─────────────────────────────────────────────────
|
||||||
|
"wl-paste": true,
|
||||||
|
"wl-copy": true,
|
||||||
|
"wlr-randr": true,
|
||||||
|
"kanshi": true, // output manager
|
||||||
|
"wlsunset": true,
|
||||||
|
"gammastep": true,
|
||||||
|
"swaylock": true,
|
||||||
|
"swayidle": true,
|
||||||
|
"swaybg": true,
|
||||||
|
"swaync": true, // SwayNotificationCenter
|
||||||
|
"waybar": true, // Wayland statusbar
|
||||||
|
"eww": true, // ElKowar's wacky widgets
|
||||||
|
"ags": true, // Aylur's GTK Shell
|
||||||
|
"rofi": true, // app launcher (used in many WMs)
|
||||||
|
"wofi": true, // Wayland rofi
|
||||||
|
"fuzzel": true, // Wayland launcher
|
||||||
|
"tofi": true,
|
||||||
|
"dmenu": true,
|
||||||
|
"bemenu": true,
|
||||||
|
|
||||||
|
// ── Session / Login managers ──────────────────────────────────────────────
|
||||||
|
"gdm": true,
|
||||||
|
"gdm3": true,
|
||||||
|
"sddm": true,
|
||||||
|
"lightdm": true,
|
||||||
|
"lxdm": true,
|
||||||
|
"slim": true,
|
||||||
|
"greetd": true,
|
||||||
|
"gnome-session": true,
|
||||||
|
"gnome-session-bi": true, // prefix matches binary
|
||||||
|
"lxsession": true,
|
||||||
|
"startx": true,
|
||||||
|
"xinit": true,
|
||||||
|
|
||||||
|
// ── Terminals ─────────────────────────────────────────────────────────────
|
||||||
"xterm": true,
|
"xterm": true,
|
||||||
"gnome-terminal": true,
|
"gnome-terminal": true,
|
||||||
|
"gnome-terminal-": true, // prefix for server process
|
||||||
|
"ptyxis": true,
|
||||||
"konsole": true,
|
"konsole": true,
|
||||||
"ptyxis": true, // Ubuntu terminal
|
"kitty": true,
|
||||||
|
"alacritty": true,
|
||||||
|
"wezterm": true,
|
||||||
|
"wezterm-gui": true,
|
||||||
|
"foot": true,
|
||||||
|
"xfce4-terminal": true,
|
||||||
|
"tilix": true,
|
||||||
|
"terminator": true,
|
||||||
|
"urxvt": true,
|
||||||
|
"rxvt": true,
|
||||||
|
"sakura": true,
|
||||||
|
"st": true,
|
||||||
|
|
||||||
|
// ── Shells ────────────────────────────────────────────────────────────────
|
||||||
"bash": true,
|
"bash": true,
|
||||||
"zsh": true,
|
"zsh": true,
|
||||||
"sh": true,
|
"sh": true,
|
||||||
|
"fish": true,
|
||||||
|
"dash": true,
|
||||||
|
"ksh": true,
|
||||||
|
|
||||||
|
// ── Input methods (critical — killing these breaks Vietnamese typing) ─────
|
||||||
|
"ibus-daemon": true,
|
||||||
|
"ibus-x11": true,
|
||||||
|
"ibus-": true, // prefix: ibus-extension-, ibus-portal, ibus-engine-...
|
||||||
|
"fcitx5": true,
|
||||||
|
"fcitx": true,
|
||||||
|
"fcitx-": true, // prefix
|
||||||
|
"uim": true,
|
||||||
|
"scim": true,
|
||||||
|
"sogou-qimpanel": true,
|
||||||
|
"gcin": true,
|
||||||
|
"kimpanel": true,
|
||||||
|
|
||||||
|
// ── GNOME core services ───────────────────────────────────────────────────
|
||||||
|
"gjs": true,
|
||||||
|
"gsd-": true, // prefix: gsd-keyboard, gsd-media-keys, gsd-power, gsd-color...
|
||||||
|
"goa-daemon": true,
|
||||||
|
"goa-identity-ser": true,
|
||||||
|
"evolution-": true, // prefix: evolution-calendar, evolution-addressbook...
|
||||||
|
"gnome-keyring-d": true,
|
||||||
|
"gnome-keyring": true,
|
||||||
|
"polkit-gnome-au": true,
|
||||||
|
"polkitd": true,
|
||||||
|
"gnome-settings-d": true,
|
||||||
|
"gnome-initial-se": true,
|
||||||
|
"gnome-control-ce": true,
|
||||||
|
"gvfsd": true,
|
||||||
|
"gvfsd-": true, // prefix
|
||||||
|
"tracker-miner-": true, // prefix
|
||||||
|
"tracker3": true,
|
||||||
|
"zeitgeist": true,
|
||||||
|
"zeitgeist-": true,
|
||||||
|
"accounts-daemon": true,
|
||||||
|
"colord": true,
|
||||||
|
"power-profiles-": true,
|
||||||
|
"fprintd": true,
|
||||||
|
"fwupd": true,
|
||||||
|
"udisksd": true,
|
||||||
|
"upowerd": true,
|
||||||
|
"packagekitd": true,
|
||||||
|
"nm-dispatcher": true,
|
||||||
|
|
||||||
|
// ── D-Bus / XDG / AT-SPI ─────────────────────────────────────────────────
|
||||||
|
"xdg-": true, // prefix: xdg-desktop-portal, xdg-permission-store...
|
||||||
|
"at-spi": true, // prefix
|
||||||
|
"at-spi-bus-laun": true,
|
||||||
|
"at-spi2-registr": true,
|
||||||
|
"dbus-daemon": true,
|
||||||
|
"dbus-launch": true,
|
||||||
|
|
||||||
|
// ── System services (often have GTK tray icons) ───────────────────────────
|
||||||
|
"systemd": true, // prefix match handles systemd-*
|
||||||
|
"snapd-": true, // prefix
|
||||||
|
"snapd": true,
|
||||||
|
|
||||||
|
// ── Network/Bluetooth tray (critical for connectivity UI) ─────────────────
|
||||||
|
"nm-applet": true, // NetworkManager tray — if killed, students lose wifi UI
|
||||||
|
"nm-tray": true,
|
||||||
|
"network-manager-": true, // prefix
|
||||||
|
"blueman-applet": true, // Bluetooth tray — kills BT management
|
||||||
|
"blueman-tray": true,
|
||||||
|
"blueman-manager": true,
|
||||||
|
"kdeconnectd": true, // KDE Connect daemon
|
||||||
|
"kdeconnect-indi": true, // KDE Connect indicator
|
||||||
|
|
||||||
|
// ── Notification daemons ──────────────────────────────────────────────────
|
||||||
|
"dunst": true,
|
||||||
|
"mako": true,
|
||||||
|
"notify-osd": true,
|
||||||
|
"xfce4-notifyd": true,
|
||||||
|
"fnott": true,
|
||||||
|
|
||||||
|
// ── Polkit authentication agents ─────────────────────────────────────────
|
||||||
|
"lxqt-policykit-": true,
|
||||||
|
"xfce-polkit": true,
|
||||||
|
"mate-polkit": true,
|
||||||
|
"pkttyagent": true,
|
||||||
|
|
||||||
|
// ── Clipboard managers (killing breaks copy/paste) ────────────────────────
|
||||||
|
"copyq": true,
|
||||||
|
"clipit": true,
|
||||||
|
"xclip": true,
|
||||||
|
"xsel": true,
|
||||||
|
"clipman": true,
|
||||||
|
"greenclip": true,
|
||||||
|
|
||||||
|
// ── GTK/GNOME image helpers ───────────────────────────────────────────────
|
||||||
|
"glycin": true,
|
||||||
|
"zenity": true,
|
||||||
|
"yad": true,
|
||||||
|
"kdialog": true,
|
||||||
|
|
||||||
|
// ── Screensaver / Lock ────────────────────────────────────────────────────
|
||||||
|
"gnome-screensave": true,
|
||||||
|
"xscreensaver": true,
|
||||||
|
"xlock": true,
|
||||||
|
"i3lock": true,
|
||||||
|
|
||||||
|
// ── WebKit subprocesses (used by many GTK apps) ───────────────────────────
|
||||||
|
"webkit": true, // prefix
|
||||||
|
"webkit2gtk": true,
|
||||||
|
"WebKitWebProcess": true,
|
||||||
|
"WebKitNetworkPro": true, // prefix
|
||||||
|
|
||||||
|
// ── Remote support tools (safety) ────────────────────────────────────────
|
||||||
|
"rustdesk": true,
|
||||||
|
"rustdesk-bin": true,
|
||||||
|
"anydesk": true,
|
||||||
|
"teamviewer": true,
|
||||||
|
"remmina": true,
|
||||||
|
|
||||||
|
// ── Our app + dev tools ───────────────────────────────────────────────────
|
||||||
"wails": true,
|
"wails": true,
|
||||||
"code": true,
|
"code": true,
|
||||||
"cursor": true,
|
"cursor": true,
|
||||||
@@ -29,27 +221,7 @@ var systemAllowed = map[string]bool{
|
|||||||
"idea": true,
|
"idea": true,
|
||||||
"client": true,
|
"client": true,
|
||||||
"simple_care_v1.0": true,
|
"simple_care_v1.0": true,
|
||||||
"xwayland": true,
|
"simple_care": true,
|
||||||
"mutter-x11-fram": true,
|
|
||||||
"ibus-x11": true,
|
|
||||||
"ibus-daemon": true,
|
|
||||||
"gsd-": true, // prefix
|
|
||||||
"ibus-": true, // prefix
|
|
||||||
"xdg-": true, // prefix
|
|
||||||
"at-spi": true, // prefix
|
|
||||||
"evolution-": true, // prefix
|
|
||||||
"goa-daemon": true,
|
|
||||||
"gjs": true,
|
|
||||||
"snapd-": true, // prefix
|
|
||||||
"systemd": true,
|
|
||||||
"webkit": true, // WebKit subprocesses
|
|
||||||
"glycin": true, // GNOME image helper
|
|
||||||
"zenity": true, // Dialog utility
|
|
||||||
"rustdesk": true, // Remote support safety
|
|
||||||
"rustdesk-bin": true,
|
|
||||||
"anydesk": true,
|
|
||||||
"teamviewer": true,
|
|
||||||
"remmina": true,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func isSystemAllowed(name string) bool {
|
func isSystemAllowed(name string) bool {
|
||||||
|
|||||||
@@ -187,34 +187,156 @@ func EnumerateGUIWindows() ([]WindowInfo, map[uint32]uint32, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var systemAllowed = map[string]bool{
|
var systemAllowed = map[string]bool{
|
||||||
"explorer.exe": true,
|
// ── Windows Shell & Explorer ───────────────────────────────────────────────
|
||||||
|
"explorer.exe": true, // Windows Explorer / desktop shell
|
||||||
|
"shellexperiencehost.exe": true, // Start menu, Action Center shell
|
||||||
|
"startmenuexperiencehost.exe": true, // Start menu host
|
||||||
|
"searchhost.exe": true, // Windows Search UI
|
||||||
|
"searchapp.exe": true, // Windows Search (older)
|
||||||
|
"searchindexer.exe": true,
|
||||||
|
"sihost.exe": true, // Shell Infrastructure Host (taskbar, notification icons)
|
||||||
|
"taskhostw.exe": true, // Task Host Window
|
||||||
|
"taskbar.exe": true,
|
||||||
|
"lockapp.exe": true, // Lock screen
|
||||||
|
"logonui.exe": true, // Login/Logon UI
|
||||||
|
"winlogon.exe": true, // Windows Logon Process
|
||||||
|
"userinit.exe": true,
|
||||||
|
"dwm.exe": true, // Desktop Window Manager — kill = black screen
|
||||||
|
"csrss.exe": true, // Client Server Runtime — kill = BSOD
|
||||||
|
|
||||||
|
// ── UWP / Modern App Infrastructure ───────────────────────────────────────
|
||||||
|
"applicationframehost.exe": true, // Host for ALL UWP apps (Camera, Calculator, Photos...)
|
||||||
|
"runtimebroker.exe": true, // UWP permission broker
|
||||||
|
"backgroundtaskhost.exe": true, // UWP background tasks
|
||||||
|
"wwahost.exe": true, // Web app host
|
||||||
|
"microsoftedgecp.exe": true, // Edge content process (older)
|
||||||
|
"textinputhost.exe": true, // Touch keyboard / handwriting panel
|
||||||
|
|
||||||
|
// ── Input Methods & Language ───────────────────────────────────────────────
|
||||||
|
"ctfmon.exe": true, // Collaborative Translation Framework — input method manager
|
||||||
|
"chsime.exe": true, // Chinese IME
|
||||||
|
"imetip.exe": true, // IME tip
|
||||||
|
"imecmnt.exe": true,
|
||||||
|
"googlepinyin.exe": true,
|
||||||
|
"baidu.exe": true, // Baidu IME
|
||||||
|
"openkey.exe": true, // OpenKey Vietnamese IME
|
||||||
|
"openkey64.exe": true,
|
||||||
|
"gotiengviet.exe": true, // GoTiengViet
|
||||||
|
"unikeyvnt.exe": true, // Unikey
|
||||||
|
"unikey.exe": true,
|
||||||
|
|
||||||
|
// ── Security / Credential / UAC ───────────────────────────────────────────
|
||||||
|
"consent.exe": true, // UAC consent dialog — kill breaks all elevation
|
||||||
|
"credentialuibroker.exe": true, // Credential UI
|
||||||
|
"smartscreen.exe": true, // Windows SmartScreen
|
||||||
|
"securityhealthsystray.exe": true, // Windows Security tray icon
|
||||||
|
"securityhealthservice.exe": true,
|
||||||
|
"wscsvc.exe": true,
|
||||||
|
"msseces.exe": true, // Microsoft Security Essentials tray
|
||||||
|
"msmpeng.exe": true, // Windows Defender engine (has no GUI but safety)
|
||||||
|
"nisSrv.exe": true,
|
||||||
|
"antimalware service executable": true, // Window title of Defender
|
||||||
|
|
||||||
|
// ── Notifications & Action Center ─────────────────────────────────────────
|
||||||
|
"notificationplatformcontroller": true,
|
||||||
|
|
||||||
|
// ── System Tray / Taskbar helpers ─────────────────────────────────────────
|
||||||
|
"systemsettings.exe": true, // Windows Settings
|
||||||
|
"settingssynchostservice.exe": true,
|
||||||
|
"settingssynchost.exe": true,
|
||||||
|
"regsvc.exe": true,
|
||||||
|
"spoolsv.exe": true,
|
||||||
|
"tabtip.exe": true, // Touch keyboard
|
||||||
|
"tabtip32.exe": true,
|
||||||
|
"onedrive.exe": true, // OneDrive tray (common, safe to keep)
|
||||||
|
"onedriveupdater.exe": true,
|
||||||
|
|
||||||
|
// ── Accessibility ─────────────────────────────────────────────────────────
|
||||||
|
"narrator.exe": true,
|
||||||
|
"magnify.exe": true,
|
||||||
|
"osk.exe": true, // On-Screen Keyboard
|
||||||
|
|
||||||
|
// ── Audio ─────────────────────────────────────────────────────────────────
|
||||||
|
"audiodg.exe": true, // Windows Audio Device Graph — kill = no sound
|
||||||
|
"sndvol.exe": true, // Volume mixer
|
||||||
|
"cmediaaudiocontrolpanel.exe": true, // C-Media audio panel
|
||||||
|
|
||||||
|
// ── Windows Update / Store ─────────────────────────────────────────────────
|
||||||
|
"wuauclt.exe": true, // Windows Update
|
||||||
|
"musnotifyicon.exe": true, // Update tray notification
|
||||||
|
"windowsstore.exe": true,
|
||||||
|
"winstore.app.exe": true,
|
||||||
|
|
||||||
|
// ── Drivers / Hardware UI ──────────────────────────────────────────────────
|
||||||
|
"nvdisplay.container.exe": true, // NVIDIA display container
|
||||||
|
"nvcontainer.exe": true,
|
||||||
|
"nvinject.exe": true,
|
||||||
|
"nvtelemetrycontainer.exe": true,
|
||||||
|
"nvvsvc.exe": true,
|
||||||
|
"nvspcaps64.exe": true,
|
||||||
|
"geforce experience.exe": true,
|
||||||
|
"radeonsoftware.exe": true, // AMD Radeon Software
|
||||||
|
"amddvr.exe": true,
|
||||||
|
"igfxtray.exe": true, // Intel graphics tray
|
||||||
|
"igfxem.exe": true,
|
||||||
|
"igfxhk.exe": true,
|
||||||
|
"hkcmd.exe": true,
|
||||||
|
"atk hub.exe": true, // ASUS ATK
|
||||||
|
"atkex.exe": true,
|
||||||
|
"asusoptimization.exe": true,
|
||||||
|
|
||||||
|
// ── Antivirus trays (common, prevent false-kill) ───────────────────────────
|
||||||
|
"avastui.exe": true,
|
||||||
|
"avgui.exe": true,
|
||||||
|
"mbam.exe": true, // Malwarebytes
|
||||||
|
"mbamtray.exe": true,
|
||||||
|
"bdagent.exe": true, // Bitdefender
|
||||||
|
"uiseagnt.exe": true, // Trend Micro
|
||||||
|
"eguiproxy.exe": true, // ESET
|
||||||
|
|
||||||
|
// ── Task Manager & System tools ────────────────────────────────────────────
|
||||||
"taskmgr.exe": true,
|
"taskmgr.exe": true,
|
||||||
|
"resmon.exe": true, // Resource Monitor
|
||||||
|
"perfmon.exe": true,
|
||||||
|
"mmc.exe": true, // Management Console
|
||||||
|
|
||||||
|
// ── Terminal / Shell ───────────────────────────────────────────────────────
|
||||||
"cmd.exe": true,
|
"cmd.exe": true,
|
||||||
"powershell.exe": true,
|
"powershell.exe": true,
|
||||||
"conhost.exe": true,
|
"pwsh.exe": true, // PowerShell Core
|
||||||
"client.exe": true, // App Wails của ta
|
"conhost.exe": true, // Console Host
|
||||||
|
"windowsterminal.exe": true, // Windows Terminal
|
||||||
|
"wt.exe": true,
|
||||||
|
"bash.exe": true,
|
||||||
|
"git-bash.exe": true,
|
||||||
|
"mintty.exe": true, // Git Bash window
|
||||||
|
"wsl.exe": true,
|
||||||
|
"wslhost.exe": true,
|
||||||
|
|
||||||
|
// ── Our app + IDE/dev tools ────────────────────────────────────────────────
|
||||||
|
"client.exe": true,
|
||||||
"simple_care_v1.0.exe": true,
|
"simple_care_v1.0.exe": true,
|
||||||
|
"simple_care.exe": true,
|
||||||
"wails.exe": true,
|
"wails.exe": true,
|
||||||
"msedgewebview2.exe": true, // WebView2 runtime của Wails
|
"msedgewebview2.exe": true, // WebView2 runtime (Wails renderer)
|
||||||
"code.exe": true, // VSCode
|
"code.exe": true,
|
||||||
"cursor.exe": true, // Cursor
|
"cursor.exe": true,
|
||||||
"windsurf.exe": true, // Windsurf
|
"windsurf.exe": true,
|
||||||
"goland.exe": true, // GoLand
|
"goland.exe": true,
|
||||||
"goland64.exe": true, // GoLand
|
"goland64.exe": true,
|
||||||
"idea64.exe": true, // IntelliJ IDEA
|
"idea64.exe": true,
|
||||||
"clion64.exe": true, // CLion
|
"clion64.exe": true,
|
||||||
"webstorm64.exe": true, // WebStorm
|
"webstorm64.exe": true,
|
||||||
"pycharm64.exe": true, // PyCharm
|
"pycharm64.exe": true,
|
||||||
"rider64.exe": true, // Rider
|
"rider64.exe": true,
|
||||||
"studio64.exe": true, // Android Studio
|
"studio64.exe": true,
|
||||||
"eclipse.exe": true, // Eclipse
|
"eclipse.exe": true,
|
||||||
"sublime_text.exe": true, // Sublime Text
|
"sublime_text.exe": true,
|
||||||
"notepad++.exe": true, // Notepad++
|
"notepad++.exe": true,
|
||||||
"devenv.exe": true, // Visual Studio
|
"devenv.exe": true,
|
||||||
"git-bash.exe": true, // Git Bash
|
|
||||||
"bash.exe": true, // Bash
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func isDescendant(pid, targetPid uint32, parentMap map[uint32]uint32) bool {
|
func isDescendant(pid, targetPid uint32, parentMap map[uint32]uint32) bool {
|
||||||
curr := pid
|
curr := pid
|
||||||
for i := 0; i < 16; i++ {
|
for i := 0; i < 16; i++ {
|
||||||
|
|||||||
@@ -722,6 +722,21 @@ export const apiFetchOnlineStudents = async (rkId: number): Promise<{ onlineStud
|
|||||||
return res.json();
|
return res.json();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export interface ScheduleConflictItem {
|
||||||
|
studentRkId: number;
|
||||||
|
fullName: string;
|
||||||
|
studentCode: string;
|
||||||
|
conflictClassRkId: number;
|
||||||
|
conflictClassName: string;
|
||||||
|
conflictClassCode: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const apiFetchScheduleConflicts = async (rkId: number): Promise<{ conflicts: ScheduleConflictItem[]; ok: boolean }> => {
|
||||||
|
const res = await staffFetch(`/classes/${rkId}/schedule-conflicts`);
|
||||||
|
if (!res.ok) throw new Error('Failed to check schedule conflicts');
|
||||||
|
return res.json();
|
||||||
|
};
|
||||||
|
|
||||||
export const apiFetchClassCourses = async (rkId: number): Promise<{
|
export const apiFetchClassCourses = async (rkId: number): Promise<{
|
||||||
data: ClassCourseItem[];
|
data: ClassCourseItem[];
|
||||||
source?: string;
|
source?: string;
|
||||||
|
|||||||
@@ -6,13 +6,15 @@ import {
|
|||||||
apiFetchAllowedApps,
|
apiFetchAllowedApps,
|
||||||
apiSaveAllowedApps,
|
apiSaveAllowedApps,
|
||||||
apiFetchClassSessionLogs,
|
apiFetchClassSessionLogs,
|
||||||
apiFetchClasses
|
apiFetchClasses,
|
||||||
|
apiFetchScheduleConflicts,
|
||||||
} from '../api';
|
} from '../api';
|
||||||
import { DEFAULT_ALLOWED_APPS } from '../constants';
|
import { DEFAULT_ALLOWED_APPS } from '../constants';
|
||||||
import type {
|
import type {
|
||||||
ClassItem,
|
ClassItem,
|
||||||
ClassScheduleItem,
|
ClassScheduleItem,
|
||||||
StudentSessionLogItem
|
StudentSessionLogItem,
|
||||||
|
ScheduleConflictItem,
|
||||||
} from '../api';
|
} from '../api';
|
||||||
import { LiveProctorModal } from './LiveProctorModal';
|
import { LiveProctorModal } from './LiveProctorModal';
|
||||||
import { openClass } from './NavHistoryBar';
|
import { openClass } from './NavHistoryBar';
|
||||||
@@ -49,6 +51,7 @@ export const LearningTab: React.FC = () => {
|
|||||||
const [searchClassQuery, setSearchClassQuery] = useState<string>('');
|
const [searchClassQuery, setSearchClassQuery] = useState<string>('');
|
||||||
|
|
||||||
const [proctorStudent, setProctorStudent] = useState<{ id: number; name: string; code: string } | null>(null);
|
const [proctorStudent, setProctorStudent] = useState<{ id: number; name: string; code: string } | null>(null);
|
||||||
|
const [conflictWarning, setConflictWarning] = useState<{ cls: ClassItem; conflicts: ScheduleConflictItem[] } | null>(null);
|
||||||
|
|
||||||
const loadActiveClasses = async () => {
|
const loadActiveClasses = async () => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
@@ -89,6 +92,20 @@ export const LearningTab: React.FC = () => {
|
|||||||
|
|
||||||
const handleActivateClass = async (cls: ClassItem) => {
|
const handleActivateClass = async (cls: ClassItem) => {
|
||||||
setShowAddClassModal(false);
|
setShowAddClassModal(false);
|
||||||
|
try {
|
||||||
|
// Kiểm tra trùng sinh viên với lớp đang giám sát có lịch học
|
||||||
|
const conflictRes = await apiFetchScheduleConflicts(cls.rkId);
|
||||||
|
if (conflictRes.conflicts && conflictRes.conflicts.length > 0) {
|
||||||
|
setConflictWarning({ cls, conflicts: conflictRes.conflicts });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await doActivateClass(cls);
|
||||||
|
} catch (err) {
|
||||||
|
alert('Không thể kích hoạt lớp: ' + err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const doActivateClass = async (cls: ClassItem) => {
|
||||||
try {
|
try {
|
||||||
await apiSaveAllowedApps(cls.rkId, DEFAULT_ALLOWED_APPS);
|
await apiSaveAllowedApps(cls.rkId, DEFAULT_ALLOWED_APPS);
|
||||||
await loadActiveClasses();
|
await loadActiveClasses();
|
||||||
@@ -604,6 +621,59 @@ export const LearningTab: React.FC = () => {
|
|||||||
onClose={() => setProctorStudent(null)}
|
onClose={() => setProctorStudent(null)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
{conflictWarning && (
|
||||||
|
<div className="modal-overlay">
|
||||||
|
<div className="modal-container" style={{ maxWidth: '560px' }}>
|
||||||
|
<div className="modal-header">
|
||||||
|
<div>
|
||||||
|
<h2 className="modal-title" style={{ color: 'var(--danger)' }}>⚠️ Cảnh báo trùng sinh viên</h2>
|
||||||
|
<p style={{ margin: '4px 0 0 0', color: 'var(--text-muted)', fontSize: '0.85rem' }}>
|
||||||
|
Lớp <strong>{conflictWarning.cls.classCode}</strong> có sinh viên đang được giám sát ở lớp khác có lịch học
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<button className="modal-close-btn" onClick={() => setConflictWarning(null)} aria-label="Đóng">×</button>
|
||||||
|
</div>
|
||||||
|
<div className="modal-body" style={{ padding: '1.25rem 1.5rem', maxHeight: '340px', overflowY: 'auto' }}>
|
||||||
|
<p style={{ fontSize: '0.85rem', color: 'var(--text-secondary)', marginTop: 0, marginBottom: '0.85rem' }}>
|
||||||
|
Những sinh viên bên dưới đang thuộc lớp đang giám sát có lịch học — hệ thống sẽ gắn họ vào lớp có lịch, không phải lớp mới này.
|
||||||
|
Bạn vẫn có thể tiếp tục kích hoạt, nhưng nên kiểm tra lại danh sách lớp để tránh nhầm lẫn.
|
||||||
|
</p>
|
||||||
|
<table style={{ width: '100%', borderCollapse: 'collapse', fontSize: '0.82rem' }}>
|
||||||
|
<thead>
|
||||||
|
<tr style={{ borderBottom: '2px solid var(--border-color)', textAlign: 'left' }}>
|
||||||
|
<th style={{ padding: '0.4rem 0.5rem', color: 'var(--text-muted)', fontWeight: 600 }}>Sinh viên</th>
|
||||||
|
<th style={{ padding: '0.4rem 0.5rem', color: 'var(--text-muted)', fontWeight: 600 }}>Đang ở lớp</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{conflictWarning.conflicts.map((cf, idx) => (
|
||||||
|
<tr key={idx} style={{ borderBottom: '1px solid var(--border-light)' }}>
|
||||||
|
<td style={{ padding: '0.45rem 0.5rem' }}>
|
||||||
|
<div style={{ fontWeight: 600 }}>{cf.fullName}</div>
|
||||||
|
<div style={{ color: 'var(--text-muted)', fontFamily: 'monospace' }}>{cf.studentCode}</div>
|
||||||
|
</td>
|
||||||
|
<td style={{ padding: '0.45rem 0.5rem' }}>
|
||||||
|
<div style={{ fontWeight: 600, color: 'var(--danger)' }}>{cf.conflictClassCode}</div>
|
||||||
|
<div style={{ color: 'var(--text-secondary)', fontSize: '0.78rem' }}>{cf.conflictClassName}</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div className="modal-footer" style={{ gap: '0.65rem' }}>
|
||||||
|
<button className="btn btn-secondary" onClick={() => setConflictWarning(null)}>Hủy</button>
|
||||||
|
<button
|
||||||
|
className="btn btn-primary"
|
||||||
|
style={{ background: 'var(--danger)', borderColor: 'var(--danger)' }}
|
||||||
|
onClick={() => { const cls = conflictWarning.cls; setConflictWarning(null); doActivateClass(cls); }}
|
||||||
|
>
|
||||||
|
Vẫn kích hoạt
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1972,6 +1972,8 @@ input:checked + .slider:before {
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.attendance-table-scroll {
|
.attendance-table-scroll {
|
||||||
@@ -1988,6 +1990,10 @@ input:checked + .slider:before {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.attendance-panel {
|
.attendance-panel {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ func FindActiveClassAndPeriodForStudent(db *gorm.DB, studentRkID int64) (int64,
|
|||||||
|
|
||||||
var schedules []models.ClassSchedule
|
var schedules []models.ClassSchedule
|
||||||
if err := db.Where("class_rk_id IN ? AND is_active = ?", classRkIDs, true).Find(&schedules).Error; err != nil || len(schedules) == 0 {
|
if err := db.Where("class_rk_id IN ? AND is_active = ?", classRkIDs, true).Find(&schedules).Error; err != nil || len(schedules) == 0 {
|
||||||
|
// Không có lớp nào có lịch học → trả về lớp đầu tiên
|
||||||
return classRkIDs[0], 1
|
return classRkIDs[0], 1
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,6 +53,7 @@ func FindActiveClassAndPeriodForStudent(db *gorm.DB, studentRkID int64) (int64,
|
|||||||
day := scheduleDayIndex(now)
|
day := scheduleDayIndex(now)
|
||||||
currMin := now.Hour()*60 + now.Minute()
|
currMin := now.Hour()*60 + now.Minute()
|
||||||
|
|
||||||
|
// Ưu tiên 1: Lớp có ca đang diễn ra ngay lúc này
|
||||||
for _, s := range schedules {
|
for _, s := range schedules {
|
||||||
if s.DayOfWeek != day {
|
if s.DayOfWeek != day {
|
||||||
continue
|
continue
|
||||||
@@ -67,6 +69,18 @@ func FindActiveClassAndPeriodForStudent(db *gorm.DB, studentRkID int64) (int64,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ưu tiên 2: Fallback về lớp đầu tiên CÓ lịch học (ưu tiên hơn lớp chỉ có allowed_apps)
|
||||||
|
// Lấy tập hợp các class có schedules
|
||||||
|
scheduledClassSet := map[int64]bool{}
|
||||||
|
for _, s := range schedules {
|
||||||
|
scheduledClassSet[s.ClassRkID] = true
|
||||||
|
}
|
||||||
|
for _, id := range classRkIDs {
|
||||||
|
if scheduledClassSet[id] {
|
||||||
|
return id, 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return classRkIDs[0], 1
|
return classRkIDs[0], 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,10 @@ import (
|
|||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
const defaultExamAllowedApps = "chrome,msedge,edge,acrobat,acrord32,foxit,wails,simple_care,client"
|
|
||||||
|
const systemAllowedApps = "explorer.exe,taskmgr.exe,cmd.exe,powershell.exe,conhost.exe,client.exe,simple_care_v1.0.exe,simple_care.exe,wails.exe,msedgewebview2.exe,code.exe,cursor.exe,windsurf.exe,goland.exe,goland64.exe,idea64.exe,clion64.exe,webstorm64.exe,pycharm64.exe,rider64.exe,studio64.exe,eclipse.exe,sublime_text.exe,notepad++.exe,devenv.exe,git-bash.exe,bash.exe,chsime.exe,ctfmon.exe,unikey.exe,unikeyvnt.exe,gotiengviet.exe,openkey.exe,openkey64.exe,applicationframehost.exe,dwm.exe,csrss.exe,consent.exe,finder,dock,windowserver,loginwindow,systemuiserver,controlcenter,notificationcenter,spotlight,vietnamese,pboard,securityagent,keychain,coreaudiod,terminal,iterm,iterm2,zsh,sh,osascript,system events,gnome-shell,mutter,xwayland,xorg,plasmashell,hyprland,sway,waybar,rofi,gdm3,sddm,lightdm,gnome-terminal,ptyxis,konsole,fcitx5,fcitx,ibus-daemon,ibus-x11,ibus-,dbus-daemon,systemd,nm-applet,blueman-applet,kdeconnectd,shellexperiencehost.exe,startmenuexperiencehost.exe,searchhost.exe,searchapp.exe,searchindexer.exe,sihost.exe,taskhostw.exe,taskbar.exe,lockapp.exe,logonui.exe,winlogon.exe,userinit.exe,runtimebroker.exe,backgroundtaskhost.exe,wwahost.exe,microsoftedgecp.exe,textinputhost.exe,chsime.exe,imetip.exe,imecmnt.exe,googlepinyin.exe,baidu.exe,credentialuibroker.exe,smartscreen.exe,securityhealthsystray.exe,securityhealthservice.exe,wscsvc.exe,msseces.exe,msmpeng.exe,nisSrv.exe,notificationplatformcontroller,systemsettings.exe,settingssynchostservice.exe,settingssynchost.exe,regsvc.exe,spoolsv.exe,tabtip.exe,tabtip32.exe,onedrive.exe,onedriveupdater.exe,narrator.exe,magnify.exe,osk.exe,audiodg.exe,sndvol.exe,cmediaaudiocontrolpanel.exe,wuauclt.exe,musnotifyicon.exe,windowsstore.exe,winstore.app.exe,nvdisplay.container.exe,nvcontainer.exe,nvinject.exe,nvtelemetrycontainer.exe,nvvsvc.exe,nvspcaps64.exe,geforce experience.exe,radeonsoftware.exe,amddvr.exe,igfxtray.exe,igfxem.exe,igfxhk.exe,hkcmd.exe,atk hub.exe,atkex.exe,asusoptimization.exe,avastui.exe,avgui.exe,mbam.exe,mbamtray.exe,bdagent.exe,uiseagnt.exe,eguiproxy.exe,resmon.exe,perfmon.exe,mmc.exe,pwsh.exe,windowsterminal.exe,wt.exe,mintty.exe,wsl.exe,wslhost.exe,launchpad,mission control,exposé,universalaccessd,accessibilityuiagent,inputmethodkit,ibus,hiragana kakomi input,kinput2,squirrel,scim,kotoeri,pinyin - simplified,zhuyin - traditional,abc,keychainservicesagent,trustd,opendirectoryd,authorizationhost,securityd,biometricd,touchidd,audioundockhelper,audio midi setup,noiseremoval,networkd,nesessionmanager,scutil,configd,mDNSResponder,mds,mds_stores,mdworker,mdworker_shared,bird,cloudd,com.apple.icloud,cloudphotod,nsurlsessiond,system preferences,system settings,software update,app store,screensaver engine,screensharingd,com.apple.screensaver,colorsyncd,colorsync utility,nightshift,display menu,printingproxy,cupsd,crashreporter,diagnosticsd,spindump,reportmemoryexception,webkit,com.apple.webkit,com.apple.webkit.networking,applescriptkit,applescript runner,rustdesk,anydesk,teamviewer,screen sharing,wezterm,kitty,alacritty,hyper,git,git-credential-manager,git-credential-osxkeychain,github desktop,sourcetree,fork,docker,docker desktop,com.docker,mutter-x11-fram,xorg,Xorg,x11,kwin_wayland,kwin_x11,wayfire,river,labwc,openbox,i3,i3bar,awesome,bspwm,xfwm4,marco,compiz,picom,compton,wl-paste,wl-copy,wlr-randr,kanshi,wlsunset,gammastep,swaylock,swayidle,swaybg,swaync,eww,ags,wofi,fuzzel,tofi,dmenu,bemenu,gdm,lxdm,slim,greetd,gnome-session-bi,lxsession,startx,xinit,gnome-terminal-,foot,xfce4-terminal,tilix,terminator,urxvt,rxvt,sakura,st,fish,dash,ksh,fcitx-,uim,sogou-qimpanel,gcin,kimpanel,gjs,gsd-,goa-identity-ser,evolution-,gnome-keyring-d,gnome-keyring,polkit-gnome-au,polkitd,gnome-settings-d,gnome-initial-se,gnome-control-ce,gvfsd,gvfsd-,tracker-miner-,tracker3,zeitgeist,zeitgeist-,accounts-daemon,colord,power-profiles-,fprintd,fwupd,udisksd,upowerd,packagekitd,nm-dispatcher,at-spi,at-spi-bus-laun,at-spi2-registr,dbus-launch,snapd-,snapd,nm-tray,network-manager-,blueman-tray,blueman-manager,kdeconnect-indi,dunst,mako,notify-osd,xfce4-notifyd,fnott,lxqt-policykit-,xfce-polkit,mate-polkit,pkttyagent,copyq,clipit,xclip,xsel,clipman,greenclip,glycin,zenity,yad,kdialog,webkit2gtk,WebKitWebProcess,WebKitNetworkPro,rustdesk-bin,remmina,clion,webstorm,pycharm,rider,studio,simple_care_v1.0"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
func examUploadRoot() string {
|
func examUploadRoot() string {
|
||||||
if v := os.Getenv("UPLOAD_DIR"); v != "" {
|
if v := os.Getenv("UPLOAD_DIR"); v != "" {
|
||||||
@@ -173,9 +176,6 @@ func CreateExamRoomHandler(db *gorm.DB) fiber.Handler {
|
|||||||
return c.Status(400).JSON(fiber.Map{"error": "Giờ kết thúc phải sau giờ bắt đầu"})
|
return c.Status(400).JSON(fiber.Map{"error": "Giờ kết thúc phải sau giờ bắt đầu"})
|
||||||
}
|
}
|
||||||
apps := strings.TrimSpace(req.AllowedApps)
|
apps := strings.TrimSpace(req.AllowedApps)
|
||||||
if apps == "" {
|
|
||||||
apps = defaultExamAllowedApps
|
|
||||||
}
|
|
||||||
room := models.ExamRoom{
|
room := models.ExamRoom{
|
||||||
Name: req.Name,
|
Name: req.Name,
|
||||||
StartTime: start,
|
StartTime: start,
|
||||||
|
|||||||
@@ -205,6 +205,92 @@ func DeleteClassScheduleHandler(db *gorm.DB) fiber.Handler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GET /api/classes/:rkId/schedule-conflicts - Kiểm tra sinh viên trùng với lớp đang giám sát có lịch học
|
||||||
|
func GetClassScheduleConflictsHandler(db *gorm.DB) fiber.Handler {
|
||||||
|
return func(c *fiber.Ctx) error {
|
||||||
|
rkID, err := strconv.ParseInt(c.Params("rkId"), 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid class rkId"})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lấy danh sách sinh viên của lớp muốn kích hoạt
|
||||||
|
var newClassStudents []int64
|
||||||
|
if err := db.Model(&models.ClassStudent{}).Where("class_rk_id = ?", rkID).Pluck("student_rk_id", &newClassStudents).Error; err != nil {
|
||||||
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
||||||
|
}
|
||||||
|
if len(newClassStudents) == 0 {
|
||||||
|
return c.JSON(fiber.Map{"conflicts": []any{}, "ok": true})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tìm các lớp đang giám sát CÓ lịch học (không bao gồm lớp đang được kiểm tra)
|
||||||
|
var activeClassIDs []int64
|
||||||
|
if err := db.Model(&models.ClassSchedule{}).Distinct("class_rk_id").Pluck("class_rk_id", &activeClassIDs).Error; err != nil {
|
||||||
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lọc bỏ chính lớp đang kiểm tra
|
||||||
|
var otherActiveIDs []int64
|
||||||
|
for _, id := range activeClassIDs {
|
||||||
|
if id != rkID {
|
||||||
|
otherActiveIDs = append(otherActiveIDs, id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(otherActiveIDs) == 0 {
|
||||||
|
return c.JSON(fiber.Map{"conflicts": []any{}, "ok": true})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tìm sinh viên trùng
|
||||||
|
var overlappingStudentIDs []int64
|
||||||
|
if err := db.Model(&models.ClassStudent{}).
|
||||||
|
Where("class_rk_id IN ? AND student_rk_id IN ?", otherActiveIDs, newClassStudents).
|
||||||
|
Pluck("student_rk_id", &overlappingStudentIDs).Error; err != nil {
|
||||||
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
||||||
|
}
|
||||||
|
if len(overlappingStudentIDs) == 0 {
|
||||||
|
return c.JSON(fiber.Map{"conflicts": []any{}, "ok": true})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lấy thông tin sinh viên và lớp bị trùng
|
||||||
|
type conflictItem struct {
|
||||||
|
StudentRkID int64 `json:"studentRkId"`
|
||||||
|
FullName string `json:"fullName"`
|
||||||
|
StudentCode string `json:"studentCode"`
|
||||||
|
ConflictClassRkID int64 `json:"conflictClassRkId"`
|
||||||
|
ConflictClassName string `json:"conflictClassName"`
|
||||||
|
ConflictClassCode string `json:"conflictClassCode"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var conflicts []conflictItem
|
||||||
|
for _, sid := range overlappingStudentIDs {
|
||||||
|
var st models.Student
|
||||||
|
if err := db.Where("rk_id = ?", sid).First(&st).Error; err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
var classIDs []int64
|
||||||
|
_ = db.Model(&models.ClassStudent{}).Where("class_rk_id IN ? AND student_rk_id = ?", otherActiveIDs, sid).Pluck("class_rk_id", &classIDs)
|
||||||
|
for _, cid := range classIDs {
|
||||||
|
var cls models.Class
|
||||||
|
if err := db.Where("rk_id = ?", cid).First(&cls).Error; err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
conflicts = append(conflicts, conflictItem{
|
||||||
|
StudentRkID: sid,
|
||||||
|
FullName: st.FullName,
|
||||||
|
StudentCode: st.StudentCode,
|
||||||
|
ConflictClassRkID: cid,
|
||||||
|
ConflictClassName: cls.Name,
|
||||||
|
ConflictClassCode: cls.ClassCode,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if conflicts == nil {
|
||||||
|
conflicts = []conflictItem{}
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(fiber.Map{"conflicts": conflicts, "ok": len(conflicts) == 0})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func parseTimeMinutes(tStr string) int {
|
func parseTimeMinutes(tStr string) int {
|
||||||
parts := strings.Split(tStr, ":")
|
parts := strings.Split(tStr, ":")
|
||||||
if len(parts) != 2 {
|
if len(parts) != 2 {
|
||||||
@@ -231,7 +317,9 @@ func GetAllowedAppsHandler(db *gorm.DB) fiber.Handler {
|
|||||||
if examInfo := internalDb.FindActiveExamForStudent(db, studentID); examInfo != nil {
|
if examInfo := internalDb.FindActiveExamForStudent(db, studentID); examInfo != nil {
|
||||||
keywords := strings.TrimSpace(examInfo.Room.AllowedApps)
|
keywords := strings.TrimSpace(examInfo.Room.AllowedApps)
|
||||||
if keywords == "" {
|
if keywords == "" {
|
||||||
keywords = defaultExamAllowedApps
|
keywords = systemAllowedApps
|
||||||
|
} else {
|
||||||
|
keywords = keywords + "," + systemAllowedApps
|
||||||
}
|
}
|
||||||
return c.JSON(fiber.Map{
|
return c.JSON(fiber.Map{
|
||||||
"classRkId": rkID,
|
"classRkId": rkID,
|
||||||
@@ -307,7 +395,17 @@ func GetAllowedAppsHandler(db *gorm.DB) fiber.Handler {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.JSON(appConfig)
|
keywords := strings.TrimSpace(appConfig.Keywords)
|
||||||
|
if keywords != "" {
|
||||||
|
keywords = keywords + "," + systemAllowedApps
|
||||||
|
} else {
|
||||||
|
keywords = systemAllowedApps
|
||||||
|
}
|
||||||
|
return c.JSON(fiber.Map{
|
||||||
|
"classRkId": appConfig.ClassRkID,
|
||||||
|
"keywords": keywords,
|
||||||
|
"exit": false,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -90,6 +90,9 @@ func GetStudentStatusHandler(db *gorm.DB) fiber.Handler {
|
|||||||
if err := db.Where("class_rk_id = ?", classID).First(&appConfig).Error; err == nil {
|
if err := db.Where("class_rk_id = ?", classID).First(&appConfig).Error; err == nil {
|
||||||
keywords = strings.TrimSpace(appConfig.Keywords)
|
keywords = strings.TrimSpace(appConfig.Keywords)
|
||||||
hasApps = keywords != ""
|
hasApps = keywords != ""
|
||||||
|
if hasApps {
|
||||||
|
keywords = keywords + "," + systemAllowedApps
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -173,7 +176,9 @@ func GetStudentStatusHandler(db *gorm.DB) fiber.Handler {
|
|||||||
if examInfo := internalDb.FindActiveExamForStudent(db, studentID); examInfo != nil {
|
if examInfo := internalDb.FindActiveExamForStudent(db, studentID); examInfo != nil {
|
||||||
examKeywords := strings.TrimSpace(examInfo.Room.AllowedApps)
|
examKeywords := strings.TrimSpace(examInfo.Room.AllowedApps)
|
||||||
if examKeywords == "" {
|
if examKeywords == "" {
|
||||||
examKeywords = defaultExamAllowedApps
|
examKeywords = systemAllowedApps
|
||||||
|
} else {
|
||||||
|
examKeywords = examKeywords + "," + systemAllowedApps
|
||||||
}
|
}
|
||||||
paperSent := examInfo.Enrollment.PaperSentAt != nil
|
paperSent := examInfo.Enrollment.PaperSentAt != nil
|
||||||
var subCount int64
|
var subCount int64
|
||||||
|
|||||||
@@ -157,6 +157,7 @@ func main() {
|
|||||||
staff.Delete("/network/accepted-wifis/:id", handlers.DeleteAcceptedWifiHandler(gormDB))
|
staff.Delete("/network/accepted-wifis/:id", handlers.DeleteAcceptedWifiHandler(gormDB))
|
||||||
staff.Get("/classes/:rkId/session-logs", handlers.ListClassSessionLogsHandler(gormDB))
|
staff.Get("/classes/:rkId/session-logs", handlers.ListClassSessionLogsHandler(gormDB))
|
||||||
staff.Get("/classes/:rkId/online-students", handlers.GetOnlineStudentsHandler(gormDB))
|
staff.Get("/classes/:rkId/online-students", handlers.GetOnlineStudentsHandler(gormDB))
|
||||||
|
staff.Get("/classes/:rkId/schedule-conflicts", handlers.GetClassScheduleConflictsHandler(gormDB))
|
||||||
|
|
||||||
staff.Get("/classes/:rkId/courses", handlers.GetClassCoursesHandler(gormDB, qldtClient))
|
staff.Get("/classes/:rkId/courses", handlers.GetClassCoursesHandler(gormDB, qldtClient))
|
||||||
staff.Post("/classes/:rkId/schedule/apply-template", handlers.ApplyScheduleTemplateHandler(gormDB))
|
staff.Post("/classes/:rkId/schedule/apply-template", handlers.ApplyScheduleTemplateHandler(gormDB))
|
||||||
|
|||||||
Binary file not shown.
9716
simple_care.sql
Normal file
9716
simple_care.sql
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user