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