@organizasyon/meeting-nanaman-app-backend 1.0.0

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.
Files changed (101) hide show
  1. package/dist/controllers/auth/index.d.ts +65 -0
  2. package/dist/controllers/auth/index.js +525 -0
  3. package/dist/controllers/employees/index.d.ts +38 -0
  4. package/dist/controllers/employees/index.js +185 -0
  5. package/dist/controllers/health/index.d.ts +9 -0
  6. package/dist/controllers/health/index.js +42 -0
  7. package/dist/controllers/index.d.ts +16 -0
  8. package/dist/controllers/index.js +19 -0
  9. package/dist/controllers/meetings/index.d.ts +23 -0
  10. package/dist/controllers/meetings/index.js +233 -0
  11. package/dist/controllers/modules/index.d.ts +5 -0
  12. package/dist/controllers/modules/index.js +104 -0
  13. package/dist/controllers/users/index.d.ts +103 -0
  14. package/dist/controllers/users/index.js +841 -0
  15. package/dist/data/modules.json +94 -0
  16. package/dist/database/config/index.d.ts +2 -0
  17. package/dist/database/config/index.js +32 -0
  18. package/dist/database/index.d.ts +9 -0
  19. package/dist/database/index.js +9 -0
  20. package/dist/database/seeder/employees/index.d.ts +1 -0
  21. package/dist/database/seeder/employees/index.js +40 -0
  22. package/dist/database/seeder/index.d.ts +4 -0
  23. package/dist/database/seeder/index.js +20 -0
  24. package/dist/database/seeder/users/index.d.ts +1 -0
  25. package/dist/database/seeder/users/index.js +46 -0
  26. package/dist/index.d.ts +9 -0
  27. package/dist/index.js +23 -0
  28. package/dist/jobs/index.d.ts +1 -0
  29. package/dist/jobs/index.js +18 -0
  30. package/dist/jobs/mailer/index.d.ts +4 -0
  31. package/dist/jobs/mailer/index.js +186 -0
  32. package/dist/jobs/mailer/templates/auth.d.ts +11 -0
  33. package/dist/jobs/mailer/templates/auth.js +117 -0
  34. package/dist/jobs/mailer/templates/index.d.ts +1 -0
  35. package/dist/jobs/mailer/templates/index.js +17 -0
  36. package/dist/jobs/queues/index.d.ts +3 -0
  37. package/dist/jobs/queues/index.js +115 -0
  38. package/dist/middlewares/audit/index.d.ts +0 -0
  39. package/dist/middlewares/audit/index.js +1 -0
  40. package/dist/middlewares/guard/index.d.ts +11 -0
  41. package/dist/middlewares/guard/index.js +53 -0
  42. package/dist/middlewares/index.d.ts +2 -0
  43. package/dist/middlewares/index.js +7 -0
  44. package/dist/middlewares/meeting.d.ts +9 -0
  45. package/dist/middlewares/meeting.js +34 -0
  46. package/dist/models/employees/index.d.ts +83 -0
  47. package/dist/models/employees/index.js +70 -0
  48. package/dist/models/index.d.ts +570 -0
  49. package/dist/models/index.js +17 -0
  50. package/dist/models/meetings/index.d.ts +227 -0
  51. package/dist/models/meetings/index.js +112 -0
  52. package/dist/models/passkeys/index.d.ts +77 -0
  53. package/dist/models/passkeys/index.js +55 -0
  54. package/dist/models/queues/index.d.ts +77 -0
  55. package/dist/models/queues/index.js +57 -0
  56. package/dist/models/users/index.d.ts +107 -0
  57. package/dist/models/users/index.js +92 -0
  58. package/dist/queues/index.d.ts +1 -0
  59. package/dist/queues/index.js +17 -0
  60. package/dist/queues/mailer/index.d.ts +4 -0
  61. package/dist/queues/mailer/index.js +74 -0
  62. package/dist/types/index.d.ts +33 -0
  63. package/dist/types/index.js +2 -0
  64. package/dist/utils/notifications.d.ts +2 -0
  65. package/dist/utils/notifications.js +51 -0
  66. package/package.json +39 -0
  67. package/public/health.html +215 -0
  68. package/src/controllers/auth/index.ts +609 -0
  69. package/src/controllers/employees/index.ts +210 -0
  70. package/src/controllers/health/index.ts +41 -0
  71. package/src/controllers/index.ts +9 -0
  72. package/src/controllers/meetings/index.ts +251 -0
  73. package/src/controllers/modules/index.ts +74 -0
  74. package/src/controllers/users/index.ts +981 -0
  75. package/src/data/modules.json +94 -0
  76. package/src/database/config/index.ts +26 -0
  77. package/src/database/index.ts +5 -0
  78. package/src/database/seeder/employees/index.ts +35 -0
  79. package/src/database/seeder/index.ts +18 -0
  80. package/src/database/seeder/users/index.ts +44 -0
  81. package/src/index.ts +10 -0
  82. package/src/jobs/index.ts +2 -0
  83. package/src/jobs/mailer/index.ts +154 -0
  84. package/src/jobs/mailer/templates/auth.ts +113 -0
  85. package/src/jobs/mailer/templates/index.ts +1 -0
  86. package/src/jobs/queues/index.ts +125 -0
  87. package/src/middlewares/audit/index.ts +0 -0
  88. package/src/middlewares/guard/index.ts +64 -0
  89. package/src/middlewares/index.ts +5 -0
  90. package/src/middlewares/meeting.ts +45 -0
  91. package/src/models/employees/index.ts +70 -0
  92. package/src/models/index.ts +8 -0
  93. package/src/models/meetings/index.ts +112 -0
  94. package/src/models/passkeys/index.ts +53 -0
  95. package/src/models/queues/index.ts +55 -0
  96. package/src/models/users/index.ts +92 -0
  97. package/src/queues/index.ts +1 -0
  98. package/src/queues/mailer/index.ts +80 -0
  99. package/src/types/index.ts +38 -0
  100. package/src/utils/notifications.ts +66 -0
  101. package/tsconfig.json +18 -0
@@ -0,0 +1,65 @@
1
+ import { Response } from 'express';
2
+ import type { MulterRequest } from '../../types';
3
+ import Users from './../../controllers/users';
4
+ export declare class AuthController extends Users {
5
+ /**
6
+ * Get user profile
7
+ */
8
+ getProfile(req: MulterRequest, res: Response): Promise<void>;
9
+ /**
10
+ * Forgot password - send reset email
11
+ */
12
+ forgotPassword(req: MulterRequest & {
13
+ body?: Record<string, any>;
14
+ }, res: Response): Promise<void>;
15
+ /**
16
+ * Change password
17
+ */
18
+ changePassword(req: MulterRequest & {
19
+ body?: Record<string, any>;
20
+ }, res: Response): Promise<void>;
21
+ /**
22
+ * Start passkey registration challenge
23
+ */
24
+ registerPasskey(req: MulterRequest & {
25
+ body?: Record<string, any>;
26
+ }, res: Response): Promise<void>;
27
+ /**
28
+ * Complete passkey registration
29
+ */
30
+ completePasskeyRegistration(req: MulterRequest & {
31
+ body?: Record<string, any>;
32
+ }, res: Response): Promise<void>;
33
+ /**
34
+ * Get user passkeys
35
+ */
36
+ getPasskeys(req: MulterRequest, res: Response): Promise<void>;
37
+ /**
38
+ * Delete passkey
39
+ */
40
+ deletePasskey(req: MulterRequest, res: Response): Promise<void>;
41
+ /**
42
+ * Start passkey authentication challenge
43
+ */
44
+ authenticatePasskey(req: MulterRequest & {
45
+ body?: Record<string, any>;
46
+ }, res: Response): Promise<void>;
47
+ /**
48
+ * Validate reset token
49
+ */
50
+ validateResetToken(req: MulterRequest & {
51
+ body?: Record<string, any>;
52
+ }, res: Response): Promise<void>;
53
+ /**
54
+ * Reset password with token
55
+ */
56
+ resetPassword(req: MulterRequest & {
57
+ body?: Record<string, any>;
58
+ }, res: Response): Promise<void>;
59
+ /**
60
+ * Complete passkey authentication
61
+ */
62
+ verifyPasskey(req: MulterRequest & {
63
+ body?: Record<string, any>;
64
+ }, res: Response): Promise<void>;
65
+ }
@@ -0,0 +1,525 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.AuthController = void 0;
7
+ const users_1 = __importDefault(require("../../models/users"));
8
+ const passkeys_1 = __importDefault(require("../../models/passkeys"));
9
+ const users_2 = __importDefault(require("./../../controllers/users"));
10
+ const queues_1 = require("../../queues");
11
+ const bcrypt_1 = __importDefault(require("bcrypt"));
12
+ const crypto_1 = __importDefault(require("crypto"));
13
+ class AuthController extends users_2.default {
14
+ /**
15
+ * Get user profile
16
+ */
17
+ async getProfile(req, res) {
18
+ console.log('=== Backend AuthController.getProfile called ===');
19
+ console.log('req.user:', req.user);
20
+ try {
21
+ // Get user ID from JWT token
22
+ const userId = req.user?.userId;
23
+ console.log('Get profile for userId:', userId);
24
+ if (!userId) {
25
+ console.log('No userId in request');
26
+ res.status(401).json({ message: 'Unauthorized' });
27
+ return;
28
+ }
29
+ const user = await users_1.default.findOne({
30
+ _id: userId,
31
+ deleted_at: null
32
+ })
33
+ .select('-password -__v')
34
+ .populate('employee_id', 'first_name last_name middle_name email position department contact_number address')
35
+ .lean()
36
+ .exec();
37
+ console.log('User found:', !!user);
38
+ if (!user) {
39
+ console.log('User not found for id:', userId);
40
+ res.status(404).json({ message: 'User not found' });
41
+ return;
42
+ }
43
+ // Format user data same as login response
44
+ const userData = {
45
+ id: user._id,
46
+ email: user.email,
47
+ first_name: user.first_name,
48
+ last_name: user.last_name,
49
+ middle_name: user.middle_name,
50
+ full_name: `${user.first_name} ${user.last_name}`,
51
+ role: user.role,
52
+ employee_id: user.employee_id,
53
+ is_active: user.is_active,
54
+ created_at: user.created_at
55
+ };
56
+ const response = {
57
+ message: 'Profile retrieved successfully',
58
+ user: userData
59
+ };
60
+ console.log('Sending response:', response);
61
+ console.log('=== Backend AuthController.getProfile completed ===');
62
+ res.json(response);
63
+ }
64
+ catch (err) {
65
+ console.error('Get profile error', err);
66
+ console.log('=== Backend AuthController.getProfile failed ===');
67
+ res.status(500).json({ message: 'Failed to retrieve profile', error: String(err) });
68
+ }
69
+ }
70
+ /**
71
+ * Forgot password - send reset email
72
+ */
73
+ async forgotPassword(req, res) {
74
+ try {
75
+ const body = req.body || {};
76
+ const { email } = body;
77
+ if (!email) {
78
+ res.status(400).json({ message: 'Email is required' });
79
+ return;
80
+ }
81
+ // Find user by email
82
+ const user = await users_1.default.findOne({
83
+ email: email.trim().toLowerCase(),
84
+ deleted_at: null,
85
+ is_active: true
86
+ }).lean().exec();
87
+ if (!user) {
88
+ res.status(404).json({ message: 'User with this email not found' });
89
+ return;
90
+ }
91
+ // Generate reset token
92
+ const resetToken = crypto_1.default.randomBytes(32).toString('hex');
93
+ const resetTokenExpiry = Date.now() + (60 * 60 * 1000); // 1 hour
94
+ // Update user with reset token
95
+ await users_1.default.updateOne({ _id: user._id }, {
96
+ reset_token: resetToken,
97
+ reset_token_expires: resetTokenExpiry,
98
+ updated_at: Date.now()
99
+ });
100
+ // Add to email queue
101
+ await (0, queues_1.getEmailQueue)().add('send-email', {
102
+ type: 'password_reset',
103
+ data: {
104
+ user_id: user._id,
105
+ email: user.email,
106
+ reset_token: resetToken,
107
+ name: user.full_name || `${user.first_name} ${user.last_name}`
108
+ }
109
+ });
110
+ res.json({
111
+ message: 'Password reset email sent successfully',
112
+ success: true
113
+ });
114
+ }
115
+ catch (err) {
116
+ console.error('Forgot password error', err);
117
+ res.status(500).json({ message: 'Failed to send password reset email', error: String(err) });
118
+ }
119
+ }
120
+ /**
121
+ * Change password
122
+ */
123
+ async changePassword(req, res) {
124
+ try {
125
+ const body = req.body || {};
126
+ const { current_password, new_password } = body;
127
+ if (!current_password || !new_password) {
128
+ res.status(400).json({ message: 'Current password and new password are required' });
129
+ return;
130
+ }
131
+ // Get user ID from JWT token
132
+ const userId = req.user?.userId;
133
+ if (!userId) {
134
+ res.status(401).json({ message: 'Unauthorized' });
135
+ return;
136
+ }
137
+ // Find user
138
+ const user = await users_1.default.findOne({
139
+ _id: userId,
140
+ deleted_at: null,
141
+ is_active: true
142
+ }).exec();
143
+ if (!user) {
144
+ res.status(404).json({ message: 'User not found' });
145
+ return;
146
+ }
147
+ // Verify current password
148
+ const isCurrentPasswordValid = await bcrypt_1.default.compare(current_password, user.password);
149
+ if (!isCurrentPasswordValid) {
150
+ res.status(400).json({ message: 'Current password is incorrect' });
151
+ return;
152
+ }
153
+ // Hash new password
154
+ const hashedNewPassword = await bcrypt_1.default.hash(new_password, 10);
155
+ // Update password
156
+ await users_1.default.updateOne({ _id: userId }, {
157
+ password: hashedNewPassword,
158
+ updated_at: Date.now()
159
+ });
160
+ // Add to email queue
161
+ await (0, queues_1.getEmailQueue)().add('send-email', {
162
+ type: 'password_changed',
163
+ data: {
164
+ user_id: user._id,
165
+ email: user.email,
166
+ name: `${user.first_name} ${user.middle_name ? user.middle_name + ' ' : ''}${user.last_name}`
167
+ }
168
+ });
169
+ res.json({
170
+ message: 'Password changed successfully',
171
+ success: true
172
+ });
173
+ }
174
+ catch (err) {
175
+ console.error('Change password error', err);
176
+ res.status(500).json({ message: 'Failed to change password', error: String(err) });
177
+ }
178
+ }
179
+ /**
180
+ * Start passkey registration challenge
181
+ */
182
+ async registerPasskey(req, res) {
183
+ try {
184
+ const body = req.body || {};
185
+ const { user_id, email } = body;
186
+ if (!user_id || !email) {
187
+ res.status(400).json({ message: 'User ID and email are required' });
188
+ return;
189
+ }
190
+ // Find user
191
+ const user = await users_1.default.findOne({
192
+ _id: user_id,
193
+ email: email.trim().toLowerCase(),
194
+ deleted_at: null,
195
+ is_active: true
196
+ }).lean().exec();
197
+ if (!user) {
198
+ res.status(404).json({ message: 'User not found' });
199
+ return;
200
+ }
201
+ // Generate WebAuthn challenge
202
+ const challenge = crypto_1.default.randomBytes(32);
203
+ const deviceId = crypto_1.default.randomBytes(16).toString('hex');
204
+ res.json({
205
+ challenge: challenge.toString('base64'),
206
+ device_id: deviceId,
207
+ device_name: `Device ${new Date().toLocaleDateString()}`
208
+ });
209
+ }
210
+ catch (err) {
211
+ console.error('Register passkey challenge error', err);
212
+ res.status(500).json({ message: 'Failed to start registration', error: String(err) });
213
+ }
214
+ }
215
+ /**
216
+ * Complete passkey registration
217
+ */
218
+ async completePasskeyRegistration(req, res) {
219
+ try {
220
+ const body = req.body || {};
221
+ const { user_id, credential_id, device_id, name, public_key } = body;
222
+ if (!user_id || !credential_id || !device_id || !name || !public_key) {
223
+ res.status(400).json({ message: 'All fields are required' });
224
+ return;
225
+ }
226
+ // Check if passkey already exists
227
+ const existingPasskey = await passkeys_1.default.findOne({
228
+ credential_id,
229
+ deleted_at: null
230
+ }).lean().exec();
231
+ if (existingPasskey) {
232
+ res.status(409).json({ message: 'Passkey already registered' });
233
+ return;
234
+ }
235
+ // Get user info for email
236
+ const user = await users_1.default.findOne({ _id: user_id }).lean().exec();
237
+ if (!user) {
238
+ res.status(404).json({ message: 'User not found' });
239
+ return;
240
+ }
241
+ // Create new passkey - public_key is COSE format CBOR from WebAuthn
242
+ await passkeys_1.default.create({
243
+ user_id,
244
+ credential_id,
245
+ device_id,
246
+ name,
247
+ public_key: public_key, // Store COSE public key
248
+ created_at: Date.now(),
249
+ updated_at: Date.now()
250
+ });
251
+ // Add to email queue
252
+ await (0, queues_1.getEmailQueue)().add('send-email', {
253
+ type: 'passkey',
254
+ data: {
255
+ user_id: user._id,
256
+ email: user.email,
257
+ name: `${user.first_name} ${user.middle_name ? user.middle_name + ' ' : ''}${user.last_name}`,
258
+ device_name: name
259
+ }
260
+ });
261
+ res.json({
262
+ message: 'Passkey registered successfully',
263
+ success: true,
264
+ device_id,
265
+ device_name: name
266
+ });
267
+ }
268
+ catch (err) {
269
+ console.error('Complete passkey registration error', err);
270
+ res.status(500).json({ message: 'Failed to register passkey', error: String(err) });
271
+ }
272
+ }
273
+ /**
274
+ * Get user passkeys
275
+ */
276
+ async getPasskeys(req, res) {
277
+ try {
278
+ const { user_id } = req.params;
279
+ if (!user_id) {
280
+ res.status(400).json({ message: 'User ID is required' });
281
+ return;
282
+ }
283
+ const passkeys = await passkeys_1.default.find({
284
+ user_id,
285
+ deleted_at: null,
286
+ is_active: true
287
+ })
288
+ .select('-__v')
289
+ .sort({ created_at: -1 })
290
+ .lean()
291
+ .exec();
292
+ res.json({
293
+ message: 'Passkeys retrieved successfully',
294
+ passkeys
295
+ });
296
+ }
297
+ catch (err) {
298
+ console.error('Get passkeys error', err);
299
+ res.status(500).json({ message: 'Failed to retrieve passkeys', error: String(err) });
300
+ }
301
+ }
302
+ /**
303
+ * Delete passkey
304
+ */
305
+ async deletePasskey(req, res) {
306
+ try {
307
+ const { passkeyId } = req.params;
308
+ if (!passkeyId) {
309
+ res.status(400).json({ message: 'Passkey ID is required' });
310
+ return;
311
+ }
312
+ // Soft delete the passkey
313
+ await passkeys_1.default.updateOne({ _id: passkeyId }, { deleted_at: Date.now(), is_active: false });
314
+ res.json({
315
+ message: 'Passkey deleted successfully',
316
+ success: true
317
+ });
318
+ }
319
+ catch (err) {
320
+ console.error('Delete passkey error', err);
321
+ res.status(500).json({ message: 'Failed to delete passkey', error: String(err) });
322
+ }
323
+ }
324
+ /**
325
+ * Start passkey authentication challenge
326
+ */
327
+ async authenticatePasskey(req, res) {
328
+ try {
329
+ const body = req.body || {};
330
+ const { email, device_id } = body;
331
+ if (!email || !device_id) {
332
+ res.status(400).json({ message: 'Email and device ID are required' });
333
+ return;
334
+ }
335
+ // Find user by email
336
+ const user = await users_1.default.findOne({
337
+ email: email.trim().toLowerCase(),
338
+ deleted_at: null,
339
+ is_active: true
340
+ }).lean().exec();
341
+ if (!user) {
342
+ res.status(401).json({ message: 'Invalid credentials' });
343
+ return;
344
+ }
345
+ // Find the latest active passkey for this user and device
346
+ const passkey = await passkeys_1.default.findOne({
347
+ user_id: user._id,
348
+ device_id,
349
+ is_active: true,
350
+ deleted_at: null
351
+ })
352
+ .sort({ created_at: -1 })
353
+ .lean()
354
+ .exec();
355
+ if (!passkey) {
356
+ res.status(401).json({ message: 'No passkey found for this device' });
357
+ return;
358
+ }
359
+ // Generate WebAuthn challenge
360
+ const challenge = crypto_1.default.randomBytes(32);
361
+ res.json({
362
+ challenge: challenge.toString('base64'),
363
+ user_id: user._id,
364
+ credential_id: passkey.credential_id
365
+ });
366
+ }
367
+ catch (err) {
368
+ console.error('Passkey authentication challenge error', err);
369
+ res.status(500).json({ message: 'Failed to start authentication', error: String(err) });
370
+ }
371
+ }
372
+ /**
373
+ * Validate reset token
374
+ */
375
+ async validateResetToken(req, res) {
376
+ try {
377
+ const body = req.body || {};
378
+ const { token } = body;
379
+ if (!token) {
380
+ res.status(400).json({ message: 'Token is required' });
381
+ return;
382
+ }
383
+ // Find user with valid reset token
384
+ const user = await users_1.default.findOne({
385
+ reset_token: token,
386
+ reset_token_expires: { $gt: Date.now() },
387
+ deleted_at: null,
388
+ is_active: true
389
+ }).lean().exec();
390
+ if (!user) {
391
+ res.status(400).json({ message: 'Invalid or expired token' });
392
+ return;
393
+ }
394
+ res.json({
395
+ message: 'Token is valid',
396
+ valid: true
397
+ });
398
+ }
399
+ catch (err) {
400
+ console.error('Validate reset token error', err);
401
+ res.status(500).json({ message: 'Failed to validate token', error: String(err) });
402
+ }
403
+ }
404
+ /**
405
+ * Reset password with token
406
+ */
407
+ async resetPassword(req, res) {
408
+ try {
409
+ const body = req.body || {};
410
+ const { token, new_password } = body;
411
+ if (!token || !new_password) {
412
+ res.status(400).json({ message: 'Token and new password are required' });
413
+ return;
414
+ }
415
+ // Find user with valid reset token
416
+ const user = await users_1.default.findOne({
417
+ reset_token: token,
418
+ reset_token_expires: { $gt: Date.now() },
419
+ deleted_at: null,
420
+ is_active: true
421
+ }).exec();
422
+ if (!user) {
423
+ res.status(400).json({ message: 'Invalid or expired token' });
424
+ return;
425
+ }
426
+ // Hash new password
427
+ const hashedPassword = await bcrypt_1.default.hash(new_password, 10);
428
+ // Update password and clear reset token
429
+ await users_1.default.updateOne({ _id: user._id }, {
430
+ password: hashedPassword,
431
+ reset_token: null,
432
+ reset_token_expires: null,
433
+ updated_at: Date.now()
434
+ });
435
+ // Add to email queue for password changed notification
436
+ await (0, queues_1.getEmailQueue)().add('send-email', {
437
+ type: 'password_changed',
438
+ data: {
439
+ user_id: user._id,
440
+ email: user.email,
441
+ name: `${user.first_name} ${user.middle_name ? user.middle_name + ' ' : ''}${user.last_name}`
442
+ }
443
+ });
444
+ res.json({
445
+ message: 'Password reset successfully',
446
+ success: true
447
+ });
448
+ }
449
+ catch (err) {
450
+ console.error('Reset password error', err);
451
+ res.status(500).json({ message: 'Failed to reset password', error: String(err) });
452
+ }
453
+ }
454
+ /**
455
+ * Complete passkey authentication
456
+ */
457
+ async verifyPasskey(req, res) {
458
+ try {
459
+ const body = req.body || {};
460
+ const { email, device_id, credential_id, authenticator_data } = body;
461
+ if (!email || !device_id || !credential_id || !authenticator_data) {
462
+ res.status(400).json({ message: 'All authentication data is required' });
463
+ return;
464
+ }
465
+ // Find user by email
466
+ const user = await users_1.default.findOne({
467
+ email: email.trim().toLowerCase(),
468
+ deleted_at: null,
469
+ is_active: true
470
+ }).lean().exec();
471
+ if (!user) {
472
+ res.status(401).json({ message: 'Invalid credentials' });
473
+ return;
474
+ }
475
+ // Find the passkey
476
+ const passkey = await passkeys_1.default.findOne({
477
+ user_id: user._id,
478
+ credential_id,
479
+ device_id,
480
+ is_active: true,
481
+ deleted_at: null
482
+ }).lean().exec();
483
+ if (!passkey) {
484
+ res.status(401).json({ message: 'Invalid passkey' });
485
+ return;
486
+ }
487
+ // For now, trust the client-side WebAuthn verification
488
+ // The authenticator already verified the user locally with biometrics
489
+ // We'll implement full cryptographic signature verification in a future update
490
+ // Update last used timestamp
491
+ await passkeys_1.default.updateOne({ _id: passkey._id }, { last_used_at: Date.now(), updated_at: Date.now() });
492
+ // Generate JWT token
493
+ const jwtSecret = process.env.JWT_SECRET;
494
+ if (!jwtSecret) {
495
+ throw new Error('JWT_SECRET environment variable is required');
496
+ }
497
+ const jwt = require('jsonwebtoken');
498
+ const token = jwt.sign({
499
+ userId: user._id,
500
+ email: user.email,
501
+ role: user.role
502
+ }, jwtSecret, { expiresIn: '24h' });
503
+ res.json({
504
+ message: 'Passkey authentication successful',
505
+ token,
506
+ user: {
507
+ id: user._id,
508
+ email: user.email,
509
+ first_name: user.first_name,
510
+ middle_name: user.middle_name,
511
+ last_name: user.last_name,
512
+ full_name: `${user.first_name} ${user.last_name}`,
513
+ role: user.role,
514
+ employee_id: user.employee_id,
515
+ is_active: user.is_active
516
+ }
517
+ });
518
+ }
519
+ catch (err) {
520
+ console.error('Passkey verification error', err);
521
+ res.status(500).json({ message: 'Passkey authentication failed', error: String(err) });
522
+ }
523
+ }
524
+ }
525
+ exports.AuthController = AuthController;
@@ -0,0 +1,38 @@
1
+ import { Response } from 'express';
2
+ import type { MulterRequest } from '../../types';
3
+ declare class Employees {
4
+ /**
5
+ * Create a new employee
6
+ * Accepts form data with:
7
+ * - first_name (required)
8
+ * - middle_name (optional)
9
+ * - last_name (required)
10
+ * - address (optional)
11
+ * - contact_number (optional)
12
+ * - email (optional)
13
+ * - position (optional)
14
+ * - department (optional)
15
+ */
16
+ create(req: MulterRequest & {
17
+ body?: Record<string, any>;
18
+ }, res: Response): Promise<void>;
19
+ /**
20
+ * Get all employees
21
+ */
22
+ getAll(req: MulterRequest, res: Response): Promise<void>;
23
+ /**
24
+ * Get employee by ID
25
+ */
26
+ getById(req: MulterRequest, res: Response): Promise<void>;
27
+ /**
28
+ * Update employee
29
+ */
30
+ update(req: MulterRequest & {
31
+ body?: Record<string, any>;
32
+ }, res: Response): Promise<void>;
33
+ /**
34
+ * Soft delete employee
35
+ */
36
+ delete(req: MulterRequest, res: Response): Promise<void>;
37
+ }
38
+ export default Employees;