655 lines
20 KiB
JavaScript
655 lines
20 KiB
JavaScript
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; |