const express = require('express'); const fs = require('fs'); const multer = require('multer'); const fsExtra = require('fs-extra'); const path = require('path'); const Joi = require('joi'); const { httpStatus } = require('../helpers/http_status'); const {lockFile, unlockFile} = require("../helpers/lock_mechanism"); const app = express(); app.use(express.json()); app.use('/share_file', express.raw({ type: 'application/octet-stream', limit: 'Infinity' })); const server = require('http').createServer(app); const connections = []; // Store active connections to close them on shutdown server.on('connection', (conn) => { connections.push(conn); conn.on('close', () => { connections.splice(connections.indexOf(conn), 1); }); }); const filePathSchema = Joi.object({ filePath: Joi.string().required() }); async function getBaseDirectory() { const filePath = path.join(__dirname, '..', 'dirBackup.json'); try { const data = await fs.promises.readFile(filePath, 'utf8'); const jsonData = JSON.parse(data); return jsonData.path; } catch (error) { console.error('Error reading the base directory:', error); throw error; } } app.post('/file_path', async (req, res) => { const { error, value } = filePathSchema.validate(req.body); if (error) { return res.status(httpStatus.BAD_REQUEST).json({ message: 'Invalid input', data: error.details }); } try { const basePath = await getBaseDirectory(); let { filePath } = value; const baseDirName = path.basename(basePath); if (filePath.startsWith(baseDirName + path.sep)) { filePath = filePath.slice(baseDirName.length + 1); } const fullPath = path.resolve(basePath, filePath); fs.stat(fullPath, (err, stats) => { if (err) { if (err.code === 'ENOENT') { return res.status(httpStatus.NOT_FOUND).json({ message: 'File not found' }); } return res.status(httpStatus.INTERNAL_SERVER_ERROR).json({ message: 'Error accessing the file' }); } if (!stats.isFile()) { return res.status(httpStatus.BAD_REQUEST).json({ message: 'Path is not a file' }); } // Stream the file as binary data res.writeHead(httpStatus.OK, { 'Content-Type': 'application/octet-stream', 'Content-Disposition': `attachment; filename="${path.basename(fullPath)}"` }); const fileStream = fs.createReadStream(fullPath); fileStream.pipe(res); }); } catch (error) { return res.status(httpStatus.INTERNAL_SERVER_ERROR).json({ message: 'Error processing request' }); } }); async function addFilePathToJson(filePath) { const jsonFilePath = path.join(__dirname, '..', 'filesReceived.json'); try { let data = { receivedFiles: [] }; if (await fsExtra.pathExists(jsonFilePath)) { await lockFile(jsonFilePath); data = await fsExtra.readJson(jsonFilePath); } else { await lockFile(jsonFilePath); } data.receivedFiles.push(filePath); await fsExtra.writeJson(jsonFilePath, data, { spaces: 2 }); await unlockFile(jsonFilePath); console.log('File path added successfully.'); } catch (error) { console.error('Error updating JSON file:', error); throw error; // Rethrow the error for further handling if necessary } } const shareFileSchema = Joi.object({ idUser: Joi.string().required(), nameOfFile: Joi.string().required(), sizeOfFile: Joi.number().required() }); const storage = multer.diskStorage({ destination: function (req, file, cb) { const { error, value } = shareFileSchema.validate(req.body); if (error) { cb(error, undefined); return; } const baseDir = path.join(__dirname, '..', 'uploads', value.idUser); // Temp storage location fsExtra.ensureDirSync(baseDir); cb(null, baseDir); }, filename: function (req, file, cb) { cb(null, req.body.nameOfFile); // Using directly assuming it has been validated already } }); const upload = multer({ storage: storage }).single('file'); app.post('/upload', (req, res) => { upload(req, res, async (err) => { if (err instanceof multer.MulterError) { res.status(httpStatus.INTERNAL_SERVER_ERROR).send(`Multer error: ${err.message}`); return; } else if (err) { res.status(httpStatus.BAD_REQUEST).send(`Validation error: ${err.message}`); return; } try { const { idUser, nameOfFile } = req.body; const dirConfig = await fsExtra.readJson(path.join(__dirname, '..', 'dirShare.json')); const baseDir = dirConfig.path; const finalPath = path.join(baseDir, idUser, nameOfFile); await fsExtra.move(req.file.path, finalPath, { overwrite: true }); await addFilePathToJson(finalPath); res.status(httpStatus.OK).json({ message: 'Upload successful.' }); } catch (error) { console.error('Failed to move file:', error); res.status(httpStatus.INTERNAL_SERVER_ERROR).json({ message: 'Server error while moving the file.' }); } }); }); console.log('external_endpoints.json started.'); const PORT = process.env.PORT || 3000; server.listen(PORT, () => console.log(`Server running on port ${PORT}`)); process.on('SIGINT', () => { console.log('SIGINT signal received. Shutting down gracefully.'); server.close(() => { console.log('Server closed.'); // Ensure all connections are closed connections.forEach(conn => conn.end()); setTimeout(() => connections.forEach(conn => conn.destroy()), 5000); process.exit(0); }); });