diff --git a/routes/api.js b/routes/api.js index 318dfc9..dbb7686 100644 --- a/routes/api.js +++ b/routes/api.js @@ -12,6 +12,7 @@ const dbTester = require('../test/tester'); const cors = require('cors'); // Import the cors package const projectManager = require('../src/project/projectManager.js'); const measureManager = require('../src/measure/measureManager.js'); +const fileWatcher = require('../src/data/filewatcher.js'); router.use(cors({ origin: ['http://127.0.0.1:5500', 'http://localhost:5500', 'http://localhost:3000'], @@ -260,6 +261,20 @@ router.get('/measurements/:id', (req, res) => { }); }); +router.get('/measurements/:projectId/:orderId', async (req, res) => { + const projectId = req.params.projectId; + const orderId = req.params.orderId; + if (!projectId || isNaN(projectId) || !orderId || isNaN(orderId)) { + return res.status(400).json({ error: 'Invalid project ID or order ID' }); + } + try { + const measurement = await measureManager.getMeasurement(projectId, orderId); + res.json(measurement); + } catch (error) { + serverError.sendError('Error getting measurement:', res, error); + } +}); + /** * @swagger * /measurements: @@ -272,12 +287,12 @@ router.get('/measurements/:id', (req, res) => { * description: Internal server error */ router.post('/measurements', (req, res) => { - const { project_id, timestamp, image_path, temperature, humidity, completed } = req.body; - if (!project_id || !timestamp || !image_path || !temperature || !humidity || !completed) { + const { project_id, timestamp, image_path, temperature, humidity} = req.body; + if (!project_id || !timestamp || !image_path || !temperature || !humidity) { return res.status(400).json({ error: 'All fields are required' }); } - const query = 'INSERT INTO public.measurements (project_id, timestamp, image_path, temperature, humidity, completed) VALUES ($1, $2, $3, $4, $5, $6) RETURNING id'; - db.query(query, [project_id, timestamp, image_path, temperature, humidity, completed], (err, results) => { + const query = 'INSERT INTO public.measurements (project_id, timestamp, image_path, temperature, humidity) VALUES ($1, $2, $3, $4, $5, $6) RETURNING id'; + db.query(query, [project_id, timestamp, image_path, temperature, humidity], (err, results) => { if (err) { serverError.sendError('Erreur lors de l\'ajout de la mesure:', res, err); } @@ -305,21 +320,31 @@ router.post('/measurements', (req, res) => { * 500: * description: Internal server error */ -router.delete('/measurements/:id', (req, res) => { +router.delete('/measurements/:id', async (req, res) => { const measurementId = req.params.id; if (!measurementId || isNaN(measurementId)) { return res.status(400).json({ error: 'Invalid measurement ID' }); } - const query = 'DELETE FROM public.measurements WHERE id = $1 RETURNING id'; - db.query(query, [measurementId], (err, results) => { - if (err) { - serverError.sendError('Erreur lors de la suppression de la mesure:', res, err); - } - if (results.rowCount === 0) { - return res.status(404).json({ error: 'Aucune mesure trouvée avec cet ID.' }); - } - res.status(200).json({ message: 'Mesure supprimée avec succès', id: measurementId }); - }); + try { + const measurement = await measureManager.deleteMeasurement(measurementId); + res.status(200).json({ message: 'Measurement deleted successfully', id: measurementId }); + } catch (error) { + serverError.sendError('Error deleting measurement:', res, error); + } +}); + +router.delete('/measurements/:projectId/:orderId', async (req, res) => { + const projectId = req.params.projectId; + const orderId = req.params.orderId; + if (!projectId || isNaN(projectId) || !orderId || isNaN(orderId)) { + return res.status(400).json({ error: 'Invalid project ID or order ID' }); + } + try { + const measurement = await measureManager.deleteMeasurementByOrderId(projectId, orderId); + res.status(200).json({ message: 'Measurement deleted successfully', id: measurement.id }); + } catch (error) { + serverError.sendError('Error deleting measurement:', res, error); + } }); /** @@ -561,7 +586,6 @@ const upload = multer({ storage: multer.memoryStorage() }); * 500: * description: Internal server error */ - router.post('/upload', upload.single('image'), async (req, res) => { const { projectId, orderId } = req.body; const image = req.file; // Multer adds the file to req.file @@ -578,4 +602,54 @@ router.post('/upload', upload.single('image'), async (req, res) => { } }); +/** + * @swagger + * /uploadmeasurement: + * post: + * description: Use to upload a measurement with an image + * requestBody: + * required: true + * content: + * multipart/form-data: + * schema: + * type: object + * properties: + * image: + * type: string + * format: binary + * projectId: + * type: integer + * timestamp: + * type: string + * format: date-time + * temperature: + * type: number + * humidity: + * type: number + * responses: + * 200: + * description: Measurement uploaded successfully + * 400: + * description: All fields are required + * 500: + * description: Internal server error + */ +router.post('/uploadmeasurement', upload.single('image'), async (req, res) => { + const { projectId, timestamp, temperature, humidity } = req.body; + const image = req.file; // Multer adds the file to req.file + + if (!image || !projectId || !timestamp || !temperature || !humidity) { + return res.status(400).json({ error: 'All fields are required' }); + } + + try { + const nextOrderId = await measureManager.getNextOrderId(projectId); + const imagePath = await measureManager.uploadMeasureImage(image, projectId, nextOrderId); + const measurement = await measureManager.addMeasureToProject(projectId, timestamp, imagePath, temperature, humidity, nextOrderId); + res.json({ message: 'Measurement uploaded successfully', path: imagePath, id: measurement.id }); + } catch (error) { + serverError.sendError('Error uploading measurement:', res, error); + } +}); + module.exports = router; \ No newline at end of file diff --git a/src/data/filewatcher.js b/src/data/filewatcher.js new file mode 100644 index 0000000..7aa3fc1 --- /dev/null +++ b/src/data/filewatcher.js @@ -0,0 +1,42 @@ +import db from '../../db.js'; +import path from 'path'; +import storageManager from '../data/storageManager.js'; +import fs from 'fs'; + +async function checkAndRemoveInvalidEntries() { + console.log('Checking for invalid entries...'); + try { + const measurementsRes = await db.query('SELECT id, path FROM measurements'); + console.log('Fetched measurements:', measurementsRes.rows); + for (const row of measurementsRes.rows) { + console.log('Checking file path:', row.path); + if (!fs.existsSync(row.path)) { + // Remove invalid entry + await db.query('DELETE FROM measurements WHERE id = $1', [row.id]); + console.log(`Deleted invalid measurement entry with id: ${row.id}`); + } + } + + // Scan all images in storage + const allImages = await storageManager.scanAllImages(); + console.log('Scanned all images:', allImages); + for (const imagePath of allImages) { + const entryRes = await db.query('SELECT id FROM measurements WHERE path = $1', [imagePath]); + if (entryRes.rows.length === 0) { + // Remove the file if the entry does not exist + fs.unlinkSync(imagePath); + console.log(`Deleted file at path: ${imagePath} as its database entry does not exist`); + } + } + } catch (err) { + console.error('Error checking and removing invalid entries:', err); + } +} + + + +// Run the check periodically +setInterval(checkAndRemoveInvalidEntries, 10000); // Every second + +// Initial run +checkAndRemoveInvalidEntries(); \ No newline at end of file diff --git a/src/data/storageManager.js b/src/data/storageManager.js index 7b39755..5fe4f0f 100644 --- a/src/data/storageManager.js +++ b/src/data/storageManager.js @@ -19,6 +19,27 @@ function deleteFolder(name){ } } +function scanAllImages(dir = 'storage') { + const projectDir = path.join(PROJECTS_DIR, dir); + let results = []; + + function scanDirectory(directory) { + const files = fs.readdirSync(directory); + files.forEach(file => { + const filePath = path.join(directory, file); + const stat = fs.statSync(filePath); + if (stat.isDirectory()) { + scanDirectory(filePath); + } else if (file.endsWith('.jpg')) { + results.push(filePath); + } + }); + } + + scanDirectory(projectDir); + return results; +} + function saveFile(filePath, content) { // Ensure content is a buffer if (Buffer.isBuffer(content)) { @@ -28,16 +49,23 @@ function saveFile(filePath, content) { } } - function getFile(name){ const filePath = path.join(PROJECTS_DIR, `${name}`); return fs .readFileSync(filePath); } +function deleteFile(name){ + const filePath = path.join(PROJECTS_DIR, `${name}`); + if (fs.existsSync(filePath)) + fs.rmSync(filePath); +} + module.exports = { createFolder, deleteFolder, + scanAllImages, saveFile, - getFile + getFile, + deleteFile }; \ No newline at end of file diff --git a/src/measure/measureManager.js b/src/measure/measureManager.js index e8a95d2..9634e17 100644 --- a/src/measure/measureManager.js +++ b/src/measure/measureManager.js @@ -11,21 +11,77 @@ async function uploadMeasureImage(image, projectId, orderId) { return imagePath; } - async function getMeasureImage(projectId, orderId) { const projectPath = `${projectId}`; const imagePath = `${projectPath}/${orderId}.jpg`; return storageManager.getFile(imagePath); } -async function addMeasureToProject(projectId, timestamp, imagePath, temperature, humidity, completed) { - const query = 'INSERT INTO public.measurements (project_id, timestamp, image_path, temperature, humidity, completed) VALUES ($1, $2, $3, $4, $5, $6) RETURNING *'; - const values = [projectId, timestamp, imagePath, temperature, humidity, completed]; +async function getNextOrderId(projectId) { + const query = 'SELECT MAX(order_id) FROM public.measurements WHERE project_id = $1'; + const values = [projectId]; + const res = await db.query(query, values); + return res.rows[0].max + 1; +} + +async function addMeasureToProject(projectId, orderId, timestamp, path, temperature, humidity) { + const query = 'INSERT INTO public.measurements (project_id, timestamp, path, temperature, humidity, order_id) VALUES ($1, $2, $3, $4, $5, $6) RETURNING *'; + const values = [projectId, orderId, timestamp, path, temperature, humidity]; + const res = await db.query(query, values); + return res.rows[0]; +} + +async function getMeasurements(projectId) { + const query = 'SELECT * FROM public.measurements WHERE project_id = $1'; + const values = [projectId]; + const res = await db.query(query, values); + return res.rows; +} + +async function getMeasurement(projectId, orderId) { + const query = 'SELECT * FROM public.measurements WHERE project_id = $1 AND order_id = $2'; + const values = [projectId, orderId]; + const res = await db.query(query, values); + return res.rows[0]; +} + +async function getMeasurementById(id) { + const query = 'SELECT * FROM public.measurements WHERE id = $1'; + const values = [id]; + const res = await db.query(query, values); + return res.rows[0]; +} + +async function updateMeasurement(projectId, orderId, timestamp, path, temperature, humidity) { + const query = 'UPDATE public.measurements SET timestamp = $3, path = $4, temperature = $5, humidity = $6 WHERE project_id = $1 AND order_id = $2 RETURNING *'; + const values = [projectId, orderId, timestamp, path, temperature, humidity]; + const res = await db.query(query, values); + return res.rows[0]; +} + +async function updateMeasurementById(id, timestamp, path, temperature, humidity) { + const query = 'UPDATE public.measurements SET timestamp = $2, path = $3, temperature = $4, humidity = $5 WHERE id = $1 RETURNING *'; + const values = [id, timestamp, path, temperature, humidity]; + const res = await db.query(query, values); + return res.rows[0]; +} + +async function deleteMeasurement(id) { + const query = 'DELETE FROM public.measurements WHERE id = $1'; + const values = [id]; const res = await db.query(query, values); return res.rows[0]; } export { uploadMeasureImage, - addMeasureToProject -} \ No newline at end of file + addMeasureToProject, + getNextOrderId, + getMeasurements, + getMeasurement, + updateMeasurement, + deleteMeasurement, + getMeasureImage, + getMeasurementById, + updateMeasurementById +}