← Hướng dẫn sử dụng · tài liệu kỹ thuật, không chính thức; thông tin cá nhân đã được ẩn
9698 Photobooth v3.5.0 — Khảo sát giao diện, chức năng, API và kiến trúc
Nguồn: thao tác trực tiếp trên app đang chạy + đọc mã đã bundle trong app.asar (chỉ đọc) + log trong %APPDATA%\9698-photobooth.
1. Công nghệ
- Electron (main / preload / renderer), React 18 + react-router, antd 5, lucide-react, đóng gói bằng electron-vite, cài bằng Inno/NSIS, tự cập nhật qua
electron-updater(providergeneric,https://app.9698media.com/app/, kênhlatest). - Máy ảnh: webcam (getUserMedia) hoặc Canon EDSDK (
napi-canon-cameras, native). Cổng serial (serialport) cho thiết bị ngoài. - AI: OpenAI
gpt-image-2(key của khách, gọi trực tiếp từ máy), MediaPipe tasks-vision + onnxruntime-node (xử lý ảnh cục bộ, ảnh thẻ/tách nền),mp4-muxer(video/timelapse),qrcode. - In:
webContents.print+getPrintersAsynccủa Electron (máy in chính/phụ, ICC, in ngang). - Dữ liệu cục bộ: JSON trong
%APPDATA%\9698-photobooth\data, ảnh trongDocuments\PhotoboothPhotos, hàng đợi uploadupload-queue.json, logdongbo-log.txt,luong-log.txt,canon-runtime-log.txt. 192 kênh IPC (ipcMain.handle, ví dụconfig:get,config:set,session:list).
2. Giao diện đã chụp (kiểm tra thực tế)
| Màn hình | Nội dung |
|---|---|
| Sự kiện (trang chủ) | Danh sách thẻ sự kiện, tìm kiếm, "Tạo Sự Kiện", "Đồng bộ ngay"; thẻ có loại Đám cưới / Bản quán, số ảnh, số in, doanh thu, nút Cài đặt / Chạy / Sửa ảnh đại diện, QR album cả tiệc (anh.9698media.com/e/<eventId>) |
| Báo cáo (Tổng quan) | Lọc Ngày/Tháng/Quý/Năm/Tự chọn; tổng phiên, tổng giấy in, doanh thu, biểu đồ; phiên bản + "Kiểm tra bản mới"; nơi lưu dữ liệu; "Mở thư mục lưu" |
| Cấu hình → Màn hình | 3 tab: chào / chụp / chia sẻ, có preview trực tiếp + toàn màn hình; nền, nút Chạm để bắt đầu, Xem lại, Cài đặt, lật ảnh, kiểu hiển thị, xoay camera, tự động chụp… |
| Cấu hình → Chủ đề | Nền app, ảnh ướm vào khung (thư mục AnhPreview) |
| Cấu hình → Máy ảnh | Webcam/Canon, chọn webcam, lật, FPS khung hình |
| Cấu hình → Smartbooth | Nhận in từ điện thoại khách (quét QR, chụp bằng máy khách, in tại booth), mã booth |
| Cấu hình → Âm thanh | Âm theo luồng Photobooth / Ảnh thẻ / Ảnh AI, mỗi bước tải file riêng |
| Cấu hình → Máy in | Máy in chính/phụ, số bản mặc định, in ngang, tự in, ICC, giới hạn lượt in (100–200/không giới hạn), đặt lại bộ đếm |
| Cấu hình → Thanh toán | Bật thu tiền (chỉ sự kiện Bản quán), bảng giá theo khổ giấy (2×6, 4×6…), tiền mặt / QR / voucher, SePay |
| Cấu hình → Thêm khung | Trình thiết kế template (canvas, lớp, ảnh từ booth, QR, undo/redo, zoom) |
| Cấu hình → Bộ lọc | 9 bộ lọc (Gốc, Ngọt ngào, Vintage…), LUT .cube, chỉnh màu/da/sticker, chụp thử, in thử |
| Cấu hình → Mã API | Hướng dẫn lấy và nhập key OpenAI |
| Hồ sơ | Tài khoản ([email tài khoản]), gói "Dùng thử" còn 25 giờ, mã máy, đổi mật khẩu, đăng xuất, mua gói 1 năm 2.000.000đ / vĩnh viễn, add-on Ảnh thẻ và Photobooth AI |
| Ảnh thẻ / Ảnh AI | Bị khoá với tài khoản hiện tại (hộp thoại mời mua) |
| Chế độ chạy (kiosk) | Màn chào → Chọn khung ảnh → Chụp → Chia sẻ (xem mã QR, in, chụp lại). Thoát bằng Ctrl+Shift+A |
3. Kết quả test
| Chức năng | Kết quả |
|---|---|
| Mở tất cả 10 tab Cấu hình, Báo cáo, Hồ sơ | Chạy tốt, không lỗi |
| Ảnh thẻ, Ảnh AI | Hiện hộp thoại cần mua (đúng thiết kế) |
| Chạy sự kiện → màn chào → chạm | Vào "Chọn khung ảnh", trống vì sự kiện chưa có khung ("Vào Cài đặt → Thêm khung") |
| Chụp / in / thanh toán / chia sẻ | Chưa test được: cần có khung ảnh, webcam và máy in; không tự tạo khung hay mua gói để tránh thay đổi dữ liệu và phát sinh chi phí |
| Đồng bộ ngay | Bấm một lần nhưng log chưa ghi dòng mới. Lần đồng bộ trước đó (08:47Z) ghi "ĐẨY OK"; chưa xác nhận lần bấm này |
| Mua gói, đổi mật khẩu, đăng xuất | Không bấm (có tác dụng thật) |
Test bổ sung: đã tạo mẫu khung (1 ô "Ảnh từ booth") ở Thêm khung, lưu thành công ("Đã lưu mẫu", tự lưu sau mỗi thay đổi). Chạy kiosk → Chọn khung ảnh hiện khung mới (có ảnh minh hoạ) → Tiếp tục → màn chụp báo "Không mở được camera" (webcam Logitech C922 đã cài driver nhưng đang không cắm, máy không có camera khác). Nút Thử lại vẫn lỗi như cũ; nút Thoát/Quay lại hoạt động. Vì vậy đếm ngược, chụp, chọn bộ lọc, màn chia sẻ/QR, in và thanh toán vẫn chưa test được; cần cắm webcam. Máy in có sẵn: EPSON WF-C5290BA, EPSON L8050, Microsoft Print to PDF.
Lưu ý nhỏ: nút "Chạy" ở dock Hồ sơ cần bấm 2 lần mới vào (lần đầu chỉ hiện tooltip) — có thể là lỗi UX do hộp thoại "Ảnh thẻ" vừa đóng.
3.1 Test đầy đủ với Canon EOS 70D + DNP DS-RX1 (đã chạy thật)
| Bước | Kết quả |
|---|---|
| Nhận Canon | App tự đổi sang "Canon DSLR (EDSDK)", live view hiển thị, đọc thông số (AE Manual, 4800K, ISO 4000, 1/100, f/5.6), có "Chụp thử", "Chẩn đoán chi tiết" |
| Chọn máy in DS-RX1 + "In thử" | Báo "Đã gửi in thử"; in-log.txt: XONG, máy=DS-RX1 |
| Kiosk: chào → chọn khung → chụp | Live view Canon, đếm ngược 3, tự chụp, ảnh gốc may-anh-goc\IMG_0001.JPG (7,1 MB) |
| Màn chia sẻ | Hiện 9 bộ lọc, ảnh ghép vào khung, QR lấy ảnh, số tấm in, "In ảnh", "Chụp lại tất cả", "Hoàn tất" |
| Bộ lọc | Chọn "Ngọt ngào" áp dụng, nhãn "Đã in – không đổi được" sau khi in |
| In ảnh | luong-log: IN so_to=1, cách=miễn phí; in-log: XONG, ban=1, DS-RX1; hàng đợi Windows trống sau khi in |
| Timelapse | Tự dựng _tl\*.mp4 (360 KB, 52 khung) |
| Đồng bộ sau phiên | dongbo-log: ĐẨY OK (cập nhật=1), album đẩy 1 mã, "ĐẨY KHUNG VỪA LƯU OK — 1 khung" |
| Hàng đợi upload | Lúc kiểm tra còn 4 job chờ (f/<mã>/1.jpg, anh.jpg, tl.mp4; trang p/<mã> đã XONG 13 KB / 1,3 s) — chưa xác nhận đã đẩy hết lên R2 |
Cấu trúc file một phiên: <Sự kiện>\<mã>.jpg (ảnh in), _web\…-3600.jpg, _thumb\, _don\ (ảnh đơn), _tl\ (timelapse), may-anh-goc\ (RAW/JPG gốc từ Canon). Mã phiên 8 ký tự (vd <mã>) là khoá chung cho QR, f/<mã>/… và p/<mã> trên R2.
Nhật ký luồng cho thấy các trạng thái: TAM-VE → CHUP-XONG → DOI-ANH → VAO-SUA → DOI-LOC → GHEP-IN → IN → DON-LUOT (đáng dùng khi thiết kế máy trạng thái).
3.2 Test thu tiền, voucher, Smartbooth, QR (không in thêm)
| Chức năng | Kết quả |
|---|---|
| Link công khai | anh.9698media.com/e/<eventId> (album), /p/<mã> (trang ảnh), /f/<mã>/anh.jpg, 1.jpg, tl.mp4 đều trả 200 với đúng dung lượng (219.696 / 1.531.108 / 360.521 B) → hàng đợi upload đã đẩy xong lên R2 |
| Thu tiền | Chỉ bật được với loại sự kiện Bản quán (tôi đổi tạm rồi trả về Đám cưới). Bật thu tiền → chọn dịch vụ (Photobooth / Ảnh thẻ / Photobooth AI), thu lúc nào, bảng giá theo khổ (4×6: 25.000đ…) |
| Voucher | Bật Voucher → "Tạo thêm voucher" sinh 10 mã 6 số, mỗi mã 1 tờ, đồng bộ lên server (/api/voucher-tu-may). Mã sai (111111) báo "Mã không có trong danh sách"; mã đúng được chấp nhận, màn chia sẻ hiện "1 bản · đã thanh toán" |
| Luồng Bản quán | Chào → Chọn kích thước ảnh (4×6, 25.000đ/tấm) → Chọn khung → Thanh toán (Tiền mặt / Chuyển khoản / Nhập mã) → chụp 8 ảnh → chọn ảnh + bộ lọc → chia sẻ (video kỷ niệm, ảnh sẽ in, QR) → Xong. Không in, hàng đợi in trống |
| Chuyển khoản / SePay | Không test: cần nhập số tài khoản và token SePay thật, tôi không nhập thông tin tài chính |
| Tiền mặt | Không bấm (chờ người vận hành xác nhận) |
| Smartbooth | Bật + mã booth tạo link khách https://tk.9698media.com/b/<mã> và mã QR (tải/copy được); trang trả 200 ("9698 Smartbooth"). Mã phải ≥ 2 ký tự, báo lỗi khi quá ngắn. Chưa test chụp từ điện thoại thật (cần máy khách) |
| Lưu ý lỗi | Ô nhập "Mã booth" mất focus sau mỗi ký tự (gõ tự động chỉ nhận ký tự đầu/cuối) → nên sửa debounce/giữ focus |
Đã khôi phục: tắt thu tiền, tắt Smartbooth, loại sự kiện về Đám cưới. Còn lại: 10 voucher thử chưa dùng (mỗi mã 1 tờ) trong cài đặt thanh toán, mã booth "1t" trên server, phiên Bản quán 8 ảnh trong thư mục dữ liệu.
Chưa test: quét QR bằng điện thoại thật, chuyển khoản/SePay, Smartbooth từ điện thoại, Ảnh thẻ và Ảnh AI (khoá theo gói).
3.3 Màn hình/chức năng bổ sung (rà soát lần 2)
Danh sách màn trong mã nguồn (20 chunk) đều đã được xem, trừ các màn khoá theo gói: AnhTheSettings, ManAnhThe, AiSettings, ManPhotoboothAi, và DangNhap (đăng nhập, không test vì đang đăng nhập). Đã xem thêm: hộp thoại Tạo sự kiện mới (5 bước: tên + loại, ảnh bìa, frame, 300/600 dpi, giới hạn lượt in), Sửa ảnh đại diện, trang Xem lại ảnh (xoá từng ảnh, mở thư mục), trang Chọn sự kiện, cài đặt PIN quản trị / PIN doanh thu / Tên booth / Yêu cầu mật khẩu khi thu nhỏ, và web event.9698media.com ("Tạo sự kiện & khung ảnh", cổng web đồng bộ với app, cần đăng nhập, chưa test).
Không có file hướng dẫn chính thức; đã lập huong-dan-su-dung.md.
3.4 Web quản lý event.9698media.com ("9698 Event — Tạo sự kiện & khung ảnh")
Web dạng SPA mobile-first (thanh dưới 4 tab, nút tròn "+"), đã đăng nhập bằng [email tài khoản]. Kiểm tra bằng thao tác xem và mở form; không lưu dữ liệu nào (thử bấm Lưu gói chụp thì hệ thống chặn thao tác ghi lên dữ liệu dùng chung, tôi không làm cách khác để vượt qua).
| Màn hình | Nội dung |
|---|---|
Tổng quan (/) | Lọc Hôm nay/7 ngày qua/Tháng này/Quý này/Năm nay/Tất cả/Tự chọn; 4 số: tổng sự kiện (2), đang hoạt động (0), lượt khách (1), doanh thu (0đ); ô tìm kiếm; 3 danh sách: Bản quán, Đám cưới chưa triển khai, Đám cưới đã triển khai. Thẻ sự kiện: ảnh bìa, số ảnh, số tấm đã in, số khung, máy được gán; nút Quản lý khung, Link ảnh, Xoá (không bấm Xoá) |
| Sửa sự kiện (Quản lý khung) | Loại sự kiện, tên (dùng làm {couple} trên khung), ngày chụp ({date}), ảnh bìa, 300/600 dpi, giới hạn lượt in, danh sách máy thấy sự kiện (chỉ xem; máy tự nhận bản mới trong 30 giây), Khung ảnh: "Chọn file khung (nhiều file)", PNG khoét ô trong suốt hoặc tô ruột màu xanh lá, web tự cắt ruột và xếp lượt chụp. Sự kiện tạo ở máy booth thì ảnh/phiên/doanh thu nằm ở máy, web chỉ xem và thêm khung |
| Nút "+" | "Tạo sự kiện cho khách nào?": chọn đơn trong sổ khách (điền sẵn tên, ngày) hoặc "Tạo sự kiện rời, không gắn đơn" |
Khách (/danh-sach-khach) | Sổ đơn khách: Danh sách / Lịch tháng / Bảng giá gói / "+ Lên đơn"; lọc tháng, tìm tên/sđt/địa điểm; tổng tiền, đã thu, còn phải thu. Lên đơn mới: tên khách, sđt, ngày cưới, gói chụp, giờ bắt đầu/kết thúc (tự tính theo số giờ của gói), địa điểm, tổng tiền, đã cọc, thanh toán (Chưa cọc / Đã cọc / Đã thanh toán), ghi chú |
| Lịch tháng | Lưới tháng 11/2026, bấm ô trống để lên đơn, bấm đơn để sửa, đánh dấu "Hôm nay" |
Bảng giá gói (/goi-chup) | "+ Thêm gói": tên gói (vd PRO), giá, số giờ chụp (dùng dấu phẩy cho số lẻ), mô tả. Đổi giá gói sau này không đổi các đơn đã chốt. Hiện 0 gói |
Voucher (/voucher) | "Chép mã", "+ Tạo mã", xuất .txt; mỗi mã 1 lượt chụp miễn phí, máy nhận qua đồng bộ. Hiện 0 mã, xác nhận 10 voucher thử đã xoá khỏi server |
| Tài khoản | Tên, email, "Đăng xuất" (không bấm) |
Test ghi dữ liệu (được bạn cho phép, đã dọn sạch):
| Chức năng | Kết quả |
|---|---|
Thêm gói (TEST Claude, 100.000đ, 2 giờ) | OK, toast "Đã lưu gói", hiện trong bảng giá |
Lên đơn (tên giả, sđt giả 0900000000, 15/11/2026, 09:00) | OK, "Đã thêm đơn". Chọn gói thì giờ kết thúc tự tính (11:00) và tổng tiền tự điền (100.000đ); đơn hiện đúng trên Lịch tháng ở ngày 15 và trong danh sách (tổng/đã thu/còn phải thu). Bấm đơn trên lịch mở lại form sửa (không có nút xoá ở đó) |
| Xoá đơn | Từ danh sách (nút "Xoá" cuối hàng) → hộp thoại "Xoá đơn?" → OK, về 0 đơn |
| Xoá gói | Nút "Xoá" trong bảng giá, không hỏi xác nhận → về 0 gói |
Còn lại chưa test: nút "+ Tạo sự kiện" gắn đơn, upload khung, tạo/xuất voucher trên web, "Đặt tên" máy, "+ Thêm mẫu" ảnh AI.
Hai màn bổ sung chỉ hiện ở menu bản desktop (bản mobile không có nút, truy cập qua /may và /prompt):
- Danh sách máy (
/may): liệt kê máy đã đăng nhập (Máy 1 · mãXXXX-XXXX-XXXX· [tên máy]), nút Đặt tên để đặt tên dễ nhớ; tên hiện ở chỗ chọn máy khi tạo sự kiện. - Mẫu Ảnh AI (
/prompt, 22 mẫu): thư viện prompt cho Photobooth AI (vd "Báo Phú Quốc", "Võ Thuật Cute", "áo dài truyền thống Việt Nam"); mẫu chung của 9698 chỉ admin sửa/xoá; "+ Thêm mẫu" để thêm mẫu riêng. Tương ứng/api/promptcủa app (đã tải "22 mẫu AI" khi đồng bộ).
API của web (cùng Bearer/cookie đăng nhập): POST /api/dang-nhap, GET /api/me, /api/may, /api/su-kien, /api/anh-khung?key=, /api/dat-lich?thang=&tim=, /api/goi-chup, /api/voucher; POST /cdn-cgi/rum là đo hiệu năng của Cloudflare. Web và app dùng chung backend event.9698media.com (mục 4.2).
3.5 Màn hình và link chưa vào được / chưa kiểm tra (rà soát lần 3)
| Mục | Tình trạng |
|---|---|
App: Ảnh thẻ, Ảnh AI (và cài đặt AnhTheSettings, AiSettings, màn khách ManAnhThe, ManPhotoboothAi) | Không vào được: tài khoản chưa mua add-on, app hiện hộp thoại mời mua. Cần mua (1.000.000đ mỗi add-on) mới xem được |
App: màn Đăng nhập/Đăng ký (DangNhap), đổi mật khẩu, quên mật khẩu | Không test: app đang đăng nhập, không muốn đăng xuất/đổi mật khẩu |
| App: chuyển khoản/SePay, Tiền mặt, tạo QR ngân hàng | Không test (cần số tài khoản và token thật) |
| App: cập nhật phiên bản | Chưa test, https://app.9698media.com/app/ và latest.yml đều trả 200 |
https://tk.9698media.com/ ("Quản trị khách — 9698 Photobooth") | Có trang nhưng cần mật khẩu quản trị (sổ khách của nhà cung cấp), không có mật khẩu nên không vào được; không nhập mật khẩu hộ |
https://9698media.com, www.9698media.com | Không phân giải được tên miền (DNS lỗi), không có trang web giới thiệu/hướng dẫn |
https://anh.9698.vn (tên miền ngắn dùng cho QR trong mã nguồn) | Không phân giải được (DNS lỗi) — QR hiện tại dùng anh.9698media.com, nên tên miền ngắn có vẻ chưa được cấu hình |
https://anh.9698media.com/ và https://app.9698media.com/ (gốc) | Trả 404 là bình thường (chỉ phục vụ /e/, /p/, /f/ và /app/) |
Web event.9698media.com: /tao, /sua, /dat-lich, /may, /prompt, /voucher, /goi-chup, /danh-sach-khach | Vào được. /tao là form tạo sự kiện (loại, tên, ngày, ảnh bìa, dpi, giới hạn, gán máy một lần, lưu xong là khoá, chọn khung). Mọi đường dẫn lạ vẫn trả trang SPA 200 (không có trang 404 riêng). /api/* trả 401 nếu chưa đăng nhập |
| Web: tải khung, tạo voucher, đặt tên máy, thêm mẫu AI | Chưa test thao tác ghi |
my.sepay.vn | Trang đăng nhập SePay mở được (không đăng nhập) |
4. API gọi lên server
Ba host chính, đều HTTPS, Bearer token (Authorization: Bearer <the>), JSON; helper goi() timeout 12 s mặc định, trả về {ma: httpStatus, ...body}.
4.1 tk.9698media.com — tài khoản, giấy phép, in từ xa (qua Cloudflare)
| Endpoint | Phương thức | Dùng cho |
|---|---|---|
/api/khoa-cong-khai | GET | Lấy khoá công khai để xác minh chữ ký giấy phép |
/api/kiem-may | POST {maMay} | Kiểm tra máy |
/api/goi?sanPham=photobooth | GET | Danh sách gói |
/api/phien-ban | GET | Phiên bản mới nhất |
/api/dang-ky, /api/xac-thuc, /api/gui-lai-ma | POST | Đăng ký, xác thực mã email, gửi lại mã |
/api/dang-nhap | POST | Đăng nhập, trả the (token) + giayPhep đã ký |
/api/giay-phep | POST | Làm mới giấy phép |
/api/sua-ho-so, /api/doi-mat-khau | POST | Hồ sơ, mật khẩu |
/api/mua, /api/gia-nang-cap, /api/mua-nang-cap, /api/kiem-tra-don, /api/huy-don | POST | Mua gói / add-on, hỏi giá, kiểm tra và huỷ đơn |
/api/in/dang-ky-booth, /api/in/hang-doi, /api/in/xong | POST | Smartbooth: đăng ký booth, lấy hàng đợi lệnh in từ điện thoại khách (poll), báo đã in |
/api/in/khung, /api/in/khung-anh?key= | POST | Đẩy manifest và ảnh khung cho trang khách |
/api/tai-anh?key=&ct=&cache= | POST (octet-stream) | Đẩy ảnh/video lên kho (Cloudflare R2) qua máy chủ, hàng đợi có retry |
Giấy phép là chuỗi đã ký, app kiểm tra bằng crypto.subtle.verify với khoá công khai (nên chạy offline được trong thời hạn); mã máy (maMay) sinh theo phần cứng (dạng XXXX-XXXX-XXXX).
4.2 event.9698media.com — web sự kiện, đồng bộ đa máy
| Endpoint | Phương thức | Dùng cho |
|---|---|---|
/api/dong-bo?tu=<ts>&maMay=&tenMay= (+chiDem=1) | GET | Kéo sự kiện, khung, mẫu AI thay đổi từ mốc tu (log: "tải về — 1 sự kiện, 0 khung, 22 mẫu AI") |
/api/su-kien-tu-may | POST | Đẩy sự kiện, khung, ngày từ máy lên ("ĐẨY OK … mới / cập nhật") |
/api/anh-khung | POST/GET | Tải/đẩy ảnh khung |
/api/anh-thieu | POST {keys} | Hỏi server ảnh nào còn thiếu |
/api/prompt, /api/prompt-xoa, /api/prompt-thu-tu | GET/POST | Quản lý prompt AI |
/api/voucher-tu-may, /api/voucher-xoa, /api/voucher-dung | POST | Đồng bộ, xoá, báo dùng voucher |
/api/album-ma | POST | Đẩy danh sách mã album khách |
4.3 Dịch vụ khác
anh.9698media.com(trỏ vào R2 qua Cloudflare):/f/<mã>/anh.jpg,/goc.jpg, video;/e/<eventId>là album cả tiệc; trang khách dò ảnh bằng HEAD +?v=<timestamp>để né cache 404 của Cloudflare (3 phút).anh.9698.vn: tên miền ngắn cho QR.my.sepay.vn/userapi/transactions/list: SePay, app tự đối soát chuyển khoản theo token của khách.api.openai.com/v1: sinh ảnh AI bằng key của khách (mô hìnhgpt-image-2).app.9698media.com/app/: kênh cập nhật.- Cục bộ
127.0.0.1:<cổng>: HTTP nội bộ phục vụ/khung-nho/…,/anh-uom/…cho renderer.
5. Kiến trúc để triển khai app tương tự
┌──────────────── Máy booth (Electron) ───────────────┐
│ Renderer (React): Admin / Kiosk / Designer │
│ │ contextBridge (preload) — 192 kênh IPC │
│ Main: config, license, sync, upload queue, in ấn, │
│ Canon SDK, SePay poll, OpenAI, local HTTP │
│ Lưu: JSON + file ảnh + upload-queue.json │
└───────┬───────────────┬────────────────┬────────────┘
│ │ │
tk.* (Auth/License event.* (Sync anh.* (R2 CDN)
/Billing/Print) sự kiện, voucher) ▲
│ │ │
└── Cloudflare Workers + D1/KV ──── R2 ◄── trang khách (/e/…, /f/…)
Nguyên tắc thiết kế nên giữ:
1. Offline-first: mọi thứ chạy từ dữ liệu cục bộ; server chỉ để tài khoản, giấy phép, đồng bộ, chia sẻ ảnh. Giấy phép ký bất đối xứng (Ed25519/ECDSA), máy tự xác minh, có hạn dùng thử.
2. Hàng đợi upload bền vững (upload-queue.json: jobs, sessions, albums, mã ảnh) với retry; ảnh đẩy lên qua /api/tai-anh vào R2, khách xem bằng mã ngắn (QR).
3. Đồng bộ theo mốc thời gian (tu=<ts>): kéo và đẩy delta, server trả số mới/cập nhật; ảnh khung đồng bộ riêng, hỏi "ảnh thiếu" trước khi đẩy.
4. Tách host theo vai trò (tài khoản/giấy phép, sự kiện, ảnh) để mở rộng và cache độc lập.
5. Tích hợp bên thứ ba bằng key của khách (OpenAI, SePay) để không gánh chi phí.
6. Smartbooth: máy booth poll /api/in/hang-doi, khách chụp bằng điện thoại và tải ảnh lên, booth tải về và in, báo /api/in/xong.
Stack đề xuất để dựng lại: Electron + electron-vite + React; backend Cloudflare Workers + D1 (tài khoản, đơn, sự kiện) + R2 (ảnh) + KV (voucher, hàng đợi in); thanh toán chuyển khoản VietQR + webhook/poll SePay; cập nhật bằng electron-updater trỏ vào thư mục tĩnh.
6. Việc chưa xác nhận
- Chưa bắt traffic thật (proxy/Fiddler); danh sách endpoint lấy từ mã nguồn bundle nên có thể thiếu endpoint gọi động.
- Chưa test chụp/in/thanh toán/voucher/Smartbooth/Canon do thiếu khung, thiết bị và gói mua.
- Schema chi tiết của request/response chưa đọc hết.