Update package-lock.json and package.json to reflect openai version change from 4.80.1 to 4.82.0
This commit is contained in:
112
README.md
112
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=<comando>`: 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 <URL_DEL_REPOSITORIO>
|
||||
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
|
||||
);
|
||||
```
|
||||
16
src/app.js
16
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);
|
||||
|
||||
@@ -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 });
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user