Giao diện
Kiến trúc plugin-users
Tổng quan
plugin-users chịu trách nhiệm quản lý toàn bộ vòng đời người dùng trong hệ thống: định nghĩa collection users, cung cấp model xử lý dữ liệu nhạy cảm, tự động gắn thông tin audit (createdBy/updatedBy), đăng ký filter operator đặc biệt, và hỗ trợ đồng bộ user từ nguồn bên ngoài.
Sơ đồ kiến trúc
UserModel — desensitize
UserModel kế thừa Model từ @digiforce-nc/database và bổ sung method desensitize(). Method này duyệt tất cả field của collection, chỉ giữ lại những field không có hidden: true, loại bỏ dữ liệu nhạy cảm (như password, resetToken) trước khi trả về client.
typescript
// packages/plugins/@digiforce/plugin-users/src/server/models/UserModel.ts
export class UserModel extends Model {
desensitize() {
const { fields } = (this.constructor as typeof UserModel).collection;
const result = (this.constructor as typeof UserModel).build({}, { isNewRecord: this.isNewRecord });
for (const [name, value] of Object.entries(this.get())) {
const field = fields.get(name);
if (field && !field.options.hidden) {
result.set(name, value);
}
}
return result;
}
}Các field bị ẩn trong collection users: password (hidden: true) và resetToken (hidden: true).
Tự động gắn createdBy / updatedBy
Plugin lắng nghe hai event của database:
| Event | Mục đích |
|---|---|
afterDefineCollection | Khi collection được định nghĩa với createdBy: true hoặc updatedBy: true, tự động thêm field createdById (type context, createOnly: true) và relation createdBy (belongsTo → users) |
field.afterAdd | Khi một field mới có interface createdBy hoặc updatedBy được thêm vào collection bất kỳ, inject field context tương ứng |
Field context lấy giá trị từ state.currentUser.id, kiểu bigInt, có index. Field createdById chỉ ghi khi tạo mới (createOnly: true), trong khi updatedById cập nhật mỗi lần sửa.
typescript
// Ví dụ: collection orders tự động có createdBy
const Orders = db.collection({
name: 'orders',
createdBy: true,
updatedBy: true,
fields: [{ name: 'title', type: 'string' }],
});
// → orders tự động có: createdById, createdBy, updatedById, updatedByDB operators
Plugin đăng ký 3 operator tùy chỉnh trong beforeLoad():
| Operator | Logic | Sử dụng |
|---|---|---|
$isCurrentUser | Chuyển thành Op.eq với ctx.state.currentUser.id (fallback -1 nếu chưa đăng nhập) | Lọc record thuộc user hiện tại |
$isNotCurrentUser | Chuyển thành Op.ne với ctx.state.currentUser.id | Lọc record không thuộc user hiện tại |
$isVar | Parse giá trị dạng template từ ctx.state, chuyển thành Op.eq | Filter động theo biến hệ thống |
Ví dụ sử dụng trong filter:
json
{
"createdById": { "$isCurrentUser": true }
}ACL — bảo vệ hệ thống
Plugin đăng ký fixedParams với ACL để bảo vệ dữ liệu quan trọng:
- Không xóa được user id=1 (root user): action
users:destroyluôn thêm filterid.$ne: 1 - Không xóa được collection users: action
collections:destroyluôn thêm filtername.$ne: 'users' - Action
updateProfilevàupdateLangđược phép cho tất cả user đã đăng nhập (loggedIn) - Toàn bộ action
users:*nằm trong ACL snippetpm.users
UserDataSyncResource — đồng bộ bên ngoài
Khi plugin user-data-sync được kích hoạt, UserDataSyncResource đăng ký như một resource đồng bộ:
| Method | Hành vi |
|---|---|
create() | Tìm user theo matchKey (phone/email/username). Nếu tìm thấy → update, nếu không → tạo mới. Tự động lọc bỏ các field hệ thống (id, password, createdAt...) |
update() | Cập nhật user hiện có. Nếu isDeleted: true → xóa user (trừ khi user có role root). Nếu user không tồn tại → tạo lại |
Tạo root user khi cài đặt
Trong method install(), plugin đọc biến môi trường để tạo user đầu tiên:
| Biến môi trường | Giá trị mặc định |
|---|---|
INIT_ROOT_EMAIL | admin@digiforce.vn |
INIT_ROOT_PASSWORD | DigiM@t3 |
INIT_ROOT_DISPLAYNAME | Super Admin |
INIT_ROOT_USERNAME | digiforce |
Sau khi tạo root user, plugin insert 3 UI schema cho profile form: admin create, admin edit, và user profile edit (uid: digiforce-user-profile-edit-form).
Xử lý password change
Khi field password thay đổi (event users.beforeUpdate), plugin thực hiện:
- Ghi lại thời điểm thay đổi vào
passwordChangeTz(timestamp) - Xóa cache roles của user (
cache:del:roles) - Xóa cache auth của user (
cache:del:auth)
Điều này buộc user phải đăng nhập lại sau khi đổi mật khẩu, đảm bảo session cũ không còn hiệu lực.