Skip to content

GachaDev/takeaway-backend

Repository files navigation

Takeaway Backend

API Backend para la aplicación de pedido a domicilio MDK Burguer construida con NestJS. Esta API proporciona endpoints para gestionar:

  • Autenticación y autorización de usuarios
  • Categorías y productos del menú
  • Procesamiento y gestión de pedidos
  • Funcionalidades específicas para empleados

Características principales:

  • Autenticación robusta:

    • JWT (JSON Web Tokens) para seguridad
    • Sistema de roles (público y empleado)
    • Tokens de acceso y refresco
    • Validación de tokens en tiempo real
  • Gestión de usuarios:

    • Registro y autenticación de usuarios
    • Perfil de usuario personalizable
    • Gestión de contraseñas segura
    • Sistema de confirmación por correo
  • Sistema de pedidos:

    • Creación y gestión de pedidos en tiempo real
    • Sistema SSE (Server-Sent Events) para actualizaciones
    • Historial de pedidos por usuario
    • Estado de pedidos en tiempo real
  • Gestión de productos:

    • Catálogo de productos dinámico
    • Sistema de categorías
    • Gestión de ingredientes
    • Subida de imágenes de productos
  • Integraciones:

    • Base de datos PostgreSQL en AWS Supabase
    • Servicio de correo electrónico
    • Almacenamiento de imágenes en Imgur
    • API RESTful y documentada
  • Documentación Swagger:

    • Documentación interactiva de la API disponible en /api
    • Endpoints agrupados por funcionalidad (usuarios, productos, pedidos, etc.)
  • Seguridad:

    • Validación de datos
    • Protección contra inyecciones SQL
    • Rate limiting
    • CORS configurado

Endpoints

Usuarios

GET /users/:id

  • Descripción: Obtener usuario por ID
  • Requiere autenticación: Sí
  • Parámetros:
    • id (path): ID del usuario
  • Respuesta:
    {
      "id": number,
      "email": string,
      "first_name": string,
      "last_name": string,
      "phone": string,
      "points": number,
      "created_at": string,
      "employee": boolean,
      "activated": boolean
    }

POST /users/login

  • Descripción: Login de usuario
  • Requiere autenticación: No
  • Cuerpo de entrada:
    {
      "email": string,
      "password": string
    }
  • Respuesta:
    {
      "success": boolean,
      "token": string | null,
      "message": string | null
    }

POST /users

  • Descripción: Crear nuevo usuario
  • Requiere autenticación: No
  • Cuerpo de entrada:
    {
      "email": string,
      "password": string,
      "first_name": string,
      "last_name": string,
      "phone": string
    }
  • Respuesta:
    {
      "lastInsertId": number,
      "affectedRows": number,
      "error": string | null
    }

PUT /users/change-password

  • Descripción: Cambiar contraseña
  • Requiere autenticación: Sí
  • Cuerpo de entrada:
    {
      "currentPassword": string,
      "newPassword": string
    }
  • Respuesta: Mensaje de éxito

PUT /users/

  • Descripción: Actualizar información del usuario
  • Requiere autenticación: Sí
  • Cuerpo de entrada:
    {
      "first_name": string,
      "last_name": string,
      "phone": string
    }
  • Respuesta: Mensaje de éxito

DELETE /users/:id

  • Descripción: Eliminar usuario
  • Requiere autenticación: Sí
  • Requiere empleado: Sí
  • Parámetros:
    • id (path): ID del usuario
  • Respuesta: Mensaje de éxito

Pedidos

GET /orders

  • Descripción: Obtener todos los pedidos
  • Requiere autenticación: Sí
  • Requiere empleado: Sí
  • Respuesta:
    [
      {
        "id": number,
        "id_user": number,
        "delivery": boolean,
        "address": string,
        "date_ordered": string,
        "date_aprox_recollect": string,
        "payment_method": "cash" | "card" | "paypal" | "other",
        "delivered": boolean,
        "state": "pending" | "in_progress" | "completed" | "in_kitchen" | "in_delivery" | "delivered",
        "orderProducts": [
          {
            "id": number,
            "amount": number,
            "product": {
              "id": number,
              "name": string,
              "price": number
            }
          }
        ]
      }
    ]

GET /orders/sse

  • Descripción: Endpoint SSE para pedidos en tiempo real
  • Requiere autenticación: Sí
  • Requiere empleado: Sí
  • Respuesta: Stream de eventos

GET /orders/userOrders/:userId

  • Descripción: Obtener pedidos del usuario
  • Requiere autenticación: Sí
  • Parámetros:
    • userId (path): ID del usuario
  • Respuesta: Lista de pedidos del usuario

GET /orders/:userId

  • Descripción: Endpoint SSE para pedidos del usuario
  • Requiere autenticación: Sí
  • Parámetros:
    • userId (path): ID del usuario
  • Respuesta: Stream de eventos

POST /orders

  • Descripción: Crear nuevo pedido
  • Requiere autenticación: Sí
  • Cuerpo de entrada:
    {
      "id_user": number,
      "delivery": boolean,
      "address": string,
      "date_aprox_recollect": string,
      "payment_method": "cash" | "card" | "paypal" | "other",
      "orderProducts": [
        {
          "product": {
            "id": number
          },
          "amount": number
        }
      ]
    }
  • Respuesta:
    {
      "lastInsertId": number,
      "affectedRows": number
    }

PUT /orders

  • Descripción: Actualizar pedido
  • Requiere autenticación: Sí
  • Requiere empleado: Sí
  • Cuerpo de entrada:
    {
      "id": number,
      "state": "pending" | "in_progress" | "completed" | "in_kitchen" | "in_delivery" | "delivered"
    }
  • Respuesta: Pedido actualizado

DELETE /orders/:id

  • Descripción: Eliminar pedido
  • Requiere autenticación: Sí
  • Requiere empleado: Sí
  • Parámetros:
    • id (path): ID del pedido
  • Respuesta: Mensaje de éxito

Productos

GET /products

  • Descripción: Obtener todos los productos
  • Requiere autenticación: No
  • Respuesta: Lista de productos

POST /products

  • Descripción: Crear nuevo producto
  • Requiere autenticación: Sí
  • Requiere empleado: Sí
  • Cuerpo de entrada:
    {
      "name": string,
      "description": string,
      "price": number,
      "category": {
        "id": number
      },
      "ingredientsToAdd": [
        {
          "id": number
        }
      ]
    }
  • Respuesta: Producto creado

PUT /products

  • Descripción: Actualizar producto
  • Requiere autenticación: Sí
  • Requiere empleado: Sí
  • Cuerpo de entrada:
    {
      "id": number,
      "name": string,
      "description": string,
      "price": number,
      "category": {
        "id": number
      }
    }
  • Respuesta: Producto actualizado

DELETE /products/:id

  • Descripción: Eliminar producto
  • Requiere autenticación: Sí
  • Requiere empleado: Sí
  • Parámetros:
    • id (path): ID del producto
  • Respuesta: Mensaje de éxito

POST /products/upload/:id

  • Descripción: Subir imagen del producto
  • Requiere autenticación: Sí
  • Requiere empleado: Sí
  • Parámetros:
    • id (path): ID del producto
  • Cuerpo de entrada: Archivo de imagen
  • Respuesta: URL de la imagen

GET /products/:filename

  • Descripción: Servir imagen del producto
  • Requiere autenticación: No
  • Parámetros:
    • filename (path): Nombre del archivo
  • Respuesta: Redirección a la imagen

Categorías

GET /categories

  • Descripción: Obtener todas las categorías
  • Requiere autenticación: No
  • Respuesta: Lista de categorías

POST /categories

  • Descripción: Crear nueva categoría
  • Requiere autenticación: Sí
  • Requiere empleado: Sí
  • Cuerpo de entrada:
    {
      "name": string,
      "description": string
    }
  • Respuesta: Categoría creada

PUT /categories

  • Descripción: Actualizar categoría
  • Requiere autenticación: Sí
  • Requiere empleado: Sí
  • Cuerpo de entrada:
    {
      "id": number,
      "name": string,
      "description": string
    }
  • Respuesta: Categoría actualizada

DELETE /categories/:id

  • Descripción: Eliminar categoría
  • Requiere autenticación: Sí
  • Requiere empleado: Sí
  • Parámetros:
    • id (path): ID de la categoría
  • Respuesta: Mensaje de éxito

Ingredientes

GET /ingredients

  • Descripción: Obtener todos los ingredientes
  • Requiere autenticación: Sí
  • Respuesta: Lista de ingredientes

POST /ingredients

  • Descripción: Crear nuevo ingrediente
  • Requiere autenticación: Sí
  • Requiere empleado: Sí
  • Cuerpo de entrada:
    {
      "name": string,
      "description": string
    }
  • Respuesta: Ingrediente creado

PUT /ingredients

  • Descripción: Actualizar ingrediente
  • Requiere autenticación: Sí
  • Requiere empleado: Sí
  • Cuerpo de entrada:
    {
      "id": number,
      "name": string,
      "description": string
    }
  • Respuesta: Ingrediente actualizado

DELETE /ingredients/:id

  • Descripción: Eliminar ingrediente
  • Requiere autenticación: Sí
  • Requiere empleado: Sí
  • Parámetros:
    • id (path): ID del ingrediente
  • Respuesta: Mensaje de éxito

Autenticación

  • Todos los endpoints excepto los marcados como público requieren autenticación
  • Endpoints exclusivos de empleados requieren autenticación y privilegios de empleado
  • La autenticación se maneja mediante JWT tokens

Despliegue

La API está desplegada en Render y está accesible en la siguiente URL:

Nota sobre el modo suspensión:

  • La API entra en modo suspensión después de 15 minutos sin recibir solicitudes al tener un plan gratuito
  • La primera solicitud después del periodo de suspensión puede tardar aproximadamente 1 minuto en responder mientras el servidor se reinicia

La base de datos está alojada en Supabase.

Requisitos previos

  • Node.js 20 o superior
  • Docker (para despliegue)
  • pnpm (gestor de paquetes recomendado)

Variables de Entorno

La configuración de la aplicación se maneja a través del archivo .env. Las variables más importantes son:

  • DB_HOST: Host de la base de datos en AWS Supabase
  • DB_PORT: Puerto de la base de datos (6543 en Supabase)
  • DB_USER: Usuario de la base de datos
  • DB_PASSWORD: Contraseña de la base de datos
  • DB_NAME: Nombre de la base de datos
  • JWT_SECRET: Clave secreta para JWT necesita ser la misma que la del frontend
  • JWT_EXPIRES_IN: Tiempo de expiración del token JWT
  • IMGUR_CLIENT_ID: ID de cliente para subir imágenes
  • IMGUR_CLIENT_SECRET: Clave secreta de Imgur
  • EMAIL_HOST: Host del servidor de correo
  • EMAIL_PORT: Puerto del servidor de correo
  • EMAIL_HOST_USER: Usuario del servidor de correo
  • EMAIL_HOST_PASSWORD: Contraseña del servidor de correo

Despliegue con Docker

Este proyecto incluye un Dockerfile para facilitar el despliegue. La base de datos está alojada en AWS Supabase, por lo que no es necesario crear un contenedor separado para la base de datos.

Para construir y ejecutar la aplicación:

# Construir la imagen
$ docker build -t takeaway-backend .

# Ejecutar el contenedor
$ docker run -d -p 4000:4000 --name takeaway-backend takeaway-backend

La aplicación estará disponible en http://localhost:4000.

Desarrollo Local

Para desarrollo local:

# Instalar dependencias
$ pnpm install

# Copiar el archivo .env.example a .env
$ cp .env.example .env

# Iniciar en modo desarrollo
$ pnpm run start:dev

# Iniciar en modo producción
$ pnpm run start:prod

Ejecución con Docker Compose

Requisitos

  • Docker
  • Docker Compose
  • Proyecto Backend y Frontend clonados en la misma carpeta

Pasos

  1. Crear un archivo docker-compose.yml en la raíz del proyecto es decir fuera de la carpeta takeaway-frontend y fuera de takeaway-backend y poner el siguiente contenido:
services:
  backend:
    build:
      context: ./takeaway-backend # Ruta al directorio donde está el Dockerfile del backend
    ports:
      - "4000:4000"
    env_file:
      - ./takeaway-backend/.env
    networks:
      - app-network

  frontend:
    build:
      context: ./takeaway-frontend # Ruta al directorio donde está el Dockerfile del frontend
    ports:
      - "3000:3000"
    env_file:
      - ./takeaway-frontend/.env
    depends_on:
      - backend
    networks:
      - app-network

networks:
  app-network:
    driver: bridge
  1. Asignar en el archivo .env del frontend la variable NEXT_PUBLIC_API_URL a la url del backend
NEXT_PUBLIC_API_URL="http://backend:4000"
  1. Construir la imagen:
docker-compose up --build
  1. Ejecutar el contenedor:
docker-compose up

Licencia

Este proyecto está bajo licencia. Consulta el archivo LICENSE.md para más detalles.

Releases

No releases published

Packages

No packages published

Languages