From 01491626eedc3d6e10b3f1511de6954f372f927b Mon Sep 17 00:00:00 2001 From: pablinux Date: Tue, 11 Nov 2025 03:24:23 -0500 Subject: [PATCH] `Update package-lock.json and package.json to reflect openai version change from 4.80.1 to 4.82.0` --- README.md | 112 ++++++++++++++++++++++++ src/app.js | 16 ++++ src/controladores/controladorGeneral.js | 35 ++++---- src/controladores/controlador_auth.js | 22 +++-- 4 files changed, 153 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index e69de29..0ea9cfe 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,112 @@ +# Audio Control - Backend + +Este es el proyecto de backend para la aplicación **SoundWave**. Es una API RESTful construida con Node.js y Express, diseñada para gestionar la autenticación de usuarios, controlar un reproductor de audio y administrar una lista de reproducción de videos de YouTube. + +## Tabla de Contenidos + +- [Acerca del Proyecto](#acerca-del-proyecto) +- [Estructura de la API](#estructura-de-la-api) +- [Tecnologías](#tecnologías) +- [Instalación y Uso](#instalación-y-uso) +- [Variables de Entorno](#variables-de-entorno) +- [Estructura de la Base de Datos](#estructura-de-la-base-de-datos) + +## Acerca del Proyecto + +Este backend proporciona los servicios necesarios para que el frontend `audio-player` funcione correctamente. + +- **API RESTful**: Endpoints claros y definidos para la gestión de recursos. +- **Autenticación Segura**: Maneja la autenticación de usuarios a través de Google OAuth 2.0 y genera tokens JWT para sesiones seguras. +- **Gestión de Base de Datos**: Se conecta a una base de datos MySQL para persistir datos de usuarios y listas de reproducción. +- **Panel de Control Integrado**: Sirve una interfaz web simple con EJS y Materialize CSS para el control directo del reproductor. +- **Manejo de Errores Centralizado**: Utiliza un middleware de Express para gestionar todos los errores de forma consistente. + +## Estructura de la API + +A continuación se describen los endpoints principales: + +### Autenticación + +- `POST /api/auth/google`: Autentica a un usuario con un token de Google. Crea el usuario si no existe y devuelve un token JWT de la aplicación. + +### Playlist de YouTube + +- `GET /api/youtube-playlist`: Devuelve la lista de reproducción de videos de YouTube. +- `POST /api/youtube-video`: Añade un nuevo video a la lista de reproducción. + +### Control Remoto + +- `GET /cmd?consulta=`: Endpoint para enviar comandos a un reproductor (ej. `play_video`, `pausa_video`). + +## Tecnologías + +- **Node.js**: Entorno de ejecución de JavaScript. +- **Express.js**: Framework web para Node.js. +- **MySQL**: Sistema de gestión de bases de datos. +- **jsonwebtoken**: Para la creación y verificación de tokens JWT. +- **google-auth-library**: Para verificar los tokens de ID de Google. +- **dotenv**: Para la gestión de variables de entorno. +- **EJS**: Motor de plantillas para servir el panel de control. + +## Instalación y Uso + +1. **Clona el repositorio:** + ```bash + git clone + cd audio_control + ``` + +2. **Instala las dependencias:** + ```bash + npm install + ``` + +3. **Configura las variables de entorno:** + Crea un archivo `.env` en la raíz del proyecto (`src/.env`). Consulta la sección Variables de Entorno. + +4. **Inicia el servidor:** + ```bash + npm start + ``` + El servidor se iniciará en el puerto 2000 (o el que se especifique). + +## Variables de Entorno + +Crea un archivo `.env` en el directorio `src/` con las siguientes variables. **Es crucial para la seguridad y el funcionamiento de la aplicación.** + +``` +# Secret para firmar los tokens JWT (usa un valor largo y aleatorio) +JWT_SECRET=tu_super_secreto_para_jwt + +# Credenciales de Google Cloud para OAuth 2.0 +GOOGLE_CLIENT_ID=tu_client_id_de_google.apps.googleusercontent.com +``` + +## Estructura de la Base de Datos + +El proyecto requiere una base de datos MySQL con al menos las siguientes tablas: + +**Tabla `users`**: +```sql +CREATE TABLE users ( + id INT AUTO_INCREMENT PRIMARY KEY, + google_id VARCHAR(255) UNIQUE, + email VARCHAR(255) NOT NULL UNIQUE, + name VARCHAR(255), + picture_url VARCHAR(255), + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); +``` + +**Tabla `rep_youtube`**: +```sql +CREATE TABLE rep_youtube ( + id INT AUTO_INCREMENT PRIMARY KEY, + video_id VARCHAR(50) NOT NULL, + title VARCHAR(255) NOT NULL, + channel VARCHAR(255), + thumbnail VARCHAR(255), + duration VARCHAR(20), + added_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); +``` \ No newline at end of file diff --git a/src/app.js b/src/app.js index 1864224..abeb819 100644 --- a/src/app.js +++ b/src/app.js @@ -66,6 +66,22 @@ app.get('/cmd', (req, res) => { //archivos staticos app.use(express.static(path.join(__dirname, 'public'))); +// --- MANEJO DE ERRORES --- + +// Middleware para capturar rutas no encontradas (404) +app.use((req, res, next) => { + res.status(404).json({ message: 'Recurso no encontrado.' }); +}); + +// Middleware de manejo de errores centralizado +// Se activa cuando se llama a next(error) en cualquier parte de la aplicación. +app.use((err, req, res, next) => { + console.error('[ERROR STACK]:', err.stack); // Loguear el error completo para depuración + const status = err.status || 500; + const message = err.message || 'Error interno del servidor.'; + res.status(status).json({ message }); +}); + //inicia servidor app.listen(app.get('port'),() =>{ console.log('Servidor activo App SIGMA Puerto: '+puertoApp); diff --git a/src/controladores/controladorGeneral.js b/src/controladores/controladorGeneral.js index 89871cb..9aca517 100644 --- a/src/controladores/controladorGeneral.js +++ b/src/controladores/controladorGeneral.js @@ -1,17 +1,14 @@ const controlador = {}; -controlador.panel = (req, res) => { + +controlador.panel = (req, res, next) => { res.render('panel'); }; - -controlador.clientes = (req, res) => { +controlador.clientes = (req, res, next) => { req.getConnection((err, conn) => { + if (err) return next(err); conn.query('SELECT * FROM rep_youtube order by id DESC LIMIT 50', (err, rows) => {//se obtiene error o consulta filas(rows) - if(err){ - res.json(err); - //next(err); - } - //console.log(rows); + if (err) return next(err); res.render('panel',{ data:rows }); @@ -19,37 +16,35 @@ controlador.clientes = (req, res) => { }); }; -controlador.getYouTubePlaylist = (req, res) => { +controlador.getYouTubePlaylist = (req, res, next) => { req.getConnection((err, conn) => { - if (err) { - console.error("Error de conexión a la BD:", err); - return res.status(500).json({ message: "Error de conexión a la base de datos" }); - } + if (err) return next(err); + // Asumiendo que la tabla rep_youtube tiene al menos 'video_id' y 'title' conn.query('SELECT id, video_id, title FROM rep_youtube ORDER BY id DESC LIMIT 50', (err, rows) => { - if (err) { - return res.status(500).json({ error: "Error al obtener la playlist de YouTube" }); - } + if (err) return next(err); res.json({ playlist: rows }); }); }); }; -controlador.addYouTubeVideo = (req, res) => { +controlador.addYouTubeVideo = (req, res, next) => { // Extraemos los datos del cuerpo de la petición const { video_id, title, channel, thumbnail, duration } = req.body; if (!video_id || !title) { - return res.status(400).json({ message: "Los campos 'video_id' y 'title' son obligatorios." }); + const error = new Error("Los campos 'video_id' y 'title' son obligatorios."); + error.status = 400; + return next(error); } const newVideo = { video_id, title, channel, thumbnail, duration }; req.getConnection((err, conn) => { - if (err) return res.status(500).json({ message: "Error de conexión a la base de datos" }); + if (err) return next(err); conn.query('INSERT INTO rep_youtube SET ?', [newVideo], (err, result) => { - if (err) return res.status(500).json({ message: "Error al guardar el video", error: err }); + if (err) return next(err); res.status(201).json({ message: "Video añadido con éxito", id: result.insertId }); }); }); diff --git a/src/controladores/controlador_auth.js b/src/controladores/controlador_auth.js index ae36089..4133256 100644 --- a/src/controladores/controlador_auth.js +++ b/src/controladores/controlador_auth.js @@ -11,17 +11,18 @@ function generateTokenAndRespond(user, res) { } // --- NUEVA RUTA DE AUTENTICACIÓN CON GOOGLE --- -controlador_auth.authenticateWithGoogle = (req, res) => { +controlador_auth.authenticateWithGoogle = (req, res, next) => { // Añadido 'next' const { token } = req.body; if (!token) { - return res.status(400).json({ message: 'No se proporcionó el token de Google.' }); + const error = new Error('No se proporcionó el token de Google.'); + error.status = 400; + return next(error); } req.getConnection(async (err, conn) => { if (err) { - console.error("Error de conexión a la BD:", err); - return res.status(500).json({ message: "Error de conexión a la base de datos" }); + return next(err); // Pasa el error al middleware central } try { @@ -35,8 +36,7 @@ controlador_auth.authenticateWithGoogle = (req, res) => { // 2. Buscar usuario por email conn.query('SELECT * FROM users WHERE email = ?', [email], (err, rows) => { if (err) { - console.error("Error en consulta SQL:", err); - return res.status(500).json({ message: "Error en la base de datos" }); + return next(err); // Pasa el error al middleware central } const user = rows[0]; @@ -45,10 +45,7 @@ controlador_auth.authenticateWithGoogle = (req, res) => { // Crear nuevo usuario const newUser = { google_id: googleId, email, name, picture_url: picture }; conn.query('INSERT INTO users SET ?', newUser, (err, result) => { - if (err) { - console.error("Error al crear usuario:", err); - return res.status(500).json({ message: "Error al registrar usuario" }); - } + if (err) return next(err); // Pasa el error al middleware central const createdUser = { id: result.insertId, ...newUser }; generateTokenAndRespond(createdUser, res); }); @@ -58,8 +55,9 @@ controlador_auth.authenticateWithGoogle = (req, res) => { } }); } catch (error) { - console.error('Error en autenticación con Google:', error.message); - return res.status(401).json({ message: 'Token de Google inválido o expirado.' }); + const authError = new Error('Token de Google inválido o expirado.'); + authError.status = 401; + return next(authError); // Pasa el error al middleware central } }); };