@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,64 @@
1
+ import { Request, Response, NextFunction } from 'express';
2
+ import jwt from 'jsonwebtoken';
3
+
4
+ interface AuthenticatedRequest extends Request {
5
+ user?: {
6
+ userId: string;
7
+ email: string;
8
+ role: string;
9
+ };
10
+ }
11
+
12
+ export const authenticateToken = (req: AuthenticatedRequest, res: Response, next: NextFunction): void => {
13
+ const authHeader = req.headers['authorization'];
14
+ const token = authHeader && authHeader.split(' ')[1]; // Bearer TOKEN
15
+
16
+ if (!token) {
17
+ res.status(401).json({ message: 'Access token required' });
18
+ return;
19
+ }
20
+
21
+ const jwtSecret = process.env.JWT_SECRET;
22
+ if (!jwtSecret) {
23
+ console.error('JWT_SECRET environment variable is required');
24
+ res.status(500).json({ message: 'Server configuration error' });
25
+ return;
26
+ }
27
+
28
+ jwt.verify(token, jwtSecret, (err: any, decoded: any) => {
29
+ if (err) {
30
+ if (err.name === 'TokenExpiredError') {
31
+ res.status(401).json({ message: 'Token expired' });
32
+ return;
33
+ }
34
+ res.status(403).json({ message: 'Invalid token' });
35
+ return;
36
+ }
37
+
38
+ req.user = {
39
+ userId: decoded.userId,
40
+ email: decoded.email,
41
+ role: decoded.role
42
+ };
43
+
44
+ next();
45
+ });
46
+ };
47
+
48
+ export const requireRole = (roles: string[]) => {
49
+ return (req: AuthenticatedRequest, res: Response, next: NextFunction): void => {
50
+ if (!req.user) {
51
+ res.status(401).json({ message: 'Authentication required' });
52
+ return;
53
+ }
54
+
55
+ if (!roles.includes(req.user.role)) {
56
+ res.status(403).json({ message: 'Insufficient permissions' });
57
+ return;
58
+ }
59
+
60
+ next();
61
+ };
62
+ };
63
+
64
+ export default authenticateToken;
@@ -0,0 +1,5 @@
1
+ // Import middleware modules when they are implemented
2
+ import { authenticateToken, requireRole } from './guard';
3
+
4
+ // Placeholder for future middleware exports
5
+ export { authenticateToken, requireRole };
@@ -0,0 +1,45 @@
1
+ import { Request, Response, NextFunction } from 'express';
2
+ import { Users as UserModel, Meetings as MeetingModel } from '../models';
3
+
4
+ export const canJoinMeeting = async (req: Request, res: Response, next: NextFunction) => {
5
+ try {
6
+ const { meetingId } = req.params;
7
+ const userId = (req as any).user?._id;
8
+
9
+ const meeting = await MeetingModel.findById(meetingId);
10
+ if (!meeting) {
11
+ return res.status(404).json({ error: 'Meeting not found' });
12
+ }
13
+
14
+ if (!meeting.is_active) {
15
+ return res.status(403).json({ error: 'Meeting is not active' });
16
+ }
17
+
18
+ // Check if user is host or participant
19
+ const isHost = meeting.host_id.toString() === userId;
20
+ const isParticipant = meeting.participants.some((p: any) => p.user_id.toString() === userId);
21
+
22
+ if (!isHost && !isParticipant) {
23
+ return res.status(403).json({ error: 'You are not authorized to join this meeting' });
24
+ }
25
+
26
+ // Check meeting capacity
27
+ if ((meeting as any).participant_count >= meeting.max_participants && !isHost) {
28
+ return res.status(403).json({ error: 'Meeting is full' });
29
+ }
30
+
31
+ req.meeting = meeting;
32
+ next();
33
+ } catch (error) {
34
+ console.error('Error in canJoinMeeting middleware:', error);
35
+ res.status(500).json({ error: 'Internal server error' });
36
+ }
37
+ };
38
+
39
+ declare global {
40
+ namespace Express {
41
+ interface Request {
42
+ meeting?: any;
43
+ }
44
+ }
45
+ }
@@ -0,0 +1,70 @@
1
+ import mongoose from 'mongoose';
2
+
3
+ const employeeSchema = new mongoose.Schema({
4
+ first_name: {
5
+ type: String,
6
+ required: true,
7
+ trim: true,
8
+ },
9
+ middle_name: {
10
+ type: String,
11
+ trim: true,
12
+ },
13
+ last_name: {
14
+ type: String,
15
+ required: true,
16
+ trim: true,
17
+ },
18
+ address: {
19
+ type: String,
20
+ trim: true,
21
+ },
22
+ contact_number: {
23
+ type: String,
24
+ trim: true,
25
+ },
26
+ email: {
27
+ type: String,
28
+ trim: true,
29
+ index: true,
30
+ },
31
+ position: {
32
+ type: String,
33
+ trim: true,
34
+ },
35
+ department: {
36
+ type: String,
37
+ trim: true,
38
+ },
39
+ created_at: {
40
+ type: Number,
41
+ default: () => Date.now(),
42
+ },
43
+ updated_at: {
44
+ type: Number,
45
+ default: () => Date.now(),
46
+ },
47
+ deleted_at: {
48
+ type: Number,
49
+ default: null,
50
+ index: true,
51
+ },
52
+ });
53
+
54
+ // Virtual for full name
55
+ employeeSchema.virtual('full_name').get(function() {
56
+ let fullName = `${this.first_name} ${this.last_name}`;
57
+ if (this.middle_name) {
58
+ fullName = `${this.first_name} ${this.middle_name} ${this.last_name}`;
59
+ }
60
+ return fullName;
61
+ });
62
+
63
+ // Ensure virtual fields are included in JSON output
64
+ employeeSchema.set('toJSON', { virtuals: true });
65
+ employeeSchema.set('toObject', { virtuals: true });
66
+
67
+ // Compound indexes for better query performance
68
+ employeeSchema.index({ first_name: 1, last_name: 1 });
69
+
70
+ export default mongoose.model('_employees', employeeSchema);
@@ -0,0 +1,8 @@
1
+ import Employees from './employees';
2
+ import Users from './users';
3
+ import Passkeys from './passkeys';
4
+ import Queues from './queues';
5
+ import Meetings from './meetings';
6
+
7
+ export { Employees, Users, Passkeys, Queues, Meetings };
8
+ export default { Employees, Users, Passkeys, Queues, Meetings };
@@ -0,0 +1,112 @@
1
+ import mongoose from 'mongoose';
2
+
3
+ const meetingSchema = new mongoose.Schema({
4
+ title: {
5
+ type: String,
6
+ required: true,
7
+ trim: true,
8
+ },
9
+ description: {
10
+ type: String,
11
+ trim: true,
12
+ },
13
+ host_id: {
14
+ type: mongoose.Schema.Types.ObjectId,
15
+ ref: '_users',
16
+ required: true,
17
+ index: true,
18
+ },
19
+ participants: [{
20
+ user_id: {
21
+ type: mongoose.Schema.Types.ObjectId,
22
+ ref: '_users',
23
+ required: false,
24
+ },
25
+ email: {
26
+ type: String,
27
+ required: false,
28
+ },
29
+ joined_at: {
30
+ type: Number,
31
+ default: null,
32
+ },
33
+ left_at: {
34
+ type: Number,
35
+ default: null,
36
+ },
37
+ is_active: {
38
+ type: Boolean,
39
+ default: false,
40
+ },
41
+ status: {
42
+ type: String,
43
+ enum: ['pending', 'accepted'],
44
+ default: 'accepted',
45
+ },
46
+ }],
47
+ scheduled_at: {
48
+ type: Number,
49
+ default: () => Date.now(),
50
+ },
51
+ started_at: {
52
+ type: Number,
53
+ default: null,
54
+ },
55
+ ended_at: {
56
+ type: Number,
57
+ default: null,
58
+ },
59
+ meeting_type: {
60
+ type: String,
61
+ enum: ['video', 'audio'],
62
+ default: 'video',
63
+ },
64
+ meeting_url: {
65
+ type: String,
66
+ unique: true,
67
+ required: true,
68
+ },
69
+ max_participants: {
70
+ type: Number,
71
+ default: 50,
72
+ },
73
+ is_active: {
74
+ type: Boolean,
75
+ default: true,
76
+ },
77
+ created_at: {
78
+ type: Number,
79
+ default: () => Date.now(),
80
+ },
81
+ updated_at: {
82
+ type: Number,
83
+ default: () => Date.now(),
84
+ },
85
+ deleted_at: {
86
+ type: Number,
87
+ default: null,
88
+ index: true,
89
+ },
90
+ });
91
+
92
+ // Virtual for participant count
93
+ meetingSchema.virtual('participant_count').get(function() {
94
+ return this.participants.filter(p => p.is_active).length;
95
+ });
96
+
97
+ // Virtual for duration (in minutes)
98
+ meetingSchema.virtual('duration_minutes').get(function() {
99
+ if (!this.started_at) return 0;
100
+ const end = this.ended_at || Date.now();
101
+ return Math.floor((end - this.started_at) / (1000 * 60));
102
+ });
103
+
104
+ // Ensure virtual fields are included in JSON output
105
+ meetingSchema.set('toJSON', { virtuals: true });
106
+ meetingSchema.set('toObject', { virtuals: true });
107
+
108
+ // Indexes for better query performance
109
+ meetingSchema.index({ host_id: 1, scheduled_at: -1 });
110
+ meetingSchema.index({ 'participants.user_id': 1 });
111
+
112
+ export default mongoose.model('_meetings', meetingSchema);
@@ -0,0 +1,53 @@
1
+ import mongoose from 'mongoose';
2
+
3
+ const passkeySchema = new mongoose.Schema({
4
+ user_id: {
5
+ type: mongoose.Schema.Types.ObjectId,
6
+ ref: '_users',
7
+ required: true,
8
+ index: true,
9
+ },
10
+ credential_id: {
11
+ type: String,
12
+ required: true,
13
+ unique: true,
14
+ },
15
+ device_id: {
16
+ type: String,
17
+ required: true,
18
+ },
19
+ name: {
20
+ type: String,
21
+ required: true,
22
+ },
23
+ public_key: {
24
+ type: String,
25
+ required: true,
26
+ },
27
+ created_at: {
28
+ type: Number,
29
+ default: () => Date.now(),
30
+ },
31
+ updated_at: {
32
+ type: Number,
33
+ default: () => Date.now(),
34
+ },
35
+ last_used_at: {
36
+ type: Number,
37
+ default: null,
38
+ },
39
+ is_active: {
40
+ type: Boolean,
41
+ default: true,
42
+ },
43
+ deleted_at: {
44
+ type: Number,
45
+ default: null,
46
+ index: true,
47
+ },
48
+ });
49
+
50
+ // Compound indexes for better query performance
51
+ passkeySchema.index({ user_id: 1, credential_id: 1 });
52
+
53
+ export default mongoose.model('_passkeys', passkeySchema);
@@ -0,0 +1,55 @@
1
+ import mongoose from 'mongoose';
2
+
3
+ const queueSchema = new mongoose.Schema({
4
+ type: {
5
+ type: String,
6
+ required: true,
7
+ enum: ['email', 'password_reset', 'passkey'],
8
+ index: true,
9
+ },
10
+ data: {
11
+ type: mongoose.Schema.Types.Mixed,
12
+ required: true,
13
+ },
14
+ status: {
15
+ type: String,
16
+ required: true,
17
+ enum: ['pending', 'processing', 'completed', 'failed'],
18
+ default: 'pending',
19
+ index: true,
20
+ },
21
+ attempts: {
22
+ type: Number,
23
+ default: 0,
24
+ },
25
+ max_attempts: {
26
+ type: Number,
27
+ default: 3,
28
+ },
29
+ error_message: {
30
+ type: String,
31
+ default: null,
32
+ },
33
+ created_at: {
34
+ type: Number,
35
+ default: () => Date.now(),
36
+ },
37
+ updated_at: {
38
+ type: Number,
39
+ default: () => Date.now(),
40
+ },
41
+ processed_at: {
42
+ type: Number,
43
+ default: null,
44
+ },
45
+ deleted_at: {
46
+ type: Number,
47
+ default: null,
48
+ index: true,
49
+ },
50
+ });
51
+
52
+ // Compound indexes for better query performance
53
+ queueSchema.index({ type: 1, status: 1, created_at: -1 });
54
+
55
+ export default mongoose.model('_queues', queueSchema);
@@ -0,0 +1,92 @@
1
+ import mongoose from 'mongoose';
2
+
3
+ const userSchema = new mongoose.Schema({
4
+ email: {
5
+ type: String,
6
+ required: true,
7
+ unique: true,
8
+ trim: true,
9
+ lowercase: true,
10
+ index: true,
11
+ },
12
+ password: {
13
+ type: String,
14
+ required: true,
15
+ },
16
+ first_name: {
17
+ type: String,
18
+ required: true,
19
+ trim: true,
20
+ },
21
+ middle_name: {
22
+ type: String,
23
+ trim: true,
24
+ },
25
+ last_name: {
26
+ type: String,
27
+ required: true,
28
+ trim: true,
29
+ },
30
+ role: {
31
+ type: String,
32
+ enum: ['admin', 'manager', 'employee'],
33
+ default: 'employee',
34
+ },
35
+ is_active: {
36
+ type: Boolean,
37
+ default: true,
38
+ },
39
+ employee_id: {
40
+ type: mongoose.Schema.Types.ObjectId,
41
+ ref: '_employees',
42
+ default: null,
43
+ index: true,
44
+ },
45
+ created_at: {
46
+ type: Number,
47
+ default: () => Date.now(),
48
+ },
49
+ updated_at: {
50
+ type: Number,
51
+ default: () => Date.now(),
52
+ },
53
+ reset_token: {
54
+ type: String,
55
+ default: null,
56
+ },
57
+ reset_token_expires: {
58
+ type: Number,
59
+ default: null,
60
+ },
61
+ deleted_at: {
62
+ type: Number,
63
+ default: null,
64
+ index: true,
65
+ },
66
+ fcm_token: {
67
+ type: String,
68
+ default: null,
69
+ },
70
+ has_passkey: {
71
+ type: Boolean,
72
+ default: false,
73
+ },
74
+ });
75
+
76
+ // Virtual for full name
77
+ userSchema.virtual('full_name').get(function() {
78
+ let fullName = `${this.first_name} ${this.last_name}`;
79
+ if (this.middle_name) {
80
+ fullName = `${this.first_name} ${this.middle_name} ${this.last_name}`;
81
+ }
82
+ return fullName;
83
+ });
84
+
85
+ // Ensure virtual fields are included in JSON output
86
+ userSchema.set('toJSON', { virtuals: true });
87
+ userSchema.set('toObject', { virtuals: true });
88
+
89
+ // Additional compound indexes for better query performance
90
+ userSchema.index({ first_name: 1, last_name: 1 });
91
+
92
+ export default mongoose.model('_users', userSchema);
@@ -0,0 +1 @@
1
+ export * from './mailer';
@@ -0,0 +1,80 @@
1
+ import { Queue, Worker } from 'bullmq';
2
+ import { Redis } from 'ioredis';
3
+ import { sendPasswordResetEmail, sendPasswordChangedEmail, sendPasskeyRegisteredEmail } from '../../jobs/mailer';
4
+
5
+ // Redis connection
6
+ const redisConnection = new Redis({
7
+ host: process.env.REDIS_HOST || 'localhost',
8
+ port: Number(process.env.REDIS_PORT) || 6379,
9
+ password: process.env.REDIS_PASSWORD || (process.env.NODE_ENV === 'production' ? undefined : ''),
10
+ maxRetriesPerRequest: null, // Required for BullMQ
11
+ lazyConnect: true,
12
+ });
13
+
14
+ // Email queue - create only when needed to avoid connection issues
15
+ let emailQueue: Queue | null = null;
16
+
17
+ const getEmailQueue = () => {
18
+ if (!emailQueue) {
19
+ emailQueue = new Queue('email', {
20
+ connection: redisConnection,
21
+ defaultJobOptions: {
22
+ removeOnComplete: 50,
23
+ removeOnFail: 100,
24
+ },
25
+ });
26
+ }
27
+ return emailQueue;
28
+ };
29
+
30
+ // Worker will be created only when explicitly started
31
+ let emailWorker: Worker | null = null;
32
+
33
+ export const startEmailWorker = () => {
34
+ if (emailWorker) return emailWorker;
35
+
36
+ emailWorker = new Worker('email', async (job) => {
37
+ const { type, data } = job.data;
38
+
39
+ try {
40
+ switch (type) {
41
+ case 'password_reset':
42
+ await sendPasswordResetEmail(data.email, data.name, data.reset_token);
43
+ break;
44
+ case 'password_changed':
45
+ await sendPasswordChangedEmail(data.email, data.name);
46
+ break;
47
+ case 'passkey':
48
+ await sendPasskeyRegisteredEmail(data.email, data.name, data.device_name);
49
+ break;
50
+ default:
51
+ throw new Error(`Unknown email type: ${type}`);
52
+ }
53
+
54
+ console.log(`Email job ${job.id} processed successfully`);
55
+ } catch (error) {
56
+ console.error(`Email job ${job.id} failed:`, error);
57
+ throw error;
58
+ }
59
+ }, {
60
+ connection: redisConnection,
61
+ concurrency: 5, // Process up to 5 emails concurrently
62
+ limiter: {
63
+ max: 10, // Max 10 jobs per duration
64
+ duration: 1000, // Per 1 second
65
+ },
66
+ });
67
+
68
+ // Event listeners
69
+ emailWorker.on('completed', (job) => {
70
+ console.log(`Email job ${job.id} completed`);
71
+ });
72
+
73
+ emailWorker.on('failed', (job, err) => {
74
+ console.error(`Email job ${job?.id} failed:`, err);
75
+ });
76
+
77
+ return emailWorker;
78
+ };
79
+
80
+ export { getEmailQueue };
@@ -0,0 +1,38 @@
1
+ import { Request } from 'express'
2
+ import multer from 'multer'
3
+
4
+ export interface MulterRequest extends Request {
5
+ file?: Express.Multer.File
6
+ }
7
+
8
+ export interface User {
9
+ id: string;
10
+ username: string;
11
+ email: string;
12
+ first_name: string;
13
+ middle_name?: string;
14
+ last_name: string;
15
+ role: 'admin' | 'manager' | 'employee';
16
+ employee_id?: string;
17
+ is_active: boolean;
18
+ }
19
+
20
+ export interface Employee {
21
+ id: string;
22
+ first_name: string;
23
+ middle_name?: string;
24
+ last_name: string;
25
+ email?: string;
26
+ contact_number?: string;
27
+ position?: string;
28
+ department?: string;
29
+ address?: string;
30
+ }
31
+
32
+ export interface Module {
33
+ id: string;
34
+ name: string;
35
+ icon?: string;
36
+ path: string;
37
+ children?: Module[];
38
+ }
@@ -0,0 +1,66 @@
1
+ import axios from 'axios';
2
+
3
+ const NOTIFICATIONS_SERVICE_URL = process.env.NOTIFICATIONS_SERVICE_URL || 'http://localhost:3003';
4
+
5
+ export const sendNotification = async (
6
+ userIds: string | string[],
7
+ title: string,
8
+ body: string,
9
+ data: any = {},
10
+ type: string = 'general'
11
+ ) => {
12
+ try {
13
+ const payload = {
14
+ title,
15
+ body,
16
+ data,
17
+ type
18
+ };
19
+
20
+ const userIdsArray = Array.isArray(userIds) ? userIds : [userIds];
21
+
22
+ // Send notification to each user
23
+ for (const userId of userIdsArray) {
24
+ await axios.post(`${NOTIFICATIONS_SERVICE_URL}/send-notification`,
25
+ { ...payload, userId },
26
+ {
27
+ headers: {
28
+ 'Authorization': `Bearer ${process.env.NOTIFICATIONS_SERVICE_TOKEN || 'service-token'}`
29
+ }
30
+ }
31
+ );
32
+ }
33
+
34
+ console.log(`Notifications sent to users: ${userIdsArray.join(', ')}`);
35
+ } catch (error) {
36
+ console.error('Error sending notifications:', error);
37
+ }
38
+ };
39
+
40
+ export const sendMeetingInvitation = async (
41
+ meetingId: string,
42
+ participants: string[],
43
+ title: string,
44
+ description: string,
45
+ hostId: string
46
+ ) => {
47
+ try {
48
+ await axios.post(`${NOTIFICATIONS_SERVICE_URL}/meeting-invitation`,
49
+ {
50
+ meetingId,
51
+ participants,
52
+ title,
53
+ description
54
+ },
55
+ {
56
+ headers: {
57
+ 'Authorization': `Bearer ${process.env.NOTIFICATIONS_SERVICE_TOKEN || 'service-token'}`
58
+ }
59
+ }
60
+ );
61
+
62
+ console.log(`Meeting invitations sent for meeting: ${meetingId}`);
63
+ } catch (error) {
64
+ console.error('Error sending meeting invitations:', error);
65
+ }
66
+ };