User báo cáo → (tùy chọn) AI auto-filter → vào hàng đợi admin → admin quyết định: bỏ qua / ẩn bài / xóa bài + ban.
Admin có 3 hành động: khóa vĩnh viễn (lock), ban tạm thời (7 ngày), hoặc mở khóa (unlock).
| # | Task | Chi tiết | Status |
|---|---|---|---|
| 01 | Admin guard middleware | adminHandler kiểm tra req.user.roles.includes(ADMIN) từ JWT payload. Đặt trước tất cả admin routes. ⚠️ Lưu ý: đang bị duplicate với checkRole per-route. | done |
| 02 | PATCH /admin/users/:userId/ban Ban user | Cập nhật UserStatus thành BANNED qua userManagementService.banUser(). | done |
| 03 | GET /admin/users Danh sách users | Trả về: id, name, email, username, status, verifiedAt, createdAt, roles[], isVerified. Chưa có filter/search/pagination. | done |
| 04 | RBAC: JWT roles[] Tích hợp role vào JWT | Register → auto assign USER. Login/Refresh → load roles vào JWT payload {userId, status, roles[]}. | done |
| 05 | RBAC: checkRole + checkPermission | checkRole kiểm tra JWT roles. checkPermission query DB (⚠️ cache chưa hoạt động đúng). Cần fix: UnauthorizedException → ForbiddenException. | done |
| 06 | Bảng Report Prisma migration | Model Report { id, reporterId, targetType (POST/USER), targetId, reason, status, adminNote, createdAt }. | todo |
| 07 | PATCH /admin/reports/:id Xử lý báo cáo | Route + controller có. Service/repo stub. | stub |
| 08 | GET /admin/hashtags/trending | Route + controller có. Service/repo stub. | stub |
| 09 | GET /admin/stats | Route + controller có. Service/repo stub. | stub |
| 10 | Cron: auto-remove-ban | Job chạy mỗi giờ: tìm users có bannedUntil < now() → update status ACTIVE. | todo |
AdminAuditLog để truy vết.UserRoleType đã có sẵn.| # | Severity | Issue | Chi tiết | Status |
|---|---|---|---|---|
| B1 | blocking | Duplicate admin role check routes/index.ts + admin.routes.ts | adminHandler ở router level VÀ checkRole(ADMIN) ở mỗi route → check 2 lần. Chọn 1: bỏ adminHandler hoặc bỏ checkRole per-route. | todo |
| B2 | blocking | Dead import: userRepositorymiddlewares/admin.ts:3 | Import userRepository không còn sử dụng sau khi refactor sang JWT roles. Đã xóa file. | done |
| B3 | blocking | Dead import: NotFound (AWS SDK)access-control/middleware/index.ts:6 | Import NotFound từ @aws-sdk/client-s3 không liên quan access control. Đã xóa import. | done |
| B4 | medium | checkPermission cache không đọcaccess-control/middleware/index.ts:9-41 | Đã fix: đọc cache bằng `redisService.get`, đổi sang dùng `req.user.sub` làm key. | done |
| B5 | medium | checkRole throw sai error typeaccess-control/middleware/index.ts:51 | Thiếu role → 403 Forbidden. Đã fix dùng ForbiddenException thay UnauthorizedException. | done |
| B6 | medium | sendInvitation empty if blockcircle/service/circle.service.ts:55-64 | Đã sửa thành guard clause: if (!role) throw. | done |
| B7 | medium | sendInvitation không tạo invitationcircle/service/circle.service.ts:40-64 | Đã bổ sung gọi circleInvitationRepository.create(...) bằng transaction. | done |
| B8 | medium | CircleInvitation cursor: Number(after) có thể NaNcircle/repository/circle-invation.repository.ts:45 | Cursor dùng int id nhưng after là string. Không validate → Number("abc") = NaN. Nên thêm validation hoặc dùng publicId. | todo |
| B9 | nit | acceptInvitation dùng req.user.idcircle/controller/circle.controller.ts:63 | Các method khác đã chuyển sang req.user.sub. Method này vẫn dùng req.user.id cũ. | todo |
| B10 | nit | prisma import trực tiếp trong servicecircle/service/circle.service.ts:1,86 | Đã chuyển sang dùng transactionService.doInTransaction để giữ abstraction layer. | done |