const express = require('express'); const router = express.Router(); const db = require('../db'); const path = require('path'); const fs = require('fs'); const ffmpeg = require('../ffmpeg'); const fileUtils = require('../utils/fileUtils'); const serverError = require('../utils/serverError'); const multer = require('multer'); const video = require('../utils/video'); 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'], methods: ['GET', 'POST', 'PUT', 'DELETE'], allowedHeaders: ['Content-Type'], credentials: true, })); /** * @swagger * /projects: * get: * description: Use to request all projects * responses: * 200: * description: A successful response * 500: * description: Internal server error */ router.get('/projects', async (req, res) => { try { const projects = await projectManager.getAllProjects(); res.json(projects); } catch (error) { serverError.sendError('Error getting all projects:', res, error); } }); /** * @swagger * /projects/{id}: * get: * description: Use to request a specific project by id * parameters: * - in: path * name: id * required: true * description: Numeric ID of the project to retrieve. * schema: * type: integer * responses: * 200: * description: A successful response * 500: * description: Internal server error */ router.get('/projects/:id', async (req, res) => { const projectId = req.params.id; if (!projectId || isNaN(projectId)) { return res.status(400).json({ error: 'Invalid project ID' }); } try { const project = await projectManager.getProjectById(projectId); res.json(project); } catch (error) { serverError.sendError('Error getting project by ID:', res, error); } }); /** * @swagger * /projects/{id}/videos: * get: * description: Use to request all videos for a specific project by project id * parameters: * - in: path * name: id * required: true * description: Numeric ID of the project to retrieve videos for. * schema: * type: integer * responses: * 200: * description: A successful response * 500: * description: Internal server error */ router.get('/projects/:id/videos', async (req, res) => { const projectId = req.params.id; if (!projectId || isNaN(projectId)) { return res.status(400).json({ error: 'Invalid project ID' }); } try { const videos = await projectManager.getVideosByProjectId(projectId); res.json(videos); } catch (error) { serverError.sendError('Error getting videos by project ID:', res, error); } }); /** * @swagger * /projects/{id}/measurements: * get: * description: Use to request all measurements for a specific project by project id * parameters: * - in: path * name: id * required: true * description: Numeric ID of the project to retrieve measurements for. * schema: * type: integer * responses: * 200: * description: A successful response * 500: * description: Internal server error */ router.get('/projects/:id/measurements', async (req, res) => { const projectId = req.params.id; if (!projectId || isNaN(projectId)) { return res.status(400).json({ error: 'Invalid project ID' }); } try { const measurements = await projectManager.getMeasurementsByProjectId(projectId); res.json(measurements); } catch (error) { serverError.sendError('Error getting measurements by project ID:', res, error); } }); /** * @swagger * /projects: * post: * description: Use to add a new project * requestBody: * required: true * content: * application/json: * schema: * type: object * properties: * name: * type: string * description: * type: string * responses: * 201: * description: Project added successfully * 500: * description: Internal server error */ router.post('/projects', async (req, res) => { const { name, description } = req.body; if (!name || !description) { return res.status(400).json({ error: 'Name and description are required' }); } try { project = await projectManager.createProject(name, description, new Date(), 0); projectManager.createProjectDirectory(project.id); res.status(201).json({ message: 'Project added successfully', id: project.id }); } catch (error) { serverError.sendError('Error creating project:', res, error); } }); /** * @swagger * /projects/{id}: * delete: * description: Use to delete a project by ID * parameters: * - in: path * name: id * required: true * description: Numeric ID of the project to delete. * schema: * type: integer * responses: * 200: * description: Project deleted successfully * 404: * description: No project found with this ID * 500: * description: Internal server error */ router.delete('/projects/:id', async (req, res) => { const projectId = req.params.id; if (!projectId || isNaN(projectId)) { return res.status(400).json({ error: 'Invalid project ID' }); } try { projectManager.deleteProjectDirectory(projectId); projectManager.deleteProjectById(projectId); res.status(200).json({ message: 'Project deleted successfully', id: projectId }); } catch (error) { serverError.sendError('Error deleting project:', res, error); } }); /** * @swagger * /measurements: * get: * description: Use to request all measurements * responses: * 200: * description: A successful response * 500: * description: Internal server error */ router.get('/measurements', (req, res) => { const query = 'SELECT * FROM public.measurements'; db.query(query, (err, results) => { if (err) { serverError.sendError('Erreur lors de la récupération des mesures:', res, err); } res.json(results.rows); }); }); /** * @swagger * /measurements/{id}: * get: * description: Use to request a specific measurement by id * parameters: * - in: path * name: id * required: true * description: Numeric ID of the measurement to retrieve. * schema: * type: integer * responses: * 200: * description: A successful response * 500: * description: Internal server error */ router.get('/measurements/:id', (req, res) => { const measurementId = req.params.id; if (!measurementId || isNaN(measurementId)) { return res.status(400).json({ error: 'Invalid measurement ID' }); } const query = 'SELECT * FROM public.measurements WHERE id = $1'; db.query(query, [measurementId], (err, results) => { if (err) { serverError.sendError('Erreur lors de la récupération de la mesure:', res, err); } res.json(results.rows); }); }); 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: * post: * description: Use to add a new measurement * responses: * 201: * description: Measurement added successfully * 500: * description: Internal server error */ router.post('/measurements', (req, res) => { 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) 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); } res.status(201).json({ message: 'Mesure ajoutée avec succès', id: results.rows[0].id }); }); }); /** * @swagger * /measurements/{id}: * delete: * description: Use to delete a measurement by ID * parameters: * - in: path * name: id * required: true * description: Numeric ID of the measurement to delete. * schema: * type: integer * responses: * 200: * description: Measurement deleted successfully * 404: * description: No measurement found with this ID * 500: * description: Internal server error */ 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' }); } 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); } }); /** * @swagger * /videos: * get: * description: Use to request all videos * responses: * 200: * description: A successful response * 500: * description: Internal server error */ router.get('/videos', (req, res) => { const query = 'SELECT * FROM public.videos'; db.query(query, (err, results) => { if (err) { serverError.sendError('Erreur lors de la récupération des vidéos:', res, err); } res.json(results.rows); }); }); /** * @swagger * /videos/{id}: * get: * description: Use to request a specific video by id * parameters: * - in: path * name: id * required: true * description: Numeric ID of the video to retrieve. * schema: * type: integer * responses: * 200: * description: A successful response * 500: * description: Internal server error */ router.get('/videos/:id', (req, res) => { const videoId = req.params.id; if (!videoId || isNaN(videoId)) { return res.status(400).json({ error: 'Invalid video ID' }); } const query = 'SELECT * FROM public.videos WHERE id = $1'; db.query(query, [videoId], (err, results) => { if (err) { serverError.sendError('Erreur lors de la récupération de la vidéo:', res, err); } res.json(results.rows); }); }); /** * @swagger * /videos: * post: * description: Use to create a new video * requestBody: * required: true * content: * application/json: * schema: * type: object * properties: * project_id: * type: integer * measurement_ids: * type: string * video_path: * type: string * duration: * type: number * resolution: * type: string * name: * type: string * responses: * 201: * description: Video created successfully * 500: * description: Internal server error */ router.post('/videos', (req, res) => { const { project_id, measurement_ids, video_path, duration, resolution, name } = req.body; if (!project_id || !measurement_ids || !video_path || !duration || !resolution || !name) { return res.status(400).json({ error: 'All fields are required' }); } const list_ids = measurement_ids.split(','); const image_count = list_ids.length; const videoPath = '/videos/' + name + '.mp4'; const query_first = 'SELECT timestamp FROM public.measurements WHERE id = $1'; const query_last = 'SELECT timestamp FROM public.measurements WHERE id = $1'; db.query(query_first, [list_ids[0]], (err, results) => { if (err) { serverError.sendError('Erreur lors de la récupération du timestamp de la première image:', res, err); } const start_timestamp = results.rows[0].timestamp; db.query(query_last, [list_ids[image_count - 1]], (err, results) => { if (err) { serverError.sendError('Erreur lors de la récupération du timestamp de la dernière image:', res, err); } const end_timestamp = results.rows[0].timestamp; const fps = image_count / duration; const query = 'INSERT INTO public.videos (project_id, measurement_ids, video_path, start_timestamp, end_timestamp, image_count, resolution, duration, fps, status, name) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11) RETURNING id'; db.query(query, [project_id, measurement_ids, videoPath, start_timestamp, end_timestamp, image_count, resolution, duration, fps, 0, name], (err, results) => { if (err) { serverError.sendError('Erreur lors de l\'ajout de la vidéo:', res, err); } res.status(201).json({ message: 'Vidéo ajoutée avec succès', id: results.rows[0].id }); }); }); }); }); /** * @swagger * /videos/{id}: * delete: * description: Use to delete a video by ID * parameters: * - in: path * name: id * required: true * description: Numeric ID of the video to delete. * schema: * type: integer * responses: * 200: * description: Video deleted successfully * 404: * description: No video found with this ID * 500: * description: Internal server error */ router.delete('/videos/:id', (req, res) => { const videoId = req.params.id; if (!videoId || isNaN(videoId)) { return res.status(400).json({ error: 'Invalid video ID' }); } const query = 'DELETE FROM public.videos WHERE id = $1 RETURNING id'; db.query(query, [videoId], (err, results) => { if (err) { serverError.sendError('Erreur lors de la suppression de la vidéo:', res, err); } if (results.rowCount === 0) { return res.status(404).json({ error: 'Aucune vidéo trouvée avec cet ID.' }); } res.status(200).json({ message: 'Vidéo supprimée avec succès', id: videoId }); }); }); // Test Image /** * @swagger * /smile: * get: * description: Use to request the smile image * responses: * 200: * description: A successful response * 404: * description: Image not found */ router.get('/smile', (req, res) => { const imagePath = dbTester.getSmileImage(); fs.access(imagePath, fs.constants.F_OK, (err) => { if (err) { console.error('Image not found:', err); return res.status(404).json({ error: 'Image not found' }); } res.sendFile(imagePath); }); }); /** * @swagger * /image/{filename}: * get: * description: Use to request a specific image by filename * parameters: * - in: path * name: filename * required: true * description: Name of the image file to retrieve. * schema: * type: string * example: image.jpg * responses: * 200: * description: A successful response * 404: * description: Image not found */ router.get('/image/:filename', (req, res) => { const imagePath = path.join('/storage/image', req.params.filename); fs.access(imagePath, fs.constants.F_OK, (err) => { if (err) { console.error('Image not found:', err); return res.status(404).json({ error: 'Image not found' }); } res.sendFile(imagePath); }); }); const upload = multer({ storage: multer.memoryStorage() }); /** * @swagger * /upload: * post: * description: Use to upload an image * requestBody: * required: true * content: * multipart/form-data: * schema: * type: object * properties: * image: * type: string * format: binary * projectId: * type: integer * orderId: * type: integer * responses: * 200: * description: Image uploaded successfully * 400: * description: All fields are required * 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 if (!image || !projectId || !orderId) { return res.status(400).json({ error: 'All fields are required' }); } try { const imagePath = await measureManager.uploadMeasureImage(image, projectId, orderId); res.json({ message: 'Image uploaded successfully', path: imagePath }); } catch (error) { serverError.sendError('Error uploading image:', res, error); } }); /** * @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;