feat: Razorpay, Minio, multi-role patient dashboard, and advanced profiles
Build Curio HMS / build-and-deploy (push) Successful in 1m6s Details

This commit is contained in:
Deep Koluguri 2026-05-26 22:10:24 -04:00
parent 1d9c314c4f
commit 8b27b68932
11 changed files with 1466 additions and 12 deletions

View File

@ -30,6 +30,7 @@ CREATE TABLE IF NOT EXISTS tenants (
working_hours JSONB DEFAULT '[{"start":"09:00","end":"13:00"},{"start":"17:00","end":"22:00"}]', working_hours JSONB DEFAULT '[{"start":"09:00","end":"13:00"},{"start":"17:00","end":"22:00"}]',
days_off JSONB DEFAULT '[0]', days_off JSONB DEFAULT '[0]',
holidays JSONB DEFAULT '[]', holidays JSONB DEFAULT '[]',
services_catalog JSONB DEFAULT '[]',
created_at TIMESTAMPTZ DEFAULT now() created_at TIMESTAMPTZ DEFAULT now()
); );
@ -39,7 +40,10 @@ CREATE TABLE IF NOT EXISTS users (
email TEXT UNIQUE NOT NULL, email TEXT UNIQUE NOT NULL,
password_hash TEXT NOT NULL, password_hash TEXT NOT NULL,
name TEXT NOT NULL, name TEXT NOT NULL,
role TEXT NOT NULL CHECK (role IN ('SUPER_ADMIN','OWNER','DOCTOR','RECEPTIONIST','PHARMACIST','LAB_TECH')), role TEXT NOT NULL CHECK (role IN ('SUPER_ADMIN','OWNER','DOCTOR','RECEPTIONIST','PHARMACIST','LAB_TECH','PATIENT')),
profile_media JSONB DEFAULT '{}',
q_and_a JSONB DEFAULT '[]',
blocked_dates JSONB DEFAULT '[]',
status TEXT DEFAULT 'active', status TEXT DEFAULT 'active',
created_at TIMESTAMPTZ DEFAULT now() created_at TIMESTAMPTZ DEFAULT now()
); );
@ -58,9 +62,30 @@ CREATE TABLE IF NOT EXISTS pending_registrations (
created_at TIMESTAMPTZ DEFAULT now() created_at TIMESTAMPTZ DEFAULT now()
); );
CREATE TABLE IF NOT EXISTS transactions (
id SERIAL PRIMARY KEY,
order_id TEXT NOT NULL UNIQUE,
payment_id TEXT,
amount INTEGER NOT NULL,
currency TEXT DEFAULT 'INR',
status TEXT DEFAULT 'created',
tenant_id TEXT REFERENCES tenants(id),
user_id INTEGER REFERENCES users(id),
created_at TIMESTAMPTZ DEFAULT now()
);
CREATE INDEX IF NOT EXISTS idx_users_tenant ON users(tenant_id); CREATE INDEX IF NOT EXISTS idx_users_tenant ON users(tenant_id);
CREATE INDEX IF NOT EXISTS idx_users_email ON users(email); CREATE INDEX IF NOT EXISTS idx_users_email ON users(email);
CREATE INDEX IF NOT EXISTS idx_pending_status ON pending_registrations(status); CREATE INDEX IF NOT EXISTS idx_pending_status ON pending_registrations(status);
CREATE INDEX IF NOT EXISTS idx_transactions_order ON transactions(order_id);
-- Migrations for existing DBs
ALTER TABLE tenants ADD COLUMN IF NOT EXISTS services_catalog JSONB DEFAULT '[]';
ALTER TABLE users ADD COLUMN IF NOT EXISTS profile_media JSONB DEFAULT '{}';
ALTER TABLE users ADD COLUMN IF NOT EXISTS q_and_a JSONB DEFAULT '[]';
ALTER TABLE users ADD COLUMN IF NOT EXISTS blocked_dates JSONB DEFAULT '[]';
ALTER TABLE users DROP CONSTRAINT IF EXISTS users_role_check;
ALTER TABLE users ADD CONSTRAINT users_role_check CHECK (role IN ('SUPER_ADMIN','OWNER','DOCTOR','RECEPTIONIST','PHARMACIST','LAB_TECH','PATIENT'));
`; `;
// ── Seed super admin ───────────────────────────────────────────────────────── // ── Seed super admin ─────────────────────────────────────────────────────────

View File

@ -11,6 +11,13 @@ const { getTenantConfig, updateTenantConfig } = require('./configManager');
const { getTemporalClient } = require('./temporalClient'); const { getTemporalClient } = require('./temporalClient');
const { query, initSchema } = require('./db'); const { query, initSchema } = require('./db');
const { signToken, requireAuth } = require('./authMiddleware'); const { signToken, requireAuth } = require('./authMiddleware');
const Razorpay = require('razorpay');
const { upload, initStorage, uploadFile } = require('./storage');
const razorpayInstance = new Razorpay({
key_id: process.env.RAZORPAY_KEY_ID || 'rzp_test_dummy_key_id',
key_secret: process.env.RAZORPAY_KEY_SECRET || 'dummy_secret_key',
});
const app = express(); const app = express();
const port = 3000; const port = 3000;
@ -462,7 +469,7 @@ app.post('/api/super/pending/:id/reject', requireAuth('SUPER_ADMIN'), async (req
* PATCH /api/clinic/settings clinic owner updates own settings * PATCH /api/clinic/settings clinic owner updates own settings
*/ */
app.patch('/api/clinic/settings', requireAuth('OWNER'), async (req, res) => { app.patch('/api/clinic/settings', requireAuth('OWNER'), async (req, res) => {
const { consultationFee, workingHours, daysOff, holidays, primaryColor, secondaryColor } = req.body; const { consultationFee, workingHours, daysOff, holidays, primaryColor, secondaryColor, servicesCatalog } = req.body;
const tenantId = req.user.tenantId; const tenantId = req.user.tenantId;
try { try {
@ -476,6 +483,7 @@ app.patch('/api/clinic/settings', requireAuth('OWNER'), async (req, res) => {
if (workingHours !== undefined) { fields.push(`working_hours=$${i++}::jsonb`); values.push(JSON.stringify(workingHours)); } if (workingHours !== undefined) { fields.push(`working_hours=$${i++}::jsonb`); values.push(JSON.stringify(workingHours)); }
if (daysOff !== undefined) { fields.push(`days_off=$${i++}::jsonb`); values.push(JSON.stringify(daysOff)); } if (daysOff !== undefined) { fields.push(`days_off=$${i++}::jsonb`); values.push(JSON.stringify(daysOff)); }
if (holidays !== undefined) { fields.push(`holidays=$${i++}::jsonb`); values.push(JSON.stringify(holidays)); } if (holidays !== undefined) { fields.push(`holidays=$${i++}::jsonb`); values.push(JSON.stringify(holidays)); }
if (servicesCatalog !== undefined) { fields.push(`services_catalog=$${i++}::jsonb`); values.push(JSON.stringify(servicesCatalog)); }
if (fields.length === 0) return res.status(400).json({ error: 'No fields to update' }); if (fields.length === 0) return res.status(400).json({ error: 'No fields to update' });
@ -488,6 +496,114 @@ app.patch('/api/clinic/settings', requireAuth('OWNER'), async (req, res) => {
} }
}); });
/**
* PATCH /api/doctor/profile Doctor updates own profile
*/
app.patch('/api/doctor/profile', requireAuth('DOCTOR'), async (req, res) => {
const { blockedDates, qAndA } = req.body;
const userId = req.user.userId;
try {
const fields = [];
const values = [];
let i = 1;
if (blockedDates !== undefined) { fields.push(`blocked_dates=$${i++}::jsonb`); values.push(JSON.stringify(blockedDates)); }
if (qAndA !== undefined) { fields.push(`q_and_a=$${i++}::jsonb`); values.push(JSON.stringify(qAndA)); }
if (fields.length === 0) return res.status(400).json({ error: 'No fields to update' });
values.push(userId);
await query(`UPDATE users SET ${fields.join(',')} WHERE id=$${i}`, values);
res.json({ success: true });
} catch (err) {
console.error('[Doctor Profile]', err);
res.status(500).json({ error: 'Failed to update profile' });
}
});
/**
* POST /api/doctor/upload-media Upload images/videos to MinIO
*/
app.post('/api/doctor/upload-media', requireAuth('DOCTOR'), upload.single('media'), async (req, res) => {
if (!req.file) return res.status(400).json({ error: 'No file uploaded' });
try {
const userId = req.user.userId;
const ext = path.extname(req.file.originalname) || '.jpg';
const fileName = `profile_${userId}_${Date.now()}${ext}`;
const fileUrl = await uploadFile(fileName, req.file.buffer, req.file.mimetype);
// Append to profile_media JSON
const userRow = await query('SELECT profile_media FROM users WHERE id = $1', [userId]);
const media = userRow.rows[0].profile_media || {};
const mediaList = media.uploads || [];
mediaList.push({ url: fileUrl, type: req.file.mimetype, name: req.file.originalname });
await query('UPDATE users SET profile_media = $1::jsonb WHERE id = $2', [JSON.stringify({ uploads: mediaList }), userId]);
res.json({ success: true, url: fileUrl });
} catch (err) {
console.error('[Media Upload]', err);
res.status(500).json({ error: 'Failed to upload media' });
}
});
// ── RAZORPAY PAYMENTS ─────────────────────────────────────────────────────────
app.post('/api/payments/create-order', requireAuth(), async (req, res) => {
const { amount, currency = 'INR', notes } = req.body;
if (!amount) return res.status(400).json({ error: 'Amount is required' });
try {
const options = {
amount: amount * 100, // Razorpay works in smallest currency unit (paise)
currency,
receipt: `rcpt_${Date.now()}`,
notes,
};
const order = await razorpayInstance.orders.create(options);
// Save transaction to DB
await query(
`INSERT INTO transactions (order_id, amount, currency, tenant_id, user_id)
VALUES ($1, $2, $3, $4, $5)`,
[order.id, amount, currency, req.user.tenantId, req.user.userId]
);
res.json({ success: true, orderId: order.id, amount: order.amount, currency: order.currency });
} catch (err) {
console.error('[Create Order Error]', err);
res.status(500).json({ error: 'Failed to create payment order' });
}
});
app.post('/api/payments/verify', requireAuth(), async (req, res) => {
const { razorpay_order_id, razorpay_payment_id, razorpay_signature } = req.body;
const secret = process.env.RAZORPAY_KEY_SECRET || 'dummy_secret_key';
const body = razorpay_order_id + '|' + razorpay_payment_id;
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(body.toString())
.digest('hex');
if (expectedSignature === razorpay_signature) {
// Mark as paid
await query('UPDATE transactions SET status = $1, payment_id = $2 WHERE order_id = $3',
['PAID', razorpay_payment_id, razorpay_order_id]);
res.json({ success: true });
} else {
res.status(400).json({ error: 'Invalid signature' });
}
});
// ── WHATSAPP WEBHOOK ────────────────────────────────────────────────────────── // ── WHATSAPP WEBHOOK ──────────────────────────────────────────────────────────
// Using twilio.webhook() middleware for request validation // Using twilio.webhook() middleware for request validation
@ -495,9 +611,10 @@ app.patch('/api/clinic/settings', requireAuth('OWNER'), async (req, res) => {
const shouldValidate = process.env.NODE_ENV === 'production'; const shouldValidate = process.env.NODE_ENV === 'production';
app.post('/whatsapp/webhook', twilio.webhook({ validate: shouldValidate }), async (req, res) => { app.post('/whatsapp/webhook', twilio.webhook({ validate: shouldValidate }), async (req, res) => {
const from = req.body.From || req.body.from; const from = req.body.From || req.body.from;
const to = req.body.To || req.body.to;
const body = req.body.Body || req.body.body; const body = req.body.Body || req.body.body;
console.log(`[WhatsApp Webhook] Received message from ${from}: ${body}`); console.log(`[WhatsApp Webhook] Received message from ${from} to ${to}: ${body}`);
if (!from || !body) return res.status(400).send('Missing from or body'); if (!from || !body) return res.status(400).send('Missing from or body');
try { try {
@ -515,7 +632,7 @@ app.post('/whatsapp/webhook', twilio.webhook({ validate: shouldValidate }), asyn
} catch (err) { /* start new */ } } catch (err) { /* start new */ }
await temporalClient.workflow.start('AppointmentBookingWorkflow', { await temporalClient.workflow.start('AppointmentBookingWorkflow', {
args: [body, from], taskQueue: 'curaflow-tasks', workflowId, args: [body, from, to], taskQueue: 'curaflow-tasks', workflowId,
}); });
res.type('text/xml').send('<Response></Response>'); res.type('text/xml').send('<Response></Response>');
} catch (err) { } catch (err) {
@ -659,7 +776,9 @@ app.get('/api/ai/lab/alerts/:patientId', async (req, res) => {
// ── START ───────────────────────────────────────────────────────────────────── // ── START ─────────────────────────────────────────────────────────────────────
initSchema().then(() => { initSchema().then(() => {
app.listen(port, '0.0.0.0', () => { initStorage().then(() => {
console.log(`Curio HMS running at http://0.0.0.0:${port}`); app.listen(port, '0.0.0.0', () => {
console.log(`Curio HMS running at http://0.0.0.0:${port}`);
});
}); });
}); });

85
backend/storage.js Normal file
View File

@ -0,0 +1,85 @@
const Minio = require('minio');
const multer = require('multer');
// Configure MinIO Client
const minioClient = new Minio.Client({
endPoint: process.env.MINIO_ENDPOINT || 'localhost',
port: parseInt(process.env.MINIO_PORT) || 9000,
useSSL: process.env.MINIO_USE_SSL === 'true',
accessKey: process.env.MINIO_ACCESS_KEY || 'minioadmin',
secretKey: process.env.MINIO_SECRET_KEY || 'minioadmin'
});
const BUCKET_NAME = process.env.MINIO_BUCKET_NAME || 'curio-media';
// Ensure bucket exists
async function initStorage() {
try {
const exists = await minioClient.bucketExists(BUCKET_NAME);
if (!exists) {
await minioClient.makeBucket(BUCKET_NAME, 'us-east-1');
console.log(`[Storage] Bucket '${BUCKET_NAME}' created successfully.`);
// Set basic public read policy for media
const policy = {
Version: '2012-10-17',
Statement: [
{
Action: ['s3:GetObject'],
Effect: 'Allow',
Principal: '*',
Resource: [`arn:aws:s3:::${BUCKET_NAME}/*`],
},
],
};
await minioClient.setBucketPolicy(BUCKET_NAME, JSON.stringify(policy));
} else {
console.log(`[Storage] Bucket '${BUCKET_NAME}' already exists.`);
}
} catch (err) {
console.error('[Storage] MinIO Init Error:', err.message);
}
}
// Multer in-memory storage for handling multipart form data before streaming to MinIO
const upload = multer({
storage: multer.memoryStorage(),
limits: {
fileSize: 10 * 1024 * 1024, // 10MB max file size
}
});
/**
* Upload a file buffer to MinIO
* @param {string} fileName
* @param {Buffer} fileBuffer
* @param {string} mimeType
* @returns {Promise<string>} Public URL of the uploaded file
*/
async function uploadFile(fileName, fileBuffer, mimeType) {
try {
await minioClient.putObject(BUCKET_NAME, fileName, fileBuffer, fileBuffer.length, {
'Content-Type': mimeType
});
const protocol = process.env.MINIO_USE_SSL === 'true' ? 'https' : 'http';
const port = process.env.MINIO_PORT ? `:${process.env.MINIO_PORT}` : '';
// If MinIO is accessible publicly, return the direct URL.
// For production behind proxy, this might need to be the proxy URL.
const publicUrl = process.env.MINIO_PUBLIC_URL
? `${process.env.MINIO_PUBLIC_URL}/${BUCKET_NAME}/${fileName}`
: `${protocol}://${process.env.MINIO_ENDPOINT || 'localhost'}${port}/${BUCKET_NAME}/${fileName}`;
return publicUrl;
} catch (err) {
console.error('[Storage] Upload Error:', err);
throw err;
}
}
module.exports = {
minioClient,
upload,
initStorage,
uploadFile
};

View File

@ -54,7 +54,9 @@
try { try {
const p = JSON.parse(atob(token.split('.')[1])); const p = JSON.parse(atob(token.split('.')[1]));
if (p.exp && Date.now() / 1000 < p.exp) { if (p.exp && Date.now() / 1000 < p.exp) {
window.location.href = p.role === 'SUPER_ADMIN' ? 'super-admin.html' : 'dashboard.html'; if (p.role === 'SUPER_ADMIN') window.location.href = 'super-admin.html';
else if (p.role === 'PATIENT') window.location.href = 'patient-dashboard.html';
else window.location.href = 'dashboard.html';
} }
} catch(_) {} } catch(_) {}
} }
@ -104,6 +106,8 @@
// Route by role // Route by role
if (data.role === 'SUPER_ADMIN') { if (data.role === 'SUPER_ADMIN') {
window.location.href = 'super-admin.html'; window.location.href = 'super-admin.html';
} else if (data.role === 'PATIENT') {
window.location.href = 'patient-dashboard.html';
} else { } else {
window.location.href = 'dashboard.html'; window.location.href = 'dashboard.html';
} }

385
node_modules/.package-lock.json generated vendored
View File

@ -75,6 +75,18 @@
"url": "https://opencollective.com/js-sdsl" "url": "https://opencollective.com/js-sdsl"
} }
}, },
"node_modules/@nodable/entities": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@nodable/entities/-/entities-2.1.0.tgz",
"integrity": "sha512-nyT7T3nbMyBI/lvr6L5TyWbFJAI9FTgVRakNoBqCD+PmID8DzFrrNdLLtHMwMszOtqZa8PAOV24ZqDnQrhQINA==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/nodable"
}
],
"license": "MIT"
},
"node_modules/@protobufjs/aspromise": { "node_modules/@protobufjs/aspromise": {
"version": "1.1.2", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
@ -296,12 +308,24 @@
"url": "https://github.com/chalk/ansi-styles?sponsor=1" "url": "https://github.com/chalk/ansi-styles?sponsor=1"
} }
}, },
"node_modules/append-field": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz",
"integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==",
"license": "MIT"
},
"node_modules/array-flatten": { "node_modules/array-flatten": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/async": {
"version": "3.2.6",
"resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz",
"integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==",
"license": "MIT"
},
"node_modules/asynckit": { "node_modules/asynckit": {
"version": "0.4.0", "version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
@ -329,6 +353,15 @@
"bcrypt": "bin/bcrypt" "bcrypt": "bin/bcrypt"
} }
}, },
"node_modules/block-stream2": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/block-stream2/-/block-stream2-2.1.0.tgz",
"integrity": "sha512-suhjmLI57Ewpmq00qaygS8UgEq2ly2PCItenIyhMqVjo4t4pGzqMvfgJuX8iWTeSDdfSSqS6j38fL4ToNL7Pfg==",
"license": "MIT",
"dependencies": {
"readable-stream": "^3.4.0"
}
},
"node_modules/body-parser": { "node_modules/body-parser": {
"version": "1.20.5", "version": "1.20.5",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.5.tgz", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.5.tgz",
@ -353,12 +386,44 @@
"npm": "1.2.8000 || >= 1.4.16" "npm": "1.2.8000 || >= 1.4.16"
} }
}, },
"node_modules/browser-or-node": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/browser-or-node/-/browser-or-node-2.1.1.tgz",
"integrity": "sha512-8CVjaLJGuSKMVTxJ2DpBl5XnlNDiT4cQFeuCJJrvJmts9YrTZDizTX7PjC2s6W4x+MBGZeEY6dGMrF04/6Hgqg==",
"license": "MIT"
},
"node_modules/buffer-crc32": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-1.0.0.tgz",
"integrity": "sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==",
"license": "MIT",
"engines": {
"node": ">=8.0.0"
}
},
"node_modules/buffer-equal-constant-time": { "node_modules/buffer-equal-constant-time": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
"integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==",
"license": "BSD-3-Clause" "license": "BSD-3-Clause"
}, },
"node_modules/buffer-from": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
"license": "MIT"
},
"node_modules/busboy": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
"integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==",
"dependencies": {
"streamsearch": "^1.1.0"
},
"engines": {
"node": ">=10.16.0"
}
},
"node_modules/bytes": { "node_modules/bytes": {
"version": "3.1.2", "version": "3.1.2",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
@ -441,6 +506,21 @@
"node": ">= 0.8" "node": ">= 0.8"
} }
}, },
"node_modules/concat-stream": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz",
"integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==",
"engines": [
"node >= 6.0"
],
"license": "MIT",
"dependencies": {
"buffer-from": "^1.0.0",
"inherits": "^2.0.3",
"readable-stream": "^3.0.2",
"typedarray": "^0.0.6"
}
},
"node_modules/content-disposition": { "node_modules/content-disposition": {
"version": "0.5.4", "version": "0.5.4",
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
@ -498,6 +578,15 @@
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/decode-uri-component": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz",
"integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==",
"license": "MIT",
"engines": {
"node": ">=0.10"
}
},
"node_modules/delayed-stream": { "node_modules/delayed-stream": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
@ -660,6 +749,12 @@
"node": ">=6" "node": ">=6"
} }
}, },
"node_modules/eventemitter3": {
"version": "5.0.4",
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz",
"integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==",
"license": "MIT"
},
"node_modules/express": { "node_modules/express": {
"version": "4.22.2", "version": "4.22.2",
"resolved": "https://registry.npmjs.org/express/-/express-4.22.2.tgz", "resolved": "https://registry.npmjs.org/express/-/express-4.22.2.tgz",
@ -712,6 +807,53 @@
"integrity": "sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ==", "integrity": "sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ==",
"license": "Unlicense" "license": "Unlicense"
}, },
"node_modules/fast-xml-builder": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.2.0.tgz",
"integrity": "sha512-00aAWieqff+ZJhsXA4g1g7M8k+7AYoMUUHF+/zFb5U6Uv/P0Vl4QZo84/IcufzYalLuEj9928bXN9PbbFzMF0Q==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/NaturalIntelligence"
}
],
"license": "MIT",
"dependencies": {
"path-expression-matcher": "^1.5.0",
"xml-naming": "^0.1.0"
}
},
"node_modules/fast-xml-parser": {
"version": "5.8.0",
"resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.8.0.tgz",
"integrity": "sha512-6bIM7fsJxeo3uXv7OncQYsBAMPJ7V16Slahl/6M98C/i2q+vB1+4a0MtrvYwDFEUrwDSbAmeLDRXsOBwrL7yAg==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/NaturalIntelligence"
}
],
"license": "MIT",
"dependencies": {
"@nodable/entities": "^2.1.0",
"fast-xml-builder": "^1.2.0",
"path-expression-matcher": "^1.5.0",
"strnum": "^2.3.0",
"xml-naming": "^0.1.0"
},
"bin": {
"fxparser": "src/cli/cli.js"
}
},
"node_modules/filter-obj": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz",
"integrity": "sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==",
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/finalhandler": { "node_modules/finalhandler": {
"version": "1.3.2", "version": "1.3.2",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz",
@ -1044,6 +1186,12 @@
"safe-buffer": "^5.0.1" "safe-buffer": "^5.0.1"
} }
}, },
"node_modules/lodash": {
"version": "4.18.1",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz",
"integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==",
"license": "MIT"
},
"node_modules/lodash.camelcase": { "node_modules/lodash.camelcase": {
"version": "4.3.0", "version": "4.3.0",
"resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
@ -1167,6 +1315,39 @@
"node": ">= 0.6" "node": ">= 0.6"
} }
}, },
"node_modules/minio": {
"version": "8.0.7",
"resolved": "https://registry.npmjs.org/minio/-/minio-8.0.7.tgz",
"integrity": "sha512-E737MgufW8CeQAsTAtnEMrxZ9scMSf29kkhZoXzDTKj/Jszzo2SfeZUH9wbDQH2Rsq6TCtl/yQL0+XdVKZansQ==",
"license": "Apache-2.0",
"dependencies": {
"async": "^3.2.4",
"block-stream2": "^2.1.0",
"browser-or-node": "^2.1.1",
"buffer-crc32": "^1.0.0",
"eventemitter3": "^5.0.1",
"fast-xml-parser": "^5.3.4",
"ipaddr.js": "^2.0.1",
"lodash": "^4.17.21",
"mime-types": "^2.1.35",
"query-string": "^7.1.3",
"stream-json": "^1.8.0",
"through2": "^4.0.2",
"xml2js": "^0.5.0 || ^0.6.2"
},
"engines": {
"node": "^16 || ^18 || >=20"
}
},
"node_modules/minio/node_modules/ipaddr.js": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.4.0.tgz",
"integrity": "sha512-9VGk3HGanVE6JoZXHiCpnGy5X0jYDnN4EA4lntFPj+1vIWlFhIylq2CrrCOJH9EAhc5CYhq18F2Av2tgoAPsYQ==",
"license": "MIT",
"engines": {
"node": ">= 10"
}
},
"node_modules/ms": { "node_modules/ms": {
"version": "3.0.0-canary.1", "version": "3.0.0-canary.1",
"resolved": "https://registry.npmjs.org/ms/-/ms-3.0.0-canary.1.tgz", "resolved": "https://registry.npmjs.org/ms/-/ms-3.0.0-canary.1.tgz",
@ -1176,6 +1357,25 @@
"node": ">=12.13" "node": ">=12.13"
} }
}, },
"node_modules/multer": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/multer/-/multer-2.1.1.tgz",
"integrity": "sha512-mo+QTzKlx8R7E5ylSXxWzGoXoZbOsRMpyitcht8By2KHvMbf3tjwosZ/Mu/XYU6UuJ3VZnODIrak5ZrPiPyB6A==",
"license": "MIT",
"dependencies": {
"append-field": "^1.0.0",
"busboy": "^1.6.0",
"concat-stream": "^2.0.0",
"type-is": "^1.6.18"
},
"engines": {
"node": ">= 10.16.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/express"
}
},
"node_modules/negotiator": { "node_modules/negotiator": {
"version": "0.6.3", "version": "0.6.3",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
@ -1248,6 +1448,21 @@
"node": ">= 0.8" "node": ">= 0.8"
} }
}, },
"node_modules/path-expression-matcher": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.5.0.tgz",
"integrity": "sha512-cbrerZV+6rvdQrrD+iGMcZFEiiSrbv9Tfdkvnusy6y0x0GKBXREFg/Y65GhIfm0tnLntThhzCnfKwp1WRjeCyQ==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/NaturalIntelligence"
}
],
"license": "MIT",
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/path-to-regexp": { "node_modules/path-to-regexp": {
"version": "0.1.13", "version": "0.1.13",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.13.tgz", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.13.tgz",
@ -1456,6 +1671,24 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/query-string": {
"version": "7.1.3",
"resolved": "https://registry.npmjs.org/query-string/-/query-string-7.1.3.tgz",
"integrity": "sha512-hh2WYhq4fi8+b+/2Kg9CEge4fDPvHS534aOOvOZeQ3+Vf2mCFsaFBYj0i+iXcAq6I9Vzp5fjMFBlONvayDC1qg==",
"license": "MIT",
"dependencies": {
"decode-uri-component": "^0.2.2",
"filter-obj": "^1.1.0",
"split-on-first": "^1.0.0",
"strict-uri-encode": "^2.0.0"
},
"engines": {
"node": ">=6"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/range-parser": { "node_modules/range-parser": {
"version": "1.2.1", "version": "1.2.1",
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
@ -1480,6 +1713,29 @@
"node": ">= 0.8" "node": ">= 0.8"
} }
}, },
"node_modules/razorpay": {
"version": "2.9.6",
"resolved": "https://registry.npmjs.org/razorpay/-/razorpay-2.9.6.tgz",
"integrity": "sha512-zsHAQzd6e1Cc6BNoCNZQaf65ElL6O6yw0wulxmoG5VQDr363fZC90Mp1V5EktVzG45yPyNomNXWlf4cQ3622gQ==",
"license": "MIT",
"dependencies": {
"axios": "^1.6.8"
}
},
"node_modules/readable-stream": {
"version": "3.6.2",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
"integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
"license": "MIT",
"dependencies": {
"inherits": "^2.0.3",
"string_decoder": "^1.1.1",
"util-deprecate": "^1.0.1"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/require-directory": { "node_modules/require-directory": {
"version": "2.1.1", "version": "2.1.1",
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
@ -1515,6 +1771,15 @@
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/sax": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/sax/-/sax-1.6.0.tgz",
"integrity": "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==",
"license": "BlueOak-1.0.0",
"engines": {
"node": ">=11.0.0"
}
},
"node_modules/scmp": { "node_modules/scmp": {
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/scmp/-/scmp-2.1.0.tgz", "resolved": "https://registry.npmjs.org/scmp/-/scmp-2.1.0.tgz",
@ -1657,6 +1922,15 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/split-on-first": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz",
"integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==",
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/split2": { "node_modules/split2": {
"version": "4.2.0", "version": "4.2.0",
"resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz",
@ -1685,6 +1959,47 @@
"node": ">= 0.8" "node": ">= 0.8"
} }
}, },
"node_modules/stream-chain": {
"version": "2.2.5",
"resolved": "https://registry.npmjs.org/stream-chain/-/stream-chain-2.2.5.tgz",
"integrity": "sha512-1TJmBx6aSWqZ4tx7aTpBDXK0/e2hhcNSTV8+CbFJtDjbb+I1mZ8lHit0Grw9GRT+6JbIrrDd8esncgBi8aBXGA==",
"license": "BSD-3-Clause"
},
"node_modules/stream-json": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/stream-json/-/stream-json-1.9.1.tgz",
"integrity": "sha512-uWkjJ+2Nt/LO9Z/JyKZbMusL8Dkh97uUBTv3AJQ74y07lVahLY4eEFsPsE97pxYBwr8nnjMAIch5eqI0gPShyw==",
"license": "BSD-3-Clause",
"dependencies": {
"stream-chain": "^2.2.5"
}
},
"node_modules/streamsearch": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz",
"integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==",
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/strict-uri-encode": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz",
"integrity": "sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==",
"license": "MIT",
"engines": {
"node": ">=4"
}
},
"node_modules/string_decoder": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
"license": "MIT",
"dependencies": {
"safe-buffer": "~5.2.0"
}
},
"node_modules/string-width": { "node_modules/string-width": {
"version": "4.2.3", "version": "4.2.3",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
@ -1711,6 +2026,27 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/strnum": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/strnum/-/strnum-2.3.0.tgz",
"integrity": "sha512-ums3KNd42PGyx5xaoVTO1mjU1bH3NpY4vsrVlnv9PNGqQj8wd7rJ6nEypLrJ7z5vxK5RP0yMLo6J/Gsm62DI5Q==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/NaturalIntelligence"
}
],
"license": "MIT"
},
"node_modules/through2": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz",
"integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==",
"license": "MIT",
"dependencies": {
"readable-stream": "3"
}
},
"node_modules/toidentifier": { "node_modules/toidentifier": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
@ -1757,6 +2093,12 @@
"node": ">= 0.6" "node": ">= 0.6"
} }
}, },
"node_modules/typedarray": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
"integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==",
"license": "MIT"
},
"node_modules/undici-types": { "node_modules/undici-types": {
"version": "7.24.6", "version": "7.24.6",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz",
@ -1772,6 +2114,12 @@
"node": ">= 0.8" "node": ">= 0.8"
} }
}, },
"node_modules/util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
"license": "MIT"
},
"node_modules/utils-merge": { "node_modules/utils-merge": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
@ -1820,6 +2168,43 @@
"url": "https://github.com/chalk/wrap-ansi?sponsor=1" "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
} }
}, },
"node_modules/xml-naming": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/xml-naming/-/xml-naming-0.1.0.tgz",
"integrity": "sha512-k8KO9hrMyNk6tUWqUfkTEZbezRRpONVOzUTnc97VnCvyj6Tf9lyUR9EDAIeiVLv56jsMcoXEwjW8Kv5yPY52lw==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/NaturalIntelligence"
}
],
"license": "MIT",
"engines": {
"node": ">=16.0.0"
}
},
"node_modules/xml2js": {
"version": "0.6.2",
"resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.2.tgz",
"integrity": "sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==",
"license": "MIT",
"dependencies": {
"sax": ">=0.6.0",
"xmlbuilder": "~11.0.0"
},
"engines": {
"node": ">=4.0.0"
}
},
"node_modules/xml2js/node_modules/xmlbuilder": {
"version": "11.0.1",
"resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz",
"integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==",
"license": "MIT",
"engines": {
"node": ">=4.0"
}
},
"node_modules/xmlbuilder": { "node_modules/xmlbuilder": {
"version": "13.0.2", "version": "13.0.2",
"resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-13.0.2.tgz", "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-13.0.2.tgz",

388
package-lock.json generated
View File

@ -14,8 +14,11 @@
"dotenv": "^17.4.2", "dotenv": "^17.4.2",
"express": "^4.18.2", "express": "^4.18.2",
"jsonwebtoken": "^9.0.3", "jsonwebtoken": "^9.0.3",
"minio": "^8.0.7",
"multer": "^2.1.1",
"openai": "^6.39.0", "openai": "^6.39.0",
"pg": "^8.21.0", "pg": "^8.21.0",
"razorpay": "^2.9.6",
"twilio": "^6.0.2", "twilio": "^6.0.2",
"uuid": "^14.0.0" "uuid": "^14.0.0"
} }
@ -91,6 +94,18 @@
"url": "https://opencollective.com/js-sdsl" "url": "https://opencollective.com/js-sdsl"
} }
}, },
"node_modules/@nodable/entities": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@nodable/entities/-/entities-2.1.0.tgz",
"integrity": "sha512-nyT7T3nbMyBI/lvr6L5TyWbFJAI9FTgVRakNoBqCD+PmID8DzFrrNdLLtHMwMszOtqZa8PAOV24ZqDnQrhQINA==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/nodable"
}
],
"license": "MIT"
},
"node_modules/@protobufjs/aspromise": { "node_modules/@protobufjs/aspromise": {
"version": "1.1.2", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
@ -312,12 +327,24 @@
"url": "https://github.com/chalk/ansi-styles?sponsor=1" "url": "https://github.com/chalk/ansi-styles?sponsor=1"
} }
}, },
"node_modules/append-field": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz",
"integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==",
"license": "MIT"
},
"node_modules/array-flatten": { "node_modules/array-flatten": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/async": {
"version": "3.2.6",
"resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz",
"integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==",
"license": "MIT"
},
"node_modules/asynckit": { "node_modules/asynckit": {
"version": "0.4.0", "version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
@ -345,6 +372,15 @@
"bcrypt": "bin/bcrypt" "bcrypt": "bin/bcrypt"
} }
}, },
"node_modules/block-stream2": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/block-stream2/-/block-stream2-2.1.0.tgz",
"integrity": "sha512-suhjmLI57Ewpmq00qaygS8UgEq2ly2PCItenIyhMqVjo4t4pGzqMvfgJuX8iWTeSDdfSSqS6j38fL4ToNL7Pfg==",
"license": "MIT",
"dependencies": {
"readable-stream": "^3.4.0"
}
},
"node_modules/body-parser": { "node_modules/body-parser": {
"version": "1.20.5", "version": "1.20.5",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.5.tgz", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.5.tgz",
@ -369,12 +405,44 @@
"npm": "1.2.8000 || >= 1.4.16" "npm": "1.2.8000 || >= 1.4.16"
} }
}, },
"node_modules/browser-or-node": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/browser-or-node/-/browser-or-node-2.1.1.tgz",
"integrity": "sha512-8CVjaLJGuSKMVTxJ2DpBl5XnlNDiT4cQFeuCJJrvJmts9YrTZDizTX7PjC2s6W4x+MBGZeEY6dGMrF04/6Hgqg==",
"license": "MIT"
},
"node_modules/buffer-crc32": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-1.0.0.tgz",
"integrity": "sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==",
"license": "MIT",
"engines": {
"node": ">=8.0.0"
}
},
"node_modules/buffer-equal-constant-time": { "node_modules/buffer-equal-constant-time": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
"integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==",
"license": "BSD-3-Clause" "license": "BSD-3-Clause"
}, },
"node_modules/buffer-from": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
"license": "MIT"
},
"node_modules/busboy": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
"integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==",
"dependencies": {
"streamsearch": "^1.1.0"
},
"engines": {
"node": ">=10.16.0"
}
},
"node_modules/bytes": { "node_modules/bytes": {
"version": "3.1.2", "version": "3.1.2",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
@ -457,6 +525,21 @@
"node": ">= 0.8" "node": ">= 0.8"
} }
}, },
"node_modules/concat-stream": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz",
"integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==",
"engines": [
"node >= 6.0"
],
"license": "MIT",
"dependencies": {
"buffer-from": "^1.0.0",
"inherits": "^2.0.3",
"readable-stream": "^3.0.2",
"typedarray": "^0.0.6"
}
},
"node_modules/content-disposition": { "node_modules/content-disposition": {
"version": "0.5.4", "version": "0.5.4",
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
@ -514,6 +597,15 @@
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/decode-uri-component": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz",
"integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==",
"license": "MIT",
"engines": {
"node": ">=0.10"
}
},
"node_modules/delayed-stream": { "node_modules/delayed-stream": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
@ -676,6 +768,12 @@
"node": ">=6" "node": ">=6"
} }
}, },
"node_modules/eventemitter3": {
"version": "5.0.4",
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz",
"integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==",
"license": "MIT"
},
"node_modules/express": { "node_modules/express": {
"version": "4.22.2", "version": "4.22.2",
"resolved": "https://registry.npmjs.org/express/-/express-4.22.2.tgz", "resolved": "https://registry.npmjs.org/express/-/express-4.22.2.tgz",
@ -728,6 +826,53 @@
"integrity": "sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ==", "integrity": "sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ==",
"license": "Unlicense" "license": "Unlicense"
}, },
"node_modules/fast-xml-builder": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.2.0.tgz",
"integrity": "sha512-00aAWieqff+ZJhsXA4g1g7M8k+7AYoMUUHF+/zFb5U6Uv/P0Vl4QZo84/IcufzYalLuEj9928bXN9PbbFzMF0Q==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/NaturalIntelligence"
}
],
"license": "MIT",
"dependencies": {
"path-expression-matcher": "^1.5.0",
"xml-naming": "^0.1.0"
}
},
"node_modules/fast-xml-parser": {
"version": "5.8.0",
"resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.8.0.tgz",
"integrity": "sha512-6bIM7fsJxeo3uXv7OncQYsBAMPJ7V16Slahl/6M98C/i2q+vB1+4a0MtrvYwDFEUrwDSbAmeLDRXsOBwrL7yAg==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/NaturalIntelligence"
}
],
"license": "MIT",
"dependencies": {
"@nodable/entities": "^2.1.0",
"fast-xml-builder": "^1.2.0",
"path-expression-matcher": "^1.5.0",
"strnum": "^2.3.0",
"xml-naming": "^0.1.0"
},
"bin": {
"fxparser": "src/cli/cli.js"
}
},
"node_modules/filter-obj": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz",
"integrity": "sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==",
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/finalhandler": { "node_modules/finalhandler": {
"version": "1.3.2", "version": "1.3.2",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz",
@ -1060,6 +1205,12 @@
"safe-buffer": "^5.0.1" "safe-buffer": "^5.0.1"
} }
}, },
"node_modules/lodash": {
"version": "4.18.1",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz",
"integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==",
"license": "MIT"
},
"node_modules/lodash.camelcase": { "node_modules/lodash.camelcase": {
"version": "4.3.0", "version": "4.3.0",
"resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
@ -1183,6 +1334,39 @@
"node": ">= 0.6" "node": ">= 0.6"
} }
}, },
"node_modules/minio": {
"version": "8.0.7",
"resolved": "https://registry.npmjs.org/minio/-/minio-8.0.7.tgz",
"integrity": "sha512-E737MgufW8CeQAsTAtnEMrxZ9scMSf29kkhZoXzDTKj/Jszzo2SfeZUH9wbDQH2Rsq6TCtl/yQL0+XdVKZansQ==",
"license": "Apache-2.0",
"dependencies": {
"async": "^3.2.4",
"block-stream2": "^2.1.0",
"browser-or-node": "^2.1.1",
"buffer-crc32": "^1.0.0",
"eventemitter3": "^5.0.1",
"fast-xml-parser": "^5.3.4",
"ipaddr.js": "^2.0.1",
"lodash": "^4.17.21",
"mime-types": "^2.1.35",
"query-string": "^7.1.3",
"stream-json": "^1.8.0",
"through2": "^4.0.2",
"xml2js": "^0.5.0 || ^0.6.2"
},
"engines": {
"node": "^16 || ^18 || >=20"
}
},
"node_modules/minio/node_modules/ipaddr.js": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.4.0.tgz",
"integrity": "sha512-9VGk3HGanVE6JoZXHiCpnGy5X0jYDnN4EA4lntFPj+1vIWlFhIylq2CrrCOJH9EAhc5CYhq18F2Av2tgoAPsYQ==",
"license": "MIT",
"engines": {
"node": ">= 10"
}
},
"node_modules/ms": { "node_modules/ms": {
"version": "3.0.0-canary.1", "version": "3.0.0-canary.1",
"resolved": "https://registry.npmjs.org/ms/-/ms-3.0.0-canary.1.tgz", "resolved": "https://registry.npmjs.org/ms/-/ms-3.0.0-canary.1.tgz",
@ -1192,6 +1376,25 @@
"node": ">=12.13" "node": ">=12.13"
} }
}, },
"node_modules/multer": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/multer/-/multer-2.1.1.tgz",
"integrity": "sha512-mo+QTzKlx8R7E5ylSXxWzGoXoZbOsRMpyitcht8By2KHvMbf3tjwosZ/Mu/XYU6UuJ3VZnODIrak5ZrPiPyB6A==",
"license": "MIT",
"dependencies": {
"append-field": "^1.0.0",
"busboy": "^1.6.0",
"concat-stream": "^2.0.0",
"type-is": "^1.6.18"
},
"engines": {
"node": ">= 10.16.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/express"
}
},
"node_modules/negotiator": { "node_modules/negotiator": {
"version": "0.6.3", "version": "0.6.3",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
@ -1264,6 +1467,21 @@
"node": ">= 0.8" "node": ">= 0.8"
} }
}, },
"node_modules/path-expression-matcher": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.5.0.tgz",
"integrity": "sha512-cbrerZV+6rvdQrrD+iGMcZFEiiSrbv9Tfdkvnusy6y0x0GKBXREFg/Y65GhIfm0tnLntThhzCnfKwp1WRjeCyQ==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/NaturalIntelligence"
}
],
"license": "MIT",
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/path-to-regexp": { "node_modules/path-to-regexp": {
"version": "0.1.13", "version": "0.1.13",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.13.tgz", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.13.tgz",
@ -1472,6 +1690,24 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/query-string": {
"version": "7.1.3",
"resolved": "https://registry.npmjs.org/query-string/-/query-string-7.1.3.tgz",
"integrity": "sha512-hh2WYhq4fi8+b+/2Kg9CEge4fDPvHS534aOOvOZeQ3+Vf2mCFsaFBYj0i+iXcAq6I9Vzp5fjMFBlONvayDC1qg==",
"license": "MIT",
"dependencies": {
"decode-uri-component": "^0.2.2",
"filter-obj": "^1.1.0",
"split-on-first": "^1.0.0",
"strict-uri-encode": "^2.0.0"
},
"engines": {
"node": ">=6"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/range-parser": { "node_modules/range-parser": {
"version": "1.2.1", "version": "1.2.1",
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
@ -1496,6 +1732,29 @@
"node": ">= 0.8" "node": ">= 0.8"
} }
}, },
"node_modules/razorpay": {
"version": "2.9.6",
"resolved": "https://registry.npmjs.org/razorpay/-/razorpay-2.9.6.tgz",
"integrity": "sha512-zsHAQzd6e1Cc6BNoCNZQaf65ElL6O6yw0wulxmoG5VQDr363fZC90Mp1V5EktVzG45yPyNomNXWlf4cQ3622gQ==",
"license": "MIT",
"dependencies": {
"axios": "^1.6.8"
}
},
"node_modules/readable-stream": {
"version": "3.6.2",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
"integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
"license": "MIT",
"dependencies": {
"inherits": "^2.0.3",
"string_decoder": "^1.1.1",
"util-deprecate": "^1.0.1"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/require-directory": { "node_modules/require-directory": {
"version": "2.1.1", "version": "2.1.1",
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
@ -1531,6 +1790,15 @@
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/sax": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/sax/-/sax-1.6.0.tgz",
"integrity": "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==",
"license": "BlueOak-1.0.0",
"engines": {
"node": ">=11.0.0"
}
},
"node_modules/scmp": { "node_modules/scmp": {
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/scmp/-/scmp-2.1.0.tgz", "resolved": "https://registry.npmjs.org/scmp/-/scmp-2.1.0.tgz",
@ -1673,6 +1941,15 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/split-on-first": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz",
"integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==",
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/split2": { "node_modules/split2": {
"version": "4.2.0", "version": "4.2.0",
"resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz",
@ -1701,6 +1978,47 @@
"node": ">= 0.8" "node": ">= 0.8"
} }
}, },
"node_modules/stream-chain": {
"version": "2.2.5",
"resolved": "https://registry.npmjs.org/stream-chain/-/stream-chain-2.2.5.tgz",
"integrity": "sha512-1TJmBx6aSWqZ4tx7aTpBDXK0/e2hhcNSTV8+CbFJtDjbb+I1mZ8lHit0Grw9GRT+6JbIrrDd8esncgBi8aBXGA==",
"license": "BSD-3-Clause"
},
"node_modules/stream-json": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/stream-json/-/stream-json-1.9.1.tgz",
"integrity": "sha512-uWkjJ+2Nt/LO9Z/JyKZbMusL8Dkh97uUBTv3AJQ74y07lVahLY4eEFsPsE97pxYBwr8nnjMAIch5eqI0gPShyw==",
"license": "BSD-3-Clause",
"dependencies": {
"stream-chain": "^2.2.5"
}
},
"node_modules/streamsearch": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz",
"integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==",
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/strict-uri-encode": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz",
"integrity": "sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==",
"license": "MIT",
"engines": {
"node": ">=4"
}
},
"node_modules/string_decoder": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
"license": "MIT",
"dependencies": {
"safe-buffer": "~5.2.0"
}
},
"node_modules/string-width": { "node_modules/string-width": {
"version": "4.2.3", "version": "4.2.3",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
@ -1727,6 +2045,27 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/strnum": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/strnum/-/strnum-2.3.0.tgz",
"integrity": "sha512-ums3KNd42PGyx5xaoVTO1mjU1bH3NpY4vsrVlnv9PNGqQj8wd7rJ6nEypLrJ7z5vxK5RP0yMLo6J/Gsm62DI5Q==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/NaturalIntelligence"
}
],
"license": "MIT"
},
"node_modules/through2": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz",
"integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==",
"license": "MIT",
"dependencies": {
"readable-stream": "3"
}
},
"node_modules/toidentifier": { "node_modules/toidentifier": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
@ -1773,6 +2112,12 @@
"node": ">= 0.6" "node": ">= 0.6"
} }
}, },
"node_modules/typedarray": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
"integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==",
"license": "MIT"
},
"node_modules/undici-types": { "node_modules/undici-types": {
"version": "7.24.6", "version": "7.24.6",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz",
@ -1788,6 +2133,12 @@
"node": ">= 0.8" "node": ">= 0.8"
} }
}, },
"node_modules/util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
"license": "MIT"
},
"node_modules/utils-merge": { "node_modules/utils-merge": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
@ -1836,6 +2187,43 @@
"url": "https://github.com/chalk/wrap-ansi?sponsor=1" "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
} }
}, },
"node_modules/xml-naming": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/xml-naming/-/xml-naming-0.1.0.tgz",
"integrity": "sha512-k8KO9hrMyNk6tUWqUfkTEZbezRRpONVOzUTnc97VnCvyj6Tf9lyUR9EDAIeiVLv56jsMcoXEwjW8Kv5yPY52lw==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/NaturalIntelligence"
}
],
"license": "MIT",
"engines": {
"node": ">=16.0.0"
}
},
"node_modules/xml2js": {
"version": "0.6.2",
"resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.2.tgz",
"integrity": "sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==",
"license": "MIT",
"dependencies": {
"sax": ">=0.6.0",
"xmlbuilder": "~11.0.0"
},
"engines": {
"node": ">=4.0.0"
}
},
"node_modules/xml2js/node_modules/xmlbuilder": {
"version": "11.0.1",
"resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz",
"integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==",
"license": "MIT",
"engines": {
"node": ">=4.0"
}
},
"node_modules/xmlbuilder": { "node_modules/xmlbuilder": {
"version": "13.0.2", "version": "13.0.2",
"resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-13.0.2.tgz", "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-13.0.2.tgz",

View File

@ -5,7 +5,8 @@
"main": "backend/server.js", "main": "backend/server.js",
"scripts": { "scripts": {
"start": "node backend/server.js", "start": "node backend/server.js",
"dev": "nodemon backend/server.js" "dev": "nodemon backend/server.js",
"logs": "node tools/log-viewer/server.js"
}, },
"dependencies": { "dependencies": {
"@anthropic-ai/sdk": "^0.98.0", "@anthropic-ai/sdk": "^0.98.0",
@ -14,8 +15,11 @@
"dotenv": "^17.4.2", "dotenv": "^17.4.2",
"express": "^4.18.2", "express": "^4.18.2",
"jsonwebtoken": "^9.0.3", "jsonwebtoken": "^9.0.3",
"minio": "^8.0.7",
"multer": "^2.1.1",
"openai": "^6.39.0", "openai": "^6.39.0",
"pg": "^8.21.0", "pg": "^8.21.0",
"razorpay": "^2.9.6",
"twilio": "^6.0.2", "twilio": "^6.0.2",
"uuid": "^14.0.0" "uuid": "^14.0.0"
} }

169
patient-dashboard.html Normal file
View File

@ -0,0 +1,169 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Patient Portal | Curio</title>
<link rel="stylesheet" href="globals.css">
<link rel="stylesheet" href="index.css">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Outfit:wght@500;600;700&display=swap" rel="stylesheet">
<script src="https://checkout.razorpay.com/v1/checkout.js"></script>
<style>
.patient-layout {
display: grid;
grid-template-columns: 300px 1fr;
min-height: 100vh;
}
@media (max-width: 768px) {
.patient-layout { grid-template-columns: 1fr; }
.sidebar { display: none; }
}
.main-content {
padding: 2rem;
max-width: 1200px;
margin: 0 auto;
}
.bill-card {
display: flex;
justify-content: space-between;
align-items: center;
padding: 1rem;
border: 1px solid var(--border);
border-radius: var(--radius-md);
margin-bottom: 1rem;
background: white;
}
</style>
</head>
<body class="dashboard-body patient-layout">
<aside class="sidebar">
<div class="logo">
<span class="logo-icon">C</span>
<span class="logo-text">Curio</span>
</div>
<nav class="side-nav">
<a href="#" class="active"><span>🏠</span> Overview</a>
<a href="#"><span>📅</span> Appointments</a>
<a href="#"><span>📄</span> Medical Records</a>
</nav>
<div class="user-profile">
<div class="avatar" id="avatarInitials">PA</div>
<div class="info">
<strong id="patientName">Patient Name</strong>
<span>Patient</span>
<a href="login.html" class="logout-btn" onclick="localStorage.clear()"><span>🚪</span> Logout</a>
</div>
</div>
</aside>
<main class="main-content">
<header style="margin-bottom: 2rem;">
<h1>Welcome back, <span id="welcomeName">Patient</span></h1>
<p class="text-muted">Manage your appointments, records, and payments.</p>
</header>
<section class="settings-grid">
<div class="section-card">
<h2>Pending Payments</h2>
<div id="pendingBills">
<div class="bill-card">
<div>
<strong>Consultation Fee</strong>
<div class="text-muted" style="font-size:0.875rem;">Dr. Sharma - Cardiology</div>
</div>
<div style="text-align:right;">
<div style="font-weight:700; color:var(--primary); font-size:1.1rem;">₹500</div>
<button class="btn-primary" style="padding:0.4rem 1rem; margin-top:0.5rem;" onclick="initiatePayment(500, 'Consultation Fee')">Pay Now</button>
</div>
</div>
</div>
</div>
<div class="section-card">
<h2>Upcoming Appointments</h2>
<div class="bill-card">
<div>
<strong>Dr. Sharma</strong>
<div class="text-muted" style="font-size:0.875rem;">Tomorrow, 10:30 AM</div>
</div>
<span class="badge badge-success">Confirmed</span>
</div>
</div>
</section>
</main>
<script>
document.addEventListener('DOMContentLoaded', () => {
const token = localStorage.getItem('curio_token');
if (!token) window.location.href = 'login.html';
const name = localStorage.getItem('userName') || 'Patient';
document.getElementById('patientName').innerText = name;
document.getElementById('welcomeName').innerText = name;
document.getElementById('avatarInitials').innerText = name.substring(0, 2).toUpperCase();
});
async function initiatePayment(amount, description) {
try {
// 1. Create order on backend
const res = await fetch('/api/payments/create-order', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${localStorage.getItem('curio_token')}`
},
body: JSON.stringify({ amount, notes: { description } })
});
const orderData = await res.json();
if (!res.ok) throw new Error(orderData.error);
// 2. Initialize Razorpay Checkout
const options = {
key: 'rzp_test_dummy_key_id', // Replace with Razorpay Key ID
amount: orderData.amount,
currency: orderData.currency,
name: 'Curio HMS',
description: description,
order_id: orderData.orderId,
handler: async function (response) {
// 3. Verify Payment
const verifyRes = await fetch('/api/payments/verify', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${localStorage.getItem('curio_token')}`
},
body: JSON.stringify(response)
});
const verifyData = await verifyRes.json();
if (verifyData.success) {
alert('Payment successful!');
location.reload();
} else {
alert('Payment verification failed.');
}
},
prefill: {
name: localStorage.getItem('userName'),
email: localStorage.getItem('userEmail'),
},
theme: {
color: '#0F172A' // Curio primary color
}
};
const rzp = new Razorpay(options);
rzp.open();
} catch (err) {
console.error(err);
alert('Error initiating payment. Please try again.');
}
}
</script>
</body>
</html>

View File

@ -93,6 +93,19 @@
<input type="number" value="20"> <input type="number" value="20">
</div> </div>
</div> </div>
<div class="section-card">
<h2>Services Catalog</h2>
<p>Add specialized services/amenities offered by your clinic.</p>
<div id="servicesList" style="margin-bottom: 1rem;">
<!-- Dynamic List -->
</div>
<div class="input-group" style="display:flex; gap:0.5rem;">
<input type="text" id="newServiceName" placeholder="Service Name (e.g. ECG)">
<input type="number" id="newServicePrice" placeholder="Price (₹)" style="width: 100px;">
<button class="btn-secondary" onclick="addService()">+ Add</button>
</div>
</div>
</section> </section>
<section class="settings-grid doctor-only hidden"> <section class="settings-grid doctor-only hidden">
@ -132,11 +145,35 @@
</div> </div>
<div class="section-card"> <div class="section-card">
<h2>Digital Signature</h2> <h2>Blocked Dates</h2>
<div class="signature-upload" style="border: 2px dashed var(--border); padding: 2rem; text-align: center; border-radius: var(--radius-md);"> <p>Select dates you are unavailable for consultations.</p>
<p>Click to upload or drag your signature image</p> <div class="input-group" style="display:flex; gap:0.5rem;">
<button class="btn-small secondary">Upload Image</button> <input type="date" id="blockDateInput">
<button class="btn-secondary" onclick="addBlockDate()">+ Block Date</button>
</div> </div>
<ul id="blockedDatesList" style="list-style:none; margin-top:1rem;"></ul>
</div>
<div class="section-card">
<h2>Profile Media (Public)</h2>
<p>Upload a profile video or image for the patient portal.</p>
<div class="signature-upload" style="border: 2px dashed var(--border); padding: 2rem; text-align: center; border-radius: var(--radius-md);">
<input type="file" id="mediaUploadInput" accept="image/*,video/*" style="display:none;" onchange="uploadMedia()">
<p>Click to upload image or video</p>
<button class="btn-small secondary" onclick="document.getElementById('mediaUploadInput').click()">Upload Media</button>
</div>
<div id="mediaPreview" style="margin-top: 1rem; display:flex; gap: 1rem; flex-wrap: wrap;"></div>
</div>
<div class="section-card">
<h2>Q&A (Public)</h2>
<p>Add common questions patients ask you.</p>
<div id="qaList" style="margin-bottom: 1rem;"></div>
<div class="input-group">
<input type="text" id="qaQuestion" placeholder="Question" style="margin-bottom:0.5rem;">
<textarea id="qaAnswer" placeholder="Answer" rows="2" style="width:100%; border: 1px solid var(--border); border-radius: var(--radius-md); padding: 0.5rem; font-family: inherit;"></textarea>
</div>
<button class="btn-secondary" onclick="addQA()" style="margin-top:0.5rem;">+ Add Q&A</button>
</div> </div>
</section> </section>
</main> </main>

View File

@ -0,0 +1,183 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Curio Live Logs</title>
<style>
body {
background-color: #0d1117;
color: #c9d1d9;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
margin: 0;
display: flex;
flex-direction: column;
height: 100vh;
}
header {
background-color: #161b22;
padding: 16px 24px;
border-bottom: 1px solid #30363d;
display: flex;
justify-content: space-between;
align-items: center;
}
h1 {
font-size: 18px;
margin: 0;
display: flex;
align-items: center;
gap: 12px;
}
.status-dot {
width: 10px;
height: 10px;
background-color: #238636;
border-radius: 50%;
box-shadow: 0 0 8px #238636;
}
.controls {
display: flex;
gap: 12px;
}
button {
background-color: #21262d;
border: 1px solid #30363d;
color: #c9d1d9;
padding: 6px 16px;
border-radius: 6px;
cursor: pointer;
font-weight: 500;
transition: all 0.2s;
}
button:hover {
background-color: #30363d;
border-color: #8b949e;
}
button.active {
background-color: #1f6feb;
border-color: #1f6feb;
color: #ffffff;
}
#terminal {
flex: 1;
padding: 16px;
overflow-y: auto;
font-family: "Fira Code", "Consolas", monospace;
font-size: 13px;
line-height: 1.5;
background-color: #010409;
}
.log-line {
margin: 0;
word-wrap: break-word;
white-space: pre-wrap;
border-bottom: 1px solid rgba(255,255,255,0.02);
padding: 2px 0;
}
.log-error {
color: #f85149;
}
.log-info {
color: #58a6ff;
}
.log-warn {
color: #d29922;
}
.log-success {
color: #3fb950;
}
.log-timestamp {
color: #8b949e;
user-select: none;
margin-right: 12px;
}
</style>
</head>
<body>
<header>
<h1><div class="status-dot"></div> Curio Live Logs</h1>
<div class="controls">
<button id="btn-curio-app" class="active" onclick="switchService('curio-app')">Node Backend</button>
<button id="btn-curaflow-agent" onclick="switchService('curaflow-agent')">Python AI Agent</button>
<button onclick="clearLogs()">Clear</button>
</div>
</header>
<div id="terminal"></div>
<script>
let eventSource = null;
const terminal = document.getElementById('terminal');
function getTimestamp() {
const now = new Date();
return now.toISOString().split('T')[1].slice(0, 8);
}
function highlightLine(text) {
if (text.includes('[ERROR]') || text.includes('Exception') || text.includes('Failed')) return 'log-error';
if (text.includes('[WARN]')) return 'log-warn';
if (text.includes('success') || text.includes('Connected') || text.includes('ready')) return 'log-success';
if (text.includes('Processing') || text.includes('Triggering')) return 'log-info';
return '';
}
function appendLog(text, isError) {
const line = document.createElement('div');
line.className = 'log-line';
const timeSpan = document.createElement('span');
timeSpan.className = 'log-timestamp';
timeSpan.textContent = `[${getTimestamp()}]`;
const textSpan = document.createElement('span');
textSpan.className = isError ? 'log-error' : highlightLine(text);
textSpan.textContent = text;
line.appendChild(timeSpan);
line.appendChild(textSpan);
terminal.appendChild(line);
// Auto-scroll
terminal.scrollTop = terminal.scrollHeight;
}
function connectSSE(service) {
if (eventSource) {
eventSource.close();
}
appendLog(`--- Connected to ${service === 'curio-app' ? 'Node Backend' : 'Python AI Agent'} Logs ---`, false);
eventSource = new EventSource(`/api/logs?service=${service}`);
eventSource.onmessage = (event) => {
const data = JSON.parse(event.data);
appendLog(data.text, data.isError);
};
eventSource.onerror = (error) => {
appendLog('[SSE Error] Connection lost. Reconnecting...', true);
eventSource.close();
setTimeout(() => connectSSE(service), 3000);
};
}
function switchService(service) {
document.getElementById('btn-curio-app').classList.remove('active');
document.getElementById('btn-curaflow-agent').classList.remove('active');
document.getElementById(`btn-${service}`).classList.add('active');
terminal.innerHTML = '';
connectSSE(service);
}
function clearLogs() {
terminal.innerHTML = '';
}
// Init
connectSSE('curio-app');
</script>
</body>
</html>

View File

@ -0,0 +1,55 @@
const express = require('express');
const { spawn } = require('child_process');
const path = require('path');
const app = express();
const PORT = 9999;
app.use(express.static(path.join(__dirname, 'public')));
app.get('/api/logs', (req, res) => {
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');
res.flushHeaders();
const service = req.query.service || 'curio-app';
let label = 'app=curio-app';
if (service === 'curaflow-agent') {
label = 'app.kubernetes.io/name=curaflow-agent';
}
// Spawn kubectl logs
const kubectl = spawn('kubectl', ['logs', '-f', '-n', 'curio', '-l', label, '--tail=100']);
kubectl.stdout.on('data', (data) => {
const lines = data.toString().split('\n');
lines.forEach(line => {
if (line.trim()) {
res.write(`data: ${JSON.stringify({ text: line, isError: false })}\n\n`);
}
});
});
kubectl.stderr.on('data', (data) => {
const lines = data.toString().split('\n');
lines.forEach(line => {
if (line.trim()) {
res.write(`data: ${JSON.stringify({ text: line, isError: true })}\n\n`);
}
});
});
kubectl.on('close', (code) => {
res.write(`data: ${JSON.stringify({ text: `[Process exited with code ${code}]`, isError: true })}\n\n`);
res.end();
});
req.on('close', () => {
kubectl.kill();
});
});
app.listen(PORT, () => {
console.log(`Curio Log Viewer running at http://localhost:${PORT}`);
});