尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證製作者的介紹僅有上面的一句話。 更多詳情請閱讀儲存庫 README。 儲存庫 README ↗
正在載入完整 README。
檢視完整 README ↗Nền tảng web nội bộ cho bộ phận Vision: bộ chọn thiết bị theo bài toán, cẩm nang kỹ thuật song ngữ, và hệ thống phân quyền 5 cấp.
Đặc tả đầy đủ nằm ở CLAUDE.md — file đó là nguồn sự thật cho data model, quy ước đặt tên và phạm vi dự án.
| Thành phần | Lựa chọn |
|---|---|
| Frontend | Next.js 16 (App Router) + Tailwind CSS v4 |
| Backend / DB / Auth | Supabase (Postgres + Auth + Row Level Security) |
| i18n | next-intl — Việt (mặc định) / Anh, chọn qua cookie NEXT_LOCALE |
| Rich text | Tiptap |
| Hosting | Vercel |
Phân quyền được thực thi ở tầng database bằng Row Level Security, không phải ở giao diện. Việc ẩn/hiện nút trong React chỉ để trải nghiệm dễ chịu; nếu ai đó lấy anon key từ mã nguồn trang và gọi thẳng REST API thì RLS vẫn chặn.
Kiểm chứng điều này bất cứ lúc nào:
npm run probe:rls
npm install
supabase.com/dashboard → New project → chọn region Southeast Asia (Singapore).
cp .env.example .env.local
Điền hai giá trị lấy từ Project Settings → API:
NEXT_PUBLIC_SUPABASE_URL=https://<project-ref>.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=<anon / publishable key>
SUPABASE_SERVICE_ROLE_KEYbypass toàn bộ RLS. Chỉ điền khi thật sự cần cho tác vụ phía server, và tuyệt đối không đặt tiền tốNEXT_PUBLIC_cho nó — biến có tiền tố đó bị nhúng vào JavaScript gửi xuống trình duyệt.npm run check:envcó bẫy phát hiện nhầm lẫn này.
Kiểm tra SQL trước khi đụng vào database thật (chạy trên Postgres in-process, không cần Docker):
npm run test:db
Rồi áp lên Supabase — SQL Editor → dán từng file → Run, theo đúng thứ tự:
supabase/migrations/20260823000001_init_schema.sqlsupabase/migrations/20260824000001_selector_engine.sqlsupabase/seed.sqlWindows: đừng dùng
cliptừ Git Bash để copy file SQL.clip.exediễn giải byte theo codepage OEM chứ không phải UTF-8, làmĐo lường 2Dthành─Éo l╞░ß╗¥ng 2D. Dùng PowerShell:Get-Content -Raw -Encoding UTF8 "supabase\seed.sql" | Set-ClipboardHoặc mở file trong VS Code rồi Ctrl+A / Ctrl+C.
Chi tiết thêm ở supabase/README.md.
Không có đường tự đăng ký thành admin — đúng theo thiết kế. Đăng ký bình thường qua
/dang-ky, rồi chạy trong SQL Editor:
update public.profiles set role = 'admin' where email = '[email protected]';
Câu lệnh chạy được ở đây vì SQL Editor không có JWT (auth.uid() là NULL) nên trigger
enforce_profile_role_change cho qua. Cùng câu lệnh đó gọi từ trình duyệt bằng anon
key sẽ bị từ chối.
npm run dev
| Lệnh | Việc |
|---|---|
npm run dev | Chạy dev server |
npm run build | Build production (tự kiểm biến môi trường trước) |
npm run lint | ESLint |
npm run check:env | Kiểm biến môi trường |
npm run test:unit | Unit test engine chọn thiết bị, xử lý nội dung, nhất quán dữ liệu seed |
npm run test:pdf | Kiểm tra PDF xuất ra mã hoá đúng tiếng Việt |
npm run test:db | Áp migration + seed + test RLS trên Postgres in-process |
npm run test:db:mutate | Cố tình phá từng luật bảo mật, kiểm bộ test có bắt được không |
npm run probe:rls | Dò RLS qua REST API thật với vai khách |
npm run test:all | Chạy tất cả trừ probe:rls |
npm run import:rules:template | Sinh file Excel mẫu cho đội kỹ thuật điền bảng luật |
npm run import:rules -- <file> | Kiểm tra file Excel rồi chuyển thành SQL |
test:db:mutateMột bộ test bảo mật luôn báo pass thì vô dụng. Script này phá từng luật RLS một
(bỏ gating theo tier, bỏ trigger chặn nâng quyền, cho mọi tài khoản ghi bài viết…)
rồi chạy lại bộ test. Mọi lỗ hổng đều phải bị bắt. Dòng BỎ SÓT nghĩa là bộ test
có điểm mù cần bổ sung assert.
Chạy lại mỗi khi sửa policy trong migration.
src/
app/
admin/ Trang quản trị (chỉ role admin)
cam-nang/ Cẩm nang: danh sách + chi tiết
cong-cu-chon-thiet-bi/ Bộ chọn thiết bị
actions/ Server actions (auth, selector, admin)
lib/
auth.ts Đọc phiên + helper phân quyền cho UI
selector/ Engine rule-based (không chứa luật nào)
html.ts Làm sạch HTML bằng allowlist
supabase/ Client cho browser / server / proxy
messages/ Chuỗi dịch vi + en
supabase/
migrations/ Schema + RLS
seed.sql Dữ liệu tham chiếu + bảng luật + bài viết mẫu
tests/rls_smoke_test.sql Kiểm chứng RLS cho cả 4 vai trò
scripts/ Test và công cụ kiểm tra
Engine không chứa luật kỹ thuật nào. Toàn bộ luật nằm ở bảng selector_rules
trong database và sửa được qua /admin/luat-goi-y. Code chỉ làm hai việc: tính các
đại lượng suy ra theo công thức ở CLAUDE.md mục 4, và ghép kết quả từ những luật khớp
theo thứ tự ưu tiên.
Kết quả tính toán được đưa ngược vào ngữ cảnh đánh giá luật, nên luật viết được điều
kiện kiểu required_resolution_px > 5000 → line scan. Nhờ vậy ngưỡng chọn cảm biến
vẫn do admin sửa, còn công thức thì ở trong code.
Thêm bài toán mới = thêm dòng vào task_types kèm input_fields, không sửa code —
miễn là dùng lại các trường đã có trong catalog src/lib/selector/fields.ts. Bảy bài
toán hiện có (3 bài MVP + 3D, OCR/OCV, đọc mã vạch, dẫn hướng robot) đều được thêm
theo đúng cách này.
Đổi lại, gõ sai một tên trường sẽ không gây lỗi ở đâu cả — form lặng lẽ bỏ qua,
hoặc luật không bao giờ khớp. Vì vậy npm run test:unit có bộ kiểm tra đối chiếu dữ
liệu seed với catalog, bắt các trường hợp: trường lạ, trường không thuộc bài toán đó,
bài toán thiếu luật nền.
Đội kỹ thuật điền bảng luật trong Excel thay vì gõ tay từng dòng vào trang quản trị:
npm run import:rules:template # sinh file mẫu, gửi cho đội kỹ thuật
npm run import:rules -- bang-luat.xlsx # kiểm tra rồi chuyển thành SQL
File mẫu có sẵn sheet hướng dẫn liệt kê toàn bộ trường dùng được, cú pháp điều kiện, và cách engine ghép kết quả theo thứ tự ưu tiên.
Công cụ kiểm tra từng dòng trước khi sinh SQL: mã trùng, bài toán không tồn tại, JSON sai cú pháp, và quan trọng nhất — điều kiện tham chiếu tới trường không thuộc bài toán đó. Lỗi cuối nguy hiểm nhất vì luật vẫn lưu được nhưng không bao giờ khớp, không có thông báo nào. Danh sách bài toán đọc từ Supabase thật nên phản ánh cả những bài đội kỹ thuật thêm qua trang quản trị.
SQL sinh ra idempotent theo cột code: chạy lại thì cập nhật luật cũ chứ không nhân
bản, và luật đang có trong database mà không nằm trong file thì giữ nguyên.
git remote add origin <url-repo-cua-ban>
git push -u origin main
vercel.com/new → chọn repo. Next.js được nhận diện tự động, không cần chỉnh build command.
Project Settings → Environment Variables, đặt cho cả ba môi trường (Production, Preview, Development):
| Biến | Giá trị |
|---|---|
NEXT_PUBLIC_SUPABASE_URL | https://<project-ref>.supabase.co |
NEXT_PUBLIC_SUPABASE_ANON_KEY | anon / publishable key |
NEXT_PUBLIC_SITE_URL | https://<domain-cua-ban> |
NEXT_PUBLIC_SITE_URL quyết định link trong email xác nhận trỏ về đâu. Đặt sai thì
người dùng bấm link xong bị đưa về localhost.
Build sẽ dừng ngay với thông báo rõ ràng nếu thiếu biến — đó là việc của
npm run check:env, chạy tự động trước next build.
Authentication → URL Configuration:
https://<domain-cua-ban>https://<domain-cua-ban>/auth/callbackThiếu bước này thì xác nhận email và đăng nhập sẽ hỏng trên production.
NEXT_PUBLIC_SUPABASE_URL=https://<ref>.supabase.co \
NEXT_PUBLIC_SUPABASE_ANON_KEY=<anon-key> \
npm run probe:rls
Và chạy supabase/tests/rls_smoke_test.sql trong SQL Editor của project production —
script tự tạo user thử nghiệm cho cả 4 vai trò rồi ROLLBACK, không ghi gì vĩnh viễn.
Xem CLAUDE.md mục 10. Tóm tắt: không có thanh toán (VIP cấp thủ công), chưa làm bài toán 3D / OCR / barcode / robot guidance, không có tính năng cộng đồng, không có app mobile riêng.
/admin/luat-goi-y, hoặc nhập hàng loạt từ Excel bằng
npm run import:rules.