Skip to content

Commit b3022a6

Browse files
authored
Merge pull request #17 from DangNghia17/cursor/import-export-theme-v3-1-1c9b
v3.1.0 — Encrypted Import/Export Backup & Light/Dark Theme
2 parents 94d4ab3 + dedc239 commit b3022a6

24 files changed

Lines changed: 1055 additions & 30 deletions

README.md

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Riot Account Manager
22

33
[![GitHub](https://img.shields.io/badge/GitHub-Repository-blue?style=flat-square&logo=github)](https://github.com/DangNghia17/ChangeLogin_Account_RiotClient)
4-
[![Release](https://img.shields.io/badge/Release-v3.0.0-green?style=flat-square)](https://github.com/DangNghia17/ChangeLogin_Account_RiotClient/releases)
4+
[![Release](https://img.shields.io/badge/Release-v3.1.0-green?style=flat-square)](https://github.com/DangNghia17/ChangeLogin_Account_RiotClient/releases)
55
[![License](https://img.shields.io/badge/License-MIT-yellow?style=flat-square)](LICENSE)
66
[![Tauri](https://img.shields.io/badge/Tauri-2.x-24C8DB?style=flat-square&logo=tauri)](https://tauri.app/)
77
[![React](https://img.shields.io/badge/React-18-61DAFB?style=flat-square&logo=react)](https://react.dev/)
@@ -26,6 +26,12 @@
2626
- 🚪 **Đăng xuất** — thoát phiên hiện tại để chuyển sang tài khoản khác.
2727
- 🎨 **Giao diện cải tiến** — nút bo góc hiện đại, hiệu ứng hover/active, shadow nhẹ.
2828
-**Trải nghiệm người dùng tốt hơn** — toast thông báo, badge trạng thái, animation mượt.
29+
- 📤 **Export dữ liệu** — xuất toàn bộ dữ liệu ra file `.backup`.
30+
- 📥 **Import dữ liệu** — khôi phục dữ liệu từ file backup.
31+
- 🔑 **Sao lưu được mã hóa bằng mật khẩu** — AES-256-GCM + PBKDF2 (Web Crypto API); mật khẩu không bao giờ được lưu.
32+
- ♻️ **Khôi phục dữ liệu từ file backup** — có xác nhận trước khi ghi đè và kiểm tra toàn vẹn.
33+
- 🌗 **Light / Dark Theme** — hỗ trợ giao diện Sáng, Tối và theo Hệ thống.
34+
- 💾 **Ghi nhớ lựa chọn giao diện** — giữ nguyên theme sau khi khởi động lại.
2935
- 🌐 **Đa ngôn ngữ** — Tiếng Việt & Tiếng Anh.
3036
- 🛡️ **An toàn với game** — chỉ mô phỏng thao tác bàn phím/chuột, KHÔNG hook/inject, tương thích Vanguard.
3137

@@ -91,11 +97,12 @@ npm run test # cargo test -p ram-core
9197
├── app-tauri/ # Ứng dụng Tauri + React
9298
│ ├── src/ # Mã nguồn frontend (React/TypeScript)
9399
│ │ ├── components/ # AccountTable, ConfigPanel, ConfirmDialog, Toast
94-
│ │ ├── dialogs/ # Account, Settings, About, Welcome, LoginStatus
100+
│ │ ├── dialogs/ # Account, Settings, About, Welcome, LoginStatus, Backup
95101
│ │ ├── i18n/ # Đa ngôn ngữ (vi/en)
96-
│ │ ├── lib/ # api, platform, mock-store
102+
│ │ ├── theme/ # ThemeContext (Light/Dark/System)
103+
│ │ ├── lib/ # api, platform, mock-store, backup (mã hóa)
97104
│ │ ├── App.tsx # Thành phần gốc
98-
│ │ └── styles.css # Toàn bộ style
105+
│ │ └── styles.css # Toàn bộ style (biến theme sáng/tối)
99106
│ ├── src-tauri/ # Lớp shell Tauri (Rust) + cấu hình
100107
│ └── crates/core/ # ram-core: lõi nghiệp vụ Rust (store, crypto, riot…)
101108
├── docs/ # Tài liệu bổ sung
@@ -112,6 +119,8 @@ npm run test # cargo test -p ram-core
112119
3. **Đăng nhập** — chọn tài khoản → bấm **Đăng nhập** để tự động điền vào Riot Client.
113120
4. **Sửa / Xóa** — chọn tài khoản rồi bấm ✏️ hoặc 🗑️ (xóa sẽ hỏi xác nhận).
114121
5. **Đăng xuất** — nếu đang ở phiên một tài khoản, bấm Đăng nhập lại tài khoản đó để mở hộp thoại trạng thái và **Đăng xuất**.
122+
6. **Sao lưu / Khôi phục** — mở **Cài đặt → Sao lưu & Khôi phục**, chọn **Xuất dữ liệu** (đặt mật khẩu để mã hóa file `.backup`) hoặc **Nhập dữ liệu** (chọn file, nhập mật khẩu, xác nhận ghi đè).
123+
7. **Đổi giao diện** — bấm nút ☀/🌙 trên thanh tiêu đề để chuyển nhanh Sáng/Tối, hoặc chọn Sáng/Tối/Hệ thống trong **Cài đặt → Giao diện**.
115124

116125
## An toàn & Bảo mật
117126

@@ -121,10 +130,10 @@ npm run test # cargo test -p ram-core
121130

122131
## Roadmap
123132

133+
- [x] Import/Export & sao lưu dữ liệu có mã hóa. ✅ (v3.1.0)
134+
- [x] Tùy biến theme (sáng/tối) + ghi nhớ lựa chọn. ✅ (v3.1.0)
124135
- [ ] Tìm kiếm / lọc tài khoản trong danh sách.
125136
- [ ] Sắp xếp & nhóm tài khoản theo region/ghi chú.
126-
- [ ] Import/Export & sao lưu dữ liệu có mã hóa.
127-
- [ ] Tùy biến theme (sáng/tối).
128137
- [ ] Phím tắt toàn cục để đăng nhập nhanh.
129138
- [ ] Hỗ trợ thêm nền tảng (macOS/Linux) nếu khả thi.
130139

@@ -134,6 +143,21 @@ Phát hành theo giấy phép **MIT** — xem file [LICENSE](LICENSE) để bi
134143

135144
## Changelog
136145

146+
### v3.1.0
147+
148+
**New Features**
149+
- Import dữ liệu.
150+
- Export dữ liệu.
151+
- Sao lưu có mã hóa bằng mật khẩu (AES-256-GCM + PBKDF2).
152+
- Khôi phục dữ liệu từ file backup.
153+
- Hỗ trợ Light/Dark Theme (kèm tùy chọn theo Hệ thống).
154+
- Ghi nhớ cài đặt giao diện giữa các lần mở ứng dụng.
155+
156+
🔒 **Security**
157+
- Backup được mã hóa đầu-cuối bằng mật khẩu người dùng.
158+
- Không lưu mật khẩu ở bất kỳ đâu.
159+
- Kiểm tra tính toàn vẹn dữ liệu khi khôi phục (AES-GCM authentication + kiểm tra định dạng/phiên bản).
160+
137161
### v3.0.0
138162
- Thiết kế lại cụm nút thao tác.
139163
- Chuyển sang icon-only hiện đại.

REPORT_v3.1.0.md

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
# Báo cáo nâng cấp — v3.1.0 (Encrypted Backup & Theme)
2+
3+
Tài liệu này tổng hợp các thay đổi của bản **v3.1.0**: bổ sung **Import/Export & sao lưu dữ liệu có mã hóa****hệ thống Theme Sáng/Tối**. Bản nâng cấp giữ nguyên toàn bộ chức năng hiện có và tương thích ngược với dữ liệu cũ.
4+
5+
> Bản này được xây dựng tiếp nối trên v3.0.0 (UX & Interface Upgrade).
6+
7+
---
8+
9+
## 1. Danh sách file đã sửa / thêm mới
10+
11+
### Thêm mới
12+
| File | Mục đích |
13+
|------|----------|
14+
| `app-tauri/src/lib/backup.ts` | Lõi mã hóa/giải mã backup (AES-256-GCM + PBKDF2) + định dạng envelope, lỗi có phân loại. |
15+
| `app-tauri/src/dialogs/BackupDialog.tsx` | Luồng UI Export/Import nhiều bước (mật khẩu, chọn file, xác nhận ghi đè, báo lỗi). |
16+
| `app-tauri/src/theme/ThemeContext.tsx` | Context quản lý Theme (light/dark/system), lưu lựa chọn, theo dõi thay đổi OS. |
17+
| `REPORT_v3.1.0.md` | Báo cáo này. |
18+
19+
### Đã chỉnh sửa
20+
| File | Thay đổi chính |
21+
|------|----------------|
22+
| `app-tauri/src-tauri/src/commands.rs` | Thêm command `replace_accounts`, `read_text_file`, `write_text_file`. |
23+
| `app-tauri/src-tauri/src/lib.rs` | Đăng ký 3 command mới vào `invoke_handler`. |
24+
| `app-tauri/src-tauri/capabilities/default.json` | Bổ sung quyền `dialog:allow-save` (lưu file backup). |
25+
| `app-tauri/src/lib/api.ts` | Thêm `replaceAccounts`, `saveTextFile`, `pickAndReadTextFile` (đa nền tảng Tauri/web). |
26+
| `app-tauri/src/lib/mock-store.ts` | Thêm `replaceAccounts` cho chế độ web preview. |
27+
| `app-tauri/src/dialogs/SettingsDialog.tsx` | Thêm khu vực **Giao diện** (segmented Light/Dark/System) và **Sao lưu & Khôi phục** (Export/Import). |
28+
| `app-tauri/src/App.tsx` | Nút chuyển nhanh Sáng/Tối ở header; state `backupMode`; render `BackupDialog`; refresh sau import. |
29+
| `app-tauri/src/main.tsx` | Bọc ứng dụng trong `ThemeProvider`. |
30+
| `app-tauri/index.html` | Script áp dụng theme trước first paint (tránh nhấp nháy/FOUC). |
31+
| `app-tauri/src/styles.css` | Bảng biến cho dark theme, chuyển màu hardcode sang biến, transition mượt, style cho theme/backup. |
32+
| `app-tauri/src/i18n/strings.ts` | Bổ sung chuỗi cho Theme & Backup (vi + en). |
33+
| `app-tauri/src/dialogs/AboutDialog.tsx` | Cập nhật version 3.0.0 → 3.1.0. |
34+
| `package.json`, `app-tauri/package.json`, `Cargo.toml`, `Cargo.lock`, lock files, `tauri.conf.json` | Bump version 3.1.0. |
35+
| `README.md` | Bổ sung tính năng, cập nhật Roadmap (đánh dấu hoàn thành) và Changelog v3.1.0. |
36+
37+
---
38+
39+
## 2. Các thay đổi chính
40+
41+
- **Export có mã hóa:** xuất toàn bộ dữ liệu (accounts + settings + đường dẫn Riot) ra file `.backup`, mã hóa bằng mật khẩu người dùng. Bắt buộc nhập **mật khẩu + xác nhận**; nếu không khớp sẽ không cho xuất.
42+
- **Import có kiểm tra:** chọn file → nhập mật khẩu → giải mã → kiểm tra hợp lệ → **xác nhận ghi đè** rồi mới áp dụng. Báo lỗi rõ ràng cho từng trường hợp (sai mật khẩu, file hỏng, sai định dạng, phiên bản mới hơn).
43+
- **Tương thích & versioning:** envelope chứa `formatVersion` + `dataVersion` + `createdAt`, cho phép phát hiện và từ chối an toàn file từ phiên bản mới hơn; không làm crash ứng dụng.
44+
- **Theme đầy đủ:** Light / Dark / System; chuyển đổi tức thì không reload; ghi nhớ qua `localStorage`; áp dụng sớm để tránh nhấp nháy.
45+
- **Không phá vỡ dữ liệu cũ:** không thay đổi cấu trúc lưu trữ tài khoản hiện có; chỉ thêm command ghi-đè-toàn-bộ phục vụ khôi phục.
46+
47+
---
48+
49+
## 3. Kiến trúc giải pháp
50+
51+
```
52+
┌────────────────────────── Frontend (React/TS) ──────────────────────────┐
53+
│ SettingsDialog ──(Export/Import)──► App.backupMode ──► BackupDialog │
54+
│ │ │
55+
│ lib/backup.ts (Web Crypto) │ │
56+
│ encryptBackup() / decryptBackup() ◄─────────┘ │
57+
│ │ │
58+
│ lib/api.ts (platform abstraction) │
59+
│ saveTextFile / pickAndReadTextFile / replaceAccounts / get/set... │
60+
└───────────────┬───────────────────────────────────┬─────────────────────┘
61+
│ isTauri() │ web preview
62+
▼ ▼
63+
Rust commands (read/write_text_file, mock-store + Blob download /
64+
replace_accounts) + plugin-dialog <input type=file> + localStorage
65+
66+
67+
ram-core::store (lưu trữ mã hóa cục bộ - giữ nguyên)
68+
```
69+
70+
- **Mã hóa nằm hoàn toàn ở frontend** bằng **Web Crypto API**, đúng định hướng của stack; backend chỉ đọc/ghi bytes thô.
71+
- **`ThemeProvider`** đặt ngoài cùng cây React, gán `data-theme` lên `<html>`; CSS dùng biến `--*` để đổi màu tức thì.
72+
73+
## 4. Cách hoạt động của Import/Export
74+
75+
### Định dạng file `.backup`
76+
```jsonc
77+
{
78+
"app": "RiotAccountManager",
79+
"type": "backup",
80+
"formatVersion": 1, // phiên bản cấu trúc envelope
81+
"dataVersion": 1, // phiên bản schema payload
82+
"createdAt": "2026-06-12T...Z",
83+
"kdf": { "name": "PBKDF2", "hash": "SHA-256", "iterations": 210000, "salt": "<base64>" },
84+
"cipher":{ "name": "AES-GCM", "iv": "<base64>" },
85+
"data": "<base64 ciphertext>" // JSON {accounts, settings, riotPath} đã mã hóa
86+
}
87+
```
88+
89+
### Export
90+
1. Người dùng nhập mật khẩu + xác nhận (không khớp ⇒ dừng).
91+
2. Sinh `salt` (16 byte) + `iv` (12 byte) ngẫu nhiên; PBKDF2-SHA256 (210k vòng) ⇒ khóa AES-256.
92+
3. AES-GCM mã hóa payload ⇒ ghi envelope ra file (`.backup`). Mật khẩu **không** được lưu.
93+
94+
### Import
95+
1. Chọn file ⇒ đọc text ⇒ parse envelope (kiểm tra `app/type/formatVersion`).
96+
2. Nhập mật khẩu ⇒ derive khóa từ `salt` lưu trong file ⇒ AES-GCM decrypt.
97+
3. AES-GCM tự xác thực: sai mật khẩu / dữ liệu bị sửa ⇒ ném lỗi ⇒ thông báo "sai mật khẩu hoặc file hỏng".
98+
4. Kiểm tra payload hợp lệ ⇒ hiển thị xác nhận ghi đè ⇒ áp dụng (`replaceAccounts`, `setSettings`, `setRiotPath`) ⇒ refresh UI.
99+
100+
### Các luồng đã kiểm thử
101+
| Luồng | Kết quả |
102+
|-------|---------|
103+
| Export thành công | ✅ (roundtrip verify qua Web Crypto) |
104+
| Import thành công ||
105+
| Import sai mật khẩu | ✅ bị từ chối, báo lỗi rõ ràng |
106+
| Import file lỗi/hỏng | ✅ bị từ chối |
107+
108+
## 5. Cách hoạt động của Theme
109+
110+
- `ThemeContext` đọc `localStorage["ram.theme"]` (`light`/`dark`/`system`, mặc định `system`).
111+
- Theme được "resolve" thành `light|dark` và gán `data-theme` lên `<html>`. Toàn bộ màu dùng biến CSS nên đổi **ngay lập tức, không reload**.
112+
- Khi chọn `system`, lắng nghe `matchMedia("(prefers-color-scheme: dark)")` để tự đổi theo OS.
113+
- `index.html` áp dụng theme trước khi React mount ⇒ không nhấp nháy lúc khởi động.
114+
- Hai điểm điều khiển: nút ☀/🌙 ở header (đổi nhanh) và bộ chọn 3 lựa chọn trong Cài đặt.
115+
116+
| Luồng | Kết quả |
117+
|-------|---------|
118+
| Chuyển Light Mode ||
119+
| Chuyển Dark Mode ||
120+
| Theo Hệ thống (System) ||
121+
| Ghi nhớ theme sau khởi động lại | ✅ (localStorage + apply sớm) |
122+
123+
## 6. Những điểm cần lưu ý
124+
125+
- **Mật khẩu backup không thể khôi phục:** nếu quên mật khẩu sẽ không giải mã được file (đây là chủ đích bảo mật).
126+
- **Import là ghi đè toàn bộ** danh sách tài khoản hiện tại (sau xác nhận). Backend `ram-core` vẫn tự tạo bản sao lưu nội bộ khi ghi (`accounts.bak`).
127+
- **Web preview** dùng tải/đọc file qua trình duyệt và `localStorage`; bản desktop dùng dialog gốc + lệnh Rust.
128+
- **Phụ thuộc hệ thống khi build desktop:** cần GTK/WebKit (`libgtk-3-dev`, `libwebkit2gtk-4.1-dev`, …) để `cargo check`/`tauri build` chạy trên Linux.
129+
130+
## 7. Kiểm tra cuối cùng
131+
132+
| Hạng mục | Lệnh | Kết quả |
133+
|----------|------|---------|
134+
| TypeScript + build web | `npm run build` ||
135+
| Kiểm tra lõi Rust | `cargo check -p ram-core` ||
136+
| Unit test lõi Rust | `cargo test -p ram-core` | ✅ 8 passed |
137+
| Build crate Tauri (sau khi cài GTK/WebKit) | `cargo check -p riot-account-manager` ||
138+
| Mã hóa backup (roundtrip / sai mật khẩu / file hỏng) | Web Crypto verify ||
139+
140+
## 8. Đề xuất cải tiến trong tương lai
141+
142+
- Tự động sao lưu định kỳ (scheduled backup) ra thư mục do người dùng chọn.
143+
- Cho phép **merge** khi import thay vì chỉ ghi đè.
144+
- Thêm các theme/màu nhấn tùy biến và độ tương phản cao (accessibility).
145+
- Thêm thanh đo độ mạnh mật khẩu khi đặt mật khẩu backup.
146+
- Đồng bộ ngôn ngữ khi import (hiện chỉ lưu vào config, không đổi UI tức thì).
147+
148+
---
149+
150+
*Báo cáo cho phiên bản v3.1.0 — Encrypted Backup & Theme.*

app-tauri/Cargo.lock

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

app-tauri/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ resolver = "2"
33
members = ["crates/core", "src-tauri"]
44

55
[workspace.package]
6-
version = "3.0.0"
6+
version = "3.1.0"
77
edition = "2021"
88
rust-version = "1.85"
99

app-tauri/index.html

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,22 @@
44
<meta charset="UTF-8" />
55
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
66
<title>Riot Account Manager</title>
7+
<script>
8+
// Apply the saved theme before first paint to avoid a flash of the wrong theme.
9+
(function () {
10+
try {
11+
var saved = localStorage.getItem("ram.theme") || "system";
12+
var dark =
13+
saved === "dark" ||
14+
(saved === "system" &&
15+
window.matchMedia &&
16+
window.matchMedia("(prefers-color-scheme: dark)").matches);
17+
document.documentElement.setAttribute("data-theme", dark ? "dark" : "light");
18+
} catch (e) {
19+
document.documentElement.setAttribute("data-theme", "light");
20+
}
21+
})();
22+
</script>
723
</head>
824
<body>
925
<div id="root"></div>

app-tauri/package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

app-tauri/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "riot-account-manager",
33
"private": true,
4-
"version": "3.0.0",
4+
"version": "3.1.0",
55
"type": "module",
66
"scripts": {
77
"dev": "vite",

app-tauri/src-tauri/capabilities/default.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
"core:default",
88
"core:window:allow-start-dragging",
99
"dialog:default",
10-
"dialog:allow-open"
10+
"dialog:allow-open",
11+
"dialog:allow-save"
1112
]
1213
}

app-tauri/src-tauri/src/commands.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,26 @@ pub fn delete_account(index: usize) -> Result<Vec<Account>, String> {
3939
Ok(accounts)
4040
}
4141

42+
/// Replaces the entire account list (used when restoring an encrypted backup).
43+
#[tauri::command]
44+
pub fn replace_accounts(accounts: Vec<Account>) -> Result<Vec<Account>, String> {
45+
store::save_accounts(&accounts)?;
46+
Ok(accounts)
47+
}
48+
49+
/// Reads a UTF-8 text file from an absolute path (used to load a backup file the
50+
/// user picked via the native dialog).
51+
#[tauri::command]
52+
pub fn read_text_file(path: String) -> Result<String, String> {
53+
std::fs::read_to_string(&path).map_err(|e| format!("read failed: {e}"))
54+
}
55+
56+
/// Writes UTF-8 text to an absolute path (used to save an encrypted backup file).
57+
#[tauri::command]
58+
pub fn write_text_file(path: String, contents: String) -> Result<(), String> {
59+
std::fs::write(&path, contents.as_bytes()).map_err(|e| format!("write failed: {e}"))
60+
}
61+
4262
#[tauri::command]
4363
pub fn riot_status() -> RiotStatus {
4464
riot::status()

app-tauri/src-tauri/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ pub fn run() -> Result<(), String> {
2828
commands::add_account,
2929
commands::update_account,
3030
commands::delete_account,
31+
commands::replace_accounts,
32+
commands::read_text_file,
33+
commands::write_text_file,
3134
commands::riot_status,
3235
commands::focus_riot,
3336
commands::launch_riot,

0 commit comments

Comments
 (0)