vr-commons 1.0.24 → 1.0.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,78 +3,184 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.generateSearchConditions = exports.canAdminModifyUser = exports.validateUniqueFields = exports.hashPassword = exports.findSecurityClearanceByRole = exports.getUserById = exports.getAdminWithClearance = exports.isUserAdminOrSuperAdmin = exports.isUserSuperAdmin = exports.isUserAdmin = exports.formatAdminProfile = exports.formatUserForAdmin = void 0;
6
+ exports.softDeleteUser = exports.hashPassword = exports.validatePassword = exports.validateUniqueFields = exports.generateJacketId = exports.getSortOrder = exports.generateEventSearchConditions = exports.generateUserSearchConditions = exports.findSecurityClearanceByRole = exports.getUsersByRole = exports.getUserById = exports.isAccountAccessible = exports.canModifyAccount = exports.hasAllPermissions = exports.hasAnyPermission = exports.hasPermission = exports.hasRole = exports.formatUserListResponse = exports.formatUserProfile = exports.JACKET_ID_PREFIX = exports.JACKET_ID_REGEX = exports.NATIONAL_ID_REGEX = exports.PHONE_REGEX = void 0;
7
7
  const vr_models_1 = require("vr-models");
8
8
  const bcryptjs_1 = __importDefault(require("bcryptjs"));
9
9
  const sequelize_1 = require("sequelize");
10
- const formatUserForAdmin = (user) => {
11
- return {
10
+ // ============================================================================
11
+ // REGEX PATTERNS & CONSTANTS
12
+ // ============================================================================
13
+ exports.PHONE_REGEX = /^(078|079|072|073)\d{7}$/;
14
+ exports.NATIONAL_ID_REGEX = /^\d{16}$/;
15
+ exports.JACKET_ID_REGEX = /^JKT-\d{9}$/;
16
+ exports.JACKET_ID_PREFIX = "JKT";
17
+ // ============================================================================
18
+ // FORMATTING FUNCTIONS (WITH EXPLICIT RETURN TYPES)
19
+ // ============================================================================
20
+ /**
21
+ * Format user profile based on viewer type and options
22
+ */
23
+ const formatUserProfile = (user, viewerType = "ADMIN", options = {}) => {
24
+ const baseProfile = {
12
25
  id: user.id,
13
26
  firstName: user.firstName,
14
27
  lastName: user.lastName,
15
28
  phoneNumber: user.phoneNumber,
16
29
  email: user.email,
17
30
  nationalId: user.nationalId,
18
- jacketId: user.jacketId,
19
- plateNumber: user.plateNumber,
20
31
  isActive: user.isActive,
21
- isSuspended: user.isSuspended,
22
- isDeactivated: user.isDeactivated,
23
- deactivatedAt: user.deactivatedAt,
24
32
  lastLoginAt: user.lastLoginAt,
25
33
  createdAt: user.createdAt,
26
- role: user.securityClearance?.role,
27
- level: user.securityClearance?.level,
28
- permissions: user.securityClearance?.permissions || [],
29
34
  };
35
+ const securityFields = options.includeSecurity
36
+ ? {
37
+ role: user.securityClearance?.role,
38
+ level: user.securityClearance?.level,
39
+ permissions: user.securityClearance?.permissions || [],
40
+ }
41
+ : viewerType === "SUPER_ADMIN" || viewerType === "ADMIN"
42
+ ? {
43
+ role: user.securityClearance?.role,
44
+ }
45
+ : {};
46
+ // Return type-specific profiles
47
+ switch (viewerType) {
48
+ case "RIDER":
49
+ return {
50
+ ...baseProfile,
51
+ jacketId: user.jacketId,
52
+ plateNumber: user.plateNumber,
53
+ ...securityFields,
54
+ };
55
+ case "PASSENGER":
56
+ return {
57
+ ...baseProfile,
58
+ jacketId: user.jacketId,
59
+ plateNumber: user.plateNumber,
60
+ ...securityFields,
61
+ };
62
+ case "AGENT":
63
+ return {
64
+ ...baseProfile,
65
+ ...securityFields,
66
+ };
67
+ case "ADMIN":
68
+ return {
69
+ ...baseProfile,
70
+ jacketId: user.jacketId,
71
+ plateNumber: user.plateNumber,
72
+ isSuspended: user.isSuspended,
73
+ isDeactivated: user.isDeactivated,
74
+ ...securityFields,
75
+ };
76
+ case "SUPER_ADMIN":
77
+ return {
78
+ ...baseProfile,
79
+ jacketId: user.jacketId,
80
+ plateNumber: user.plateNumber,
81
+ isSuspended: user.isSuspended,
82
+ isDeactivated: user.isDeactivated,
83
+ bannedAt: user.bannedAt,
84
+ banReason: user.banReason,
85
+ suspendedAt: user.suspendedAt,
86
+ suspensionReason: user.suspensionReason,
87
+ deactivatedAt: user.deactivatedAt,
88
+ securityClearanceId: user.securityClearanceId,
89
+ ...securityFields,
90
+ };
91
+ default:
92
+ return {
93
+ ...baseProfile,
94
+ ...securityFields,
95
+ };
96
+ }
30
97
  };
31
- exports.formatUserForAdmin = formatUserForAdmin;
32
- // Format admin profile
33
- const formatAdminProfile = (user) => {
98
+ exports.formatUserProfile = formatUserProfile;
99
+ /**
100
+ * Format user list for response (paginated)
101
+ */
102
+ const formatUserListResponse = (users, total, page, limit, viewerType = "ADMIN") => {
34
103
  return {
35
- id: user.id,
36
- firstName: user.firstName,
37
- lastName: user.lastName,
38
- phoneNumber: user.phoneNumber,
39
- email: user.email,
40
- nationalId: user.nationalId,
41
- isActive: user.isActive,
42
- lastLoginAt: user.lastLoginAt,
43
- createdAt: user.createdAt,
44
- role: user.securityClearance?.role,
45
- level: user.securityClearance?.level,
46
- permissions: user.securityClearance?.permissions || [],
104
+ data: users.map((user) => (0, exports.formatUserProfile)(user, viewerType)),
105
+ pagination: {
106
+ total,
107
+ page,
108
+ limit,
109
+ pages: Math.ceil(total / limit),
110
+ },
47
111
  };
48
112
  };
49
- exports.formatAdminProfile = formatAdminProfile;
50
- // Check if user is an admin
51
- const isUserAdmin = (user) => {
52
- return user.securityClearance?.role === "ADMIN";
53
- };
54
- exports.isUserAdmin = isUserAdmin;
55
- // Check if user is a super admin
56
- const isUserSuperAdmin = (user) => {
57
- return user.securityClearance?.role === "SUPER_ADMIN";
58
- };
59
- exports.isUserSuperAdmin = isUserSuperAdmin;
60
- // Check if user is an admin or super admin
61
- const isUserAdminOrSuperAdmin = (user) => {
62
- const role = user.securityClearance?.role;
63
- return role === "ADMIN" || role === "SUPER_ADMIN";
64
- };
65
- exports.isUserAdminOrSuperAdmin = isUserAdminOrSuperAdmin;
66
- // Get admin with security clearance
67
- const getAdminWithClearance = async (userId) => {
68
- return await vr_models_1.User.findOne({
113
+ exports.formatUserListResponse = formatUserListResponse;
114
+ // ============================================================================
115
+ // USER ROLE CHECK FUNCTIONS
116
+ // ============================================================================
117
+ /**
118
+ * Check if user has a specific role
119
+ */
120
+ const hasRole = (user, role) => {
121
+ const roles = Array.isArray(role) ? role : [role];
122
+ return roles.includes(user.securityClearance?.role);
123
+ };
124
+ exports.hasRole = hasRole;
125
+ /**
126
+ * Check if user has a specific permission
127
+ */
128
+ const hasPermission = (user, permission) => {
129
+ const permissions = user.securityClearance?.permissions || [];
130
+ return permissions.includes(permission);
131
+ };
132
+ exports.hasPermission = hasPermission;
133
+ /**
134
+ * Check if user has any of the given permissions
135
+ */
136
+ const hasAnyPermission = (user, permissions) => {
137
+ const userPermissions = user.securityClearance?.permissions || [];
138
+ return permissions.some((p) => userPermissions.includes(p));
139
+ };
140
+ exports.hasAnyPermission = hasAnyPermission;
141
+ /**
142
+ * Check if user has all of the given permissions
143
+ */
144
+ const hasAllPermissions = (user, permissions) => {
145
+ const userPermissions = user.securityClearance?.permissions || [];
146
+ return permissions.every((p) => userPermissions.includes(p));
147
+ };
148
+ exports.hasAllPermissions = hasAllPermissions;
149
+ /**
150
+ * Check if account can be modified (not deactivated/suspended)
151
+ */
152
+ const canModifyAccount = (user) => {
153
+ return !user.isDeactivated && !user.isSuspended;
154
+ };
155
+ exports.canModifyAccount = canModifyAccount;
156
+ /**
157
+ * Check if account is active and accessible
158
+ */
159
+ const isAccountAccessible = (user) => {
160
+ return user.isActive && !user.isDeactivated && !user.isSuspended;
161
+ };
162
+ exports.isAccountAccessible = isAccountAccessible;
163
+ // ============================================================================
164
+ // DATABASE QUERY FUNCTIONS
165
+ // ============================================================================
166
+ /**
167
+ * Get user by ID with security clearance
168
+ */
169
+ const getUserById = async (userId, includeSecurity = true, excludeSensitive = true) => {
170
+ const query = {
69
171
  where: { id: userId },
70
- include: [
172
+ };
173
+ if (includeSecurity) {
174
+ query.include = [
71
175
  {
72
176
  model: vr_models_1.SecurityClearance,
73
177
  as: "securityClearance",
74
178
  attributes: ["role", "level", "permissions"],
75
179
  },
76
- ],
77
- attributes: {
180
+ ];
181
+ }
182
+ if (excludeSensitive) {
183
+ query.attributes = {
78
184
  exclude: [
79
185
  "password",
80
186
  "otp",
@@ -82,14 +188,57 @@ const getAdminWithClearance = async (userId) => {
82
188
  "tokenVersion",
83
189
  "forgotPassword",
84
190
  ],
85
- },
86
- });
191
+ };
192
+ }
193
+ return await vr_models_1.User.findOne(query);
87
194
  };
88
- exports.getAdminWithClearance = getAdminWithClearance;
89
- // Get user by ID with security clearance
90
- const getUserById = async (userId) => {
91
- return await vr_models_1.User.findOne({
92
- where: { id: userId },
195
+ exports.getUserById = getUserById;
196
+ /**
197
+ * Get users by role with optional filters
198
+ */
199
+ const getUsersByRole = async (role, filters = {}, pagination) => {
200
+ const where = {};
201
+ if (role) {
202
+ where["$securityClearance.role$"] = role;
203
+ }
204
+ // Apply filters
205
+ if (filters.isActive !== undefined)
206
+ where.isActive = filters.isActive;
207
+ if (filters.isSuspended !== undefined)
208
+ where.isSuspended = filters.isSuspended;
209
+ if (filters.isDeactivated !== undefined)
210
+ where.isDeactivated = filters.isDeactivated;
211
+ if (filters.isBanned !== undefined) {
212
+ if (filters.isBanned) {
213
+ where.bannedAt = { [sequelize_1.Op.ne]: null };
214
+ }
215
+ else {
216
+ where.bannedAt = null;
217
+ }
218
+ }
219
+ // Date range filter
220
+ if (filters.startDate || filters.endDate) {
221
+ where.createdAt = {};
222
+ if (filters.startDate) {
223
+ where.createdAt[sequelize_1.Op.gte] = new Date(filters.startDate);
224
+ }
225
+ if (filters.endDate) {
226
+ where.createdAt[sequelize_1.Op.lte] = new Date(filters.endDate + "T23:59:59.999Z");
227
+ }
228
+ }
229
+ // Search filter
230
+ if (filters.search) {
231
+ where[sequelize_1.Op.or] = [
232
+ { firstName: { [sequelize_1.Op.iLike]: `%${filters.search}%` } },
233
+ { lastName: { [sequelize_1.Op.iLike]: `%${filters.search}%` } },
234
+ { phoneNumber: { [sequelize_1.Op.iLike]: `%${filters.search}%` } },
235
+ { email: { [sequelize_1.Op.iLike]: `%${filters.search}%` } },
236
+ { nationalId: { [sequelize_1.Op.iLike]: `%${filters.search}%` } },
237
+ { jacketId: { [sequelize_1.Op.iLike]: `%${filters.search}%` } },
238
+ ];
239
+ }
240
+ const query = {
241
+ where,
93
242
  include: [
94
243
  {
95
244
  model: vr_models_1.SecurityClearance,
@@ -106,93 +255,251 @@ const getUserById = async (userId) => {
106
255
  "forgotPassword",
107
256
  ],
108
257
  },
109
- });
258
+ };
259
+ // Apply pagination
260
+ if (pagination) {
261
+ const { page, limit } = pagination;
262
+ query.limit = limit;
263
+ query.offset = (page - 1) * limit;
264
+ }
265
+ const { rows, count } = await vr_models_1.User.findAndCountAll(query);
266
+ return {
267
+ users: rows,
268
+ total: count,
269
+ };
110
270
  };
111
- exports.getUserById = getUserById;
112
- // Find security clearance by role
113
- const findSecurityClearanceByRole = async (role) => {
114
- return await vr_models_1.SecurityClearance.findOne({
115
- where: { role, isDefault: true },
116
- });
271
+ exports.getUsersByRole = getUsersByRole;
272
+ /**
273
+ * Find security clearance by role
274
+ */
275
+ const findSecurityClearanceByRole = async (role, requireDefault = true) => {
276
+ const where = { role };
277
+ if (requireDefault) {
278
+ where.isDefault = true;
279
+ }
280
+ return await vr_models_1.SecurityClearance.findOne({ where });
117
281
  };
118
282
  exports.findSecurityClearanceByRole = findSecurityClearanceByRole;
119
- // Hash password
120
- const hashPassword = async (password) => {
121
- return await bcryptjs_1.default.hash(password, 10);
122
- };
123
- exports.hashPassword = hashPassword;
124
- // Validate unique fields
125
- const validateUniqueFields = async (phoneNumber, nationalId, email) => {
126
- const errors = [];
127
- // Check phone number
128
- const existingPhone = await vr_models_1.User.findOne({ where: { phoneNumber } });
129
- if (existingPhone) {
130
- errors.push({
131
- field: "phoneNumber",
132
- message: "Phone number already registered",
133
- });
134
- }
135
- // Check national ID
136
- const existingNationalId = await vr_models_1.User.findOne({ where: { nationalId } });
137
- if (existingNationalId) {
138
- errors.push({
139
- field: "nationalId",
140
- message: "National ID already registered",
141
- });
142
- }
143
- // Check email if provided
144
- if (email) {
145
- const existingEmail = await vr_models_1.User.findOne({ where: { email } });
146
- if (existingEmail) {
147
- errors.push({ field: "email", message: "Email already registered" });
283
+ /**
284
+ * Generate search conditions for user queries
285
+ */
286
+ const generateUserSearchConditions = (filters) => {
287
+ const where = {};
288
+ if (filters.role) {
289
+ where["$securityClearance.role$"] = filters.role;
290
+ }
291
+ if (filters.isActive !== undefined) {
292
+ where.isActive = filters.isActive;
293
+ }
294
+ if (filters.isSuspended !== undefined) {
295
+ where.isSuspended = filters.isSuspended;
296
+ }
297
+ if (filters.isBanned !== undefined) {
298
+ if (filters.isBanned) {
299
+ where.bannedAt = { [sequelize_1.Op.ne]: null };
300
+ }
301
+ else {
302
+ where.bannedAt = null;
148
303
  }
149
304
  }
150
- return errors;
151
- };
152
- exports.validateUniqueFields = validateUniqueFields;
153
- // Check if admin can modify target user
154
- const canAdminModifyUser = (admin, targetUser) => {
155
- const adminRole = admin.securityClearance?.role;
156
- const targetRole = targetUser.securityClearance?.role;
157
- // Admins cannot modify super admins
158
- if (targetRole === "SUPER_ADMIN") {
159
- return { canModify: false, reason: "Cannot modify super admin accounts" };
160
- }
161
- // Admins cannot modify other admins (only super admins can)
162
- if (targetRole === "ADMIN" && adminRole !== "SUPER_ADMIN") {
163
- return {
164
- canModify: false,
165
- reason: "Admins cannot modify other admin accounts",
166
- };
305
+ if (filters.startDate || filters.endDate) {
306
+ where.createdAt = {};
307
+ if (filters.startDate) {
308
+ where.createdAt[sequelize_1.Op.gte] = new Date(filters.startDate);
309
+ }
310
+ if (filters.endDate) {
311
+ where.createdAt[sequelize_1.Op.lte] = new Date(filters.endDate + "T23:59:59.999Z");
312
+ }
167
313
  }
168
- // Cannot modify self through this route
169
- if (admin.id === targetUser.id) {
170
- return {
171
- canModify: false,
172
- reason: "Use profile update route for self-updates",
173
- };
314
+ // Search filter
315
+ if (filters.search) {
316
+ where[sequelize_1.Op.or] = [
317
+ { firstName: { [sequelize_1.Op.iLike]: `%${filters.search}%` } },
318
+ { lastName: { [sequelize_1.Op.iLike]: `%${filters.search}%` } },
319
+ { phoneNumber: { [sequelize_1.Op.iLike]: `%${filters.search}%` } },
320
+ { email: { [sequelize_1.Op.iLike]: `%${filters.search}%` } },
321
+ { nationalId: { [sequelize_1.Op.iLike]: `%${filters.search}%` } },
322
+ { jacketId: { [sequelize_1.Op.iLike]: `%${filters.search}%` } },
323
+ ];
174
324
  }
175
- return { canModify: true };
325
+ return where;
176
326
  };
177
- exports.canAdminModifyUser = canAdminModifyUser;
178
- // Generate search conditions
179
- const generateSearchConditions = (role, isActive, search) => {
327
+ exports.generateUserSearchConditions = generateUserSearchConditions;
328
+ /**
329
+ * Generate search conditions for event logs
330
+ */
331
+ const generateEventSearchConditions = (userId, filters) => {
180
332
  const where = {};
181
- if (role) {
182
- where["$securityClearance.role$"] = role;
333
+ // Search filter
334
+ where[sequelize_1.Op.or] = [{ actorId: userId }, { entityId: userId }];
335
+ if (filters.action) {
336
+ where.action = filters.action;
183
337
  }
184
- if (isActive !== undefined) {
185
- where.isActive = isActive;
338
+ if (filters.entity) {
339
+ where.entity = filters.entity;
186
340
  }
187
- if (search) {
188
- where[sequelize_1.Op.or] = [
189
- { firstName: { [sequelize_1.Op.iLike]: `%${search}%` } },
190
- { lastName: { [sequelize_1.Op.iLike]: `%${search}%` } },
191
- { phoneNumber: { [sequelize_1.Op.iLike]: `%${search}%` } },
192
- { email: { [sequelize_1.Op.iLike]: `%${search}%` } },
193
- { nationalId: { [sequelize_1.Op.iLike]: `%${search}%` } },
194
- ];
341
+ if (filters.startDate || filters.endDate) {
342
+ where.createdAt = {};
343
+ if (filters.startDate) {
344
+ where.createdAt[sequelize_1.Op.gte] = new Date(filters.startDate);
345
+ }
346
+ if (filters.endDate) {
347
+ where.createdAt[sequelize_1.Op.lte] = new Date(filters.endDate + "T23:59:59.999Z");
348
+ }
195
349
  }
196
350
  return where;
197
351
  };
198
- exports.generateSearchConditions = generateSearchConditions;
352
+ exports.generateEventSearchConditions = generateEventSearchConditions;
353
+ /**
354
+ * Get sort order for queries
355
+ */
356
+ const getSortOrder = (sortBy = "createdAt", order = "desc") => {
357
+ const validSortFields = ["firstName", "lastLoginAt", "createdAt"];
358
+ const sortField = validSortFields.includes(sortBy) ? sortBy : "createdAt";
359
+ const sortOrder = order.toLowerCase() === "asc" ? "ASC" : "DESC";
360
+ if (sortField === "firstName") {
361
+ return [
362
+ ["firstName", sortOrder],
363
+ ["lastName", sortOrder],
364
+ ];
365
+ }
366
+ return [[sortField, sortOrder]];
367
+ };
368
+ exports.getSortOrder = getSortOrder;
369
+ // ============================================================================
370
+ // GENERATION FUNCTIONS
371
+ // ============================================================================
372
+ /**
373
+ * Generate unique jacket ID
374
+ */
375
+ const generateJacketId = async () => {
376
+ let isUnique = false;
377
+ let jacketId = "";
378
+ while (!isUnique) {
379
+ const timestamp = Date.now().toString().slice(-6);
380
+ const random = Math.floor(Math.random() * 1000)
381
+ .toString()
382
+ .padStart(3, "0");
383
+ jacketId = `${exports.JACKET_ID_PREFIX}-${timestamp}${random}`;
384
+ const existing = await vr_models_1.User.findOne({ where: { jacketId } });
385
+ isUnique = !existing;
386
+ }
387
+ return jacketId;
388
+ };
389
+ exports.generateJacketId = generateJacketId;
390
+ // ============================================================================
391
+ // VALIDATION FUNCTIONS
392
+ // ============================================================================
393
+ /**
394
+ * Validate unique user fields
395
+ */
396
+ const validateUniqueFields = async (fields, excludeUserId) => {
397
+ const errors = [];
398
+ for (const [field, value] of Object.entries(fields)) {
399
+ if (!value)
400
+ continue;
401
+ const where = { [field]: value };
402
+ if (excludeUserId) {
403
+ where.id = { [sequelize_1.Op.ne]: excludeUserId };
404
+ }
405
+ const existing = await vr_models_1.User.findOne({ where });
406
+ if (existing) {
407
+ let message = "";
408
+ switch (field) {
409
+ case "phoneNumber":
410
+ message = "Phone number already registered";
411
+ break;
412
+ case "nationalId":
413
+ message = "National ID already registered";
414
+ break;
415
+ case "email":
416
+ message = "Email already registered";
417
+ break;
418
+ case "jacketId":
419
+ message = "Jacket ID already taken";
420
+ break;
421
+ default:
422
+ message = `${field} already exists`;
423
+ }
424
+ errors.push({ field, message });
425
+ }
426
+ }
427
+ return errors;
428
+ };
429
+ exports.validateUniqueFields = validateUniqueFields;
430
+ /**
431
+ * Validate password
432
+ */
433
+ const validatePassword = async (plainPassword, hashedPassword) => {
434
+ if (!hashedPassword)
435
+ return false;
436
+ return await bcryptjs_1.default.compare(plainPassword, hashedPassword);
437
+ };
438
+ exports.validatePassword = validatePassword;
439
+ // ============================================================================
440
+ // PASSWORD FUNCTIONS
441
+ // ============================================================================
442
+ /**
443
+ * Hash password
444
+ */
445
+ const hashPassword = async (password) => {
446
+ const salt = await bcryptjs_1.default.genSalt(10);
447
+ return await bcryptjs_1.default.hash(password, salt);
448
+ };
449
+ exports.hashPassword = hashPassword;
450
+ // ============================================================================
451
+ // ACCOUNT MANAGEMENT FUNCTIONS
452
+ // ============================================================================
453
+ /**
454
+ * Soft delete user
455
+ */
456
+ const softDeleteUser = async (userId, options) => {
457
+ const updateData = {
458
+ isActive: false,
459
+ isDeactivated: true,
460
+ deactivatedAt: new Date(),
461
+ };
462
+ // Anonymize sensitive data
463
+ const timestamp = Date.now();
464
+ updateData.email = null;
465
+ updateData.password = null;
466
+ updateData.phoneNumber = `DELETED_${timestamp}`;
467
+ updateData.nationalId = `DELETED_${timestamp}`;
468
+ updateData.jacketId = `DELETED_${timestamp}`;
469
+ await vr_models_1.User.update(updateData, {
470
+ where: { id: userId },
471
+ transaction: options?.transaction,
472
+ });
473
+ };
474
+ exports.softDeleteUser = softDeleteUser;
475
+ // ============================================================================
476
+ // EXPORT ALL FUNCTIONS
477
+ // ============================================================================
478
+ exports.default = {
479
+ // Formatting
480
+ formatUserProfile: exports.formatUserProfile,
481
+ formatUserListResponse: exports.formatUserListResponse,
482
+ // Role checks
483
+ hasRole: exports.hasRole,
484
+ hasPermission: exports.hasPermission,
485
+ hasAnyPermission: exports.hasAnyPermission,
486
+ hasAllPermissions: exports.hasAllPermissions,
487
+ canModifyAccount: exports.canModifyAccount,
488
+ isAccountAccessible: exports.isAccountAccessible,
489
+ // Database queries
490
+ getUserById: exports.getUserById,
491
+ getUsersByRole: exports.getUsersByRole,
492
+ findSecurityClearanceByRole: exports.findSecurityClearanceByRole,
493
+ generateUserSearchConditions: exports.generateUserSearchConditions,
494
+ generateEventSearchConditions: exports.generateEventSearchConditions,
495
+ getSortOrder: exports.getSortOrder,
496
+ // Generation
497
+ generateJacketId: exports.generateJacketId,
498
+ // Validation
499
+ validateUniqueFields: exports.validateUniqueFields,
500
+ validatePassword: exports.validatePassword,
501
+ // Password
502
+ hashPassword: exports.hashPassword,
503
+ // Account management
504
+ softDeleteUser: exports.softDeleteUser,
505
+ };