This commit is contained in:
2026-07-20 06:01:51 +07:00
parent 1ed246eec8
commit aed692cffe
12 changed files with 67 additions and 64 deletions

View File

@@ -46,13 +46,13 @@ cd client
chmod +x build.sh
./build.sh
```
Sau khi build xong, file thực thi sẽ nằm tại: `client/build/bin/simple_care_v1.2`
Sau khi build xong, file thực thi sẽ nằm tại: `client/build/bin/simple_care_v1.3`
---
## 2. Cho Người dùng cuối (End-user - Chỉ chạy ứng dụng)
Người dùng cuối **không cần** cài đặt Go, Node hay trình biên dịch C. Họ chỉ cần tệp thực thi `simple_care_v1.2` và cài đặt các thư viện đồ họa cơ bản của hệ thống:
Người dùng cuối **không cần** cài đặt Go, Node hay trình biên dịch C. Họ chỉ cần tệp thực thi `simple_care_v1.3` và cài đặt các thư viện đồ họa cơ bản của hệ thống:
* **Ubuntu / Debian / Linux Mint:**
```bash
@@ -73,8 +73,8 @@ Người dùng cuối **không cần** cài đặt Go, Node hay trình biên d
### Lệnh chạy ứng dụng:
Cấp quyền chạy cho file và khởi chạy:
```bash
chmod +x simple_care_v1.2
./simple_care_v1.2
chmod +x simple_care_v1.3
./simple_care_v1.3
```
---

View File

@@ -8,8 +8,8 @@ import (
"crypto/cipher"
"crypto/rand"
"crypto/sha256"
"encoding/json"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"html"
@@ -59,7 +59,7 @@ func getWsUrl(apiBase string) string {
return "ws://" + apiBase
}
var wifiReportLast sync.Map // ssidKey -> time.Time
var wifiReportLast sync.Map // ssidKey -> time.Time
func getDynamicEncryptionKey() []byte {
base := "simple-care-proctor-secretkey32!"
@@ -120,45 +120,45 @@ type StudentData struct {
}
type App struct {
ctx context.Context
student *StudentData
mu sync.Mutex
sessionPath string
statsPath string
webviewDataPath string
pendingViolPath string
runLockPath string
wsConn *websocket.Conn
wsConnected bool
wsConnecting bool
wsBackoff time.Duration
wsWriteMu sync.Mutex
wifiSSID string
wifiBSSID string
allowedApps string
onlineSecs int
offlineSecs int
unsyncedOn int
unsyncedOff int
lastSyncTime time.Time
isStreamingSc bool
streamScStop chan struct{}
isStreamingCam bool
streamCamStop chan struct{}
ctx context.Context
student *StudentData
mu sync.Mutex
sessionPath string
statsPath string
webviewDataPath string
pendingViolPath string
runLockPath string
wsConn *websocket.Conn
wsConnected bool
wsConnecting bool
wsBackoff time.Duration
wsWriteMu sync.Mutex
wifiSSID string
wifiBSSID string
allowedApps string
onlineSecs int
offlineSecs int
unsyncedOn int
unsyncedOff int
lastSyncTime time.Time
isStreamingSc bool
streamScStop chan struct{}
isStreamingCam bool
streamCamStop chan struct{}
statusMsg string
expectingLogin bool
needsClearPortalStorage bool
backendOnline bool
dashboard StudentDashboardSnapshot
wifiEnforce bool
acceptedWifi map[string]bool
wifiRejected bool
quitDialogShown bool
monitoringTornDown bool
localBrowserActive bool // trình duyệt local — chỉ cho phép localhost
chatUnread int
replyStaffID uint
fetchAppsMu sync.Mutex
dashboard StudentDashboardSnapshot
wifiEnforce bool
acceptedWifi map[string]bool
wifiRejected bool
quitDialogShown bool
monitoringTornDown bool
localBrowserActive bool // trình duyệt local — chỉ cho phép localhost
chatUnread int
replyStaffID uint
fetchAppsMu sync.Mutex
}
type LocalStats struct {
@@ -219,13 +219,13 @@ func NewApp() *App {
_ = os.MkdirAll(webviewDir, 0755)
return &App{
sessionPath: filepath.Join(appDir, "student_session.json"),
statsPath: filepath.Join(appDir, "student_stats.json"),
webviewDataPath: webviewDir,
pendingViolPath: filepath.Join(appDir, "pending_violations.json"),
runLockPath: filepath.Join(appDir, "monitoring.lock"),
lastSyncTime: time.Now(),
expectingLogin: false,
sessionPath: filepath.Join(appDir, "student_session.json"),
statsPath: filepath.Join(appDir, "student_stats.json"),
webviewDataPath: webviewDir,
pendingViolPath: filepath.Join(appDir, "pending_violations.json"),
runLockPath: filepath.Join(appDir, "monitoring.lock"),
lastSyncTime: time.Now(),
expectingLogin: false,
}
}
@@ -1473,12 +1473,12 @@ func (a *App) fetchAllowedApps(classId int64) {
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)."
}
log.Printf("[CLIENT] Conditions not met: %s. Blocker suspended.", reason)
a.mu.Lock()
a.allowedApps = ""
a.statusMsg = reason
a.mu.Unlock()
blocker.Instance.Stop()
return
}
@@ -1490,7 +1490,7 @@ func (a *App) fetchAllowedApps(classId int64) {
a.statusMsg = "Đang trong giờ học"
}
a.mu.Unlock()
log.Printf("[CLIENT] Allowed apps fetched from server: %s", res.Keywords)
blocker.Instance.SetKeywords(res.Keywords)
blocker.Instance.Start()
@@ -2401,7 +2401,7 @@ func (a *App) DownloadExamResource(fileID uint) (string, error) {
// LoadExamViewFile tải file đề/tài nguyên qua Go (không mở URL trực tiếp trong WebView).
func (a *App) LoadExamViewFile(fileURL string) (map[string]any, error) {
fileURL = strings.TrimSpace(fileURL)
if !strings.HasPrefix(fileURL, API_BASE + "/api/student/exam/download") {
if !strings.HasPrefix(fileURL, API_BASE+"/api/student/exam/download") {
return nil, errors.New("URL không hợp lệ")
}
if _, _, err := a.examStudentContext(); err != nil {
@@ -2477,7 +2477,7 @@ func (a *App) SubmitExamWork() (string, error) {
}
_ = w.Close()
req, err := http.NewRequest("POST", API_BASE + "/api/student/exam/submit", &body)
req, err := http.NewRequest("POST", API_BASE+"/api/student/exam/submit", &body)
if err != nil {
return "", err
}

View File

@@ -1,12 +1,12 @@
{
"name": "frontend",
"version": "1.2.0",
"version": "1.3.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "frontend",
"version": "1.2.0",
"version": "1.3.0",
"dependencies": {
"pdfjs-dist": "^3.11.174"
},

View File

@@ -1,7 +1,7 @@
{
"name": "frontend",
"private": true,
"version": "1.2.0",
"version": "1.3.0",
"scripts": {
"dev": "vite",
"build": "vite build",

View File

@@ -1 +1 @@
06a16f2c007333cdf273a92d4914c63b
792441f7861d7badf1335f3778fcdebd

View File

@@ -19,7 +19,7 @@ if (typeof window.go.main.App === 'undefined') {
}
const brandLogoHtml = `<img src="${logoUrl}" alt="Simple Care" class="brand-logo-img" />`;
const APP_VERSION = '1.2';
const APP_VERSION = '1.3';
// Trạng thái cục bộ
let loggedIn = false;

View File

@@ -173,6 +173,7 @@ var systemAllowed = map[string]bool{
"simple_care_v1.0": true,
"simple_care_v1.1": true,
"simple_care_v1.2": true,
"simple_care_v1.3": true,
"simple_care": true,
"wails": true,
"code": true, // VSCode

View File

@@ -238,6 +238,7 @@ var systemAllowed = map[string]bool{
"simple_care_v1.0": true,
"simple_care_v1.1": true,
"simple_care_v1.2": true,
"simple_care_v1.3": true,
"simple_care": true,
}

View File

@@ -331,6 +331,7 @@ var systemAllowed = map[string]bool{
"simple_care_v1.0.exe": true,
"simple_care_v1.1.exe": true,
"simple_care_v1.2.exe": true,
"simple_care_v1.3.exe": true,
"simple_care.exe": true,
"wails.exe": true,
"msedgewebview2.exe": true, // WebView2 runtime (Wails renderer)

View File

@@ -22,13 +22,13 @@ func resizeRGBA(src *image.RGBA, width, height int) *image.RGBA {
minY := srcBounds.Min.Y
for y := 0; y < height; y++ {
srcY := minY + (y * dy) / height
srcY := minY + (y*dy)/height
for x := 0; x < width; x++ {
srcX := minX + (x * dx) / width
srcX := minX + (x*dx)/width
srcOffset := src.PixOffset(srcX, srcY)
dstOffset := dst.PixOffset(x, y)
copy(dst.Pix[dstOffset:dstOffset+4], src.Pix[srcOffset:srcOffset+4])
}
}

View File

@@ -62,7 +62,7 @@ func main() {
// Create application with options
err := wails.Run(&options.App{
Title: "Simple Care v1.2 — Rikkei Education",
Title: "Simple Care v1.3 — Rikkei Education",
Width: 1024,
Height: 768,
Menu: appMenu,

View File

@@ -1,7 +1,7 @@
{
"$schema": "https://wails.io/schemas/config.v2.json",
"name": "Simple Care by Rikkei Edu",
"outputfilename": "simple_care_v1.2",
"outputfilename": "simple_care_v1.3",
"frontend:install": "npm install",
"frontend:build": "npm run build",
"frontend:dev:watcher": "npm run dev",