Supabase 2 — Bản thiết kế schema và phân quyền (Đợt 3A)

Ngày 12/10/2026. Đã triển khai schema/RLS/RPC trên Supabase 2 (3 migrations). Giao diện /platform/ nằm trong PR #4; chưa kiểm thử email E2E hay mở đăng ký công khai. Project đích: mhgsvxsjrzaeafvifydu (Tools_out, dự kiến vai trò APMATHS Platform). Project yhfburffwzvcayjadskr (AI-CLO cá nhân) không thuộc phạm vi schema này. Không thực hiện Cloudinary, media, upload, thanh toán trong đợt hiện tại.

Cập nhật: bản blueprint dưới đây mô tả core v1, còn schema mở rộng về ngân hàng câu hỏi/đề thi/entitlement nằm trong các migration v4–v7. Xem Báo cáo tiến độ và Supabase 2 README.

1. Mục tiêu v1

Một Supabase chứa nhiều giáo viên ngoài và học sinh của họ; từng giáo viên sở hữu một workspace, được tạo lớp, ghi danh học sinh, xem dữ liệu của chính mình. Một học sinh có thể học với nhiều giáo viên. Giáo viên tự đăng ký nhưng không tự kích hoạt quyền teacher. Chưa tái hiện toàn bộ AI-CLO hiện có.

Phân tách Auth

Supabase 1 và 2 có hai auth.users và session/JWT riêng. Website phân biệt khu vực AI-CLO cá nhân và APMATHS Platform, không giả định SSO. User ở base 2 không tự động có quyền ở base 1. Khóa bí mật, tài khoản dịch vụ và JWT không chuyển qua dự án kia.

2. Schema tối thiểu đã triển khai v1

Bảng/khóa logic đã triển khai vào Supabase 2. SQL gốc được lưu tại thư mục migrations, gồm core, bảo vệ RPC và RLS profile.

Bảng Cột/khóa bắt buộc Giới hạn và quan hệ
profiles user_id uuid PK, full_name, account_type, status, created_at user_id -> auth.users.id; type do server cấp: student, teacher, platform_admin; pending teacher chưa là teacher thực tế
teacher_applications id PK, user_id FK, organization, status, submitted_at, reviewed_at, reviewed_by Một hồ sơ mở của mỗi user; trạng thái pending/approved/rejected chỉ đổi ở backend; lưu audit
teacher_application_events id PK, application_id FK, action, actor_id, created_at Audit chỉ server ghi, không sửa/xóa phía client
workspaces id PK, owner_user_id FK, name, status, created_at Owner là teacher đã duyệt; ownership không do client tùy ý đặt
workspace_members workspace_id FK, user_id FK, role, created_at Unique (workspace_id,user_id); role owner/teacher_assistant trước; tránh policy đệ quy
classes id PK, workspace_id FK, name, status, created_at Workspace sở hữu lớp; không được đổi workspace qua UPDATE client
class_enrollments class_id FK, student_user_id FK, status, joined_at Unique (class_id,student_user_id); ghi danh qua API xét mã mời/giáo viên duyệt
class_invites id PK, class_id FK, code_hash, expires_at, max_uses, used_count Chỉ lưu hash mã mời, không trả hash mã mời qua Data API; kiểm tra rate limit ở backend

Hoãn: question_banks, questions, exams, attempts, answers, subscriptions, orders, media_assets. Chỉ bổ sung khi vòng đời Auth → workspace → class → enrollments đã được kiểm thử.

3. Các invariants bắt buộc

  1. Không cho client tạo hoặc sửa profiles.account_type, profiles.status, teacher_applications.status/reviewed_by.
  2. Chỉ giáo viên đã duyệt mới có workspace; chủ sở hữu workspace chỉ đổi qua server admin có audit.
  3. Giáo viên có thể xem dữ liệu thuộc workspace được phân công, không xem được dữ liệu workspace khác.
  4. Học sinh chỉ xem lớp mình tham gia và dữ liệu của chính họ; không xem hồ sơ học sinh khác nếu không được cấp quyền.
  5. Lệnh tạo/sửa classes không được gán workspace_id của giáo viên khác; UPDATE kiểm tra cả USING và WITH CHECK.
  6. teacher_application_events append-only phía backend.
  7. Không gộp student/teacher bằng email giữa hai Supabase; nếu một người đăng ký cả hai thì đó là hai tài khoản độc lập.
  8. Không dùng user_metadata do user sửa được làm bằng chứng quyền teacher/admin. Server kiểm tra trạng thái từ database và danh sách admin được bảo vệ.
  9. Schema trong public phải bật RLS; RLS chưa đủ nếu không có GRANT phù hợp, kiểm tra Data API exposure độc lập.
  10. RLS helper cho workspace_members phải tránh tham chiếu đệ quy vào chính policy; đánh giá access qua server hoặc helper an toàn sau khi audit, không vội dùng SECURITY DEFINER công khai.

4. Ma trận đọc/ghi mong muốn

Tác vụ Khách Student Teacher đã duyệt Platform Admin
Đăng ký tài khoản/email Auth signup có chống abuse — — —
Xem hồ sơ của mình Không Có Có Có
Tự nâng role/duyệt đơn Không Không Không Chỉ backend
Xem trạng thái đơn của mình Không Có, nếu đã nộp Có Có
Tạo workspace Không Không Thông qua backend sau duyệt Có
Quản lý lớp workspace A Không Không Chỉ thành viên đủ quyền của A Có theo công vụ
Xem danh sách học sinh lớp A Không Không Chỉ teacher A Có theo công vụ
Học sinh xem bài/kết quả của mình Không Có nếu thuộc lớp và được công bố Theo vai trò được giao Có theo công vụ
Đọc lớp/đề/kết quả workspace B Không Chỉ những lớp B mà học sinh tham gia Không nếu thuộc teacher A Có theo công vụ

5. Luồng triển khai từ đầu đến cuối

  1. Đăng ký trên Supabase 2, xác minh email, tạo profiles giới hạn quyền và teacher_applications qua một backend an toàn (không dùng public role update).
  2. Admin xét duyệt và ghi audit; backend cấp role teacher + workspace, không lấy role từ payload của client.
  3. Teacher đăng nhập, tạo lớp trong workspace; membership/phân quyền xác minh server & RLS.
  4. Student đăng ký / tham gia lớp theo lời mời; mã invite chỉ redeem ở endpoint có rate limit, chống brute force.
  5. Student xem đúng lớp và bài của mình. Giáo viên A/B thử truy cập chéo đều phải bị chặn.
  6. Chỉ sau kiểm thử và review mới mở đăng ký public.

6. Test SQL đã đạt và test E2E còn lại

7. Ranh giới với Supabase 1 và kế hoạch xử lý sau

Không thay đổi Supabase 1 trong bản schema này. Các tính năng mua lớp của bạn và thi thử miễn phí vẫn thuộc Supabase 1. Khi có yêu cầu riêng, thiết kế entitlement lớp của bạn, bảo vệ đáp án public vs confidential và hạn mức Guest, không để traffic guest ảnh hưởng bài kiểm tra lớp chính thức.

Đã PASS SQL integration giả lập Admin/teacher A/teacher B/student, rollback dữ liệu test. Vẫn cần test xác minh email và trang /platform/ bằng tài khoản thực cùng URL redirect trước khi public.

Phạm vi Đợt 3 hiện tại chỉ Supabase; media/Cloudinary hoãn đến khi người dùng yêu cầu.