@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,107 @@
1
+ import mongoose from 'mongoose';
2
+ declare const _default: mongoose.Model<{
3
+ first_name: string;
4
+ last_name: string;
5
+ email: string;
6
+ created_at: number;
7
+ updated_at: number;
8
+ deleted_at: number;
9
+ password: string;
10
+ role: "admin" | "manager" | "employee";
11
+ is_active: boolean;
12
+ employee_id: mongoose.Types.ObjectId;
13
+ reset_token: string;
14
+ reset_token_expires: number;
15
+ fcm_token: string;
16
+ has_passkey: boolean;
17
+ middle_name?: string | null | undefined;
18
+ }, {}, {}, {}, mongoose.Document<unknown, {}, {
19
+ first_name: string;
20
+ last_name: string;
21
+ email: string;
22
+ created_at: number;
23
+ updated_at: number;
24
+ deleted_at: number;
25
+ password: string;
26
+ role: "admin" | "manager" | "employee";
27
+ is_active: boolean;
28
+ employee_id: mongoose.Types.ObjectId;
29
+ reset_token: string;
30
+ reset_token_expires: number;
31
+ fcm_token: string;
32
+ has_passkey: boolean;
33
+ middle_name?: string | null | undefined;
34
+ }, {}, mongoose.DefaultSchemaOptions> & {
35
+ first_name: string;
36
+ last_name: string;
37
+ email: string;
38
+ created_at: number;
39
+ updated_at: number;
40
+ deleted_at: number;
41
+ password: string;
42
+ role: "admin" | "manager" | "employee";
43
+ is_active: boolean;
44
+ employee_id: mongoose.Types.ObjectId;
45
+ reset_token: string;
46
+ reset_token_expires: number;
47
+ fcm_token: string;
48
+ has_passkey: boolean;
49
+ middle_name?: string | null | undefined;
50
+ } & {
51
+ _id: mongoose.Types.ObjectId;
52
+ } & {
53
+ __v: number;
54
+ }, mongoose.Schema<any, mongoose.Model<any, any, any, any, any, any>, {}, {}, {}, {}, mongoose.DefaultSchemaOptions, {
55
+ first_name: string;
56
+ last_name: string;
57
+ email: string;
58
+ created_at: number;
59
+ updated_at: number;
60
+ deleted_at: number;
61
+ password: string;
62
+ role: "admin" | "manager" | "employee";
63
+ is_active: boolean;
64
+ employee_id: mongoose.Types.ObjectId;
65
+ reset_token: string;
66
+ reset_token_expires: number;
67
+ fcm_token: string;
68
+ has_passkey: boolean;
69
+ middle_name?: string | null | undefined;
70
+ }, mongoose.Document<unknown, {}, mongoose.FlatRecord<{
71
+ first_name: string;
72
+ last_name: string;
73
+ email: string;
74
+ created_at: number;
75
+ updated_at: number;
76
+ deleted_at: number;
77
+ password: string;
78
+ role: "admin" | "manager" | "employee";
79
+ is_active: boolean;
80
+ employee_id: mongoose.Types.ObjectId;
81
+ reset_token: string;
82
+ reset_token_expires: number;
83
+ fcm_token: string;
84
+ has_passkey: boolean;
85
+ middle_name?: string | null | undefined;
86
+ }>, {}, mongoose.ResolveSchemaOptions<mongoose.DefaultSchemaOptions>> & mongoose.FlatRecord<{
87
+ first_name: string;
88
+ last_name: string;
89
+ email: string;
90
+ created_at: number;
91
+ updated_at: number;
92
+ deleted_at: number;
93
+ password: string;
94
+ role: "admin" | "manager" | "employee";
95
+ is_active: boolean;
96
+ employee_id: mongoose.Types.ObjectId;
97
+ reset_token: string;
98
+ reset_token_expires: number;
99
+ fcm_token: string;
100
+ has_passkey: boolean;
101
+ middle_name?: string | null | undefined;
102
+ }> & {
103
+ _id: mongoose.Types.ObjectId;
104
+ } & {
105
+ __v: number;
106
+ }>>;
107
+ export default _default;
@@ -0,0 +1,92 @@
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 mongoose_1 = __importDefault(require("mongoose"));
7
+ const userSchema = new mongoose_1.default.Schema({
8
+ email: {
9
+ type: String,
10
+ required: true,
11
+ unique: true,
12
+ trim: true,
13
+ lowercase: true,
14
+ index: true,
15
+ },
16
+ password: {
17
+ type: String,
18
+ required: true,
19
+ },
20
+ first_name: {
21
+ type: String,
22
+ required: true,
23
+ trim: true,
24
+ },
25
+ middle_name: {
26
+ type: String,
27
+ trim: true,
28
+ },
29
+ last_name: {
30
+ type: String,
31
+ required: true,
32
+ trim: true,
33
+ },
34
+ role: {
35
+ type: String,
36
+ enum: ['admin', 'manager', 'employee'],
37
+ default: 'employee',
38
+ },
39
+ is_active: {
40
+ type: Boolean,
41
+ default: true,
42
+ },
43
+ employee_id: {
44
+ type: mongoose_1.default.Schema.Types.ObjectId,
45
+ ref: '_employees',
46
+ default: null,
47
+ index: true,
48
+ },
49
+ created_at: {
50
+ type: Number,
51
+ default: () => Date.now(),
52
+ },
53
+ updated_at: {
54
+ type: Number,
55
+ default: () => Date.now(),
56
+ },
57
+ reset_token: {
58
+ type: String,
59
+ default: null,
60
+ },
61
+ reset_token_expires: {
62
+ type: Number,
63
+ default: null,
64
+ },
65
+ deleted_at: {
66
+ type: Number,
67
+ default: null,
68
+ index: true,
69
+ },
70
+ fcm_token: {
71
+ type: String,
72
+ default: null,
73
+ },
74
+ has_passkey: {
75
+ type: Boolean,
76
+ default: false,
77
+ },
78
+ });
79
+ // Virtual for full name
80
+ userSchema.virtual('full_name').get(function () {
81
+ let fullName = `${this.first_name} ${this.last_name}`;
82
+ if (this.middle_name) {
83
+ fullName = `${this.first_name} ${this.middle_name} ${this.last_name}`;
84
+ }
85
+ return fullName;
86
+ });
87
+ // Ensure virtual fields are included in JSON output
88
+ userSchema.set('toJSON', { virtuals: true });
89
+ userSchema.set('toObject', { virtuals: true });
90
+ // Additional compound indexes for better query performance
91
+ userSchema.index({ first_name: 1, last_name: 1 });
92
+ exports.default = mongoose_1.default.model('_users', userSchema);
@@ -0,0 +1 @@
1
+ export * from './mailer';
@@ -0,0 +1,17 @@
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 __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./mailer"), exports);
@@ -0,0 +1,4 @@
1
+ import { Queue, Worker } from 'bullmq';
2
+ declare const getEmailQueue: () => Queue<any, any, string, any, any, string>;
3
+ export declare const startEmailWorker: () => Worker<any, any, string>;
4
+ export { getEmailQueue };
@@ -0,0 +1,74 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getEmailQueue = exports.startEmailWorker = void 0;
4
+ const bullmq_1 = require("bullmq");
5
+ const ioredis_1 = require("ioredis");
6
+ const mailer_1 = require("../../jobs/mailer");
7
+ // Redis connection
8
+ const redisConnection = new ioredis_1.Redis({
9
+ host: process.env.REDIS_HOST || 'localhost',
10
+ port: Number(process.env.REDIS_PORT) || 6379,
11
+ password: process.env.REDIS_PASSWORD || (process.env.NODE_ENV === 'production' ? undefined : ''),
12
+ maxRetriesPerRequest: null, // Required for BullMQ
13
+ lazyConnect: true,
14
+ });
15
+ // Email queue - create only when needed to avoid connection issues
16
+ let emailQueue = null;
17
+ const getEmailQueue = () => {
18
+ if (!emailQueue) {
19
+ emailQueue = new bullmq_1.Queue('email', {
20
+ connection: redisConnection,
21
+ defaultJobOptions: {
22
+ removeOnComplete: 50,
23
+ removeOnFail: 100,
24
+ },
25
+ });
26
+ }
27
+ return emailQueue;
28
+ };
29
+ exports.getEmailQueue = getEmailQueue;
30
+ // Worker will be created only when explicitly started
31
+ let emailWorker = null;
32
+ const startEmailWorker = () => {
33
+ if (emailWorker)
34
+ return emailWorker;
35
+ emailWorker = new bullmq_1.Worker('email', async (job) => {
36
+ const { type, data } = job.data;
37
+ try {
38
+ switch (type) {
39
+ case 'password_reset':
40
+ await (0, mailer_1.sendPasswordResetEmail)(data.email, data.name, data.reset_token);
41
+ break;
42
+ case 'password_changed':
43
+ await (0, mailer_1.sendPasswordChangedEmail)(data.email, data.name);
44
+ break;
45
+ case 'passkey':
46
+ await (0, mailer_1.sendPasskeyRegisteredEmail)(data.email, data.name, data.device_name);
47
+ break;
48
+ default:
49
+ throw new Error(`Unknown email type: ${type}`);
50
+ }
51
+ console.log(`Email job ${job.id} processed successfully`);
52
+ }
53
+ catch (error) {
54
+ console.error(`Email job ${job.id} failed:`, error);
55
+ throw error;
56
+ }
57
+ }, {
58
+ connection: redisConnection,
59
+ concurrency: 5, // Process up to 5 emails concurrently
60
+ limiter: {
61
+ max: 10, // Max 10 jobs per duration
62
+ duration: 1000, // Per 1 second
63
+ },
64
+ });
65
+ // Event listeners
66
+ emailWorker.on('completed', (job) => {
67
+ console.log(`Email job ${job.id} completed`);
68
+ });
69
+ emailWorker.on('failed', (job, err) => {
70
+ console.error(`Email job ${job?.id} failed:`, err);
71
+ });
72
+ return emailWorker;
73
+ };
74
+ exports.startEmailWorker = startEmailWorker;
@@ -0,0 +1,33 @@
1
+ import { Request } from 'express';
2
+ export interface MulterRequest extends Request {
3
+ file?: Express.Multer.File;
4
+ }
5
+ export interface User {
6
+ id: string;
7
+ username: string;
8
+ email: string;
9
+ first_name: string;
10
+ middle_name?: string;
11
+ last_name: string;
12
+ role: 'admin' | 'manager' | 'employee';
13
+ employee_id?: string;
14
+ is_active: boolean;
15
+ }
16
+ export interface Employee {
17
+ id: string;
18
+ first_name: string;
19
+ middle_name?: string;
20
+ last_name: string;
21
+ email?: string;
22
+ contact_number?: string;
23
+ position?: string;
24
+ department?: string;
25
+ address?: string;
26
+ }
27
+ export interface Module {
28
+ id: string;
29
+ name: string;
30
+ icon?: string;
31
+ path: string;
32
+ children?: Module[];
33
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,2 @@
1
+ export declare const sendNotification: (userIds: string | string[], title: string, body: string, data?: any, type?: string) => Promise<void>;
2
+ export declare const sendMeetingInvitation: (meetingId: string, participants: string[], title: string, description: string, hostId: string) => Promise<void>;
@@ -0,0 +1,51 @@
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.sendMeetingInvitation = exports.sendNotification = void 0;
7
+ const axios_1 = __importDefault(require("axios"));
8
+ const NOTIFICATIONS_SERVICE_URL = process.env.NOTIFICATIONS_SERVICE_URL || 'http://localhost:3003';
9
+ const sendNotification = async (userIds, title, body, data = {}, type = 'general') => {
10
+ try {
11
+ const payload = {
12
+ title,
13
+ body,
14
+ data,
15
+ type
16
+ };
17
+ const userIdsArray = Array.isArray(userIds) ? userIds : [userIds];
18
+ // Send notification to each user
19
+ for (const userId of userIdsArray) {
20
+ await axios_1.default.post(`${NOTIFICATIONS_SERVICE_URL}/send-notification`, { ...payload, userId }, {
21
+ headers: {
22
+ 'Authorization': `Bearer ${process.env.NOTIFICATIONS_SERVICE_TOKEN || 'service-token'}`
23
+ }
24
+ });
25
+ }
26
+ console.log(`Notifications sent to users: ${userIdsArray.join(', ')}`);
27
+ }
28
+ catch (error) {
29
+ console.error('Error sending notifications:', error);
30
+ }
31
+ };
32
+ exports.sendNotification = sendNotification;
33
+ const sendMeetingInvitation = async (meetingId, participants, title, description, hostId) => {
34
+ try {
35
+ await axios_1.default.post(`${NOTIFICATIONS_SERVICE_URL}/meeting-invitation`, {
36
+ meetingId,
37
+ participants,
38
+ title,
39
+ description
40
+ }, {
41
+ headers: {
42
+ 'Authorization': `Bearer ${process.env.NOTIFICATIONS_SERVICE_TOKEN || 'service-token'}`
43
+ }
44
+ });
45
+ console.log(`Meeting invitations sent for meeting: ${meetingId}`);
46
+ }
47
+ catch (error) {
48
+ console.error('Error sending meeting invitations:', error);
49
+ }
50
+ };
51
+ exports.sendMeetingInvitation = sendMeetingInvitation;
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "@organizasyon/meeting-nanaman-app-backend",
3
+ "version": "1.0.0",
4
+ "description": "backend service for meeting nanaman real-time video/audio call application",
5
+ "license": "ISC",
6
+ "author": "Aldrin Degracia",
7
+ "type": "commonjs",
8
+ "main": "dist/index.js",
9
+ "types": "dist/index.d.ts",
10
+ "scripts": {
11
+ "build": "rm -rf dist && tsc && cp -r src/data dist/ || true",
12
+ "start": "node dist/index.js",
13
+ "dev": "ts-node src/index.ts",
14
+ "test": "jest"
15
+ },
16
+ "devDependencies": {
17
+ "@types/bcrypt": "^6.0.0",
18
+ "@types/express": "^5.0.5",
19
+ "@types/jsonwebtoken": "^9.0.10",
20
+ "@types/multer": "^2.0.0",
21
+ "@types/node": "^20.9.0",
22
+ "ts-node": "^10.9.1",
23
+ "typescript": "^5.2.2"
24
+ },
25
+ "dependencies": {
26
+ "@types/ioredis": "^4.28.10",
27
+ "@types/nodemailer": "^7.0.4",
28
+ "bcrypt": "^6.0.0",
29
+ "bullmq": "^5.66.0",
30
+ "dotenv": "^17.2.3",
31
+ "express": "^4.18.2",
32
+ "ioredis": "^5.8.2",
33
+ "jsonwebtoken": "^9.0.3",
34
+ "mongoose": "^8.19.3",
35
+ "multer": "^2.0.2",
36
+ "nodemailer": "^7.0.11",
37
+ "axios": "^1.6.0"
38
+ }
39
+ }
@@ -0,0 +1,215 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>API Health Check - Inventory Management System</title>
7
+ <style>
8
+ * {
9
+ margin: 0;
10
+ padding: 0;
11
+ box-sizing: border-box;
12
+ }
13
+
14
+ body {
15
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
16
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
17
+ min-height: 100vh;
18
+ display: flex;
19
+ align-items: center;
20
+ justify-content: center;
21
+ color: #333;
22
+ }
23
+
24
+ .container {
25
+ background: rgba(255, 255, 255, 0.95);
26
+ backdrop-filter: blur(10px);
27
+ border-radius: 20px;
28
+ padding: 3rem;
29
+ box-shadow: 0 25px 50px rgba(0, 0, 0, 0.15);
30
+ text-align: center;
31
+ max-width: 500px;
32
+ width: 90%;
33
+ border: 1px solid rgba(255, 255, 255, 0.2);
34
+ animation: slideIn 0.6s ease-out;
35
+ }
36
+
37
+ .icon {
38
+ width: 80px;
39
+ height: 80px;
40
+ background: linear-gradient(135deg, #4CAF50 0%, #45a049 100%);
41
+ border-radius: 50%;
42
+ margin: 0 auto 2rem;
43
+ display: flex;
44
+ align-items: center;
45
+ justify-content: center;
46
+ animation: pulse 2s infinite;
47
+ position: relative;
48
+ }
49
+
50
+ .icon::before {
51
+ content: '✓';
52
+ color: white;
53
+ font-size: 2.5rem;
54
+ font-weight: bold;
55
+ text-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
56
+ }
57
+
58
+ .status {
59
+ color: #4CAF50;
60
+ font-size: 2rem;
61
+ font-weight: 700;
62
+ margin-bottom: 1rem;
63
+ background: linear-gradient(45deg, #4CAF50, #45a049);
64
+ -webkit-background-clip: text;
65
+ -webkit-text-fill-color: transparent;
66
+ background-clip: text;
67
+ }
68
+
69
+ .message {
70
+ font-size: 1.2rem;
71
+ color: #666;
72
+ margin-bottom: 1.5rem;
73
+ line-height: 1.6;
74
+ }
75
+
76
+ .details {
77
+ background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
78
+ border-radius: 15px;
79
+ padding: 1.5rem;
80
+ margin: 2rem 0;
81
+ border-left: 4px solid #4CAF50;
82
+ }
83
+
84
+ .details p {
85
+ margin: 0.5rem 0;
86
+ color: #555;
87
+ font-size: 0.95rem;
88
+ }
89
+
90
+ .details strong {
91
+ color: #333;
92
+ font-weight: 600;
93
+ }
94
+
95
+ .footer {
96
+ margin-top: 2rem;
97
+ padding-top: 1.5rem;
98
+ border-top: 1px solid rgba(0, 0, 0, 0.1);
99
+ }
100
+
101
+ .timestamp {
102
+ color: #888;
103
+ font-size: 0.85rem;
104
+ margin-bottom: 0.5rem;
105
+ }
106
+
107
+ .version {
108
+ background: #667eea;
109
+ color: white;
110
+ padding: 0.3rem 0.8rem;
111
+ border-radius: 20px;
112
+ font-size: 0.8rem;
113
+ font-weight: 600;
114
+ display: inline-block;
115
+ }
116
+
117
+ @keyframes slideIn {
118
+ from {
119
+ opacity: 0;
120
+ transform: translateY(-30px);
121
+ }
122
+ to {
123
+ opacity: 1;
124
+ transform: translateY(0);
125
+ }
126
+ }
127
+
128
+ @keyframes pulse {
129
+ 0% {
130
+ transform: scale(1);
131
+ box-shadow: 0 0 0 0 rgba(76, 175, 80, 0.7);
132
+ }
133
+ 70% {
134
+ transform: scale(1.05);
135
+ box-shadow: 0 0 0 20px rgba(76, 175, 80, 0);
136
+ }
137
+ 100% {
138
+ transform: scale(1);
139
+ box-shadow: 0 0 0 0 rgba(76, 175, 80, 0);
140
+ }
141
+ }
142
+
143
+ @media (max-width: 768px) {
144
+ .container {
145
+ padding: 2rem;
146
+ margin: 1rem;
147
+ }
148
+
149
+ .status {
150
+ font-size: 1.5rem;
151
+ }
152
+
153
+ .message {
154
+ font-size: 1rem;
155
+ }
156
+ }
157
+ </style>
158
+ </head>
159
+ <body>
160
+ <div class="container">
161
+ <div class="icon"></div>
162
+ <div class="status">API is RUNNING</div>
163
+ <div class="message">
164
+ Inventory Management System API is healthy and operational!
165
+ </div>
166
+
167
+ <div class="details">
168
+ <p><strong>Service:</strong> Inventory Management API</p>
169
+ <p><strong>Status:</strong> Active ✅</p>
170
+ <p><strong>Response:</strong> All systems operational</p>
171
+ </div>
172
+
173
+ <div class="footer">
174
+ <div class="timestamp">Last checked: <span id="current-time"></span></div>
175
+ <div class="version">API v1.0.0</div>
176
+ </div>
177
+ </div>
178
+
179
+ <script>
180
+ // Update current time
181
+ function updateTime() {
182
+ const now = new Date();
183
+ const options = {
184
+ weekday: 'long',
185
+ year: 'numeric',
186
+ month: 'long',
187
+ day: 'numeric',
188
+ hour: '2-digit',
189
+ minute: '2-digit',
190
+ second: '2-digit',
191
+ timeZoneName: 'short'
192
+ };
193
+ document.getElementById('current-time').textContent = now.toLocaleDateString('en-US', options);
194
+ }
195
+
196
+ // Initial update
197
+ updateTime();
198
+
199
+ // Update every second
200
+ setInterval(updateTime, 1000);
201
+
202
+ // Add some interactive effects
203
+ document.addEventListener('DOMContentLoaded', function() {
204
+ const container = document.querySelector('.container');
205
+ container.addEventListener('mouseenter', function() {
206
+ this.style.transform = 'translateY(-5px)';
207
+ });
208
+
209
+ container.addEventListener('mouseleave', function() {
210
+ this.style.transform = 'translateY(0)';
211
+ });
212
+ });
213
+ </script>
214
+ </body>
215
+ </html>