HỆ THỐNG GREAT LOTUS · IP_POOL / ZONE2 · NIRVANA FRAMEWORK

B30 SCANIPPROXY

GIÁO TRÌNH VẬN HÀNH BẢNG ĐIỀU KHIỂN POOL PROXY 4G

Mổ xẻ từng thành phần giao diện · Vòng đời public IP qua ba hàng đợi Redis · Tự chữa hai tầng bằng MQTT và ổ cắm Tuya
Giám sát thiết bị, an toàn dữ liệu, khắc phục sự cố và giáo án thực hành

Mỗi public IP đi một vòng khép kín: 03 NEW -> 04 USING -> 05 DONE -> đổi IP -> 03 NEW.

Hạng mục

Nội dung

Đối tượng

Kỹ sư vận hành IP_Pool, người viết workflow n8n dùng proxy 4G, và học viên các lớp đào tạo nội bộ Great Lotus

Phiên bản tài liệu

Bản 1 — biên soạn ngày 18/09/2026, đối chiếu trực tiếp với mã nguồn và hệ thống đang chạy

Đối tượng mô tả

Container lotus_python_proxy_script (B30) · IP nội bộ 114.114.114.30 · cổng máy chủ 20300 · công khai tại https://scanIpProxy.lotus1104.synology.me

Hạ tầng kiểm chứng

HP1 172.16.10.220 (WP1) chạy B12, B27, B30 · box DELL_PROXY_01 172.16.30.201 và máy ảo client #101 172.16.30.21 ở WP3

Nguồn sự thật

Mã nguồn B30_Python_Proxy_Script/APP_B02_RapidProxyScript/, tài liệu nội bộ ip-pool-zone2.md, cơ sở dữ liệu audit.db, và ảnh chụp trực tiếp qua trình duyệt điều khiển B35



GHI CHÚ · CÁCH ĐỌC GIÁO TRÌNH NÀY

Mỗi màn hình được chụp từ hệ thống ĐANG CHẠY, đánh số bằng vòng tròn chỉ tô viền đặt bên ngoài phần tử, nên chi tiết bên dưới vẫn đọc được nguyên vẹn. Sau mỗi hình là một bảng giải thích đủ mọi thành phần nhìn thấy, kể cả những thứ chỉ hiện trong một trạng thái.

▸ Chữ in đậm là điều dễ làm sai nhất.

▸ Hộp ĐỎ là thao tác tác động thật lên thiết bị hoặc xoá dữ liệu — đọc hết rồi mới bấm.

▸ Hộp XANH LÁ mô tả hành vi trông như lỗi nhưng đúng thiết kế.





Lời nói đầu

ScanIpProxy là phần “nghiệp vụ” của IP_Pool: nó quyết định IP nào đang rảnh, IP nào đang có người dùng, IP nào đã dùng xong và cần đổi. Nó chạy lặng lẽ mỗi giây một lần, và khi nó sai thì hậu quả không ồn ào — hai task dùng chung một IP, một box proxy bị cắt điện theo lỗi của box khác, hay một cơ chế tự chữa không bao giờ kích hoạt mà màn hình vẫn xanh. Cả ba chuyện đó đều đã xảy ra thật, và được kể lại ở phần Phụ lục.

Vì vậy cuốn sách không dừng ở “nút này để làm gì”. Mỗi chương giải thích luôn cơ chế phía sau nút đó: nó gửi lệnh gì, tới đâu, ghi vào khoá Redis nào, và điều gì có thể làm nó trông như đã chạy mà thực ra không.

ĐÚNG THIẾT KẾ · NGUYÊN TẮC BIÊN SOẠN

Mọi con số, tên phần tử, hành vi mô tả trong tài liệu đều truy ngược được về một trong ba nguồn: (1) mã nguồn đang chạy trong container lotus_python_proxy_script; (2) tài liệu nội bộ có ghi số đo thật; (3) ảnh chụp và số liệu lấy từ hệ thống lúc biên soạn. Chỗ nào chưa kiểm chứng được thì ghi rõ là chưa kiểm chứng.



Trạng thái hệ thống lúc biên soạn

Mọi ảnh chụp lấy trong cùng một buổi chiều 18/09/2026. Trạng thái lúc đó không “đẹp”, và chính vì vậy nó là tư liệu giảng dạy tốt: bạn sẽ thấy cảnh báo, DCOM mất IP và Auto-Healing đang ở chu kỳ nghỉ.

Thành phần

Trạng thái lúc chụp ảnh (18/09/2026, khoảng 17:30–17:55)

Redis B27

Kết nối được, qua wp1.hp1:20270

MQTT

Kết nối được, broker VPS 128.199.200.17:1883

B12 RapidProxyHost

Online, uptime khoảng 13 ngày 20 giờ, 1 workpool, 1 client

Box proxy

DELL_PROXY_01 ở WP3 172.16.30.201:6868 — gọi thẳng và qua relay đều trả lời (khoảng 35 ms)

DCOM

1 dongle eth1, ZTE, VIETNAMMOBILE, LTE, sóng 4 vạch, trạng thái Connected nhưng không có public IP

Client

WP3_VM101_ProxyClient trên VM #101 — online. Client cũ trên Raspberry Pi3 (WP2) đã được tắt trong buổi biên soạn

Ba hàng đợi

03_NEW = 0 · 04_USING = 0 · 05_DONE = 0 (vì DCOM duy nhất không có IP)

Auto-Healing

DELL_PROXY_01 bật, đang lặp DRAINING -> REBOOTING -> COOLDOWN; mọi lần cắt nguồn Tuya đều thất bại từ 11/09/2026 (xem Chương 14 và Phụ lục F)

Cấu hình

USING timeout 3600 s · AUTO MOVE tắt cả ba · Web UI mapping http://172.16.30.21:6868/home

Lịch sử tích luỹ

audit.db 69.142 sự kiện từ 20/06/2026 · WP2 đã gặp 640 IP khác nhau qua 9.299 lượt xoay



LƯU Ý · VÌ SAO ẢNH CÓ CHỖ GHI 1 DCOM, CÓ CHỖ GHI 0

01_INFO do client gửi lên liên tục. Trong lúc dongle đang khởi động lại, danh sách DCOM có lúc rỗng. Vài ảnh chụp đúng khoảnh khắc đó, nên ô DCOMs hiện 0 và khung thiết bị hiện trạng thái “Không có DCOM”. Đây là trạng thái thật, không phải ảnh ghép.



Những thay đổi được làm trong buổi biên soạn

Khám phá giao diện để viết sách cũng là một lần kiểm toán. Các thay đổi sau đã được áp dụng vào hệ thống TRƯỚC khi chụp ảnh, nên ảnh phản ánh bản đã sửa:

Thay đổi

Lý do

Nơi sửa

Thêm khung THIẾT BỊ PROXY & CLIENT

Trước đó Dashboard chỉ biết những gì client kể lại; không phát hiện được box chết hay client ma

services/device_monitor.py, index.html, app.js

Trạng thái box Không có DCOM (no_dcom)

Box 0 dongle từng hiện là “Hoạt động”

device_monitor.py

Sửa bảng bị cắt mất dòng cuối

Hàm adjustTableWrapperHeight tính thiếu chiều cao khi bảng ít dòng

app.js

Đổi chữ “Raspberry Pi” thành “VM #101”

Client đã dời sang VM từ 11/09/2026

index.html, app.js

Mặc định workpool Auto-Healing là WP3

Mặc định WP2 là bẫy sau khi dời box

redis_api.py, redis_service.py

Tắt client trên Pi3

Client ma vẫn gửi heartbeat mang tên DELL_PROXY_01

Pi3 WP2





Mục lục

Mục lục do Microsoft Word sinh ra từ các đề mục, nên luôn khớp số trang thật. Nếu mở bằng Word mà chưa thấy nội dung, bấm Ctrl+A rồi F9 (hoặc chuột phải, chọn Update Field, rồi Update entire table).

⟳ Mở bằng Microsoft Word rồi bấm Ctrl+A → F9 để sinh mục lục kèm số trang.





PHẦN I

TỔNG QUAN





Chương 1 — ScanIpProxy là gì

1.1 Bài toán

Các luồng tự động hoá của Great Lotus (mở Chrome qua GPM, đăng ký tài khoản, thao tác trên web) cần đi ra Internet bằng IP của mạng di động, và cần IP đó thay đổi sau mỗi lần dùng để nền tảng đích không gom các lần truy cập về một chỗ. Nguồn IP là các DCOM 4G USB — mỗi DCOM là một SIM, một public IP, đổi được theo yêu cầu.

Có IP rồi thì phải quản lý nó: không cấp một IP cho hai task cùng lúc, biết IP nào đã dùng xong để ra lệnh đổi, phát hiện DCOM mất sóng để khởi động lại, và khi cả box hỏng thì cắt điện box. Đó là việc của ScanIpProxy.

1.2 Ba vai trò trong một container

Container lotus_python_proxy_script (B30) chạy hai tiến trình Python, mỗi tiến trình trong một phiên screen riêng do Conf/A3_taskControl.py dựng lên:

Tiến trình

Làm gì

Nhịp

App/ScanIpProxy.py

Máy trạng thái vòng đời IP: dọn IP biến mất, timeout USING, thêm IP mới, gửi lệnh đổi IP cho IP đã DONE, gửi lệnh reboot DCOM mất IP, Auto-Healing cắt điện box

1 giây (SCAN_INTERVAL_SEC=1)

WebDashboard/WebDashboardServer.py

Web Dashboard + REST API (FastAPI, cổng 8080), đẩy dữ liệu qua WebSocket, chạy bánh răng AUTO MOVE và luồng giám sát thiết bị

WebSocket 2 giây, AUTO MOVE 1 giây, giám sát thiết bị 10 giây



Cả hai đọc ghi cùng một Redis (B27). Dashboard không gọi thẳng vào vòng quét; muốn đổi hành vi vòng quét thì Dashboard ghi cấu hình vào Redis và vòng quét đọc lại ở giây kế tiếp.

1.3 Ranh giới trách nhiệm

Thành phần

Biết gì

KHÔNG biết gì

Box DELL_PROXY_01 (OBC Proxy)

Dongle nào cắm, IP của từng dongle

Ai đang dùng IP

RapidProxyClient (VM #101)

Hỏi box qua LAN, kể lại qua MQTT

Hàng đợi 03/04/05

B12 RapidProxyHost

DCOM nào tồn tại, client nào còn sống (ghi 01_INFO, 02_ALIVE)

IP nào rảnh, IP nào bận

B30 ScanIpProxy

IP nào rảnh / đang dùng / dùng xong (quản 03/04/05)

Không nói chuyện trực tiếp với thiết bị — mọi lệnh đi qua MQTT tới B12

n8n, worker GPM

Lấy IP từ 03_NEW_IP, trả về khi xong

Cách đổi IP



GHI CHÚ · MỘT NGOẠI LỆ CÓ CHỦ ĐÍCH

Khung THIẾT BỊ PROXY & CLIENT (thêm ngày 18/09/2026) là chỗ duy nhất B30 gọi thẳng tới thiết bị — và chỉ để đọc: gọi GET /proxy_list của box, mở trang relay, gõ cửa cổng SSH của máy client. Không có lệnh nào gửi đi theo đường này.



1.4 Cách truy cập

Địa chỉ

Dùng khi

https://scanIpProxy.lotus1104.synology.me

Từ bất kỳ đâu, qua Caddy (B16)

http://172.16.10.220:20300

Trong LAN WP1 hoặc qua VPN

http://114.114.114.30:8080

Từ container khác trong mạng Docker (n8n, B35)

http://lotus_python_proxy_script:8080

Từ n8n — tên container phân giải trong mạng Docker

/docs

Trang Swagger tự sinh của FastAPI, liệt kê mọi endpoint



NGUY HIỂM · DASHBOARD KHÔNG CÓ ĐĂNG NHẬP

Mọi trang và mọi endpoint REST — kể cả đổi IP, reboot, xoá hàng đợi, cắt điện Tuya — đều không đòi xác thực, và địa chỉ HTTPS mở ra Internet qua Caddy. Ai biết tên miền là điều khiển được. Xem Chương 14 mục rủi ro truy cập.





PHẦN II

KIẾN TRÚC VÀ CƠ CHẾ





Chương 2 — Kiến trúc tổng thể

2.1 Hai site, một broker

Hình 2.1. Thiết bị nằm ở WP3, phần xử lý nằm ở WP1; hai bên gặp nhau qua broker MQTT trên VPS.

Box DELL_PROXY_01 chỉ nhận điều khiển từ máy cùng LAN, nên client phải nằm trong LAN WP3. Từ ngày 11/09/2026 client là container wp3_rapid_proxy_client trên máy ảo #101 (wp3-proxy-client-01, 172.16.30.21, node pve-001); cùng máy đó có container nginx wp3_proxy_dell_6868 chuyển tiếp trang quản trị của box ra cổng 172.16.30.21:6868.

Bước

Từ

Tới

Nội dung

1

Client

MQTT SEND_FROM_RAPID_PROXY_CLIENT

aliveUpdate (nhịp tim), proxyInfoUpdate (danh sách DCOM)

2

B12

Redis

Ghi 01_INFO_JSON_MAN, 02_CLIENT_ALIVE_JSON_MAN, HOST_HEALTH

3

B30

Redis

Đọc 01_INFO, quản ba LIST 03_NEW_IP, 04_USING_IP, 05_DONE_IP

4

B30

MQTT SEND_TO_RAPID_PROXY_MAN

changeIpRequest, rebootDcomRequest…

5

B12

MQTT SEND_FROM_RAPID_PROXY_HOST

Chuyển lệnh tới client đang sống có nhịp tim mới nhất

6

Client

Box :6868

Gọi REST của OBC Proxy: /reset/id/{id}, /api/v1/dongles/{id}/reboot



LƯU Ý · TÊN TOPIC ĐỌC THEO NGƯỜI GỬI

SEND_FROM_X nghĩa là X là bên gửi. Chú thích trong RapidProxyHost_Config.py dòng 24–25 ghi ngược — tin mã subscribe() / publish(), đừng tin chú thích.



2.2 Khoá Redis

Khoá (tiền tố LOTUS:03_PROXY:)

Kiểu

Ai ghi

Nội dung

01_INFO_JSON_MAN

ReJSON

B12

WP > tên box > {proxyDevIp, proxyDevPort, proxyDevInfo[]}

02_CLIENT_ALIVE_JSON_MAN

ReJSON

B12

WP > clientUid > {proxyUidName, aliveStatus, time…}

03_NEW_IP · 04_USING_IP · 05_DONE_IP

LIST

B30, API

Mỗi phần tử là một chuỗi JSON

HOST_HEALTH

chuỗi JSON

B12

uptime, trạng thái MQTT hai phía, nhịp tim cuối

CONFIG:USING_TIMEOUT_SEC

chuỗi

Dashboard

Mặc định 3600

CONFIG:AUTO_MOVE

chuỗi JSON

Dashboard

Ba công tắc + độ trễ USING -> DONE

CONFIG:AUTO_REBOOT

chuỗi JSON

Dashboard

Cấu hình Auto-Healing theo tên box

STATE:AUTO_REBOOT

chuỗi JSON

ScanIpProxy

Trạng thái sống của Auto-Healing

CONFIG:WEB_UI_MAPPING

chuỗi JSON

Dashboard

Tên box -> URL trang quản trị

CONFIG:CLIENT_HOSTS

chuỗi JSON

Tay (tuỳ chọn)

Máy chạy từng client, để gõ cửa SSH



Ba hàng đợi là LIST có chủ đích: n8n đọc thẳng bằng LRANGE mà không cần module ReJSON. Một phần tử trông như sau:

{

"workpool": "WP3",

"proxyName": "DELL_PROXY_01",

"proxyMeta": { "proxyDevIp": "172.16.30.201", "proxyDevPort": 6868 },

"dcomEntry": { "id": "eth1", "publicIPv4Address": "103.249.22.82",

"httpPort": 2001, "socks5Port": 4001, "isp": "VIETNAMMOBILE", ... },

"enteredNewAt": 1789716000.0

}



NGUY HIỂM · KHOÁ ĐỊNH DANH LÀ PUBLIC IP, KHÔNG PHẢI DCOM ID

Mọi thao tác so khớp, chuyển, xoá đều dựa trên dcomEntry.publicIPv4Address. Khi một DCOM đổi IP, hệ thống coi đó là thực thể mới: IP cũ biến mất, IP mới xuất hiện.



Kết nối Redis của B30 đi qua wp1.hp1:20270 (tên phân giải bằng DNS của LAN ra 172.16.10.220), không qua địa chỉ nội bộ 114.114.114.27:6379. Hỏng DNS LAN hoặc đổi cổng 20270 là IP_Pool dừng, dù mạng Docker vẫn bình thường.

Chương 3 — Vòng đời IP và vòng quét mỗi giây

3.1 Ba hàng đợi

Hình 3.1. Vòng đời một public IP và quy tắc ưu tiên khi IP nằm ở hai nơi.

Hàng đợi

Nghĩa

Mốc thời gian mang theo

Ai đưa IP vào

03_NEW_IP

IP mới, chưa ai dùng

enteredNewAt

ScanIpProxy (tự phát hiện)

04_USING_IP

Đang được task dùng

enteredUsingAt

n8n qua POST /api/redis/move-ip

05_DONE_IP

Dùng xong, chờ đổi IP

enteredDoneAt

n8n, hoặc ScanIpProxy khi quá timeout



Mỗi phần tử chỉ mang đúng một mốc thời gian, ứng với hàng đợi nó đang ở. Dashboard tính cột Time từ mốc đó; phần tử mang mốc thừa sẽ hiện sai giờ.

3.2 Tám bước của một vòng quét

Hình 3.2. Thứ tự các bước trong scan_once() — thứ tự này có ý nghĩa.

1. Đọc kho DCOM 01_INFO và dựng bản đồ theo public IP.

2. Đọc ba hàng đợi trong một lệnh pipeline Redis.

3. Cưỡng chế bất biến _enforce_single_state: một IP chỉ nằm ở đúng một hàng đợi. Trùng trong cùng hàng đợi thì giữ phần tử đầu; trùng chéo thì trạng thái tiến xa hơn thắng (05_DONE > 04_USING > 03_NEW). Mỗi lần dọn ghi một sự kiện ip_dedup.

4. Dọn IP biến mất khỏi kho DCOM. IP biến mất khi đang USING thì ghi cảnh báo trong log.

5. Timeout USING: phần tử ở 04 quá USING_TIMEOUT_SEC (mặc định 3600 s) bị chuyển sang 05, ghi ip_timeout và gửi rebootDcomRequest cho DCOM đó.

6. Auto-Healing (mục 3.4).

7. Thêm IP mới: IP có trong kho DCOM mà chưa ở hàng đợi nào thì vào 03_NEW_IP.

8. Xử lý DONE: mỗi DCOM có IP ở 05 được gửi changeIpRequest một lần; rồi tự chữa: DCOM không có IP và lastReset đã quá 60 s thì gửi rebootDcomRequest, tối đa một lệnh mỗi 60 s cho mỗi DCOM. Cuối cùng chỉ ghi lại hàng đợi nào thật sự đổi.

LƯU Ý · BẪY TOÁN TỬ OR TRONG BƯỚC 4

Mã phải gọi _remove_ips_from_list(...) TRƯỚC rồi mới or với cờ dedup. Viết dedup_x or self._remove_ips_from_list(...) thì Python bỏ qua vế sau khi vế đầu đã đúng, và IP đã biến mất sẽ không bao giờ được dọn. Chú thích ngay trong mã nhắc điều này.



3.3 Đổi IP qua MQTT

Hình 3.3. Một lần đổi IP từ lúc IP vào 05_DONE tới lúc IP mới vào 03_NEW.

Lần đo end-to-end ngày 11/09/2026 qua client mới: gửi changeIpRequest WP3/eth1, client báo changeIpRequest_ACK, khoảng 9 giây sau gửi proxyInfoUpdate, và IP đổi từ 103.249.23.125 sang 103.249.22.82 sau 25 giây. Trong khoảng đó ô Public IP trên Dashboard hiện Getting new IP... (tối đa 60 giây) thay vì báo lỗi.

Lệnh chuẩn là changeIpRequest. Chính tả cũ changIpRequest (thiếu chữ e) đã được sửa ngày 14/08/2026; bên nhận vẫn chấp nhận cả hai để script cũ không gãy — xem Phụ lục A.

3.4 Tự chữa hai tầng

Tầng 1 — reboot DCOM (bước 8): rẻ, chỉ khởi động lại một dongle. Mỗi lần gửi làm tăng bộ đếm lỗi liên tiếp của DCOM đó; DCOM có IP trở lại thì bộ đếm về 0.

Tầng 2 — Auto-Healing: khi một DCOM đạt fail_threshold lần lỗi liên tiếp, vấn đề nằm ở cả box. ScanIpProxy cắt điện box qua ổ cắm thông minh Tuya.

Hình 3.4. Máy trạng thái Auto-Healing của một box.

Trạng thái

Điều kiện vào

Làm gì

Ra khi

NORMAL

Mặc định

Theo dõi bộ đếm lỗi

Một DCOM của box đạt ngưỡng (mặc định 5) và không trong cooldown

DRAINING

Đạt ngưỡng

Chuyển mọi IP của box ở 03 sang 05; chờ 04 của box về 0

Không còn IP nào của box đang USING

REBOOTING

04 của box = 0

Luồng nền gọi WCM: tắt ổ, nghỉ off_delay_sec, bật ổ

Lời gọi kết thúc — thành công hay thất bại đều sang COOLDOWN

COOLDOWN

Sau REBOOTING

Chờ cooldown_sec (mặc định 300 s), đặt lại bộ đếm lỗi

Hết thời gian

CONFIG_MISMATCH

Tên hoặc workpool trong cấu hình không khớp box nào đang online

Không làm gì, ghi log lỗi 5 phút một lần

Sửa cấu hình cho khớp

DISABLED

Bỏ tích Kích hoạt

Không làm gì

Bật lại



NGUY HIỂM · KHÔNG CHO IP ĐANG DÙNG BỊ CẮT NGANG

DRAINING chờ cho tới khi mọi IP của box ở 04_USING được trả về. Một task giữ IP 60 phút thì box chờ 60 phút mới bị cắt điện. Đó là thiết kế: không bao giờ cắt điện dưới chân một task đang chạy.



LƯU Ý · THẤT BẠI CŨNG VÀO COOLDOWN — VÀ LẶP LẠI MÃI

Nếu lệnh Tuya lỗi, box vẫn chuyển sang COOLDOWN, bộ đếm lỗi về 0, rồi vài phút sau đạt ngưỡng lại. Với ngưỡng 5, cooldown 300 s và mỗi lần reboot DCOM cách nhau khoảng 90 s, chu kỳ là khoảng 7,5 phút — 192 lần một ngày, đúng con số audit.db ghi được từ 12/09/2026.



Khoá nối giữa cấu hình và thiết bị là tên box: biến PROXY_DEVICE_UID_NAME trong .env của client phải trùng từng ký tự với ô Proxy Box Name trên Dashboard. Từ 11/09/2026 phép so còn đòi đúng workpool nếu cấu hình có ghi workpool — xem Phụ lục C và D.

3.5 AUTO MOVE

Một vòng asyncio trong WebDashboardServer, chu kỳ 1 giây, đọc CONFIG:AUTO_MOVE. Công tắc nào bật thì nó dịch phần tử đầu của hàng đợi tương ứng: NEW -> USING, USING -> DONE (sau using_to_done_delay giây), DONE -> NEW. Nó được viết để thử cả vòng đời mà không cần n8n.

NGUY HIỂM · AUTO MOVE CHẠY TRÊN HÀNG ĐỢI THẬT

Nút AUTO MOVE ở tab Verify không phải mô phỏng: nó dịch IP trong chính các LIST mà n8n đang dùng. Bật NEW -> USING khi n8n đang chạy là giành IP với n8n. Luôn tắt cả ba khi vận hành.



3.6 Tranh chấp khi lấy IP

Hình 3.5. Vì sao pop rồi push làm IP xuất hiện ở hai hàng đợi.

Nếu client lấy IP bằng hai lệnh rời LPOP 03 rồi RPUSH 04, giữa hai lệnh IP không thuộc hàng đợi nào. ScanIpProxy quét mỗi giây, gặp đúng lúc đó sẽ coi là IP mới và thêm lại vào 03. Cách đúng là một lời gọi POST /api/redis/move-ip: IP nằm yên ở 03 cho tới khi server chuyển nó. Chi tiết sự cố ở Phụ lục B.

GHI CHÚ · MOVE-IP CŨNG CHƯA NGUYÊN TỬ TUYỆT ĐỐI

move_ip_between_states đọc cả LIST nguồn, rồi trong một pipeline xoá LIST và ghi lại phần còn lại. Pipeline không có WATCH, nên hai lời gọi move-ip cùng lúc trên cùng hàng đợi về lý thuyết vẫn có thể ghi đè nhau. Với nhịp gọi của n8n hiện nay chưa ghi nhận sự cố.



3.7 Giám sát thiết bị trực tiếp

Hình 3.6. Ba phép thử mỗi 10 giây và cách suy ra trạng thái box, client.

Trạng thái box

Nghĩa

Hoạt động (ok)

Box trả lời, có client online, mọi DCOM có IP

Thiếu IP public (degraded)

Có DCOM chưa có IP

Không có DCOM (no_dcom)

Box trả lời nhưng 01_INFO không có dongle nào — thường thoáng qua lúc dongle khởi động lại; kéo dài là cắm lỏng hoặc hỏng USB

Không có client (no_client)

Không client nào đang online phục vụ box

Mất kết nối (down)

Gọi thẳng và gọi qua relay đều hỏng

Không thấy (missing)

Có cấu hình Auto-Healing nhưng box không có trong 01_INFO



Trạng thái client

Nghĩa

Online

Cờ sống bật và nhịp tim trong vòng 120 giây

Offline

Cờ sống tắt hoặc nhịp tim quá 120 giây

Client ma (ghost)

Vẫn gửi nhịp tim nhưng box nó báo không có trong 01_INFO của workpool đó, hoặc báo sai IP box



3.8 Khi dời box sang site khác

Hình 3.7. Những chỗ phải sửa khi box DELL_PROXY đổi địa điểm.

Lần dời WP2 -> WP3 ngày 11/09/2026 cho thấy có bốn chỗ phải đi cùng nhau: .env của client (WORK_POOL, TARGET_HOST), ô WorkPool trong cấu hình Auto-Healing, Web UI mapping, và client cũ ở site cũ phải được tắt. Quên chỗ nào thì Phụ lục D kể hậu quả.



PHẦN III

GIAO DIỆN — MỔ XẺ TỪNG TRANG





Chương 4 — Khung chung của mọi trang

Dashboard là một trang đơn, sáu tab. Phần đầu trang và thanh tóm tắt luôn hiện, bất kể đang ở tab nào; dữ liệu trên mọi tab được làm mới bằng một kết nối WebSocket duy nhất.

4.1 Đầu trang và thanh tab

Hình 4.1. Đầu trang: tên ứng dụng, ba đèn kết nối, chỉ báo Live, nút giao diện, sáu tab và ô tìm kiếm.

Số

Thành phần

Công dụng

Lưu ý

1

Tên ứng dụng

ScanIpProxy Dashboard

—

2

Đèn Redis

Xanh khi server đọc được Redis

Lấy từ GET /health, kiểm lại mỗi 30 giây

3

Đèn MQTT

Xanh khi tiến trình Dashboard kết nối được broker

Kết nối này dùng để GỬI lệnh từ các nút

4

Đèn Host

Xanh khi B12 còn gửi HOST_HEALTH

Tắt khi nhịp tim host quá cũ

5

Chỉ báo Live

Vòng xoay xanh khi WebSocket /ws đang mở

Rớt thì tự nối lại, giãn dần tới 30 giây

6

Nút giao diện

Đổi sáng / tối

Lưu trong localStorage của trình duyệt, mặc định tối

7

Tab Quản lý

Xem tất cả trạng thái

Chỉ tab này hiện ô tìm kiếm

8

Tab Điều khiển

Lệnh MQTT, xoá dữ liệu, cấu hình

Hầu hết nút có hộp xác nhận

9

Tab Lịch sử

Nhật ký sự kiện từ audit.db

Tải dữ liệu khi bấm vào tab

10

Tab IP Stats

Thống kê trùng lặp IP

Tải dữ liệu khi bấm vào tab

11

Tab Verify

Chuyển IP giữa ba hàng đợi bằng tay, AUTO MOVE

Tác động hàng đợi thật

12

Tab Kiến trúc

Sơ đồ động và vòng đời IP

Chỉ để đọc

13

Ô tìm kiếm

Lọc mọi bảng ở tab Quản lý

Mục 5.5



4.2 Thanh tóm tắt và năm ô thống kê

Hình 4.2. Thanh tóm tắt (luôn hiện, dính ở đầu trang) và năm ô thống kê của tab Quản lý.

Số

Thành phần

Công dụng

Lưu ý

1

Ô sức khoẻ

Healthy / Warning (n) / Critical (n)

Critical = số kết nối Redis, MQTT, Host bị mất. Warning = DCOM không có IP + client offline + box có vấn đề + client ma

2

Uptime Host

Thời gian B12 đã chạy

—

3

Client

Số client đang sống

—

4

DCOMs

Tổng số DCOM trong 01_INFO

—

5

New

Số IP ở 03_NEW_IP

—

6

Using

Số IP ở 04_USING_IP

—

7

Done

Số IP ở 05_DONE_IP

—

8

Alive Clients

Như ô 3, bản to

Chỉ ở tab Quản lý

9

Total DCOMs

Như ô 4

—

10

New IPs

Như ô 5

—

11

Using IPs

Như ô 6

—

12

Done IPs

Như ô 7

—



Mọi ô trên thanh tóm tắt đều bấm được: trang tự chuyển về tab Quản lý, cuộn tới khung tương ứng và làm khung đó sáng lên 2 giây. Ô sức khoẻ thông minh hơn một chút — nó cuộn tới khung HOST nếu mất kết nối, tới CLIENT LIST nếu có client offline, tới DCOM LIST nếu có DCOM không có IP.

Hình 4.3. Bấm ô DCOMs (1) làm trang cuộn tới DCOM LIST và khung sáng viền (2).

4.3 Giao diện tối và màn hình điện thoại

Giao diện tối — mặc định khi trình duyệt chưa từng chọn.

Ở màn hình hẹp, thanh tóm tắt xuống dòng, tab chỉ còn biểu tượng, ô thống kê xếp hai cột.

Trên điện thoại, các bảng chuyển thành thẻ: mỗi ô mang nhãn cột của nó (thuộc tính data-label), nên vẫn đọc được mà không phải cuộn ngang.

Chương 5 — Tab Quản lý

Tab mặc định. Từ trên xuống: năm ô thống kê, khung HOST, khung THIẾT BỊ PROXY & CLIENT, CLIENT LIST, DCOM LIST và ba bảng hàng đợi.

5.1 Khung HOST (RapidProxyHost)

Hình 5.1. Sức khoẻ của B12 — bộ não chuyển lệnh giữa MQTT và Redis.

Số

Thành phần

Công dụng

Lưu ý

1

Nhãn trạng thái

Online / Offline

Theo HOST_HEALTH do B12 ghi

2

Uptime

Thời gian B12 đã chạy liên tục

—

3

Heartbeat cuối

Số giây từ nhịp tim cuối của B12, đếm từng giây trên trình duyệt

Xanh dưới 60 s, vàng dưới 120 s, đỏ từ 120 s

4

Kết nối MQTT (User)

Kết nối của B12 phía người dùng (nhận lệnh)

—

5

Kết nối MQTT (Device)

Kết nối của B12 phía thiết bị (nói với client)

—

6

Số Workpool / Clients

Số workpool có client, số client sống trên tổng

—



ĐÚNG THIẾT KẾ · HEARTBEAT NHẢY LÊN GẦN 30 GIÂY RỒI VỀ 0 LÀ BÌNH THƯỜNG

B12 ghi HOST_HEALTH mỗi 30 giây. Con số đếm lên từ 0 tới khoảng 30 rồi quay về — đó là nhịp chứ không phải trễ.



5.2 Khung THIẾT BỊ PROXY & CLIENT

Hình 5.2. Thẻ box DELL_PROXY_01 lúc DCOM không có IP public — trạng thái Thiếu IP public.

Số

Thành phần

Công dụng

Lưu ý

1

Tiêu đề khung

Giám sát trực tiếp của B30

Thêm ngày 18/09/2026

2

Nhãn tóm tắt

số box ổn / tổng box · số client online

Màu cam khi có box chưa ổn

3

Dòng giải thích

Cách khung làm việc và định nghĩa client ma

—

4

Thẻ box

Mỗi box một thẻ, viền trái đổi màu theo trạng thái. Các dòng: Box (gọi thẳng) — địa chỉ, chấm xanh/đỏ, thời gian trả lời; Relay Web UI — URL relay và kết quả gọi; Client phục vụ; DCOM có IP — số có IP trên tổng, kèm số Connected; Auto-Healing — Bật / Tắt

Gọi thẳng hỏng mà relay còn sống vẫn tính là box còn sống

5

Nhãn trạng thái box

Hoạt động / Thiếu IP public / Không có DCOM / Không có client / Mất kết nối / Không thấy

Bảng đầy đủ ở mục 3.7

6

Vùng thẻ

Có nhiều box thì mỗi box một thẻ

—

7

Bảng DCOM của box

DCOM, Public IP, Kết nối, Mạng, Sóng, Nhà mạng, HTTP / SOCKS5

chưa có IP hiện chữ đỏ

8

Mở Web UI box

Mở trang quản trị OBC Proxy qua relay

Tab mới



Hình 5.3. Bảng client bên dưới thẻ box, và dòng thời điểm dò cuối.

Số

Thành phần

Công dụng

Lưu ý

1

WorkPool

Workpool client khai báo

—

2

Client UID

Mã client, ví dụ WP3_VM101_ProxyClient

Là PC_UID trong .env của client

3

Máy chạy client

Nhãn máy, địa chỉ, kết quả gõ cửa SSH và thời gian

Lấy từ CONFIG:CLIENT_HOSTS hoặc mặc định trong mã

4

Box đang phục vụ

Tên và IP box client báo

—

5

Heartbeat

Tuổi nhịp tim, ví dụ 1s trước

Quá 120 s là offline

6

Trạng thái

Online / Offline / Client ma

Client ma kèm lý do

7

Dò lần cuối

Bao lâu trước và chu kỳ 10 s

Số này tăng mãi là luồng giám sát đã chết



LƯU Ý · /PROXY_LIST TRẢ DANH SÁCH RỖNG KHÔNG CÓ NGHĨA LÀ KHÔNG CÓ DONGLE

Endpoint /proxy_list của OBC Proxy đôi khi trả [] dù dongle vẫn cắm. Khung chỉ dùng nó làm phép thử box còn sống; số DCOM lấy từ 01_INFO.



5.3 CLIENT LIST và DCOM LIST

Hình 5.4. Hai bảng lấy trực tiếp từ 02_ALIVE và 01_INFO do B12 ghi.

Số

Thành phần

Công dụng

Lưu ý

1

CLIENT LIST

Danh sách client từng gửi nhịp tim

—

2

Nhãn đếm

Số client

—

3

WorkPool

Workpool client

—

4

Client UID

Mã client

—

5

Proxy Device

Tên box client phục vụ — là liên kết mở Web UI

Theo Web UI mapping; không có mapping thì dùng IP box

6

Proxy IP

IP box — cũng là liên kết

—

7

Status

Online / Offline theo cờ aliveStatus

Cờ do B12 đặt; khung 5.2 còn xét tuổi nhịp tim

8

Last Seen

Thời điểm nhịp tim cuối

—

9

DCOM LIST

Mỗi dòng một dongle

—

10

Nhãn đếm

Số DCOM

—

11

WorkPool

—

—

12

Proxy

Tên box + liên kết Web UI + nhãn Auto-Healing

Nhãn: Draining (n USING), Rebooting..., Cooldown (n s)

13

DCOM ID

Ví dụ eth1

Là tên cổng mạng dongle trên box

14

Public IP

IP 4G hiện tại

Không có IP: dòng tô đỏ, ô ghi N/A; vừa gửi lệnh đổi IP hoặc dongle đang reset (dưới 60 s): ghi Getting new IP...

15

Last Reset

Lần reset dongle gần nhất theo box

Dùng để tính 60 s trước khi tự reboot



5.4 Ba bảng hàng đợi

Hình 5.5. Ba bảng 03_NEW_IP, 04_USING_IP, 05_DONE_IP có cùng cấu trúc.

Số

Thành phần

Công dụng

Lưu ý

1

03_NEW_IP

IP sẵn sàng cấp

Rỗng khi không DCOM nào có IP

2

Nhãn đếm

Số IP

—

3

Cột Time

Thời gian IP đã nằm ở hàng đợi này, đếm từng giây

Tính từ enteredNewAt / enteredUsingAt / enteredDoneAt

4

04_USING_IP

IP đang được task dùng

Quá timeout (mặc định 1 giờ) tự sang DONE

5

Nhãn đếm

Số IP

—



Bảng 05_DONE_IP nằm ngay dưới, giống hệt về cột. Cả ba bảng có cột WorkPool, Proxy (liên kết Web UI), DCOM ID, Public IP và Time. Bảng rỗng hiện biểu tượng hộp thư và dòng Không có dữ liệu.

ĐÚNG THIẾT KẾ · IP CHỈ NẰM Ở 05_DONE VÀI GIÂY LÀ ĐÚNG

ScanIpProxy gửi lệnh đổi IP ngay khi thấy IP ở DONE. Khoảng 25 giây sau IP cũ biến mất khỏi kho DCOM và bị dọn khỏi 05. Bảng DONE thường rỗng — không phải lỗi.



5.5 Tìm kiếm

Hình 5.6. Gõ eth1 (1): CLIENT LIST rỗng (2) vì không cột nào chứa eth1, DCOM LIST còn đúng dòng khớp (3).

Ô tìm kiếm lọc tức thì trên mọi bảng của tab Quản lý, không phân biệt hoa thường, so với WorkPool, tên proxy, DCOM ID, IP và Client UID. Nó chỉ lọc phần hiển thị; nhãn đếm trên đầu mỗi bảng vẫn là tổng thật.

Chương 6 — Tab Điều khiển

Tab duy nhất gửi lệnh ra ngoài. Mọi nút ở đây đi qua một hộp xác nhận; kết quả hiện ở góc phải dưới dạng thông báo nổi và được ghi vào khung Activity Log cuối trang.

NGUY HIỂM · MỌI NÚT Ở TAB NÀY TÁC ĐỘNG THẬT

Đổi IP làm task đang dùng IP đó mất kết nối. Reboot làm DCOM mất IP khoảng một phút. Nút Xoá xoá dữ liệu không hoàn tác được. Không có môi trường thử nghiệm riêng — Dashboard này là production.



6.1 Change IP, Reboot DCOM, Request Info

Hình 6.1. Ba khung lệnh MQTT ở đầu tab.

Số

Thành phần

Công dụng

Lưu ý

1

WorkPool (Change IP)

Chọn workpool

Danh sách lấy từ 01_INFO

2

DCOM ID (Change IP)

Chọn DCOM trong workpool đã chọn

Đổi workpool thì danh sách tự nạp lại

3

Change IP

Gửi changeIpRequest cho một DCOM

POST /api/mqtt/change-ip

4

Change All IPs

Gửi changeIpAllDcomRequest cho cả workpool

POST /api/mqtt/change-ip-all

5

WorkPool (Reboot)

Chọn workpool

—

6

DCOM ID (Reboot)

Chọn DCOM

—

7

Reboot DCOM

Gửi rebootDcomRequest — khởi động lại một dongle

POST /api/mqtt/reboot-dcom

8

Reboot All

Gửi rebootAllDcomRequest cho cả workpool

Mọi DCOM mất IP cùng lúc

9

Request Immediate Update

Gửi proxyInfoRequest: bảo mọi client gửi lại danh sách DCOM ngay

Vô hại — dùng khi nghi 01_INFO cũ



LƯU Ý · ĐỔI IP BẰNG TAY LÀM LỆCH VÒNG ĐỜI

Change IP không đi qua ba hàng đợi. IP đang ở 04_USING bị đổi thì task đang dùng mất mạng, và ScanIpProxy ghi cảnh báo disappeared while still in USING set. Cách sạch để trả một IP là chuyển nó sang 05_DONE — ScanIpProxy sẽ tự đổi.



6.2 Clear Data và Timeout USING

Hình 6.2. Chín nút xoá dữ liệu và ô cấu hình timeout USING.

Số

Thành phần

Công dụng

Lưu ý

1

Xoá Proxy Info trên Host (MQTT)

Gửi clearProxyInfoRequest: B12 xoá bộ nhớ DCOM của nó

Client gửi lại trong vài giây

2

Xoá Client Alive trên Host (MQTT)

Gửi clearClientAliveRequest

Dọn client ma cũ khỏi 02_ALIVE

3

Xoá 01_INFO (Redis Key)

DELETE /api/redis/clear/01_INFO

Mọi IP ở 03/04/05 bị coi là đã biến mất và bị dọn ở giây kế

4

Xoá 02_ALIVE

Xoá khoá nhịp tim

Tự đầy lại theo nhịp tim

5

Xoá 03_NEW_IP

Xoá hàng đợi NEW

IP còn trong kho DCOM sẽ được thêm lại ngay

6

Xoá 04_USING_IP

Xoá hàng đợi USING

Nguy hiểm: IP đang dùng quay về NEW và có thể bị cấp cho task khác

7

Xoá 05_DONE_IP

Xoá hàng đợi DONE

IP chưa kịp đổi quay về NEW — sẽ bị dùng lại

8

Xoá Database Lịch Sử (SQLite)

Xoá toàn bộ audit.db

Có mật khẩu xác nhận

9

Xoá Database IP Stats (SQLite)

Xoá toàn bộ ip_stats.db

Có mật khẩu xác nhận; mất thống kê nhiều tháng

10

Thời gian

Số

Phải lớn hơn 0

11

Đơn vị

Giây / Phút / Giờ

Trang tự đổi 3600 s thành 1 Giờ khi nạp

12

Cập nhật Timeout

Ghi CONFIG:USING_TIMEOUT_SEC

ScanIpProxy dùng ngay ở giây kế



Hộp xác nhận thường có tiêu đề, một câu mô tả và hai nút Hủy / Xác nhận:

Hình 6.3. Hộp xác nhận Change IP.

Số

Thành phần

Công dụng

Lưu ý

1

Tiêu đề

Tên thao tác

—

2

Nội dung

Nói rõ DCOM và workpool bị tác động

Đọc trước khi bấm

3

Hủy

Đóng, không gửi gì

—

4

Xác nhận

Gửi lệnh

—



Reboot All — nêu rõ sẽ ngắt kết nối toàn bộ workpool.

Xoá một khoá Redis — cảnh báo không hoàn tác được.

Cập nhật timeout — hiện cả giá trị quy ra giây.

Hộp có mật khẩu

Hình 6.4. Xoá Database Lịch sử đòi mật khẩu xác nhận.

Số

Thành phần

Công dụng

Lưu ý

1

Tiêu đề

Xoá Database Lịch sử

—

2

Nội dung

Nêu tên file audit.db và cảnh báo không hoàn tác

—

3

Ô mật khẩu

Nhập mật khẩu xác nhận

—

4

Gợi ý

Dòng Hint dưới ô

Gợi ý đủ để đoán mật khẩu

5

Xác nhận

Kiểm mật khẩu rồi gửi lệnh

—



Hình 6.5. Nhập sai: ô viền đỏ (1), thông báo Mật khẩu xác nhận không đúng (2), hộp vẫn mở.

NGUY HIỂM · MẬT KHẨU NÀY CHỈ LÀ MỘT CÁI CHỐT CỬA

Mật khẩu được so trong JavaScript của trình duyệt và viết thẳng trong app.js; server không kiểm gì. Ai gọi POST /api/audit/clear hoặc POST /api/ip-stats/clear trực tiếp là xoá được. Giáo trình cố ý không in giá trị mật khẩu. Bảo vệ thật phải nằm ở server — xem Chương 14.



6.3 Auto-Healing và Web UI mapping

Hình 6.6. Bảng cấu hình Auto-Healing và bảng Web UI mapping.

Số

Thành phần

Công dụng

Lưu ý

1

Thêm Cấu Hình Reboot

Mở hộp cấu hình trống cho một box mới

—

2

WorkPool

Workpool của box

Phải khớp nơi box đang online

3

Proxy Box Name

Tên box — khoá nối với PROXY_DEVICE_UID_NAME của client

Bánh răng nhỏ cạnh tên: bấm để sửa

4

Tuya Smart Plug

Device ID của ổ cắm và số ổ

—

5

Ngưỡng lỗi / Delay

5 lỗi / 5s ngắt / 300s CD

Ngưỡng, thời gian tắt, cooldown

6

Trạng thái

Bật / Tắt, và trạng thái sống: Bình thường, Draining, Rebooting, Cooldown (n s), hoặc cảnh báo lệch tên / lệch workpool

Cập nhật mỗi 3 giây

7

Thao tác

Ba nút bên dưới

—

8

Sửa

Mở hộp cấu hình của box

—

9

Test

Mở hộp xác nhận Power Cycle cho box

Cắt điện thật

10

Xoá

Xoá cấu hình Auto-Healing của box

Box vẫn chạy, chỉ mất tự chữa tầng 2

11

Thêm Web UI Mapping

Mở hộp mapping trống

—

12

URL Web UI Dongle

URL trang quản trị qua relay

Hiện: http://172.16.30.21:6868/home

13

Mở Web / Sửa

Mở trang quản trị, hoặc sửa mapping

—

14

Xoá mapping

Bỏ liên kết

Liên kết Proxy trên các bảng rơi về IP box



Hộp cấu hình Auto-Reboot

Hình 6.7. Nửa trên hộp cấu hình Auto-Healing của DELL_PROXY_01 (token đã che).

Số

Thành phần

Công dụng

Lưu ý

1

Tiêu đề

Tên box đang sửa

—

2

Nút đóng

Đóng không lưu

—

3

Proxy Box Name *

Khoá cấu hình — phải trùng tên client báo

Bắt buộc

4

WorkPool

Workpool box đang online

Mặc định WP3 từ 18/09/2026

5

Tuya Device ID

Mã ổ cắm Tuya

Chỉ dùng khi thi hành, không dùng để so khớp

6

Ổ cắm / Switch (1..10)

Số ổ trên dải ổ cắm

Hiện: 2

7

Số lần lỗi kích hoạt

fail_threshold

Hiện: 5

8

Thời gian tắt nguồn (giây)

off_delay_sec

Hiện: 5

9

Cooldown sau Reboot (giây)

cooldown_sec

Hiện: 300

10

WCM API Token + Ẩn/Hiện

Khoá gọi API B33 WCM

Ô mật khẩu; nút mắt để hiện

11

WCM Tuya Power API Endpoint

URL https://wcm.lotus1104.synology.me/api/tuya/power

—

12

Kích hoạt Auto-Healing

Bật / tắt tự chữa tầng 2 cho box này

Tắt thì trạng thái DISABLED

13

Mẫu cURL

Hai lệnh curl tắt, nghỉ, bật — đổi theo ô đang nhập

Hiện cả token dạng rõ (ảnh đã che)



Hình 6.8. Nửa dưới hộp: mẫu cURL và năm nút.

Số

Thành phần

Công dụng

Lưu ý

1

Mẫu cURL

Như trên

Chép được để chạy tay khi gỡ lỗi

2

Lưu Cấu Hình

POST /api/redis/config/auto-reboot

Có hiệu lực ngay ở giây quét kế

3

Test Tắt (OFF)

Tắt ổ ngay

Box mất điện tới khi bật lại

4

Test Bật (ON)

Bật ổ

—

5

Test Power Cycle

Tắt, nghỉ theo ô 8, bật

Box khởi động lại khoảng 80 giây

6

Đóng

Đóng hộp

Không lưu



NGUY HIỂM · LỖI GIAO DIỆN: HỘP XÁC NHẬN CỦA BA NÚT TEST BỊ CHE

Hộp xác nhận chung và hộp Auto-Reboot cùng z-index: 1000, và hộp Auto-Reboot đứng sau trong HTML nên nằm đè lên hộp xác nhận. Bấm Test Tắt / Bật / Power Cycle bên trong hộp này thì hộp xác nhận mở ra nhưng không nhìn thấy — trông như nút không làm gì. Khi chụp ảnh cho sách, hộp xác nhận Power Cycle không lên hình vì lý do đó. Dùng nút Test trên dòng bảng (số 9, hình trên) thay thế, hoặc sửa CSS. Xem mục 15.2.



Hộp Web UI mapping

Hình 6.9. Sửa Web UI mapping của DELL_PROXY_01.

Số

Thành phần

Công dụng

Lưu ý

1

Tiêu đề

Tên box đang sửa

—

2

Tên Proxy Device *

Phải trùng tên box client báo

—

3

URL Web UI Dongle *

URL đầy đủ, ví dụ http://172.16.30.21:6868/home

Dùng địa chỉ relay, không dùng .201

4

Hủy

Đóng

—

5

Lưu Mapping

POST /api/redis/config/web-ui-mapping

Liên kết trên bảng đổi ngay



6.4 Activity Log

Hình 6.10. Nhật ký thao tác của phiên trình duyệt hiện tại.

Số

Thành phần

Công dụng

Lưu ý

1

Khung nhật ký

Mỗi thao tác một dòng kèm giờ, xanh là thành công, đỏ là lỗi

Chỉ tồn tại trong tab trình duyệt này; tải lại trang là mất

2

Clear

Xoá khung nhật ký

Không đụng audit.db



Chương 7 — Tab Lịch sử

Đọc từ WebDashboard/data/audit.db — bảng events với các cột thời điểm, loại sự kiện, workpool, DCOM, IP và chi tiết JSON. Dữ liệu cũ hơn 90 ngày tự bị xoá khi server khởi động.

Hình 7.1. Tóm tắt 24 giờ theo workpool và bảng DCOM reboot nhiều nhất.

Số

Thành phần

Công dụng

Lưu ý

1

Khoảng thời gian

24 giờ / 48 giờ / 7 ngày / 30 ngày

Áp cho cả trang

2

Thẻ workpool

Đếm từng loại sự kiện: dcom_changeip, Reboot, Mới (NEW), Đang dùng (USING), Xong (DONE), Timeout, auto_healing_drain_start, auto_healing_power_cycle

Nhãn góc phải của thẻ luôn ghi 24h dù chọn khoảng khác

3

DCOM reboot/timeout nhiều nhất

Xếp hạng DCOM theo tổng dcom_reboot + ip_timeout

Là chỉ báo sức khoẻ phần cứng



Trong ảnh: WP3 có 960 lần Reboot và 191 lần Auto-Healing trong 24 giờ. Mỗi lần reboot là một lệnh tự chữa tầng 1 gửi cho eth1 vì DCOM không có IP — con số lớn nghĩa là DCOM mất IP gần như cả ngày.

Hình 7.2. Nhật ký sự kiện với ba bộ lọc.

Số

Thành phần

Công dụng

Lưu ý

1

Lọc workpool

Tất cả hoặc một workpool

—

2

Lọc DCOM

Tất cả hoặc một DCOM

—

3

Lọc sự kiện

IP Mới (03), Sử dụng IP (04), IP Done (05), IP Timeout, Reboot DCOM, Client Offline

Các loại auto_healing_*, ip_dedup, tuya_* không có trong danh sách lọc — chọn Tất cả để thấy

4

Thời gian

Giờ địa phương

—

5

Workpool

—

—

6

Sự kiện

Nhãn màu cho loại quen thuộc, tên thô cho loại khác

—

7

DCOM ID / Client

—

—

8

IP / Chi tiết

IP hoặc JSON chi tiết

fail_count đếm 1 tới 5 rồi Auto-Healing kích hoạt



GHI CHÚ · ĐỌC MỘT CHU KỲ AUTO-HEALING TRONG NHẬT KÝ

Từ dưới lên: Reboot với fail_count 3, 4, 5 cách nhau khoảng 90 giây, rồi auto_healing_drain_start (failures: 5), rồi hai giây sau auto_healing_power_cycle với success: false. Sau đó fail_count đếm lại từ 1.



LƯU Ý · CỘT CREATED_AT TRONG AUDIT.DB LÀ GIỜ UTC

Cột created_at do SQLite tự điền bằng datetime('now'), tức UTC — lệch 7 giờ so với giờ Việt Nam. Dashboard dùng cột timestamp (epoch) nên hiện đúng giờ; chỉ khi truy vấn tay hoặc đọc JSON từ API mới cần để ý.



Chọn 30 ngày: WP2 (vị trí cũ của box) và WP3 hiện cạnh nhau.

Chương 8 — Tab IP Stats

Đo kích thước thật của pool IP nhà mạng. Mỗi khi ScanIpProxy thấy một IP mới xuất hiện, nó ghi một dòng vào ip_stats.db: IP đó đã từng gặp chưa. Lâu dần ta biết nhà mạng chỉ xoay trong bao nhiêu IP.

Hình 8.1. WP2, 90 ngày: 2.329 lượt xoay, 601 IP khác nhau, tỷ lệ trùng 74,2 %.

Số

Thành phần

Công dụng

Lưu ý

1

Workpool

Chọn workpool

Danh sách từ /api/ip-stats/workpools

2

Nhà mạng

Tất cả / VIETTEL / MOBIFONE / VINAPHONE / VIETNAMMOBILE

Theo trường isp của DCOM

3

Khoảng thời gian

24 giờ / 7 / 30 / 90 ngày

—

4

Tổng lượt xoay IP (Hits)

Số lần gặp một IP mới xuất hiện

—

5

Tổng IP Unique đã gặp

Số IP khác nhau

—

6

Tổng IP Trùng Lặp

Số lượt gặp lại IP đã từng thấy

Hits = Unique + Trùng lặp

7

Tỷ lệ trùng lặp IP

Trùng lặp / Hits

1728 / 2329 = 74,2 %

8

Biểu đồ

Đường tích luỹ Unique (xanh) và Trùng lặp (đỏ)

Mục dưới



Hình 8.2. Biểu đồ tích luỹ, di chuột hiện số liệu từng mốc.

Số

Thành phần

Công dụng

Lưu ý

1

Vùng biểu đồ

Kéo chuột để phóng to một đoạn theo trục thời gian; giữ Ctrl rồi kéo để trượt ngang

Nút Reset Zoom chỉ hiện sau khi đã phóng

2

Dòng Min / Max / Gap

Giá trị nhỏ nhất, lớn nhất và chênh lệch của mỗi đường trong khoảng đang xem

—

3

Chú thích

Giải nghĩa hai đường

—



ĐÚNG THIẾT KẾ · ĐƯỜNG UNIQUE NẰM NGANG LÀ TIN TỐT ĐỂ HIỂU NHÀ MẠNG

Khi đường xanh bão hoà mà đường đỏ vẫn tăng, bạn đã gặp gần hết pool IP của nhà mạng ở trạm đó. Với WP2 (VIETNAMMOBILE), tích luỹ tới 18/09/2026 là 640 IP khác nhau qua 9.299 lượt — tức mỗi IP trung bình quay lại khoảng 14 lần.



Hình 8.3. 100 IP gặp lại nhiều nhất của WP2 — bấm tiêu đề cột để sắp xếp.

Số

Thành phần

Công dụng

Lưu ý

1

Hạng

Thứ hạng theo số lần

—

2

Địa chỉ IP

—

—

3

Nhà mạng

—

—

4

Số lần

Số lần gặp lại

Mũi tên chỉ cột đang sắp xếp

5

Lần cuối

Lần gặp gần nhất

—

6

Lần trước

Khoảng cách giữa hai lần gặp gần nhất

—

7

Trung bình

Khoảng cách trung bình giữa các lần gặp

Dùng để ước lượng bao lâu một IP quay lại



WP3 mới có 5 lượt, 5 IP, chưa trùng — box về WP3 từ 11/09/2026 và DCOM mất IP gần như liên tục từ đó.

Chương 9 — Tab Verify

Chuyển IP giữa ba hàng đợi bằng tay, hoặc bật bánh răng AUTO MOVE. Mọi thao tác ở đây đi qua WebSocket /ws/verify hoặc REST và dùng đúng hàm move_ip_between_states mà n8n dùng.

NGUY HIỂM · VERIFY KHÔNG PHẢI SÂN TẬP

Tên tab gợi ý thử nghiệm, nhưng nó thao tác trên hàng đợi production. Chuyển một IP sang USING là giành IP đó khỏi n8n; chuyển sang DONE là ra lệnh đổi IP thật.



Hình 9.1. Đường ống ba hàng đợi và số IP ở mỗi hàng.

Số

Thành phần

Công dụng

Lưu ý

1

03_NEW_IP

Số IP ở NEW

—

2

04_USING_IP

Số IP ở USING

—

3

05_DONE_IP

Số IP ở DONE

—



Hình 9.2. Ba thẻ chuyển trạng thái.

Số

Thành phần

Công dụng

Lưu ý

1

Chọn IP (NEW)

IP đang ở 03

Rỗng khi hàng đợi rỗng

2

Chuyển sang USING

03 -> 04, đặt enteredUsingAt

Ghi ip_using

3

AUTO MOVE (NEW -> USING)

Bật/tắt tự dịch phần tử đầu mỗi giây

Hỏi xác nhận trước khi bật

4

Chọn IP (USING)

IP đang ở 04

—

5

Delay Auto Move

0 / 15 / 30 / 60 giây

Chỉ áp cho USING -> DONE

6

Chuyển sang DONE

04 -> 05

ScanIpProxy sẽ gửi lệnh đổi IP

7

AUTO MOVE (USING -> DONE)

Tự dịch khi IP đã ở USING đủ độ trễ

—

8

Chọn IP (DONE)

IP đang ở 05

—

9

Recycle sang NEW

05 -> 03 — dùng lại IP không đổi

Chỉ hợp lý khi chắc IP chưa bị nền tảng đích đánh dấu

10

AUTO MOVE (DONE -> NEW)

Tự dịch ngược

Chặn luôn việc đổi IP



Bật AUTO MOVE luôn qua một hộp xác nhận.

Trạng thái ba công tắc lưu ở CONFIG:AUTO_MOVE trên Redis, nên mọi trình duyệt thấy cùng trạng thái và bánh răng vẫn chạy khi đã đóng trang.

Chương 10 — Tab Kiến trúc

Hình 10.1. Sơ đồ SVG động — khối đổi màu theo trạng thái kết nối.

Số

Thành phần

Công dụng

Lưu ý

1

RapidProxyClient

Docker trên VM #101, WP3

Hiện chấm cảnh báo khi có client offline

2

MQTT Broker

128.199.200.17

Viền đỏ khi Dashboard mất MQTT

3

RapidProxyHost

B12

Theo đèn Host

4

ScanIpProxy Script

Vòng quét 1 giây

—

5

Redis Server

01_INFO / 02_ALIVE / 03 / 04 / 05

Theo đèn Redis

6

Web Dashboard

FastAPI + WebSocket

—

7

Chấm cảnh báo client

Bấm để cuộn tới CLIENT LIST

—



Toàn cảnh tab Kiến trúc.

Hình 10.2. Khối Vòng đời IP DCOM.

Số

Thành phần

Công dụng

Lưu ý

1

Ba ô trạng thái

Giải nghĩa 03, 04, 05

—

2

Ghi chú Cơ chế đổi IP

Tóm tắt cách IP cũ biến mất và IP mới xuất hiện

Câu “khi Host ghi nhận IP mới từ Pi” còn sót từ thời client ở Pi3 — nay là VM #101





PHẦN IV

CÁC TÁC VỤ CHÍNH





Chương 11 — Quy trình từng bước

11.1 Lấy một IP cho task (phía n8n)

Đây là cách duy nhất được khuyến nghị. Nó đã được áp dụng cho workflow Lv1RQyThF9U9cT8Q ngày 14/08/2026.

1. GET http://lotus_python_proxy_script:8080/api/redis/new-ip — chỉ ĐỌC, IP vẫn nằm ở 03.

2. Chọn IP đầu danh sách, lấy dcomEntry.publicIPv4Address.

3. POST /api/redis/move-ip với from_key=LOTUS:03_PROXY:03_NEW_IP, to_key=LOTUS:03_PROXY:04_USING_IP, public_ip=<IP>. Server trả phần tử đã chuyển.

4. Đọc proxy từ kết quả: proxyMeta.proxyDevIp + dcomEntry.httpPort (HTTP) hoặc socks5Port.

5. Xong việc: POST /api/redis/move-ip từ 04_USING_IP sang 05_DONE_IP. ScanIpProxy tự đổi IP.

curl -X POST http://lotus_python_proxy_script:8080/api/redis/move-ip \

-H "Content-Type: application/json" \

-d '{"from_key":"LOTUS:03_PROXY:03_NEW_IP",

"to_key":"LOTUS:03_PROXY:04_USING_IP",

"public_ip":"103.249.22.82"}'



LƯU Ý · BA ĐIỀU DỄ SAI KHI DỰNG NODE TRONG N8N

move-ip trả HTTP 400 khi IP không còn ở hàng đợi nguồn (task khác đã lấy). Đừng bật Continue on Fail ở node này.

▸ Node HTTP ghi đè $json — đặt move-ip TRƯỚC node đọc proxy, rồi đọc từ item server trả về.

▸ Không cần tự gắn mốc thời gian; server đặt enteredUsingAt và xoá mốc cũ.

▸ Chưa có bước 5 thì IP nằm ở USING tới hết timeout (mặc định 1 giờ) mới được đổi.



NGUY HIỂM · PROXY CHỈ DÙNG ĐƯỢC TỪ TRONG LAN WP3

Đo ngày 11/09/2026: từ HP1 qua VPN, cổng proxy 2001/4001 của 172.16.30.201 đều timeout; từ máy cùng LAN WP3 thì chạy. Ngày 18/09/2026 cổng 6868 đã gọi được từ HP1 nhưng cổng proxy chưa được đo lại. Worker ở WP1/WP2 cần kiểm trước khi dựa vào.



11.2 Đổi IP bằng tay cho một DCOM

1. Tab Quản lý: xác nhận IP của DCOM không nằm ở 04_USING. Nếu có, hỏi người đang chạy task.

2. Tab Điều khiển, khung Change IP: chọn WorkPool, chọn DCOM ID.

3. Bấm Change IP, đọc hộp xác nhận, bấm Xác nhận.

4. Theo dõi DCOM LIST: ô Public IP chuyển sang Getting new IP..., rồi IP mới trong khoảng 25 giây.

5. IP mới tự vào 03_NEW_IP. Tab Lịch sử có thêm một dòng IP Mới.

11.3 Thêm hoặc sửa cấu hình Auto-Healing

1. Lấy tên box đúng từ DCOM LIST (cột Proxy) — đó là PROXY_DEVICE_UID_NAME của client.

2. Lấy workpool đúng từ DCOM LIST (cột WorkPool).

3. Tab Điều khiển, Thêm Cấu Hình Reboot hoặc Sửa trên dòng có sẵn.

4. Điền Proxy Box Name và WorkPool chép từ bước 1–2, không gõ tay.

5. Điền Tuya Device ID và số ổ; giữ ngưỡng 5, tắt 5 s, cooldown 300 s nếu không có lý do đổi.

6. Đánh dấu Kích hoạt, bấm Lưu Cấu Hình.

7. Trong vòng vài giây cột Trạng thái phải hiện Bình thường. Hiện cảnh báo lệch tên hoặc lệch workpool thì quay lại bước 1.

NGUY HIỂM · ĐỪNG THỬ BẰNG TEST POWER CYCLE KHI BOX ĐANG PHỤC VỤ

Nút Test cắt điện thật, mọi DCOM mất IP khoảng 80 giây, và không chờ IP đang USING như Auto-Healing. Chỉ thử khi 04_USING của box rỗng.



11.4 Đổi Web UI mapping sau khi dời relay

1. Kiểm relay sống: mở http://<máy client>:6868/ phải ra trang OBC Proxy.

2. Tab Điều khiển, dòng box trong bảng Web UI mapping, bấm Sửa.

3. Nhập URL đầy đủ có /home, bấm Lưu Mapping.

4. Tab Quản lý: liên kết tên box trong CLIENT LIST, DCOM LIST và dòng Relay Web UI của khung thiết bị đổi theo. Chấm cạnh dòng relay phải xanh sau tối đa 10 giây.

11.5 Khai báo máy chạy client cho khung giám sát

Khung THIẾT BỊ gõ cửa cổng SSH của máy chạy client để phân biệt “client chết” với “máy chết”. Mã có sẵn hai máy mặc định; thêm máy mới bằng khoá Redis, không cần sửa mã:

SET LOTUS:03_PROXY:CONFIG:CLIENT_HOSTS '{

"WP3_VM101_ProxyClient": {"host": "172.16.30.21", "port": 22,

"label": "VM #101 wp3-proxy-client-01"},

"WP2_Rasp_Pi3_b827eb729e41": null

}'



Giá trị null gỡ một máy mặc định khỏi danh sách. Khoá được đọc lại mỗi chu kỳ 10 giây.



PHẦN V

VẬN HÀNH HẰNG NGÀY





Chương 12 — Theo dõi và đọc số liệu

12.1 Năm câu hỏi mỗi sáng

Câu hỏi

Nhìn vào đâu

Bình thường là

Ba kết nối có sống không

Ba đèn đầu trang, ô sức khoẻ

Ba đèn xanh; Healthy hoặc Warning có lý do

Box và client có ổn không

Khung THIẾT BỊ PROXY & CLIENT

Nhãn 1/1 box · 1 client online, box Hoạt động

DCOM có IP không

DCOM LIST

Không dòng đỏ N/A kéo dài quá vài phút

Pool có IP để cấp không

Ô New

Lớn hơn 0 khi có DCOM có IP

Tự chữa có đang lặp không

Tab Lịch sử 24 giờ

auto_healing_power_cycle bằng 0 hoặc rất ít



12.2 Ý nghĩa các con số trong Lịch sử

Sự kiện / 24 giờ

Ý nghĩa

Ngưỡng đáng lo

dcom_reboot

Số lệnh reboot tự chữa. Tối đa khoảng một lệnh mỗi 60–90 giây mỗi DCOM

Hàng trăm = DCOM mất IP hàng giờ

ip_new

Số IP mới được đưa vào pool

Bằng 0 cả ngày = không có IP nào được đổi

dcom_changeip

Số lần ScanIpProxy ra lệnh đổi IP cho IP đã DONE

Lệch xa ip_done = lệnh bị nuốt

ip_timeout

IP bị giữ quá timeout

Tăng đều = task quên trả IP

ip_dedup

IP bị dọn vì nằm ở hai hàng đợi

Xuất hiện đều = còn client lấy IP bằng pop rồi push

auto_healing_power_cycle

Số lần cắt điện box

Hơn vài lần/ngày = box hoặc Tuya có vấn đề



Số liệu thật để so: ngày 14/08/2026 WP2/eth1 có 737 lần dcom_reboot trong 24 giờ, tức DCOM mất IP khoảng 12 giờ. Ngày 18/09/2026 WP3/eth1 có 960 lần — DCOM mất IP gần như trọn ngày.

12.3 Lệnh kiểm tra nhanh từ HP1

# Sức khoẻ tổng: redis + mqtt + host

curl -s http://127.0.0.1:20300/health | python3 -m json.tool


# Ba hàng đợi + kho DCOM

curl -s http://127.0.0.1:20300/api/redis/all | python3 -m json.tool


# Giám sát thiết bị

curl -s http://127.0.0.1:20300/api/devices/monitor | python3 -m json.tool


# Trạng thái Auto-Healing

curl -s http://127.0.0.1:20300/api/redis/state/auto-reboot


# DCOM reboot nhiều nhất

curl -s 'http://127.0.0.1:20300/api/audit/reboot-ranking?limit=10'


# Hỏi thẳng box qua relay (chỉ đọc)

curl -s http://172.16.30.21:6868/api/v1/dongles/all


# Log hai tiến trình

docker logs --tail 100 lotus_python_proxy_script



12.4 Khởi động lại B30 an toàn

Trạng thái Auto-Healing (DRAINING, COOLDOWN, bộ đếm lỗi) nằm trong bộ nhớ tiến trình ScanIpProxy. Khởi động lại container thì mọi box về NORMAL và bộ đếm về 0.

1. Xem /api/redis/state/auto-reboot: không box nào đang DRAINING hoặc REBOOTING.

2. Xem 04_USING: ghi nhận các IP đang dùng — chúng nằm trong Redis nên không mất.

3. docker restart lotus_python_proxy_script.

4. Sau khoảng 10 giây: đèn Live xanh, khung THIẾT BỊ có dòng Dò lần cuối mới.

GHI CHÚ · SỬA MÃ PYTHON THÌ PHẢI KHỞI ĐỘNG LẠI

Mã được mount thẳng từ B30_Python_Proxy_Script/APP_B02_RapidProxyScript vào container, nhưng Python không tự nạp lại. Sửa index.html, app.js, style.css thì chỉ cần tải lại trang (trang gắn số phiên bản vào đường dẫn CSS/JS để tránh bộ đệm).





PHẦN VI

AN TOÀN DỮ LIỆU VÀ SAO LƯU





Chương 13 — Cái gì nằm ở đâu, mất thì sao

13.1 Bản đồ dữ liệu

Dữ liệu

Nằm ở

Được bảo vệ bằng

Mất thì

01_INFO, 02_ALIVE, HOST_HEALTH

Redis B27

Không cần — client và B12 gửi lại liên tục

Tự đầy lại trong vài giây

Ba hàng đợi 03/04/05

Redis B27

AOF bật (appendonly yes) + ảnh chụp RDB save 900 1, volume Docker redis_data

03 tự dựng lại từ kho DCOM; 04 mất = không biết IP nào đang có người dùng

Cấu hình CONFIG:*

Redis B27

Như trên

Auto-Healing, Web UI mapping, timeout quay về mặc định trong mã

audit.db (khoảng 10,9 MB)

WebDashboard/data/ trên HP1

Không có sao lưu định kỳ. Chỉ có một bản chép tay trong data_bak/ ngày 05/06/2026

Mất lịch sử sự kiện

ip_stats.db (khoảng 0,4 MB)

WebDashboard/data/ trên HP1

Như trên

Mất thống kê pool IP nhiều tháng — không dựng lại được

Mã nguồn B30

DCP_PRODUCTION/B30_Python_Proxy_Script/APP_B02_RapidProxyScript

Kho git riêng, nhánh buddha

Thư mục nằm trong Application/ — bị kho chính gitignore

Image rapid_proxy_script:1.0

Registry 172.16.10.201:20152

Registry nội bộ

Không kéo được image khi dựng lại máy



NGUY HIỂM · CHẠY PYTEST CÓ THỂ GHI VÀO AUDIT.DB THẬT

Tests/test_single_state_invariant.py import mã thật, mà _get_audit_service() mở thẳng WebDashboard/data/audit.db — chính file đang mount vào production. Fixture _no_audit (tự áp dụng) chặn việc này. Giữ nguyên fixture khi thêm test mới đụng tới vòng quét.



13.2 Sao lưu tay đúng cách

SQLite đang được ghi liên tục; chép file bằng cp giữa lúc ghi có thể cho bản hỏng. Dùng lệnh .backup của SQLite, nó chép nhất quán:

cd .../B30_Python_Proxy_Script/APP_B02_RapidProxyScript/WebDashboard

D=$(date +%Y%m%d_%H%M)

sqlite3 data/audit.db ".backup data_bak/audit_$D.db"

sqlite3 data/ip_stats.db ".backup data_bak/ip_stats_$D.db"



Sao lưu cấu hình Redis trước khi sửa tay — lần dời box 11/09/2026 đã làm đúng như vậy:

curl -s http://127.0.0.1:20300/api/redis/config/auto-reboot \

> tmp/AUTO_REBOOT.bak_$(date +%Y%m%d_%H%M).json

curl -s http://127.0.0.1:20300/api/redis/config/web-ui-mapping \

> tmp/WEB_UI_MAPPING.bak_$(date +%Y%m%d_%H%M).json



13.3 Khôi phục

Tình huống

Cách khôi phục

Lỡ bấm Xoá 03_NEW_IP

Không cần làm gì — IP còn trong kho DCOM tự vào lại ở giây kế

Lỡ bấm Xoá 04_USING_IP

Không khôi phục được từ Dashboard. Hỏi các workflow đang chạy IP nào; dùng Verify chuyển đúng IP đó từ NEW sang USING

Lỡ xoá 05_DONE_IP

IP chưa đổi quay về NEW. Chuyển chúng sang DONE ở tab Verify để buộc đổi

Lỡ xoá cấu hình Auto-Healing

Nạp lại từ bản .json đã sao lưu bằng POST /api/redis/config/auto-reboot

Hỏng audit.db

Dừng container, chép bản .backup gần nhất vào data/audit.db, khởi động lại

Mất volume Redis

Cấu hình quay về mặc định; hàng đợi tự dựng lại từ 03. Cấu hình lại Auto-Healing, Web UI mapping, timeout





PHẦN VII

RỦI RO VÀ THẢM HOẠ





Chương 14 — Những gì có thể hỏng

14.1 Rủi ro truy cập

NGUY HIỂM · KHÔNG XÁC THỰC, MỞ RA INTERNET

https://scanIpProxy.lotus1104.synology.me qua Caddy phục vụ cả Dashboard lẫn REST. Không có đăng nhập, không có khoá API. Hệ quả: người ngoài có thể đổi IP, reboot DCOM, xoá hàng đợi, xoá audit.db và cắt điện box qua Tuya.

▸ Nhanh nhất: giới hạn IP nguồn hoặc thêm basic-auth ở Caddy cho tên miền này.

▸ Mật khẩu xoá DB chỉ kiểm trong trình duyệt — không phải lớp bảo vệ.

▸ Token WCM có giá trị mặc định viết cứng trong redis_api.py, ScanIpProxy.py và index.html, và hiện nguyên văn trong khối mẫu cURL. Ai mở hộp cấu hình là đọc được.



Thông tin đăng nhập MQTT và Redis cũng có giá trị mặc định viết trong mã (được ghi đè bằng biến môi trường khi chạy). Giáo trình không in các giá trị này.

14.2 Kịch bản hỏng

Kịch bản

Dấu hiệu trên Dashboard

Hậu quả

Xử lý

Broker MQTT VPS chết

Đèn MQTT đỏ, Host báo MQTT Disconnected

Không lệnh nào tới client; 01_INFO đứng im

Kiểm VPS 128.199.200.17; broker sống lại mà đèn vẫn đỏ thì khởi động lại B12, B30

B12 chết

Đèn Host đỏ, Heartbeat đỏ

01_INFO không cập nhật; lệnh không được chuyển

docker restart lotus_rapid_proxy_manager

Redis chết hoặc DNS wp1.hp1 hỏng

Đèn Redis đỏ, Critical

Toàn bộ IP_Pool dừng

Kiểm lotus_redis, cổng 20270, DNS LAN

VM #101 tắt

Client offline, box Không có client

Không đổi IP, không reboot DCOM được

Bật VM #101 trên pve-001 (qua B43)

Box DELL_PROXY treo

Box Mất kết nối, DCOM N/A

Auto-Healing cắt điện sau 5 lần lỗi

Nếu Tuya hỏng: rút điện tay

DCOM hết data / mất sóng

DCOM đỏ N/A, Connected nhưng không IP

Reboot lặp mãi, Auto-Healing lặp mãi

Kiểm SIM, sóng; xem mục 14.3

Ổ Tuya hoặc WCM lỗi

Lịch sử: success: false đều đặn

Tự chữa tầng 2 vô hiệu

Mục 14.3

Dời box mà quên cấu hình

Auto-Healing Workpool không khớp

Tự chữa tầng 2 tắt

Sửa WorkPool trong cấu hình



14.3 Sự cố đang diễn ra lúc biên soạn: Tuya không cắt được điện

Truy vấn audit.db ngày 18/09/2026 cho kết quả:

Ngày

Lần cắt điện Auto-Healing

Kết quả

20/08 – 22/08/2026

5

Thành công

11/09/2026

10

Thất bại

12/09 – 17/09/2026

191–192 mỗi ngày

Thất bại toàn bộ

18/09/2026 (tới 17:53)

142

Thất bại toàn bộ



Tổng cộng 1.303 lần thất bại liên tiếp từ 11/09/2026; lần thành công cuối là 22/08/2026. Khi điều tra ngày 18/09/2026, WCM trả HTTP 500 và Tuya Cloud báo lỗi 913 — IoT Core service subscription has expired (gói dịch vụ IoT Core đã hết hạn).

NGUY HIỂM · HỆ QUẢ

Box DELL_PROXY_01 không được cắt điện lần nào trong một tuần, DCOM eth1 gần như không có public IP, pool WP3 gần như không cấp được IP, trong khi Dashboard vẫn ghi mỗi 7,5 phút một chu kỳ DRAINING -> REBOOTING -> COOLDOWN như thể đang tự chữa.

▸ Gia hạn gói IoT Core trên iot.tuya.com, rồi thử lại bằng nút Test trên dòng bảng.

▸ Trong lúc chờ: cân nhắc bỏ tích Kích hoạt Auto-Healing để ngừng vòng lặp, và cắt điện box bằng tay.

▸ Sau khi box khởi động lại mà DCOM vẫn không có IP: nghi SIM hoặc sóng, không phải box.



14.4 Thao tác không hoàn tác được

Thao tác

Tại sao nguy hiểm

Xoá Database Lịch Sử / IP Stats

Không có sao lưu định kỳ; thống kê pool IP không dựng lại được

Xoá 04_USING_IP

Mất dấu IP đang có người dùng — IP có thể bị cấp trùng

Reboot All / Change All IPs

Mọi task trong workpool mất mạng cùng lúc

Test Tắt (OFF)

Box mất điện tới khi có người bấm Test Bật — nếu WCM lỗi giữa chừng, phải bật tay

AUTO MOVE khi n8n đang chạy

Giành IP với n8n; USING -> DONE làm đổi IP dưới chân task





PHẦN VIII

KHẮC PHỤC SỰ CỐ





Chương 15 — Bảng tra triệu chứng

15.1 Triệu chứng, nguyên nhân, cách chữa

Triệu chứng

Nguyên nhân thường gặp

Cách chữa

DCOM đỏ N/A, Connected, sóng tốt

Dongle có kết nối vô tuyến nhưng không lấy được IP: SIM hết data, hoặc box cần khởi động lại

Xem Lịch sử: reboot tầng 1 có chạy không, Auto-Healing có success: true không. Kiểm SIM

Auto-Healing hiện Tên proxy không khớp

PROXY_DEVICE_UID_NAME của client khác ô Proxy Box Name

Sửa một bên cho trùng từng ký tự (Phụ lục C)

Auto-Healing hiện Workpool không khớp

Box đã dời site, cấu hình còn workpool cũ

Sửa WorkPool (Phụ lục D)

Auto-Healing lặp DRAINING/COOLDOWN, success: false

WCM hoặc Tuya Cloud lỗi

Chạy mẫu cURL trong hộp cấu hình để đọc lỗi thật (Phụ lục F)

Box Không có client

Client dừng, VM tắt, hoặc client báo tên box khác

Xem cột Máy chạy client: SSH xanh mà client offline = container client chết; SSH đỏ = máy chết

Có dòng Client ma

Client ở site cũ vẫn chạy

Tắt client đó (Phụ lục D); rồi Xoá Client Alive trên Host

Hai client cùng tên box trong một workpool

Ai đó bật B14 trên HP1

Dừng B14 — client thật ở VM #101 (Phụ lục E)

Ô New luôn 0 dù DCOM có IP

AUTO MOVE NEW -> USING đang bật, hoặc task lấy IP liên tục

Tab Verify: tắt AUTO MOVE

IP xuất hiện ở cả 03 và 04

Client lấy IP bằng pop rồi push

Tự được dọn mỗi giây; sửa client dùng move-ip (Phụ lục B)

IP nằm lì ở 04 cả giờ

Workflow không trả IP về DONE

Thêm node move-ip 04 -> 05 cuối workflow

Đèn Live xoay mãi, số liệu đứng

WebSocket rớt và đã thử lại quá số lần cho phép

Tải lại trang

Bảng bị cắt mất dòng cuối

Lỗi tính chiều cao bảng (đã sửa 18/09/2026)

Tải lại trang cứng: Ctrl+Shift+R

Dò lần cuối tăng mãi

Luồng giám sát thiết bị chết

Khởi động lại B30 (mục 12.4)

Liên kết Proxy mở trang trắng

Web UI mapping trỏ địa chỉ cũ, hoặc relay chết

Kiểm http://172.16.30.21:6868/, sửa mapping



15.2 Nút Test trong hộp Auto-Reboot như không làm gì

Triệu chứng: mở hộp cấu hình Auto-Reboot, bấm Test Tắt, Test Bật hoặc Test Power Cycle — màn hình không đổi.

Nguyên nhân: trong index.html, hộp #confirm-modal khai báo trước #modal-auto-reboot; cả hai dùng lớp .modal-overlay với z-index: 1000. Cùng z-index thì phần tử đứng sau trong HTML vẽ đè lên, nên hộp xác nhận mở ra bên dưới hộp Auto-Reboot. Lệnh chưa được gửi — nó đang chờ một nút Xác nhận mà người dùng không nhìn thấy.

Cách tránh: đóng hộp cấu hình rồi dùng nút Test trên dòng bảng Auto-Healing — hộp xác nhận của nút đó không bị che. Cách sửa: cho #confirm-modal z-index cao hơn, ví dụ 1100.

NGUY HIỂM · ĐỪNG BẤM LUNG TUNG KHI HỘP XÁC NHẬN VÔ HÌNH

Phím Enter hoặc một cú bấm đúng chỗ nút Xác nhận ẩn phía dưới có thể gửi lệnh cắt điện mà bạn không thấy hộp xác nhận nào.



15.3 Quy trình khi pool cạn IP

1. Khung THIẾT BỊ: box có sống không? Không sống thì kiểm điện, mạng WP3.

2. Client có online không? Không thì kiểm VM #101 và container wp3_rapid_proxy_client.

3. DCOM LIST: DCOM có mặt không? 0 DCOM kéo dài là USB lỏng hoặc hỏng.

4. DCOM có IP không? Không có: xem Lịch sử — tầng 1 có reboot không, tầng 2 có thành công không.

5. Có IP mà New vẫn 0: kiểm AUTO MOVE, kiểm 04 và 05.

6. Hỏi thẳng box qua relay curl http://172.16.30.21:6868/api/v1/dongles/all để đối chiếu với 01_INFO.



PHẦN IX

THỰC HÀNH





Chương 16 — Giáo án thực hành

Các bài chỉ ĐỌC được làm bất cứ lúc nào. Các bài có tác động ghi rõ điều kiện; làm trên hệ thống thật nên phải có người phụ trách đồng ý.

Bài 1 — Đọc trạng thái trong 2 phút

Mục tiêu. Trả lời năm câu hỏi của mục 12.1 chỉ bằng Dashboard.

1. Mở Dashboard, đọc ba đèn và ô sức khoẻ.

2. Bấm ô sức khoẻ, ghi lại trang cuộn tới đâu và vì sao.

3. Đọc khung THIẾT BỊ: trạng thái box, client, thời gian trả lời.

4. Đọc DCOM LIST và ba hàng đợi.

Tiêu chí đạt. Viết được một câu tóm tắt tình trạng pool, có lý do cho mọi cảnh báo.

Bài 2 — So Dashboard với REST

Mục tiêu. Hiểu Dashboard chỉ là một lớp hiển thị trên REST.

1. Gọi GET /api/redis/all, đếm số phần tử của mỗi hàng đợi.

2. So với năm ô thống kê.

3. Gọi GET /api/devices/monitor, so trường summary với nhãn khung THIẾT BỊ.

Tiêu chí đạt. Mọi con số khớp; giải thích được chênh lệch nếu có (dữ liệu thay đổi giữa hai lần đọc).

Bài 3 — Đọc một chu kỳ tự chữa

Mục tiêu. Nhận ra tầng 1 và tầng 2 trong nhật ký.

1. Tab Lịch sử, 24 giờ, lọc workpool của box.

2. Tìm một chuỗi fail_count 1 tới 5.

3. Tìm cặp auto_healing_drain_start và auto_healing_power_cycle ngay sau.

4. Đọc trường success.

Tiêu chí đạt. Tính được khoảng cách giữa hai lần reboot DCOM và độ dài một chu kỳ Auto-Healing.

Bài 4 — Kích thước pool nhà mạng

Mục tiêu. Ước lượng pool IP của một trạm.

1. Tab IP Stats, workpool WP2, 90 ngày.

2. Đọc Unique, Trùng lặp, tỷ lệ.

3. Phóng to đoạn đường Unique bắt đầu nằm ngang.

4. Mở bảng 100 IP trùng nhiều nhất, sắp theo Trung bình.

Tiêu chí đạt. Nêu được số IP của pool và bao lâu một IP thường quay lại.

Bài 5 — Lấy và trả một IP bằng move-ip

Mục tiêu. Thực hiện đúng quy trình n8n bằng curl.

LƯU Ý · BÀI CÓ TÁC ĐỘNG

Chỉ làm khi 04_USING rỗng và đã báo người phụ trách.



1. Chắc chắn 03_NEW có ít nhất một IP và không workflow nào đang chạy.

2. Gọi move-ip 03 -> 04.

3. Quan sát IP sang bảng 04 với cột Time bắt đầu từ 0.

4. Gọi move-ip 04 -> 05.

5. Quan sát IP biến mất khỏi 05, DCOM hiện Getting new IP..., IP mới vào 03.

Tiêu chí đạt. Vòng đời khép kín, Lịch sử có đủ ip_using, ip_done, dcom_changeip, ip_new.

Bài 6 — Cố ý làm lệch cấu hình Auto-Healing

Mục tiêu. Thấy CONFIG_MISMATCH tận mắt, và khôi phục an toàn.

LƯU Ý · BÀI CÓ TÁC ĐỘNG

Chỉ làm khi 04_USING rỗng và đã báo người phụ trách.



1. Sao lưu cấu hình theo mục 13.2.

2. Sửa WorkPool của box thành một workpool khác, Lưu.

3. Quan sát cột Trạng thái hiện Workpool không khớp.

4. Trả WorkPool về đúng, Lưu, quan sát trở lại Bình thường.

5. So cấu hình với bản sao lưu.

Tiêu chí đạt. Cấu hình cuối trùng từng byte với bản sao lưu. Lần thử thật ngày 11/09/2026 đã làm đúng như vậy.

Bài 7 — Tìm client ma

Mục tiêu. Hiểu vì sao client ở site cũ nguy hiểm.

1. Đọc Phụ lục D.

2. Trong khung THIẾT BỊ, tìm cột Trạng thái của từng client.

3. Giải thích điều kiện để một client bị gọi là ma trong device_monitor.py.

Tiêu chí đạt. Nêu được hai lý do: box không có trong 01_INFO của workpool đó, hoặc client báo sai IP box.



Phụ lục A — Lệnh đổi IP thiếu một chữ e (14/08/2026)

Bước

Nội dung

Triệu chứng

Không có — hệ thống vẫn chạy. Lỗi được phát hiện khi đọc mã.

Sự thật

Lệnh đổi IP được viết là changIpRequest và changIpAllDcomRequest ở mọi bên gửi lẫn nhận.

Vì sao nguy hiểm

Người viết script mới gõ đúng chính tả changeIpRequest thì lệnh bị bỏ qua lặng lẽ.

Cách sửa

Mọi bên GỬI phát chính tả mới. Mọi bên NHẬN (B12, client) chấp nhận cả hai qua tuple CHANGE_IP_REQUEST_TYPES và CHANGE_IP_ALL_REQUEST_TYPES. Đã kiểm cả ba đường: lệnh tay chính tả mới, lệnh tay chính tả cũ, và đường tự động DONE -> đổi IP.

Bài học

Khi đổi tên một thông điệp giữa nhiều máy, bên nhận phải chấp nhận cả tên cũ cho tới khi chắc mọi bên gửi đã cập nhật.



Phụ lục B — Một IP nằm ở hai hàng đợi (14/08/2026)

Bước

Nội dung

Triệu chứng

Dashboard đếm sai; một IP đang được task dùng lại được cấp cho task khác.

Nghi ngờ ban đầu

Client quên xoá IP khỏi hàng đợi cũ.

Nguyên nhân thật

Tranh chấp thời gian. Workflow n8n Lv1RQyThF9U9cT8Q làm đúng trình tự pop 03 -> xử lý -> push 04, nhưng giữa pop và push IP không thuộc hàng đợi nào. ScanIpProxy quét mỗi giây, gặp đúng khoảng đó thì coi là IP mới và thêm lại vào 03.

Dấu vết

Bản ở 04 mang enteredNewAt cũ (n8n push nguyên phần tử đã pop), bản ở 03 là bản mới tạo.

Cách sửa 1 — dọn hậu quả

_enforce_single_state chạy đầu mỗi vòng quét: trạng thái tiến xa hơn thắng, ghi ip_dedup.

Cách sửa 2 — đóng cửa sổ

Client chuyển trạng thái bằng một lời gọi POST /api/redis/move-ip. Đã sửa cho Lv1RQyThF9U9cT8Q cùng ngày.

Bài học

Dọn hậu quả không thay được việc đóng cửa sổ tranh chấp. ip_dedup xuất hiện đều đặn là dấu hiệu vẫn còn client dùng pop rồi push.



Phụ lục C — Auto-Healing chưa từng chạy một lần (15/08 – 20/08/2026)

Bước

Nội dung

Triệu chứng

DCOM mất IP hàng giờ, dcom_reboot bắn 718 lần trong 24 giờ, mà Auto-Healing vẫn xanh Hoạt động bình thường và fail_counts luôn rỗng.

Nguyên nhân

Bộ đếm lỗi có khoá WP2:eth1 (workpool:DCOM), còn Auto-Healing lọc bằng if 'DELL_PROXY_01' in k. Biểu thức đó sai vĩnh viễn — không bao giờ vào DRAINING.

Cách sửa

Khoá đổi thành <workpool>:<tên box>:<DCOM>. Mọi chỗ phân tích khoá vẫn đúng vì chỉ dùng phần đầu và phần cuối. Test chặn hồi quy TestDeviceKeyCarriesProxyName.

Kiểm chứng thật

Lỗi đếm 1 tới 5 trong khoảng 6 phút -> DRAINING -> Tuya thành công sau 7 giây -> box khởi động lại khoảng 80 giây -> DCOM lấy lại IP 103.249.23.151.

Sửa kèm

Cấu hình trỏ tới tên box không tồn tại nay hiện CONFIG_MISMATCH và ghi log lỗi 5 phút một lần, thay vì im lặng báo bình thường.

Bài học

Một cơ chế an toàn chưa từng kích hoạt thì chưa được kiểm chứng. Trạng thái “bình thường” phải chứng minh được là đang quan sát đúng đối tượng.



Phụ lục D — Dời box từ WP2 sang WP3 và client ma (11/09 – 18/09/2026)

Bước

Nội dung

Sự kiện

Box DELL_PROXY_01 dời từ WP2 172.16.20.202 sang WP3 172.16.30.201. Client chuyển từ Raspberry Pi3 sang VM #101 172.16.30.21 trên pve-001.

Vấn đề 1

Client trên Pi3 vẫn chạy, vẫn gửi nhịp tim mang tên box DELL_PROXY_01 từ WP2 — một client ma.

Vấn đề 2

Auto-Healing lọc bộ đếm lỗi bằng phép in trên chuỗi: lỗi DCOM báo từ WP2 cũng đủ cắt điện box ở WP3 (cùng ổ Tuya), và DELL_PROXY_01 còn khớp nhầm DELL_PROXY_010.

Cách sửa

_key_belongs_to_box và _item_belongs_to_box: so đúng tên và, nếu cấu hình có workpool, đúng workpool. Cấu hình lệch workpool thì báo Workpool không khớp thay vì cắt điện. Thêm 7 test TestAutoHealingBoxMatching (tổng 47 test).

Kết thúc

Ngày 18/09/2026 client trên Pi3 được dừng hẳn. Khung THIẾT BỊ mới nhận diện client ma tự động.

Bài học

Dời thiết bị là bốn việc đi cùng nhau: .env client, WorkPool Auto-Healing, Web UI mapping, tắt client cũ.



Phụ lục E — Những bẫy trên máy HP1

Bẫy

Đúng là

Bật B14 lotus_rapid_proxy_client trên HP1

Không tồn tại trên HP1 và không nên bật: nó không tới được box, lại đăng ký thêm một client mang tên DELL_PROXY_01. B12 chọn client có nhịp tim mới nhất nên lệnh sẽ nhảy qua lại

grep -rn từ gốc repo tìm mã B30

grep trong shell là ugrep --ignore-files, bỏ qua file gitignored — và toàn bộ mã IP_Pool nằm trong Application/ bị gitignore. Dùng command grep -rn

Tin chú thích topic trong B12

Dòng 24–25 RapidProxyHost_Config.py ghi ngược chiều

Nghĩ B30 nói với Redis qua mạng Docker

Đi qua wp1.hp1:20270 (cổng host). DNS LAN hỏng là IP_Pool dừng

Đồng bộ mã B30 sang máy khác bằng git kho chính

Mã gitignored; phải scp rồi so sha256



Phụ lục F — Gói Tuya IoT Core hết hạn (phát hiện 18/09/2026)

Bước

Nội dung

Triệu chứng

Lịch sử đầy auto_healing_power_cycle với success: false, khoảng 192 lần mỗi ngày.

Chẩn đoán

Chạy lại lời gọi WCM: HTTP 500. Nội dung lỗi từ Tuya Cloud: mã 913, IoT Core service subscription has expired.

Mốc thời gian

Thành công cuối 22/08/2026. Thất bại đầu tiên 11/09/2026. 1.303 lần thất bại tới 18/09/2026.

Vì sao không ai thấy

Thất bại vẫn đưa box vào COOLDOWN; Dashboard hiện Cooldown như một chu kỳ tự chữa bình thường. Không có cảnh báo riêng khi lệnh Tuya lỗi.

Việc cần làm

Gia hạn IoT Core trên iot.tuya.com. Cân nhắc: không vào COOLDOWN khi lệnh lỗi, hoặc gửi webhook cảnh báo riêng cho success: false.

Bài học

Một phụ thuộc trả phí bên ngoài hết hạn trông y hệt một lỗi phần cứng. Mọi lời gọi ra ngoài cần một cảnh báo khi thất bại liên tiếp.



Phụ lục G — Khung giám sát thiết bị ra đời thế nào (18/09/2026)

Trước ngày 18/09/2026 Dashboard chỉ hiện những gì client kể lại qua Redis. Box chết mà client còn sống thì trông như DCOM mất IP; client chết thì chỉ thấy cờ offline; client ma thì không phân biệt được với client thật. Khung mới tự đi hỏi ba nơi mỗi 10 giây và đối chiếu. Hai điều chỉnh sau khi chạy thật:

Phụ lục H — Bảng API

Tất cả dưới gốc http://<máy chủ>:20300. Không endpoint nào đòi xác thực. Trang /docs liệt kê kèm lược đồ dữ liệu.

Phương thức

Đường dẫn

Công dụng

GET

/health

Redis + MQTT + Host

GET

/api/devices/monitor

Bản chụp giám sát thiết bị

GET

/api/redis/status

Kết nối Redis

GET

/api/redis/info · client-alive

01_INFO · 02_ALIVE

GET

/api/redis/new-ip · using-ip · done-ip

Ba hàng đợi

GET

/api/redis/all

Mọi thứ Dashboard cần trong một lần

DELETE

/api/redis/clear/{key_alias}

Xoá một khoá

POST

/api/redis/move-ip

Chuyển IP giữa hàng đợi — cách đúng cho client

GET, POST

/api/redis/config/using-timeout

Timeout USING

GET, POST

/api/redis/config/auto-move

Ba công tắc AUTO MOVE

GET, POST

/api/redis/config/auto-reboot

Cấu hình Auto-Healing

DELETE

/api/redis/config/auto-reboot/{proxy_name}

Xoá cấu hình một box

GET

/api/redis/state/auto-reboot

Trạng thái sống Auto-Healing

GET, POST

/api/redis/config/web-ui-mapping

Web UI mapping

DELETE

/api/redis/config/web-ui-mapping/{proxy_name}

Xoá một mapping

POST

/api/redis/action/tuya-power

Bật hoặc tắt ổ Tuya — cắt điện thật

POST

/api/redis/action/tuya-power-cycle

Tắt, nghỉ, bật — cắt điện thật

GET

/api/mqtt/status

Kết nối MQTT của Dashboard

POST

/api/mqtt/change-ip · change-ip-all

Đổi IP một / mọi DCOM

POST

/api/mqtt/reboot-dcom · reboot-all

Reboot một / mọi DCOM

POST

/api/mqtt/request-info

Yêu cầu client gửi lại danh sách DCOM

POST

/api/mqtt/clear-client-alive · clear-proxy-info

Xoá bộ nhớ của B12

GET

/api/audit/events · summary · reboot-ranking

Lịch sử

POST

/api/audit/clear

Xoá audit.db

GET

/api/ip-stats/chart · workpools · networks · pool-info · top-duplicates

IP Stats

POST

/api/ip-stats/clear

Xoá ip_stats.db

WS

/ws

Đẩy full_update mỗi 2 giây

WS

/ws/verify

Lệnh move_ip, refresh của tab Verify



Phụ lục I — Thuật ngữ

Thuật ngữ

Nghĩa trong tài liệu này

DCOM

USB 4G có SIM; mỗi DCOM một public IP

Box / DELL_PROXY

Máy DELL cắm các DCOM, chạy phần mềm OBC Proxy cổng 6868

Client

RapidProxyClient — nói với box trong LAN, kể lại qua MQTT

Relay

nginx trên máy client chuyển tiếp trang quản trị box ra cổng 6868 của máy client

Workpool (WP)

Địa điểm: WP1, WP2, WP3

Hàng đợi 03/04/05

Ba LIST Redis NEW, USING, DONE

move-ip

Endpoint chuyển một IP giữa hai hàng đợi trong một lời gọi

Tự chữa tầng 1

Reboot một DCOM không có IP

Auto-Healing (tầng 2)

Cắt điện cả box qua ổ Tuya

Drain / xả

Đẩy IP của box khỏi 03 và chờ 04 về 0 trước khi cắt điện

Cooldown

Thời gian chờ sau khi cắt điện

CONFIG_MISMATCH

Cấu hình Auto-Healing không khớp box nào đang online

Client ma

Client còn nhịp tim nhưng báo box không có trong 01_INFO của workpool đó

WCM (B33)

Máy chủ trung gian gọi Tuya Cloud

HP1

Máy 172.16.10.220 chạy Docker Compose production



Phụ lục J — Nguồn và cách dựng tài liệu

Nguồn

Dùng cho

App/ScanIpProxy.py

Vòng quét, bất biến, Auto-Healing, tự chữa

WebDashboard/WebDashboardServer.py, routers/*, services/*

API, WebSocket, AUTO MOVE, giám sát thiết bị

WebDashboard/static/index.html, js/app.js, css/style.css

Mọi phần tử giao diện, ngưỡng màu, hộp thoại

docker-compose.yml (DCP_PRODUCTION)

Biến môi trường, cổng, volume

WebDashboard/data/audit.db (đọc chế độ chỉ đọc)

Số liệu Chương 14 và Phụ lục F

Tài liệu nội bộ ip-pool-zone2.md

Số đo 14/08, 20/08, 11/09/2026 và Phụ lục A–E

Ảnh chụp qua B35 ngày 18/09/2026

38 ảnh gốc; lệnh ghi bị chặn trong trình duyệt khi chụp nên không thao tác nào tới hệ thống thật



Tài liệu dựng bằng bộ mã docs/tools/docgen theo quy trình UID_04: chụp bằng Chrome điều khiển từ xa kèm toạ độ từng phần tử, đánh số bằng vòng tròn chỉ viền, sơ đồ vẽ bằng matplotlib, ghép bằng python-docx, xuất PDF bằng LibreOffice. Token WCM được che trên mọi ảnh chụp.