fix push
This commit is contained in:
@@ -46,13 +46,13 @@ cd client
|
|||||||
chmod +x build.sh
|
chmod +x build.sh
|
||||||
./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)
|
## 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:**
|
* **Ubuntu / Debian / Linux Mint:**
|
||||||
```bash
|
```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:
|
### Lệnh chạy ứng dụng:
|
||||||
Cấp quyền chạy cho file và khởi chạy:
|
Cấp quyền chạy cho file và khởi chạy:
|
||||||
```bash
|
```bash
|
||||||
chmod +x simple_care_v1.2
|
chmod +x simple_care_v1.3
|
||||||
./simple_care_v1.2
|
./simple_care_v1.3
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ import (
|
|||||||
"crypto/cipher"
|
"crypto/cipher"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"encoding/json"
|
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"html"
|
"html"
|
||||||
@@ -59,7 +59,7 @@ func getWsUrl(apiBase string) string {
|
|||||||
return "ws://" + apiBase
|
return "ws://" + apiBase
|
||||||
}
|
}
|
||||||
|
|
||||||
var wifiReportLast sync.Map // ssidKey -> time.Time
|
var wifiReportLast sync.Map // ssidKey -> time.Time
|
||||||
|
|
||||||
func getDynamicEncryptionKey() []byte {
|
func getDynamicEncryptionKey() []byte {
|
||||||
base := "simple-care-proctor-secretkey32!"
|
base := "simple-care-proctor-secretkey32!"
|
||||||
@@ -120,45 +120,45 @@ type StudentData struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type App struct {
|
type App struct {
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
student *StudentData
|
student *StudentData
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
sessionPath string
|
sessionPath string
|
||||||
statsPath string
|
statsPath string
|
||||||
webviewDataPath string
|
webviewDataPath string
|
||||||
pendingViolPath string
|
pendingViolPath string
|
||||||
runLockPath string
|
runLockPath string
|
||||||
wsConn *websocket.Conn
|
wsConn *websocket.Conn
|
||||||
wsConnected bool
|
wsConnected bool
|
||||||
wsConnecting bool
|
wsConnecting bool
|
||||||
wsBackoff time.Duration
|
wsBackoff time.Duration
|
||||||
wsWriteMu sync.Mutex
|
wsWriteMu sync.Mutex
|
||||||
wifiSSID string
|
wifiSSID string
|
||||||
wifiBSSID string
|
wifiBSSID string
|
||||||
allowedApps string
|
allowedApps string
|
||||||
onlineSecs int
|
onlineSecs int
|
||||||
offlineSecs int
|
offlineSecs int
|
||||||
unsyncedOn int
|
unsyncedOn int
|
||||||
unsyncedOff int
|
unsyncedOff int
|
||||||
lastSyncTime time.Time
|
lastSyncTime time.Time
|
||||||
isStreamingSc bool
|
isStreamingSc bool
|
||||||
streamScStop chan struct{}
|
streamScStop chan struct{}
|
||||||
isStreamingCam bool
|
isStreamingCam bool
|
||||||
streamCamStop chan struct{}
|
streamCamStop chan struct{}
|
||||||
statusMsg string
|
statusMsg string
|
||||||
expectingLogin bool
|
expectingLogin bool
|
||||||
needsClearPortalStorage bool
|
needsClearPortalStorage bool
|
||||||
backendOnline bool
|
backendOnline bool
|
||||||
dashboard StudentDashboardSnapshot
|
dashboard StudentDashboardSnapshot
|
||||||
wifiEnforce bool
|
wifiEnforce bool
|
||||||
acceptedWifi map[string]bool
|
acceptedWifi map[string]bool
|
||||||
wifiRejected bool
|
wifiRejected bool
|
||||||
quitDialogShown bool
|
quitDialogShown bool
|
||||||
monitoringTornDown bool
|
monitoringTornDown bool
|
||||||
localBrowserActive bool // trình duyệt local — chỉ cho phép localhost
|
localBrowserActive bool // trình duyệt local — chỉ cho phép localhost
|
||||||
chatUnread int
|
chatUnread int
|
||||||
replyStaffID uint
|
replyStaffID uint
|
||||||
fetchAppsMu sync.Mutex
|
fetchAppsMu sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
type LocalStats struct {
|
type LocalStats struct {
|
||||||
@@ -219,13 +219,13 @@ func NewApp() *App {
|
|||||||
_ = os.MkdirAll(webviewDir, 0755)
|
_ = os.MkdirAll(webviewDir, 0755)
|
||||||
|
|
||||||
return &App{
|
return &App{
|
||||||
sessionPath: filepath.Join(appDir, "student_session.json"),
|
sessionPath: filepath.Join(appDir, "student_session.json"),
|
||||||
statsPath: filepath.Join(appDir, "student_stats.json"),
|
statsPath: filepath.Join(appDir, "student_stats.json"),
|
||||||
webviewDataPath: webviewDir,
|
webviewDataPath: webviewDir,
|
||||||
pendingViolPath: filepath.Join(appDir, "pending_violations.json"),
|
pendingViolPath: filepath.Join(appDir, "pending_violations.json"),
|
||||||
runLockPath: filepath.Join(appDir, "monitoring.lock"),
|
runLockPath: filepath.Join(appDir, "monitoring.lock"),
|
||||||
lastSyncTime: time.Now(),
|
lastSyncTime: time.Now(),
|
||||||
expectingLogin: false,
|
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)."
|
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)
|
log.Printf("[CLIENT] Conditions not met: %s. Blocker suspended.", reason)
|
||||||
|
|
||||||
a.mu.Lock()
|
a.mu.Lock()
|
||||||
a.allowedApps = ""
|
a.allowedApps = ""
|
||||||
a.statusMsg = reason
|
a.statusMsg = reason
|
||||||
a.mu.Unlock()
|
a.mu.Unlock()
|
||||||
|
|
||||||
blocker.Instance.Stop()
|
blocker.Instance.Stop()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -1490,7 +1490,7 @@ func (a *App) fetchAllowedApps(classId int64) {
|
|||||||
a.statusMsg = "Đang trong giờ học"
|
a.statusMsg = "Đang trong giờ học"
|
||||||
}
|
}
|
||||||
a.mu.Unlock()
|
a.mu.Unlock()
|
||||||
|
|
||||||
log.Printf("[CLIENT] Allowed apps fetched from server: %s", res.Keywords)
|
log.Printf("[CLIENT] Allowed apps fetched from server: %s", res.Keywords)
|
||||||
blocker.Instance.SetKeywords(res.Keywords)
|
blocker.Instance.SetKeywords(res.Keywords)
|
||||||
blocker.Instance.Start()
|
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).
|
// 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) {
|
func (a *App) LoadExamViewFile(fileURL string) (map[string]any, error) {
|
||||||
fileURL = strings.TrimSpace(fileURL)
|
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ệ")
|
return nil, errors.New("URL không hợp lệ")
|
||||||
}
|
}
|
||||||
if _, _, err := a.examStudentContext(); err != nil {
|
if _, _, err := a.examStudentContext(); err != nil {
|
||||||
@@ -2477,7 +2477,7 @@ func (a *App) SubmitExamWork() (string, error) {
|
|||||||
}
|
}
|
||||||
_ = w.Close()
|
_ = 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 {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|||||||
4
client/frontend/package-lock.json
generated
4
client/frontend/package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "frontend",
|
"name": "frontend",
|
||||||
"version": "1.2.0",
|
"version": "1.3.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "frontend",
|
"name": "frontend",
|
||||||
"version": "1.2.0",
|
"version": "1.3.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"pdfjs-dist": "^3.11.174"
|
"pdfjs-dist": "^3.11.174"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "frontend",
|
"name": "frontend",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "1.2.0",
|
"version": "1.3.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
"build": "vite build",
|
"build": "vite build",
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
06a16f2c007333cdf273a92d4914c63b
|
792441f7861d7badf1335f3778fcdebd
|
||||||
@@ -19,7 +19,7 @@ if (typeof window.go.main.App === 'undefined') {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const brandLogoHtml = `<img src="${logoUrl}" alt="Simple Care" class="brand-logo-img" />`;
|
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ộ
|
// Trạng thái cục bộ
|
||||||
let loggedIn = false;
|
let loggedIn = false;
|
||||||
|
|||||||
@@ -173,6 +173,7 @@ var systemAllowed = map[string]bool{
|
|||||||
"simple_care_v1.0": true,
|
"simple_care_v1.0": true,
|
||||||
"simple_care_v1.1": true,
|
"simple_care_v1.1": true,
|
||||||
"simple_care_v1.2": true,
|
"simple_care_v1.2": true,
|
||||||
|
"simple_care_v1.3": true,
|
||||||
"simple_care": true,
|
"simple_care": true,
|
||||||
"wails": true,
|
"wails": true,
|
||||||
"code": true, // VSCode
|
"code": true, // VSCode
|
||||||
|
|||||||
@@ -238,6 +238,7 @@ var systemAllowed = map[string]bool{
|
|||||||
"simple_care_v1.0": true,
|
"simple_care_v1.0": true,
|
||||||
"simple_care_v1.1": true,
|
"simple_care_v1.1": true,
|
||||||
"simple_care_v1.2": true,
|
"simple_care_v1.2": true,
|
||||||
|
"simple_care_v1.3": true,
|
||||||
"simple_care": true,
|
"simple_care": true,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -331,6 +331,7 @@ var systemAllowed = map[string]bool{
|
|||||||
"simple_care_v1.0.exe": true,
|
"simple_care_v1.0.exe": true,
|
||||||
"simple_care_v1.1.exe": true,
|
"simple_care_v1.1.exe": true,
|
||||||
"simple_care_v1.2.exe": true,
|
"simple_care_v1.2.exe": true,
|
||||||
|
"simple_care_v1.3.exe": true,
|
||||||
"simple_care.exe": true,
|
"simple_care.exe": true,
|
||||||
"wails.exe": true,
|
"wails.exe": true,
|
||||||
"msedgewebview2.exe": true, // WebView2 runtime (Wails renderer)
|
"msedgewebview2.exe": true, // WebView2 runtime (Wails renderer)
|
||||||
|
|||||||
@@ -22,13 +22,13 @@ func resizeRGBA(src *image.RGBA, width, height int) *image.RGBA {
|
|||||||
minY := srcBounds.Min.Y
|
minY := srcBounds.Min.Y
|
||||||
|
|
||||||
for y := 0; y < height; y++ {
|
for y := 0; y < height; y++ {
|
||||||
srcY := minY + (y * dy) / height
|
srcY := minY + (y*dy)/height
|
||||||
for x := 0; x < width; x++ {
|
for x := 0; x < width; x++ {
|
||||||
srcX := minX + (x * dx) / width
|
srcX := minX + (x*dx)/width
|
||||||
|
|
||||||
srcOffset := src.PixOffset(srcX, srcY)
|
srcOffset := src.PixOffset(srcX, srcY)
|
||||||
dstOffset := dst.PixOffset(x, y)
|
dstOffset := dst.PixOffset(x, y)
|
||||||
|
|
||||||
copy(dst.Pix[dstOffset:dstOffset+4], src.Pix[srcOffset:srcOffset+4])
|
copy(dst.Pix[dstOffset:dstOffset+4], src.Pix[srcOffset:srcOffset+4])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ func main() {
|
|||||||
|
|
||||||
// Create application with options
|
// Create application with options
|
||||||
err := wails.Run(&options.App{
|
err := wails.Run(&options.App{
|
||||||
Title: "Simple Care v1.2 — Rikkei Education",
|
Title: "Simple Care v1.3 — Rikkei Education",
|
||||||
Width: 1024,
|
Width: 1024,
|
||||||
Height: 768,
|
Height: 768,
|
||||||
Menu: appMenu,
|
Menu: appMenu,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://wails.io/schemas/config.v2.json",
|
"$schema": "https://wails.io/schemas/config.v2.json",
|
||||||
"name": "Simple Care by Rikkei Edu",
|
"name": "Simple Care by Rikkei Edu",
|
||||||
"outputfilename": "simple_care_v1.2",
|
"outputfilename": "simple_care_v1.3",
|
||||||
"frontend:install": "npm install",
|
"frontend:install": "npm install",
|
||||||
"frontend:build": "npm run build",
|
"frontend:build": "npm run build",
|
||||||
"frontend:dev:watcher": "npm run dev",
|
"frontend:dev:watcher": "npm run dev",
|
||||||
|
|||||||
Reference in New Issue
Block a user