Software: Apache. PHP/8.1.30 uname -a: Linux server1.tuhinhossain.com 5.15.0-163-generic #173-Ubuntu SMP Tue Oct 14 17:51:00 UTC uid=1002(picotech) gid=1003(picotech) groups=1003(picotech),0(root) Safe-mode: OFF (not secure) /home/picotech/domains/note.picotech.app/public_html/src/controllers/ drwxr-xr-x | |
| Viewing file: Select action/file-type: const path = require('path');
const fs = require('fs').promises;
const { Recording, Meeting, Transcript } = require('../models');
const { AppError, catchAsync } = require('../middleware/errorHandler');
const { processRecording } = require('../services/transcriptionService');
const { addToChunkingQueue } = require('../services/jobQueue');
const { encryptFile, decryptFile, isFileEncrypted } = require('../services/encryptionService');
exports.getMeetingRecordings = catchAsync(async (req, res, next) => {
try {
const { meetingId } = req.params;
const recordings = await Recording.findAll({
where: { meeting_id: meetingId },
order: [['created_at', 'DESC']]
});
res.json({
success: true,
data: recordings
});
} catch (error) {
next(error);
}
});
exports.uploadRecording = catchAsync(async (req, res, next) => {
try {
const { meetingId } = req.params;
if (!req.file) {
throw new AppError('No audio file uploaded', 400);
}
// Check if meeting already has a summary
const { Summary } = require('../models');
const existingSummary = await Summary.findOne({
where: { meeting_id: meetingId }
});
if (existingSummary) {
// Clean up uploaded file
await fs.unlink(req.file.path).catch(console.error);
throw new AppError('Cannot upload recording: Meeting already has a summary generated', 400);
}
// Encrypt the uploaded file before storing
const encryptedFilePath = req.file.path + '.encrypted';
await encryptFile(req.file.path, encryptedFilePath);
// Clean up the original unencrypted file
await fs.unlink(req.file.path);
const recording = await Recording.create({
meeting_id: meetingId,
file_path: encryptedFilePath,
file_name: req.file.filename,
original_name: req.file.originalname,
mime_type: req.file.mimetype,
duration: req.body.duration || 0,
file_size: req.file.size,
timestamp: new Date(),
status: 'processing'
});
// Start async processing of the recording
// Set recording status to completed and trigger chunking/transcription
recording.status = 'completed';
await recording.save();
// Add to job queue for chunking and transcription
addToChunkingQueue(recording.id).catch(console.error);
res.status(201).json({
success: true,
data: recording
});
} catch (error) {
console.error('Upload error:', error);
// Clean up uploaded file if database operation fails
if (req.file) {
await fs.unlink(req.file.path).catch(console.error);
}
next(error);
}
});
exports.getRecording = catchAsync(async (req, res, next) => {
try {
const recording = await Recording.findByPk(req.params.id);
if (!recording) {
throw new AppError('Recording not found', 404);
}
res.json({
success: true,
data: recording
});
} catch (error) {
next(error);
}
});
exports.downloadRecording = catchAsync(async (req, res, next) => {
try {
const recording = await Recording.findByPk(req.params.id);
if (!recording) {
throw new AppError('Recording not found', 404);
}
// Path traversal protection: ensure the resolved path is within the recordings directory
const recordingsDir = path.resolve(__dirname, '../../storage/recordings');
const requestedPath = path.resolve(recording.file_path);
// Check if the resolved path is within the allowed directory
if (!requestedPath.startsWith(recordingsDir)) {
throw new AppError('Access denied: Invalid file path', 403);
}
// Additional security: verify file exists and is readable
const fs = require('fs');
if (!fs.existsSync(requestedPath)) {
throw new AppError('File not found', 404);
}
// Sanitize filename to prevent header injection
const safeFilename = recording.file_name.replace(/[\r\n]/g, '');
// Check if file is encrypted and decrypt if necessary
const isEncrypted = await isFileEncrypted(requestedPath);
if (isEncrypted) {
// Create temporary decrypted file for download
const tempDir = path.resolve(__dirname, '../../storage/temp');
if (!fs.existsSync(tempDir)) {
fs.mkdirSync(tempDir, { recursive: true });
}
const tempFilePath = path.join(tempDir, `temp_${Date.now()}_${safeFilename}`);
await decryptFile(requestedPath, tempFilePath);
// Set up cleanup after download
res.on('finish', async () => {
try {
await fs.unlink(tempFilePath);
} catch (error) {
console.error('Failed to clean up temporary file:', error);
}
});
res.download(tempFilePath, safeFilename);
} else {
res.download(requestedPath, safeFilename);
}
} catch (error) {
next(error);
}
});
exports.deleteRecording = catchAsync(async (req, res, next) => {
try {
const recording = await Recording.findByPk(req.params.id);
if (!recording) {
throw new AppError('Recording not found', 404);
}
// Delete associated transcript if it exists
const transcript = await Transcript.findOne({
where: { recording_id: req.params.id }
});
if (transcript) {
await transcript.destroy();
}
// Delete encrypted file from filesystem
try {
await fs.unlink(recording.file_path);
} catch (error) {
console.error('Failed to delete encrypted recording file:', error);
// Continue with database deletion even if file deletion fails
}
// Delete from database
await recording.destroy();
res.json({
success: true,
message: 'Recording and associated transcript deleted successfully'
});
} catch (error) {
next(error);
}
}); |
:: Command execute :: | |
--[ c99shell v. 2.5 [PHP 8 Update] [24.05.2025] | Generation time: 0.0038 ]-- |