feat(database): Implement DatabaseManager for managing database structure and initialization
All checks were successful
SSH Backend Deploy / ssh-deploy (push) Successful in 1m51s
All checks were successful
SSH Backend Deploy / ssh-deploy (push) Successful in 1m51s
feat(routes): Add camera, image, measurement, project, and video routes with Swagger documentation feat(services): Create storageService and videoService for file management and video processing fix(errorHandler): Enhance error handling with standardized responses and database operation wrappers
This commit is contained in:
44
src/database/connection.js
Normal file
44
src/database/connection.js
Normal file
@@ -0,0 +1,44 @@
|
||||
// src/database/connection.js
|
||||
const { Client } = require('pg');
|
||||
const config = require('../config');
|
||||
|
||||
// Création du client PostgreSQL avec la configuration centralisée
|
||||
const client = new Client({
|
||||
host: config.database.host,
|
||||
port: config.database.port,
|
||||
user: config.database.user,
|
||||
password: config.database.password,
|
||||
database: config.database.database
|
||||
});
|
||||
|
||||
let isConnecting = false;
|
||||
|
||||
/**
|
||||
* Initialise la connexion à la base de données
|
||||
* Réessaie automatiquement si la connexion échoue
|
||||
*/
|
||||
function initDatabase() {
|
||||
if (isConnecting) {
|
||||
console.log('[DB] Tentative de connexion déjà en cours, ignorer...');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('[DB] Initialisation de la connexion à PostgreSQL...');
|
||||
isConnecting = true;
|
||||
|
||||
client.connect(err => {
|
||||
isConnecting = false;
|
||||
|
||||
if (err) {
|
||||
console.error('[DB] Erreur de connexion à la base de données:', err);
|
||||
setTimeout(initDatabase, config.database.reconnectInterval);
|
||||
} else {
|
||||
console.log('[DB] Connecté à la base de données PostgreSQL.');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Initialise la connexion lors de l'importation de ce module
|
||||
initDatabase();
|
||||
|
||||
module.exports = client;
|
||||
@@ -1,302 +1,47 @@
|
||||
const db = require('../../db.js');
|
||||
// src/database/database_manager.js
|
||||
/**
|
||||
* Ce fichier est conservé pour la rétrocompatibilité mais redirige vers les nouveaux modèles.
|
||||
* Il sera progressivement supprimé lorsque toutes les références auront été mises à jour.
|
||||
*/
|
||||
|
||||
// Fonctions de gestion de la base de données interne
|
||||
const Project = require('../models/Project');
|
||||
const Measurement = require('../models/Measurement');
|
||||
const Video = require('../models/Video');
|
||||
const Camera = require('../models/Camera');
|
||||
|
||||
async function create_database() {
|
||||
const queries = [
|
||||
`CREATE TABLE IF NOT EXISTS projects (
|
||||
id SERIAL PRIMARY KEY,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
description TEXT,
|
||||
start_date DATE,
|
||||
status INTEGER NOT NULL CHECK (status = ANY (ARRAY [0, 1, 2, 3]))
|
||||
);`,
|
||||
`ALTER TABLE projects OWNER TO timelapse;`,
|
||||
`CREATE TABLE IF NOT EXISTS measurements (
|
||||
id SERIAL PRIMARY KEY,
|
||||
project_id INTEGER REFERENCES projects ON DELETE CASCADE,
|
||||
timestamp TIMESTAMP NOT NULL,
|
||||
path VARCHAR(255),
|
||||
temperature DOUBLE PRECISION,
|
||||
humidity DOUBLE PRECISION,
|
||||
order_id INTEGER NOT NULL,
|
||||
CONSTRAINT unique_project_photo_order UNIQUE (project_id, order_id)
|
||||
);`,
|
||||
`ALTER TABLE measurements OWNER TO timelapse;`,
|
||||
`CREATE TABLE IF NOT EXISTS videos (
|
||||
id SERIAL PRIMARY KEY,
|
||||
project_id INTEGER REFERENCES projects ON DELETE CASCADE,
|
||||
measurement_ids TEXT NOT NULL,
|
||||
video_file VARCHAR(255),
|
||||
resolution VARCHAR(255),
|
||||
duration INTEGER,
|
||||
status INTEGER NOT NULL CHECK (status = ANY (ARRAY [0, 1, 2, 3])),
|
||||
name VARCHAR(255),
|
||||
progress DOUBLE PRECISION,
|
||||
started_at TIMESTAMP,
|
||||
updated_at TIMESTAMP,
|
||||
eta DOUBLE PRECISION
|
||||
);`,
|
||||
`ALTER TABLE videos OWNER TO timelapse;`,
|
||||
`CREATE TABLE IF NOT EXISTS camera (
|
||||
id SERIAL PRIMARY KEY,
|
||||
interval INTEGER NOT NULL,
|
||||
maintenance INTEGER NOT NULL,
|
||||
active INTEGER DEFAULT 0 NOT NULL
|
||||
);`,
|
||||
`ALTER TABLE camera OWNER TO timelapse;`
|
||||
];
|
||||
|
||||
try {
|
||||
for (const query of queries) {
|
||||
await db.query(query);
|
||||
}
|
||||
console.log('Database tables created or verified successfully.');
|
||||
} catch (err) {
|
||||
console.error('Error creating database tables:', err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
async function check_database_existence() {
|
||||
const query = `
|
||||
SELECT table_name
|
||||
FROM information_schema.tables
|
||||
WHERE table_schema = 'public'
|
||||
AND table_name IN ('projects', 'measurements', 'videos', 'camera');
|
||||
`;
|
||||
|
||||
try {
|
||||
const result = await db.query(query);
|
||||
const existingTables = result.rows.map(row => row.table_name);
|
||||
|
||||
const requiredTables = ['projects', 'measurements', 'videos', 'camera'];
|
||||
const missingTables = requiredTables.filter(table => !existingTables.includes(table));
|
||||
|
||||
if (missingTables.length > 0) {
|
||||
console.error('Missing or improperly constructed tables:', missingTables);
|
||||
throw new Error(`The following tables are missing or not properly constructed: ${missingTables.join(', ')}`);
|
||||
} else {
|
||||
console.log('All required tables exist and are properly constructed.');
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Error checking database tables:', err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
async function delete_database() {
|
||||
const queries = [
|
||||
`DROP TABLE IF EXISTS videos;`,
|
||||
`DROP TABLE IF EXISTS measurements;`,
|
||||
`DROP TABLE IF EXISTS projects;`,
|
||||
`DROP TABLE IF EXISTS camera;`
|
||||
];
|
||||
|
||||
try {
|
||||
for (const query of queries) {
|
||||
await db.query(query);
|
||||
}
|
||||
console.log('Database tables deleted successfully.');
|
||||
} catch (err) {
|
||||
console.error('Error deleting database tables:', err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
async function init_function() {
|
||||
try {
|
||||
await check_database_existence();
|
||||
} catch (err) {
|
||||
console.error('Database check failed:', err);
|
||||
try {
|
||||
await delete_database();
|
||||
await create_database();
|
||||
console.log('Database initialized successfully.');
|
||||
} catch (err) {
|
||||
console.error('Error initializing database:', err);
|
||||
throw err;
|
||||
}
|
||||
} finally {
|
||||
console.log('Database initialization process completed.');
|
||||
}
|
||||
}
|
||||
|
||||
init_function()
|
||||
.then(() => console.log('Database initialization completed.'))
|
||||
.catch(err => console.error('Error during database initialization:', err));
|
||||
|
||||
// Fonctions pour les projets
|
||||
function handleDatabaseOperation(operation) {
|
||||
return async (...args) => {
|
||||
try {
|
||||
return await operation(...args);
|
||||
} catch (err) {
|
||||
console.error(`Error during database operation: ${operation.name}`, err);
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const project = {
|
||||
get_all_projects: handleDatabaseOperation(async () => {
|
||||
const query = `SELECT * FROM projects;`;
|
||||
return (await db.query(query)).rows;
|
||||
}),
|
||||
get_project_by_id: handleDatabaseOperation(async (id) => {
|
||||
const query = `SELECT * FROM projects WHERE id = $1;`;
|
||||
return (await db.query(query, [id])).rows[0];
|
||||
}),
|
||||
create_project: handleDatabaseOperation(async (name, description, start_date, status) => {
|
||||
const query = `INSERT INTO projects (name, description, start_date, status) VALUES ($1, $2, $3, $4) RETURNING *;`;
|
||||
return (await db.query(query, [name, description, start_date, status])).rows[0];
|
||||
}),
|
||||
edit_project_by_id: handleDatabaseOperation(async (id, updates) => {
|
||||
const fields = Object.keys(updates).map((key, index) => `${key} = $${index + 2}`).join(', ');
|
||||
const values = [id, ...Object.values(updates)];
|
||||
const query = `UPDATE projects SET ${fields} WHERE id = $1 RETURNING *;`;
|
||||
return (await db.query(query, values)).rows[0];
|
||||
}),
|
||||
delete_project_by_id: handleDatabaseOperation(async (id) => {
|
||||
const query = `DELETE FROM projects WHERE id = $1;`;
|
||||
await db.query(query, [id]);
|
||||
}),
|
||||
find_current_rendering_project: handleDatabaseOperation(async () => {
|
||||
const query = `SELECT * FROM projects WHERE status = 1;`;
|
||||
return (await db.query(query)).rows[0];
|
||||
}),
|
||||
// Structure de redirection pour la compatibilité
|
||||
const database_manager = {
|
||||
project: {
|
||||
get_all_projects: Project.getAllProjects,
|
||||
get_project_by_id: Project.getProjectById,
|
||||
create_project: Project.createProject,
|
||||
edit_project_by_id: Project.updateProject,
|
||||
delete_project: Project.deleteProject,
|
||||
find_current_rendering_project: Project.findCurrentRenderingProject
|
||||
},
|
||||
measurement: {
|
||||
get_all_measurements: Measurement.getAllMeasurements,
|
||||
get_measurement_by_id: Measurement.getMeasurementById,
|
||||
get_measurement_by_project_id_and_order_id: Measurement.getMeasurementByProjectAndOrderId,
|
||||
get_measurements_by_project_id: Measurement.getMeasurementsByProjectId,
|
||||
create_measurement: Measurement.createMeasurement,
|
||||
edit_measurement_by_id: Measurement.updateMeasurement,
|
||||
delete_measurement: Measurement.deleteMeasurement,
|
||||
get_next_order_id: Measurement.getNextOrderId
|
||||
},
|
||||
video: {
|
||||
get_all_videos: Video.getAllVideos,
|
||||
get_video_by_id: Video.getVideoById,
|
||||
get_videos_by_project_id: Video.getVideosByProjectId,
|
||||
create_video: Video.createVideo,
|
||||
edit_video_by_id: Video.updateVideo,
|
||||
delete_video: Video.deleteVideo
|
||||
},
|
||||
capture: {
|
||||
get_camera: Camera.getCamera,
|
||||
edit_camera: Camera.updateCamera,
|
||||
init_camera: Camera.initializeCamera
|
||||
}
|
||||
};
|
||||
|
||||
const measurement = {
|
||||
get_all_measurements: handleDatabaseOperation(async () => {
|
||||
const query = `SELECT * FROM measurements;`;
|
||||
return (await db.query(query)).rows;
|
||||
}),
|
||||
get_measurement_by_id: handleDatabaseOperation(async (id) => {
|
||||
const query = `SELECT * FROM measurements WHERE id = $1;`;
|
||||
return (await db.query(query, [id])).rows[0];
|
||||
}),
|
||||
get_measurement_by_project_and_order_id: handleDatabaseOperation(async (project_id, order_id) => {
|
||||
const query = `SELECT * FROM measurements WHERE project_id = $1 AND order_id = $2;`;
|
||||
return (await db.query(query, [project_id, order_id])).rows[0];
|
||||
}),
|
||||
get_measurements_by_project_id: handleDatabaseOperation(async (project_id) => {
|
||||
const query = `SELECT * FROM measurements WHERE project_id = $1;`;
|
||||
return (await db.query(query, [project_id])).rows;
|
||||
}),
|
||||
create_measurement: handleDatabaseOperation(async (project_id, timestamp, path, temperature, humidity, order_id) => {
|
||||
const query = `INSERT INTO measurements (project_id, timestamp, path, temperature, humidity, order_id) VALUES ($1, $2, $3, $4, $5, $6) RETURNING *;`;
|
||||
return (await db.query(query, [project_id, timestamp, path, temperature, humidity, order_id])).rows[0];
|
||||
}),
|
||||
edit_measurement_by_id: handleDatabaseOperation(async (id, updates) => {
|
||||
const fields = Object.keys(updates).map((key, index) => `${key} = $${index + 2}`).join(', ');
|
||||
const values = [id, ...Object.values(updates)];
|
||||
const query = `UPDATE measurements SET ${fields} WHERE id = $1 RETURNING *;`;
|
||||
return (await db.query(query, values)).rows[0];
|
||||
}),
|
||||
|
||||
edit_measurement_by_project_and_order_id: handleDatabaseOperation(async (project_id, order_id, updates) => {
|
||||
const fields = Object.keys(updates).map((key, index) => `${key} = $${index + 3}`).join(', ');
|
||||
const values = [project_id, order_id, ...Object.values(updates)];
|
||||
const query = `UPDATE measurements SET ${fields} WHERE project_id = $1 AND order_id = $2 RETURNING *;`;
|
||||
return (await db.query(query, values)).rows[0];
|
||||
}),
|
||||
|
||||
delete_measurement_by_id: handleDatabaseOperation(async (id) => {
|
||||
const query = `DELETE FROM measurements WHERE id = $1;`;
|
||||
await db.query(query, [id]);
|
||||
}),
|
||||
|
||||
get_next_order_id: handleDatabaseOperation(async (project_id) => {
|
||||
const query = `SELECT COALESCE(MAX(order_id), 0) + 1 AS next_order_id FROM measurements WHERE project_id = $1;`;
|
||||
const result = await db.query(query, [project_id]);
|
||||
return result.rows[0].next_order_id;
|
||||
})
|
||||
};
|
||||
|
||||
const video = {
|
||||
get_all_videos: handleDatabaseOperation(async () => {
|
||||
const query = `SELECT * FROM videos;`;
|
||||
return (await db.query(query)).rows;
|
||||
}),
|
||||
|
||||
get_video_by_id: handleDatabaseOperation(async (id) => {
|
||||
const query = `SELECT * FROM videos WHERE id = $1;`;
|
||||
return (await db.query(query, [id])).rows[0];
|
||||
}),
|
||||
|
||||
get_videos_by_project_id: handleDatabaseOperation(async (project_id) => {
|
||||
const query = `SELECT * FROM videos WHERE project_id = $1;`;
|
||||
return (await db.query(query, [project_id])).rows;
|
||||
}),
|
||||
|
||||
create_video: handleDatabaseOperation(async (projectId, measurementIds, name, resolution, duration, status = 0) => {
|
||||
const query = `INSERT INTO public.videos (project_id, measurement_ids, name, resolution, duration, status) VALUES ($1, $2, $3, $4, $5, $6) RETURNING id;`;
|
||||
const values = [projectId, measurementIds, name, resolution, duration, status];
|
||||
return (await db.query(query, values)).rows[0];
|
||||
}),
|
||||
|
||||
edit_video_by_id: handleDatabaseOperation(async (id, updates) => {
|
||||
const fields = Object.keys(updates).map((key, index) => `${key} = $${index + 2}`).join(', ');
|
||||
const values = [id, ...Object.values(updates)];
|
||||
const query = `UPDATE videos SET ${fields} WHERE id = $1 RETURNING *;`;
|
||||
return (await db.query(query, values)).rows[0];
|
||||
}),
|
||||
|
||||
update_video_file_path_by_id: handleDatabaseOperation(async (id, video_file) => {
|
||||
const query = `UPDATE videos SET video_file = $1 WHERE id = $2 RETURNING *;`;
|
||||
return (await db.query(query, [video_file, id])).rows[0];
|
||||
}
|
||||
),
|
||||
|
||||
delete_video_by_id: handleDatabaseOperation(async (id) => {
|
||||
const query = `DELETE FROM videos WHERE id = $1;`;
|
||||
await db.query(query, [id]);
|
||||
}),
|
||||
|
||||
get_unfinished_videos: handleDatabaseOperation(async () => {
|
||||
// récupérer liste des vidéos dont le status est = 0, 2 ou 3
|
||||
const query = `SELECT * FROM videos WHERE status IN (0, 2, 3);`;
|
||||
return (await db.query(query)).rows;
|
||||
}),
|
||||
};
|
||||
|
||||
const capture = {
|
||||
get_camera: handleDatabaseOperation(async () => {
|
||||
const query = `SELECT * FROM camera WHERE id = 1;`;
|
||||
return (await db.query(query)).rows[0];
|
||||
}),
|
||||
|
||||
edit_camera: handleDatabaseOperation(async (id, updates) => {
|
||||
const fields = Object.keys(updates).map((key, index) => `${key} = $${index + 2}`).join(', ');
|
||||
const values = [id, ...Object.values(updates)];
|
||||
const query = `UPDATE camera SET ${fields} WHERE id = $1 RETURNING *;`;
|
||||
return (await db.query(query, values)).rows[0];
|
||||
}),
|
||||
|
||||
delete_camera: handleDatabaseOperation(async (id) => {
|
||||
const query = `DELETE FROM camera WHERE id = $1;`;
|
||||
await db.query(query, [id]);
|
||||
}),
|
||||
|
||||
init_camera: handleDatabaseOperation(async (id, interval, nb_images, maintenance, stop_flag, idle) => {
|
||||
const query = `INSERT INTO camera (id, interval, nb_images, maintenance, stop_flag, idle) VALUES ($1, $2, $3, $4, $5, $6) RETURNING *;`;
|
||||
const values = [id, interval, nb_images, maintenance, stop_flag, idle];
|
||||
return (await db.query(query, values)).rows[0];
|
||||
}),
|
||||
};
|
||||
|
||||
// zone de test
|
||||
async function test_zone(){
|
||||
//
|
||||
}
|
||||
|
||||
test_zone();
|
||||
|
||||
// Export des modules
|
||||
module.exports = {
|
||||
project,
|
||||
measurement,
|
||||
video,
|
||||
capture,
|
||||
};
|
||||
module.exports = database_manager;
|
||||
|
||||
Reference in New Issue
Block a user