@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,210 @@
1
+ import { Response } from 'express'
2
+ import EmployeesModel from '../../models/employees'
3
+ import type { MulterRequest } from '../../types'
4
+
5
+ class Employees {
6
+ /**
7
+ * Create a new employee
8
+ * Accepts form data with:
9
+ * - first_name (required)
10
+ * - middle_name (optional)
11
+ * - last_name (required)
12
+ * - address (optional)
13
+ * - contact_number (optional)
14
+ * - email (optional)
15
+ * - position (optional)
16
+ * - department (optional)
17
+ */
18
+ public async create(req: MulterRequest & { body?: Record<string, any> }, res: Response): Promise<void> {
19
+ try {
20
+ const body = req.body || {}
21
+
22
+ const firstName = (body.first_name ?? '').toString().trim()
23
+ const lastName = (body.last_name ?? '').toString().trim()
24
+
25
+ if (!firstName || !lastName) {
26
+ res.status(400).json({ message: 'first_name and last_name are required' })
27
+ return
28
+ }
29
+
30
+ // Create employee record
31
+ const employee = await EmployeesModel.create({
32
+ first_name: firstName,
33
+ middle_name: (body.middle_name ?? null),
34
+ last_name: lastName,
35
+ address: (body.address ?? null),
36
+ contact_number: (body.contact_number ?? null),
37
+ email: (body.email ?? null),
38
+ position: (body.position ?? null),
39
+ department: (body.department ?? null),
40
+ created_at: Date.now(),
41
+ updated_at: Date.now()
42
+ })
43
+
44
+ res.status(201).json({
45
+ message: 'Employee created successfully',
46
+ employee: {
47
+ id: employee._id,
48
+ first_name: employee.first_name,
49
+ middle_name: employee.middle_name,
50
+ last_name: employee.last_name,
51
+ email: employee.email,
52
+ position: employee.position,
53
+ department: employee.department,
54
+ contact_number: employee.contact_number,
55
+ address: employee.address
56
+ }
57
+ })
58
+ } catch (err) {
59
+ console.error('Create employee error', err)
60
+ res.status(500).json({ message: 'Failed to create employee', error: String(err) })
61
+ }
62
+ }
63
+
64
+ /**
65
+ * Get all employees
66
+ */
67
+ public async getAll(req: MulterRequest, res: Response): Promise<void> {
68
+ try {
69
+ const employees = await EmployeesModel.find({ deleted_at: null })
70
+ .select('-__v')
71
+ .sort({ created_at: -1 })
72
+ .lean()
73
+ .exec()
74
+
75
+ res.json({
76
+ message: 'Employees retrieved successfully',
77
+ count: employees.length,
78
+ employees
79
+ })
80
+ } catch (err) {
81
+ console.error('Get employees error', err)
82
+ res.status(500).json({ message: 'Failed to retrieve employees', error: String(err) })
83
+ }
84
+ }
85
+
86
+ /**
87
+ * Get employee by ID
88
+ */
89
+ public async getById(req: MulterRequest, res: Response): Promise<void> {
90
+ try {
91
+ const { id } = req.params
92
+
93
+ if (!id) {
94
+ res.status(400).json({ message: 'Employee ID is required' })
95
+ return
96
+ }
97
+
98
+ const employee = await EmployeesModel.findOne({
99
+ _id: id,
100
+ deleted_at: null
101
+ })
102
+ .select('-__v')
103
+ .lean()
104
+ .exec()
105
+
106
+ if (!employee) {
107
+ res.status(404).json({ message: 'Employee not found' })
108
+ return
109
+ }
110
+
111
+ res.json({
112
+ message: 'Employee retrieved successfully',
113
+ employee
114
+ })
115
+ } catch (err) {
116
+ console.error('Get employee error', err)
117
+ res.status(500).json({ message: 'Failed to retrieve employee', error: String(err) })
118
+ }
119
+ }
120
+
121
+ /**
122
+ * Update employee
123
+ */
124
+ public async update(req: MulterRequest & { body?: Record<string, any> }, res: Response): Promise<void> {
125
+ try {
126
+ const { id } = req.params
127
+ const body = req.body || {}
128
+
129
+ if (!id) {
130
+ res.status(400).json({ message: 'Employee ID is required' })
131
+ return
132
+ }
133
+
134
+ const updateData: any = {
135
+ updated_at: Date.now()
136
+ }
137
+
138
+ // Only update provided fields
139
+ const allowedFields = ['first_name', 'middle_name', 'last_name', 'address', 'contact_number', 'email', 'position', 'department']
140
+ for (const field of allowedFields) {
141
+ if (body[field] !== undefined) {
142
+ updateData[field] = body[field]
143
+ }
144
+ }
145
+
146
+ const employee = await EmployeesModel.findOneAndUpdate(
147
+ { _id: id, deleted_at: null },
148
+ updateData,
149
+ { new: true, runValidators: true }
150
+ )
151
+ .select('-__v')
152
+ .lean()
153
+ .exec()
154
+
155
+ if (!employee) {
156
+ res.status(404).json({ message: 'Employee not found' })
157
+ return
158
+ }
159
+
160
+ res.json({
161
+ message: 'Employee updated successfully',
162
+ employee
163
+ })
164
+ } catch (err) {
165
+ console.error('Update employee error', err)
166
+ res.status(500).json({ message: 'Failed to update employee', error: String(err) })
167
+ }
168
+ }
169
+
170
+ /**
171
+ * Soft delete employee
172
+ */
173
+ public async delete(req: MulterRequest, res: Response): Promise<void> {
174
+ try {
175
+ const { id } = req.params
176
+
177
+ if (!id) {
178
+ res.status(400).json({ message: 'Employee ID is required' })
179
+ return
180
+ }
181
+
182
+ const employee = await EmployeesModel.findOneAndUpdate(
183
+ { _id: id, deleted_at: null },
184
+ { deleted_at: Date.now() },
185
+ { new: true }
186
+ )
187
+ .lean()
188
+ .exec()
189
+
190
+ if (!employee) {
191
+ res.status(404).json({ message: 'Employee not found' })
192
+ return
193
+ }
194
+
195
+ res.json({
196
+ message: 'Employee deleted successfully',
197
+ employee: {
198
+ id: employee._id,
199
+ first_name: employee.first_name,
200
+ last_name: employee.last_name
201
+ }
202
+ })
203
+ } catch (err) {
204
+ console.error('Delete employee error', err)
205
+ res.status(500).json({ message: 'Failed to delete employee', error: String(err) })
206
+ }
207
+ }
208
+ }
209
+
210
+ export default Employees;
@@ -0,0 +1,41 @@
1
+ import { Request, Response } from 'express';
2
+ import fs from 'fs';
3
+ import path from 'path';
4
+
5
+ /**
6
+ * Health Check Controller
7
+ * Serves a fancy HTML page to show API is running
8
+ */
9
+ export class HealthController {
10
+ async healthCheck(req: Request, res: Response) {
11
+ try {
12
+ // Get the path to the HTML file
13
+ const htmlPath = path.join(__dirname, '../../../public/health.html');
14
+
15
+ // Read the HTML file
16
+ const htmlContent = fs.readFileSync(htmlPath, 'utf8');
17
+
18
+ // Set appropriate headers
19
+ res.setHeader('Content-Type', 'text/html');
20
+ res.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate');
21
+ res.setHeader('Pragma', 'no-cache');
22
+ res.setHeader('Expires', '0');
23
+
24
+ // Send the HTML content
25
+ res.status(200).send(htmlContent);
26
+ } catch (error) {
27
+ console.error('Health check error:', error);
28
+
29
+ // If HTML file not found, return simple JSON response
30
+ res.status(500).json({
31
+ status: 'error',
32
+ message: 'Health check page unavailable',
33
+ timestamp: new Date().toISOString(),
34
+ api: 'Inventory Management System',
35
+ version: '1.0.0'
36
+ });
37
+ }
38
+ }
39
+ }
40
+
41
+ export default HealthController;
@@ -0,0 +1,9 @@
1
+ import EmployeesController from './employees';
2
+ import UsersController from './users';
3
+ import { ModulesController } from './modules';
4
+ import { AuthController } from './auth';
5
+ import { HealthController } from './health';
6
+ import MeetingsController from './meetings';
7
+
8
+ export { EmployeesController, UsersController, ModulesController, AuthController, HealthController, MeetingsController };
9
+ export default { EmployeesController, UsersController, ModulesController, AuthController, HealthController, MeetingsController };
@@ -0,0 +1,251 @@
1
+ import { Meetings as MeetingModel, Users as UserModel } from '../../models';
2
+ // import { canJoinMeeting } from '../middlewares/meeting';
3
+ // import { sendNotification, sendMeetingInvitation } from '../utils/notifications';
4
+
5
+ class Meetings {
6
+ /**
7
+ * Create a new meeting
8
+ */
9
+ public static async create(req: any, res: any): Promise<void> {
10
+ try {
11
+ const { title, description, meeting_type = 'video', scheduled_at, participants, max_participants = 50 } = req.body;
12
+ const userId = req.user.userId;
13
+
14
+ // Generate unique meeting URL
15
+ const meeting_url = `${Date.now()}-${Math.random().toString(36).substring(2, 15)}`;
16
+
17
+ // Process participants - handle both user IDs and email objects
18
+ const participantsArray: any[] = [];
19
+ if (Array.isArray(participants)) {
20
+ for (const participant of participants) {
21
+ if (typeof participant === 'string') {
22
+ // Check if it's an email or user ID
23
+ if (participant.includes('@')) {
24
+ // Email provided - try to find user or store email
25
+ const user = await UserModel.findOne({ email: participant.toLowerCase(), deleted_at: null }).lean().exec();
26
+ if (user) {
27
+ participantsArray.push({
28
+ user_id: user._id,
29
+ is_active: false
30
+ });
31
+ } else {
32
+ // User doesn't exist - store as pending invitation
33
+ participantsArray.push({
34
+ email: participant,
35
+ is_active: false,
36
+ status: 'pending'
37
+ });
38
+ }
39
+ } else {
40
+ // Direct user ID
41
+ participantsArray.push({
42
+ user_id: participant,
43
+ is_active: false
44
+ });
45
+ }
46
+ } else if (typeof participant === 'object' && participant !== null) {
47
+ // Object with id or email
48
+ if (participant.id) {
49
+ participantsArray.push({
50
+ user_id: participant.id,
51
+ is_active: false
52
+ });
53
+ } else if (participant.email) {
54
+ // Email provided - try to find user or store email
55
+ const user = await UserModel.findOne({ email: participant.email.toLowerCase(), deleted_at: null }).lean().exec();
56
+ if (user) {
57
+ participantsArray.push({
58
+ user_id: user._id,
59
+ is_active: false
60
+ });
61
+ } else {
62
+ // User doesn't exist - store as pending invitation (could be enhanced later)
63
+ participantsArray.push({
64
+ email: participant.email,
65
+ is_active: false,
66
+ status: 'pending'
67
+ });
68
+ }
69
+ }
70
+ }
71
+ }
72
+ }
73
+
74
+ const meeting = new MeetingModel({
75
+ title,
76
+ description,
77
+ host_id: userId,
78
+ meeting_type,
79
+ scheduled_at: scheduled_at ? new Date(scheduled_at).getTime() : Date.now(),
80
+ max_participants,
81
+ meeting_url,
82
+ participants: participantsArray
83
+ });
84
+
85
+ await meeting.save();
86
+ await meeting.populate('host_id', 'first_name last_name email');
87
+ await meeting.populate('participants.user_id', 'first_name last_name email');
88
+
89
+ // Send notifications to participants
90
+ // if (participants && participants.length > 0) {
91
+ // await sendMeetingInvitation(meeting._id, participants, title, description, userId);
92
+ // }
93
+
94
+ res.status(201).json({ success: true, meeting });
95
+ } catch (error) {
96
+ console.error('Error creating meeting:', error);
97
+ res.status(500).json({ error: 'Failed to create meeting' });
98
+ }
99
+ }
100
+
101
+ /**
102
+ * Join a meeting
103
+ */
104
+ public static async join(req: any, res: any): Promise<void> {
105
+ try {
106
+ const { meetingId } = req.params;
107
+ const userId = req.user.userId;
108
+
109
+ const meeting = req.meeting;
110
+
111
+ // Mark user as active participant
112
+ const participantIndex = meeting.participants.findIndex((p: any) => p.user_id.toString() === userId);
113
+ if (participantIndex !== -1) {
114
+ meeting.participants[participantIndex].is_active = true;
115
+ meeting.participants[participantIndex].joined_at = Date.now();
116
+ }
117
+
118
+ // Start meeting if not started
119
+ if (!meeting.started_at) {
120
+ meeting.started_at = Date.now();
121
+ }
122
+
123
+ await meeting.save();
124
+
125
+ // Notify other participants
126
+ const otherParticipants = meeting.participants
127
+ .filter((p: any) => p.user_id.toString() !== userId && p.is_active)
128
+ .map((p: any) => p.user_id.toString());
129
+
130
+ // if (otherParticipants.length > 0) {
131
+ // await sendNotification(
132
+ // otherParticipants,
133
+ // 'User Joined Meeting',
134
+ // `${req.user.email} has joined the meeting`,
135
+ // { type: 'user_joined', userId, meetingId },
136
+ // 'meeting_update'
137
+ // );
138
+ // }
139
+
140
+ res.json({ success: true, meeting_url: meeting.meeting_url });
141
+ } catch (error) {
142
+ console.error('Error joining meeting:', error);
143
+ res.status(500).json({ error: 'Failed to join meeting' });
144
+ }
145
+ }
146
+
147
+ /**
148
+ * Leave a meeting
149
+ */
150
+ public static async leave(req: any, res: any): Promise<void> {
151
+ try {
152
+ const { meetingId } = req.params;
153
+ const userId = req.user.userId;
154
+
155
+ const meeting = req.meeting;
156
+
157
+ // Mark user as inactive participant
158
+ const participantIndex = meeting.participants.findIndex((p: any) => p.user_id.toString() === userId);
159
+ if (participantIndex !== -1) {
160
+ meeting.participants[participantIndex].is_active = false;
161
+ meeting.participants[participantIndex].left_at = Date.now();
162
+ }
163
+
164
+ // Check if meeting should end (only host left or no active participants)
165
+ const isHost = meeting.host_id.toString() === userId;
166
+ const activeParticipants = meeting.participants.filter((p: any) => p.is_active);
167
+
168
+ if (isHost || activeParticipants.length === 0) {
169
+ meeting.ended_at = Date.now();
170
+ meeting.is_active = false;
171
+ }
172
+
173
+ await meeting.save();
174
+
175
+ // Notify other participants
176
+ const remainingParticipants = activeParticipants
177
+ .filter((p: any) => p.user_id.toString() !== userId)
178
+ .map((p: any) => p.user_id.toString());
179
+
180
+ // if (remainingParticipants.length > 0) {
181
+ // await sendNotification(
182
+ // remainingParticipants,
183
+ // 'User Left Meeting',
184
+ // `${req.user.email} has left the meeting`,
185
+ // { type: 'user_left', userId, meetingId },
186
+ // 'meeting_update'
187
+ // );
188
+ // }
189
+
190
+ res.json({ success: true });
191
+ } catch (error) {
192
+ console.error('Error leaving meeting:', error);
193
+ res.status(500).json({ error: 'Failed to leave meeting' });
194
+ }
195
+ }
196
+
197
+ /**
198
+ * Get user's meetings
199
+ */
200
+ public static async getMyMeetings(req: any, res: any): Promise<void> {
201
+ try {
202
+ const userId = req.user.userId;
203
+ const { status = 'upcoming' } = req.query;
204
+
205
+ let filter: any = {
206
+ $or: [
207
+ { host_id: userId },
208
+ { 'participants.user_id': userId }
209
+ ]
210
+ };
211
+
212
+ if (status === 'upcoming') {
213
+ filter.scheduled_at = { $gt: Date.now() };
214
+ } else if (status === 'ongoing') {
215
+ filter.started_at = { $ne: null };
216
+ filter.ended_at = null;
217
+ filter.is_active = true;
218
+ } else if (status === 'completed') {
219
+ filter.ended_at = { $ne: null };
220
+ }
221
+
222
+ const meetings = await MeetingModel.find(filter)
223
+ .populate('host_id', 'first_name last_name email')
224
+ .populate('participants.user_id', 'first_name last_name email')
225
+ .sort({ scheduled_at: -1 });
226
+
227
+ res.json({ success: true, meetings });
228
+ } catch (error) {
229
+ console.error('Error fetching meetings:', error);
230
+ res.status(500).json({ error: 'Failed to fetch meetings' });
231
+ }
232
+ }
233
+
234
+ /**
235
+ * Get meeting details
236
+ */
237
+ public static async getMeeting(req: any, res: any): Promise<void> {
238
+ try {
239
+ const meeting = req.meeting;
240
+
241
+ await meeting.populate('host_id', 'first_name last_name email');
242
+ await meeting.populate('participants.user_id', 'first_name last_name email');
243
+
244
+ res.json({ success: true, meeting });
245
+ } catch (error) {
246
+ console.error('Error fetching meeting:', error);
247
+ res.status(500).json({ error: 'Failed to fetch meeting' });
248
+ }
249
+ }
250
+ }
251
+ export default Meetings;
@@ -0,0 +1,74 @@
1
+ import { Request, Response } from 'express';
2
+ import * as fs from 'fs';
3
+ import * as path from 'path';
4
+ import { runSeeders } from '../../database/seeder';
5
+
6
+ export class ModulesController {
7
+ async getModules(req: Request, res: Response) {
8
+ try {
9
+ // Try multiple possible paths for modules.json
10
+ let modulesPath: string | undefined;
11
+
12
+ // List of possible paths to check
13
+ const possiblePaths = [
14
+ // Development path
15
+ path.join(__dirname, '../data/modules.json'),
16
+ // Production path (when called from user-api)
17
+ path.join(__dirname, '../../data/modules.json'),
18
+ // Alternative production path
19
+ path.join(__dirname, '../../../backend/src/data/modules.json'),
20
+ // Relative to backend project root
21
+ path.join(process.cwd(), 'src/data/modules.json'),
22
+ // Relative to backend project root (dist)
23
+ path.join(process.cwd(), 'dist/data/modules.json'),
24
+ // Absolute path fallback
25
+ '/Users/abdegracia/aldrin-degracia/code/rnd/inventory-management-system/backend/src/data/modules.json'
26
+ ];
27
+
28
+ // Find the first existing path
29
+ for (const testPath of possiblePaths) {
30
+ if (fs.existsSync(testPath)) {
31
+ modulesPath = testPath;
32
+ break;
33
+ }
34
+ }
35
+
36
+ if (!modulesPath) {
37
+ throw new Error('modules.json file not found in any expected location');
38
+ }
39
+
40
+ const modulesData = JSON.parse(fs.readFileSync(modulesPath, 'utf-8'));
41
+
42
+ res.json({
43
+ success: true,
44
+ data: modulesData
45
+ });
46
+ } catch (error) {
47
+ console.error('Error reading modules data:', error);
48
+ res.status(500).json({
49
+ success: false,
50
+ message: 'Failed to load modules data',
51
+ error: String(error)
52
+ });
53
+ }
54
+ }
55
+
56
+ async runSeeders(req: Request, res: Response) {
57
+ try {
58
+ console.log('Running seeders via API call...');
59
+ await runSeeders();
60
+
61
+ res.json({
62
+ success: true,
63
+ message: 'Seeders executed successfully'
64
+ });
65
+ } catch (error) {
66
+ console.error('Error running seeders:', error);
67
+ res.status(500).json({
68
+ success: false,
69
+ message: 'Failed to run seeders',
70
+ error: String(error)
71
+ });
72
+ }
73
+ }
74
+ }