From b23c6919accb41e2cb3eb652e442dfd057fcd61b Mon Sep 17 00:00:00 2001 From: Joan Manuel Jaramillo Avila <89425013+LifeRIP@users.noreply.github.com> Date: Tue, 24 Jun 2025 13:07:15 -0500 Subject: [PATCH 1/9] feat: Add restrictions for reporting messages in moderation service - Prevents banned users from reporting messages and disallows reporting messages from admins or room owners. - Introduces a dependency on `RoomService` to verify user privileges and enforces stricter validation for reporting logic. --- internal/services/moderation_service.go | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/internal/services/moderation_service.go b/internal/services/moderation_service.go index 59bae0b..e0125bd 100644 --- a/internal/services/moderation_service.go +++ b/internal/services/moderation_service.go @@ -21,6 +21,7 @@ type ModerationService struct { messageRepo *repositories.MessageRepository roomRepo *repositories.RoomRepository userRepo *repositories.UserRepository + roomService *RoomService } // NewModerationService creates a new instance of ModerationService @@ -29,12 +30,14 @@ func NewModerationService( messageRepo *repositories.MessageRepository, roomRepo *repositories.RoomRepository, userRepo *repositories.UserRepository, + roomService *RoomService, ) *ModerationService { return &ModerationService{ reportRepo: reportRepo, messageRepo: messageRepo, roomRepo: roomRepo, userRepo: userRepo, + roomService: roomService, } } @@ -51,6 +54,20 @@ func (s *ModerationService) ReportMessage(reporterID, roomID, messageID, reason return fmt.Errorf("users cannot report their own messages") } + // Check if the reporter is banned in the room + if !s.CanUserSendMessageInRoom(roomID, reporterID) { + return fmt.Errorf("banned users cannot report messages") + } + + // Check if the reported message is from an admin or owner + isAdminOrOwner, err := s.roomService.IsUserAdminOrOwner(roomID, message.UserID) + if err != nil { + return fmt.Errorf("error checking user privileges: %v", err) + } + if isAdminOrOwner { + return fmt.Errorf("messages from admins or room owner cannot be reported") + } + // Check if user has already reported this message hasReported, err := s.reportRepo.HasUserReportedMessage(reporterID, messageID) if err != nil { From 6cd52d6a8dd781862895c9c390224d26d1762a0c Mon Sep 17 00:00:00 2001 From: Joan Manuel Jaramillo Avila <89425013+LifeRIP@users.noreply.github.com> Date: Tue, 24 Jun 2025 13:26:00 -0500 Subject: [PATCH 2/9] feat: Improves user display name retrieval in direct chats - Optimizes the retrieval of user display names by handling only two users per direct chat. - Adds fallback names for unknown users and ensures the current user is always second in the order. - Enhances clarity and consistency in user identification for direct chats. --- .../repositories/directchat_repository.go | 31 +++++++++++++++---- 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/internal/repositories/directchat_repository.go b/internal/repositories/directchat_repository.go index db17225..eaf56b1 100644 --- a/internal/repositories/directchat_repository.go +++ b/internal/repositories/directchat_repository.go @@ -113,15 +113,34 @@ func (r *DirectChatRepository) GetUserDirectChats(userID string) ([]models.Direc if err := doc.DataTo(&chat); err != nil { return nil, err } - // Obtener los nombres actualizados de los usuarios chat.DisplayNames = make([]string, len(chat.UserIDs)) - for i, id := range chat.UserIDs { - user, err := r.UserRepo.GetUserByID(ctx, id) - if err == nil && user != nil { - chat.DisplayNames[i] = user.DisplayName - } + + // Como solo hay dos usuarios en un chat directo + // Obtenemos el nombre del primer usuario + user0, err := r.UserRepo.GetUserByID(ctx, chat.UserIDs[0]) + if err == nil && user0 != nil { + chat.DisplayNames[0] = user0.DisplayName + } else { + chat.DisplayNames[0] = "Usuario Desconocido" + } + + // Obtenemos el nombre del segundo usuario + user1, err := r.UserRepo.GetUserByID(ctx, chat.UserIDs[1]) + if err == nil && user1 != nil { + chat.DisplayNames[1] = user1.DisplayName + } else { + chat.DisplayNames[1] = "Usuario Desconocido" + } + + // Si el usuario actual es el primer usuario, intercambiamos tanto los nombres como los IDs + if chat.UserIDs[0] == userID { + // Intercambiar DisplayNames + chat.DisplayNames[0], chat.DisplayNames[1] = chat.DisplayNames[1], chat.DisplayNames[0] + // Intercambiar UserIDs + chat.UserIDs[0], chat.UserIDs[1] = chat.UserIDs[1], chat.UserIDs[0] } + // Ahora tanto en UserIDs como en DisplayNames, el otro usuario está primero y el usuario actual después chats = append(chats, chat) } From 5a2a4964d0b9546ed7d442e0bf45b9fd90d043ca Mon Sep 17 00:00:00 2001 From: Joan Manuel Jaramillo Avila <89425013+LifeRIP@users.noreply.github.com> Date: Tue, 24 Jun 2025 20:25:13 -0500 Subject: [PATCH 3/9] feat: Update README to include Firestore and WebSocket details, enhancing project documentation --- README.md | 139 +++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 116 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index d101740..bd500f8 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,17 @@ # API de Parchat -Este proyecto implementa una API para una plataforma de mensajería utilizando Go, Fx para la gestión de dependencias y Firebase Authentication para la autenticación de usuarios. +Este proyecto implementa una API para una plataforma de mensajería utilizando Go, Fx para la gestión de dependencias, Firebase Authentication para la autenticación de usuarios, Firestore como base de datos en tiempo real y WebSockets para la comunicación bidireccional en tiempo real. La arquitectura está diseñada para soportar chats en grupo, mensajería directa entre usuarios y moderación de contenido. + +## Tecnologías principales + +- **Go**: Lenguaje de programación principal, elegido por su rendimiento y concurrencia. +- **Uber Fx**: Framework de inyección de dependencias para mantener una arquitectura limpia y modular. +- **Firebase Authentication**: Gestión de usuarios y autenticación segura. +- **Firestore**: Base de datos NoSQL en tiempo real para almacenar salas de chat, mensajes y perfiles de usuario. +- **WebSockets**: Implementación de comunicación bidireccional en tiempo real para mensajería instantánea. +- **Chi Router**: Router HTTP ligero y eficiente para Go. +- **Swagger**: Documentación automática de la API. +- **Docker**: Contenedorización para facilitar el despliegue y desarrollo. ## Estructura del Proyecto @@ -8,26 +19,50 @@ Este proyecto implementa una API para una plataforma de mensajería utilizando G . ├── cmd │ └── api -│ └── main.go # Punto de entrada de la aplicación +│ └── main.go # Punto de entrada de la aplicación ├── internal -│ ├── auth -│ │ └── firebase.go # Integración con Firebase Auth │ ├── config -│ │ └── config.go # Configuración de la aplicación +│ │ ├── config.go # Configuración de la aplicación +│ │ ├── firebase.go # Configuración de Firebase +│ │ └── swagger.go # Configuración de Swagger │ ├── handlers -│ │ └── user_handler.go # Manejadores HTTP +│ │ ├── auth_handler.go # Manejadores de autenticación +│ │ ├── chat_handler.go # Manejadores de chat +│ │ ├── moderation_handler.go # Manejadores de moderación +│ │ ├── user_handler.go # Manejadores de usuarios +│ │ └── websocket_handler.go # Manejadores de WebSocket │ ├── middleware -│ │ └── auth_middleware.go # Middleware de autenticación +│ │ └── auth_middleware.go # Middleware de autenticación │ ├── models -│ │ └── user.go # Modelos de datos +│ │ ├── directchat.go # Modelo de chat directo +│ │ ├── message.go # Modelo de mensaje +│ │ ├── report.go # Modelo de reporte +│ │ ├── room.go # Modelo de sala de chat +│ │ └── user.go # Modelo de usuario +│ ├── pkg +│ │ └── websocket +│ │ ├── hub.go # Hub de WebSocket +│ │ └── websocket.go # Implementación de WebSocket +│ ├── repositories +│ │ ├── directchat_repository.go # Repositorio de chat directo +│ │ ├── message_repository.go # Repositorio de mensajes +│ │ ├── report_repository.go # Repositorio de reportes +│ │ ├── room_repository.go # Repositorio de salas +│ │ └── user_repository.go # Repositorio de usuarios │ ├── routes -│ │ └── router.go # Definición de rutas +│ │ └── router.go # Definición de rutas │ └── services -│ └── user_service.go # Lógica de negocio -├── docs # Documentación generada por Swagger -├── .env.example # Ejemplo de variables de entorno -├── go.mod # Dependencias de Go -└── README.md # Documentación +│ ├── auth_service.go # Servicio de autenticación +│ ├── directchat_service.go # Servicio de chat directo +│ ├── moderation_service.go # Servicio de moderación +│ ├── room_service.go # Servicio de salas +│ └── user_service.go # Servicio de usuarios +├── docs # Documentación generada por Swagger +├── compose.yml # Configuración de Docker Compose +├── Dockerfile.dev # Dockerfile para desarrollo +├── Dockerfile.prod # Dockerfile para producción +├── go.mod # Dependencias de Go +└── README.md # Documentación ``` ## Requisitos @@ -84,24 +119,89 @@ Una vez iniciado el servidor puedes acceder a la documentación desde [http://lo ### Protegidos (requieren token JWT) +Para acceder a los endpoints protegidos, debes incluir un token de ID de Firebase en el encabezado `Authorization`: + +``` +Authorization: Bearer +``` + #### Autenticación - `GET /api/v1/auth/me`: Obtiene información del usuario actual +- `POST /api/v1/user/create`: Asegura que el usuario exista en la base de datos #### Salas de Chat - `POST /api/v1/chat/rooms`: Crea una nueva sala de chat -- `GET /api/v1/chat/rooms`: Obtiene todas las salas del usuario actual +- `GET /api/v1/chat/rooms`: Obtiene todas las salas disponibles +- `GET /api/v1/chat/rooms/me`: Obtiene todas las salas del usuario actual - `GET /api/v1/chat/rooms/{roomId}`: Obtiene información de una sala específica - `GET /api/v1/chat/rooms/{roomId}/messages`: Obtiene mensajes de una sala específica - `GET /api/v1/chat/rooms/{roomId}/messages/paginated`: Obtiene mensajes de una sala específica paginados +- `POST /api/v1/chat/rooms/{roomId}/join`: Une al usuario a una sala específica #### Chats Directos - `POST /api/v1/chat/direct/{otherUserId}`: Crea un chat directo entre el usuario autenticado y otro usuario - `GET /api/v1/chat/direct/me`: Obtiene todos los chats directos del usuario actual +- `GET /api/v1/chat/direct/{chatId}`: Obtiene información de un chat directo específico - `GET /api/v1/chat/direct/{chatId}/messages`: Obtiene mensajes de un chat directo específico +#### Moderación +- `POST /api/v1/chat/rooms/{roomId}/report`: Reporta un mensaje inapropiado +- `GET /api/v1/chat/rooms/{roomId}/banned-users`: Obtiene usuarios baneados en una sala (solo admins) +- `POST /api/v1/chat/rooms/{roomId}/clear-reports`: Elimina reportes de un usuario en una sala (solo admins) + #### WebSocket - `GET /api/v1/chat/ws`: Endpoint para establecer conexión WebSocket +## Implementación técnica + +### Firestore + +El proyecto utiliza Firestore como base de datos principal, aprovechando sus capacidades NoSQL y de tiempo real: + +- **Colecciones**: + - `users`: Almacena perfiles de usuario + - `rooms`: Almacena información de salas de chat + - `messages`: Almacena mensajes enviados en salas de chat + - `directChats`: Almacena conversaciones directas entre usuarios + - `reports`: Almacena reportes de contenido inapropiado + +- **Ventajas**: + - Sincronización en tiempo real entre clientes + - Escalabilidad automática + - Consultas eficientes mediante índices + - Integración nativa con Firebase Authentication + +### WebSockets + +La implementación de WebSockets permite una comunicación bidireccional en tiempo real: + +- **Hub central**: Gestiona todas las conexiones activas de WebSocket +- **Canales de comunicación**: + - Salas de chat (rooms) + - Chats directos entre usuarios +- **Tipos de mensajes**: La aplicación utiliza un sistema de tipos para diferenciar entre diferentes eventos (ver sección "Tipos de mensajes WebSocket") +- **Autenticación**: Cada conexión WebSocket se autentica mediante tokens JWT + +La arquitectura WebSocket está diseñada para: +- Mantener miles de conexiones simultáneas +- Entregar mensajes en tiempo real con baja latencia +- Manejar reconexiones automáticas +- Proporcionar feedback inmediato sobre el estado de los mensajes + +#### Tipos de mensajes WebSocket + +El sistema utiliza los siguientes tipos de mensajes para la comunicación WebSocket: + +- `CHAT_ROOM`: Enviar un mensaje a una sala de chat +- `DIRECT_CHAT`: Enviar un mensaje a un chat directo +- `JOIN_ROOM`: Unirse a una sala de chat +- `JOIN_DIRECT_CHAT`: Unirse a un chat directo +- `USER_LEAVE`: Notificar que un usuario ha abandonado un chat +- `ERROR`: Mensaje de error +- `SUCCESS`: Mensaje de operación exitosa +- `ROOM_CREATED`: Notificación de que se ha creado una sala + + ## Flujo de Chat Directo ### Establecer un Direct Chat entre usuarios desde Postman @@ -132,6 +232,7 @@ La respuesta será un JSON con los detalles del chat directo creado (o existente { "id": "chat-id-123456", "userIds": ["tu-usuario-id", "ID_DEL_OTRO_USUARIO"], + "displayNames": ["Nombre Del Otro Usuario", "Tu Nombre"], "createdAt": "2025-05-13T10:15:30Z", "updatedAt": "2025-05-13T10:15:30Z" } @@ -171,14 +272,6 @@ Para enviar un mensaje: } ``` -## Autenticación - -Para acceder a los endpoints protegidos, debes incluir un token de ID de Firebase en el encabezado `Authorization`: - -``` -Authorization: Bearer -``` - ## Desarrollo Para añadir nuevas funcionalidades: From 7563ed3298c5b0099253fd75a12be7a8ed3e7675 Mon Sep 17 00:00:00 2001 From: Joan Manuel Jaramillo Avila <89425013+LifeRIP@users.noreply.github.com> Date: Tue, 24 Jun 2025 20:47:19 -0500 Subject: [PATCH 4/9] feat: Revamps README structure and enhances clarity - Overhauls the README file with a cleaner structure, improved formatting, and added icons for better readability. - Introduces a table of contents, reorganized sections, and detailed explanations of features, setup, and APIs. - Reduces verbosity by replacing lists with tables and collapsible sections. - Enhances navigation and usability for developers. --- README.md | 407 +++++++++++++++++++++++++++--------------------------- 1 file changed, 201 insertions(+), 206 deletions(-) diff --git a/README.md b/README.md index bd500f8..259b1e2 100644 --- a/README.md +++ b/README.md @@ -1,284 +1,279 @@ -# API de Parchat +# 🚀 API de Parchat -Este proyecto implementa una API para una plataforma de mensajería utilizando Go, Fx para la gestión de dependencias, Firebase Authentication para la autenticación de usuarios, Firestore como base de datos en tiempo real y WebSockets para la comunicación bidireccional en tiempo real. La arquitectura está diseñada para soportar chats en grupo, mensajería directa entre usuarios y moderación de contenido. +> Plataforma de mensajería en tiempo real desarrollada en Go, con autenticación mediante Firebase, persistencia en Firestore y comunicación instantánea vía WebSockets. -## Tecnologías principales +--- -- **Go**: Lenguaje de programación principal, elegido por su rendimiento y concurrencia. -- **Uber Fx**: Framework de inyección de dependencias para mantener una arquitectura limpia y modular. -- **Firebase Authentication**: Gestión de usuarios y autenticación segura. -- **Firestore**: Base de datos NoSQL en tiempo real para almacenar salas de chat, mensajes y perfiles de usuario. -- **WebSockets**: Implementación de comunicación bidireccional en tiempo real para mensajería instantánea. -- **Chi Router**: Router HTTP ligero y eficiente para Go. -- **Swagger**: Documentación automática de la API. -- **Docker**: Contenedorización para facilitar el despliegue y desarrollo. +## 📚 Índice -## Estructura del Proyecto +* [🧰 Tecnologías principales](#tecnologías-principales) +* [🗂️ Estructura del Proyecto](#️estructura-del-proyecto) +* [✅ Requisitos](#requisitos) +* [⚙️ Configuración](#configuración) +* [▶️ Ejecución](#️ejecución) +* [📖 Documentación API (Swagger)](#documentación-api-swagger) +* [🔐 Endpoints](#endpoints) -``` + * [🟢 Públicos](#-públicos) + * [🔒 Protegidos (requieren token JWT)](#protegidos-requieren-token-jwt) +* [⚙️ Implementación técnica](#implementación-técnica) + + * [🧩 Firestore](#firestore) +* [🌐 WebSockets](#websockets) +* [💬 Flujo de Chat Directo](#flujo-de-chat-directo) +* [🚧 Desarrollo](#desarrollo) + +--- + +## 🧰 Tecnologías principales + +| Herramienta | Descripción | +| ----------------- | ------------------------------------------------------ | +| **Go** | Lenguaje principal por su rendimiento y concurrencia | +| **Uber Fx** | Inyección de dependencias para arquitectura modular | +| **Firebase Auth** | Autenticación segura de usuarios | +| **Firestore** | Base de datos NoSQL en tiempo real | +| **WebSockets** | Comunicación bidireccional para mensajería instantánea | +| **Chi** | Router HTTP ligero y eficiente | +| **Swagger** | Generación automática de documentación | +| **Docker** | Contenedorización para desarrollo y despliegue | + +--- + +## 🗂️ Estructura del Proyecto + +
+Ver estructura completa del proyecto + +```bash . -├── cmd -│ └── api -│ └── main.go # Punto de entrada de la aplicación +├── cmd/api/main.go # Entrada principal ├── internal -│ ├── config -│ │ ├── config.go # Configuración de la aplicación -│ │ ├── firebase.go # Configuración de Firebase -│ │ └── swagger.go # Configuración de Swagger -│ ├── handlers -│ │ ├── auth_handler.go # Manejadores de autenticación -│ │ ├── chat_handler.go # Manejadores de chat -│ │ ├── moderation_handler.go # Manejadores de moderación -│ │ ├── user_handler.go # Manejadores de usuarios -│ │ └── websocket_handler.go # Manejadores de WebSocket -│ ├── middleware -│ │ └── auth_middleware.go # Middleware de autenticación -│ ├── models -│ │ ├── directchat.go # Modelo de chat directo -│ │ ├── message.go # Modelo de mensaje -│ │ ├── report.go # Modelo de reporte -│ │ ├── room.go # Modelo de sala de chat -│ │ └── user.go # Modelo de usuario -│ ├── pkg -│ │ └── websocket -│ │ ├── hub.go # Hub de WebSocket -│ │ └── websocket.go # Implementación de WebSocket -│ ├── repositories -│ │ ├── directchat_repository.go # Repositorio de chat directo -│ │ ├── message_repository.go # Repositorio de mensajes -│ │ ├── report_repository.go # Repositorio de reportes -│ │ ├── room_repository.go # Repositorio de salas -│ │ └── user_repository.go # Repositorio de usuarios -│ ├── routes -│ │ └── router.go # Definición de rutas -│ └── services -│ ├── auth_service.go # Servicio de autenticación -│ ├── directchat_service.go # Servicio de chat directo -│ ├── moderation_service.go # Servicio de moderación -│ ├── room_service.go # Servicio de salas -│ └── user_service.go # Servicio de usuarios -├── docs # Documentación generada por Swagger +│ ├── config/ # Configuración general y de servicios +│ ├── handlers/ # Manejadores HTTP +│ ├── middleware/ # Middleware de autenticación +│ ├── models/ # Modelos de negocio +│ ├── pkg/websocket/ # WebSocket Hub e implementación +│ ├── repositories/ # Acceso a datos +│ ├── routes/router.go # Ruteo +│ └── services/ # Lógica de negocio +├── docs/ # Documentación Swagger +├── Dockerfile.dev / .prod # Archivos Docker ├── compose.yml # Configuración de Docker Compose -├── Dockerfile.dev # Dockerfile para desarrollo -├── Dockerfile.prod # Dockerfile para producción -├── go.mod # Dependencias de Go -└── README.md # Documentación +└── README.md # Documentación general ``` -## Requisitos +
-- [Docker Compose](https://docs.docker.com/compose/install/) -- Cuenta de Firebase con Authentication habilitado -- Archivo de credenciales de Firebase Admin SDK +--- -## Configuración +## ✅ Requisitos -1. Crea un archivo `.env` basado en `.env.example`: +* [Docker Compose](https://docs.docker.com/compose/install/) +* Cuenta Firebase con **Authentication** habilitado +* Archivo de credenciales de Firebase Admin SDK + +--- + +## ⚙️ Configuración ```bash cp .env.example .env ``` -2. Configura las variables de entorno en el archivo `.env`: +Edita `.env` con tu configuración: -``` +```env PORT=8080 -FIREBASE_CREDENTIALS=./path/to/your/firebase-credentials.json +FIREBASE_CREDENTIALS=./path/to/firebase-credentials.json ENVIRONMENT=development ``` -3. Asegúrate de tener el archivo de credenciales de Firebase Admin SDK en la ubicación especificada. +--- -## Ejecución +## ▶️ Ejecución ```bash docker compose --profile=dev up ``` -## Documentación API (Swagger) +--- -Para generar o actualizar la documentación de la API con Swagger, ejecuta: +## 📖 Documentación API (Swagger) + +Generar y formatear documentación: ```bash -# Generar documentación swag init -g cmd/api/main.go -o ./docs - -# Formatear comentarios de Swagger swag fmt ``` -Una vez iniciado el servidor puedes acceder a la documentación desde [http://localhost:8080/swagger/index.html](http://localhost:8080/swagger/index.html) +🔗 Accede a Swagger: +[http://localhost:8080/swagger/index.html](http://localhost:8080/swagger/index.html) -## Endpoints +--- -### Públicos +## 🔐 Endpoints -- `GET /health`: Verifica el estado de la API -- `POST /auth/signup`: Registra un nuevo usuario -- `POST /api/v1/auth/signup`: Registra un nuevo usuario +### 🟢 Públicos -### Protegidos (requieren token JWT) +| Método | Ruta | Descripción | +| ------ | --------------------- | --------------------- | +| `GET` | `/health` | Estado de la API | +| `POST` | `/auth/signup` | Registro de usuario | +| `POST` | `/api/v1/auth/signup` | Registro (versión v1) | -Para acceder a los endpoints protegidos, debes incluir un token de ID de Firebase en el encabezado `Authorization`: +### 🔒 Protegidos -``` +🔑 Requieren Header: + +```http Authorization: Bearer ``` -#### Autenticación -- `GET /api/v1/auth/me`: Obtiene información del usuario actual -- `POST /api/v1/user/create`: Asegura que el usuario exista en la base de datos +| Método | Ruta | Descripción | +| ------ | --------------------- | ------------------------------------------------- | +| `GET` | `/api/v1/auth/me` | Información del usuario actual | +| `POST` | `/api/v1/user/create` | Asegura que el usuario exista en la base de datos | -#### Salas de Chat -- `POST /api/v1/chat/rooms`: Crea una nueva sala de chat -- `GET /api/v1/chat/rooms`: Obtiene todas las salas disponibles -- `GET /api/v1/chat/rooms/me`: Obtiene todas las salas del usuario actual -- `GET /api/v1/chat/rooms/{roomId}`: Obtiene información de una sala específica -- `GET /api/v1/chat/rooms/{roomId}/messages`: Obtiene mensajes de una sala específica -- `GET /api/v1/chat/rooms/{roomId}/messages/paginated`: Obtiene mensajes de una sala específica paginados -- `POST /api/v1/chat/rooms/{roomId}/join`: Une al usuario a una sala específica +#### 🧑‍🤝‍🧑 Salas de Chat -#### Chats Directos -- `POST /api/v1/chat/direct/{otherUserId}`: Crea un chat directo entre el usuario autenticado y otro usuario -- `GET /api/v1/chat/direct/me`: Obtiene todos los chats directos del usuario actual -- `GET /api/v1/chat/direct/{chatId}`: Obtiene información de un chat directo específico -- `GET /api/v1/chat/direct/{chatId}/messages`: Obtiene mensajes de un chat directo específico +| Método | Ruta | Descripción | +| ------ | ------------------------------------------------ | ----------------------------------- | +| `POST` | `/api/v1/chat/rooms` | Crea una nueva sala de chat | +| `GET` | `/api/v1/chat/rooms` | Obtiene todas las salas disponibles | +| `GET` | `/api/v1/chat/rooms/me` | Salas del usuario actual | +| `GET` | `/api/v1/chat/rooms/{roomId}` | Información de una sala específica | +| `GET` | `/api/v1/chat/rooms/{roomId}/messages` | Mensajes de una sala específica | +| `GET` | `/api/v1/chat/rooms/{roomId}/messages/paginated` | Mensajes paginados de una sala | +| `POST` | `/api/v1/chat/rooms/{roomId}/join` | Une al usuario a una sala | -#### Moderación -- `POST /api/v1/chat/rooms/{roomId}/report`: Reporta un mensaje inapropiado -- `GET /api/v1/chat/rooms/{roomId}/banned-users`: Obtiene usuarios baneados en una sala (solo admins) -- `POST /api/v1/chat/rooms/{roomId}/clear-reports`: Elimina reportes de un usuario en una sala (solo admins) +#### 💬 Chats Directos -#### WebSocket -- `GET /api/v1/chat/ws`: Endpoint para establecer conexión WebSocket +| Método | Ruta | Descripción | +| ------ | --------------------------------------- | ----------------------------------------- | +| `POST` | `/api/v1/chat/direct/{otherUserId}` | Crea un chat directo con otro usuario | +| `GET` | `/api/v1/chat/direct/me` | Todos los chats directos del usuario | +| `GET` | `/api/v1/chat/direct/{chatId}` | Información de un chat directo específico | +| `GET` | `/api/v1/chat/direct/{chatId}/messages` | Mensajes de un chat directo específico | -## Implementación técnica +#### 🚨 Moderación -### Firestore +| Método | Ruta | Descripción | +| ------ | ------------------------------------------- | --------------------------------------------- | +| `POST` | `/api/v1/chat/rooms/{roomId}/report` | Reportar mensaje inapropiado | +| `GET` | `/api/v1/chat/rooms/{roomId}/banned-users` | Usuarios baneados (solo admins) | +| `POST` | `/api/v1/chat/rooms/{roomId}/clear-reports` | Eliminar reportes de un usuario (solo admins) | -El proyecto utiliza Firestore como base de datos principal, aprovechando sus capacidades NoSQL y de tiempo real: +#### 🔌 WebSocket -- **Colecciones**: - - `users`: Almacena perfiles de usuario - - `rooms`: Almacena información de salas de chat - - `messages`: Almacena mensajes enviados en salas de chat - - `directChats`: Almacena conversaciones directas entre usuarios - - `reports`: Almacena reportes de contenido inapropiado +| Método | Ruta | Descripción | +| ------ | ----------------- | ---------------------------- | +| `GET` | `/api/v1/chat/ws` | Establece conexión WebSocket | -- **Ventajas**: - - Sincronización en tiempo real entre clientes - - Escalabilidad automática - - Consultas eficientes mediante índices - - Integración nativa con Firebase Authentication +--- -### WebSockets +## ⚙️ Implementación técnica -La implementación de WebSockets permite una comunicación bidireccional en tiempo real: +### 🧩 Firestore -- **Hub central**: Gestiona todas las conexiones activas de WebSocket -- **Canales de comunicación**: - - Salas de chat (rooms) - - Chats directos entre usuarios -- **Tipos de mensajes**: La aplicación utiliza un sistema de tipos para diferenciar entre diferentes eventos (ver sección "Tipos de mensajes WebSocket") -- **Autenticación**: Cada conexión WebSocket se autentica mediante tokens JWT +**Colecciones usadas**: -La arquitectura WebSocket está diseñada para: -- Mantener miles de conexiones simultáneas -- Entregar mensajes en tiempo real con baja latencia -- Manejar reconexiones automáticas -- Proporcionar feedback inmediato sobre el estado de los mensajes +* `users` +* `rooms` +* `messages` +* `directChats` +* `reports` -#### Tipos de mensajes WebSocket +**Ventajas**: -El sistema utiliza los siguientes tipos de mensajes para la comunicación WebSocket: +* Realtime +* Escalabilidad automática +* Integración con Firebase Auth -- `CHAT_ROOM`: Enviar un mensaje a una sala de chat -- `DIRECT_CHAT`: Enviar un mensaje a un chat directo -- `JOIN_ROOM`: Unirse a una sala de chat -- `JOIN_DIRECT_CHAT`: Unirse a un chat directo -- `USER_LEAVE`: Notificar que un usuario ha abandonado un chat -- `ERROR`: Mensaje de error -- `SUCCESS`: Mensaje de operación exitosa -- `ROOM_CREATED`: Notificación de que se ha creado una sala +## 🌐 WebSockets +**URL**: `ws://localhost:8080/api/v1/chat/ws` +**Header**: `Authorization: Bearer ` -## Flujo de Chat Directo +### Tipos de mensajes -### Establecer un Direct Chat entre usuarios desde Postman +| Tipo | Descripción | +| ------------------ | --------------------------- | +| `CHAT_ROOM` | Enviar mensaje a una sala | +| `DIRECT_CHAT` | Enviar mensaje directo | +| `JOIN_ROOM` | Unirse a una sala | +| `JOIN_DIRECT_CHAT` | Unirse a chat directo | +| `USER_LEAVE` | Abandonar sala | +| `ERROR` | Mensaje de error | +| `SUCCESS` | Operación exitosa | +| `ROOM_CREATED` | Notificación de sala creada | -Para establecer un Direct Chat entre dos usuarios usando Postman, necesitas seguir estos pasos: +--- -#### 1. Obtén un token JWT +## 💬 Flujo de Chat Directo con Postman -Primero, debes autenticarte para obtener un token JWT: +### Paso 1: Obtener JWT -1. Inicia sesión con tu usuario en la API (esto dependerá de tu implementación de autenticación) -2. Obtén el token JWT de la respuesta +Autentícate y copia el token desde la respuesta. -#### 2. Crea el Direct Chat +### Paso 2: Crear Direct Chat -Una vez que tengas el token, puedes crear un Direct Chat: - -1. Configura una solicitud POST a: `http://localhost:8080/api/v1/chat/direct/{ID_DEL_OTRO_USUARIO}` - - Reemplaza `{ID_DEL_OTRO_USUARIO}` con el ID real del usuario con el que deseas chatear -2. En la pestaña "Headers", añade: - - `Content-Type: application/json` - - `Authorization: Bearer TU_TOKEN_JWT` -3. Envía la solicitud +```http +POST /api/v1/chat/direct/{otherUserId} +Authorization: Bearer +``` -La respuesta será un JSON con los detalles del chat directo creado (o existente si ya había uno): +📥 Respuesta esperada: ```json { "id": "chat-id-123456", - "userIds": ["tu-usuario-id", "ID_DEL_OTRO_USUARIO"], - "displayNames": ["Nombre Del Otro Usuario", "Tu Nombre"], - "createdAt": "2025-05-13T10:15:30Z", - "updatedAt": "2025-05-13T10:15:30Z" + "userIds": [...], + "displayNames": [...], + "createdAt": "...", + "updatedAt": "..." +} +``` + +### Paso 3: WebSocket desde Postman + +* URL: `ws://localhost:8080/api/v1/chat/ws` +* Header: `Authorization: Bearer ` + +**Unirse al chat**: + +```json +{ + "type": "JOIN_DIRECT_CHAT", + "payload": "chat-id-123456", + "timestamp": "2025-05-13T10:16:00Z" } ``` -#### 3. Para usar WebSocket desde Postman - -Si quieres probar la conexión WebSocket desde Postman: - -1. Crea una nueva pestaña de tipo "WebSocket Request" -2. Introduce esta URL: `ws://localhost:8080/api/v1/chat/ws` -3. En la sección "Headers", añade: - - `Authorization: Bearer TU_TOKEN_JWT` -4. Conecta al WebSocket - -Para unirte al chat directo recién creado: -1. En el panel "Message", envía: - ```json - { - "type": "JOIN_DIRECT_CHAT", - "payload": "chat-id-123456", - "timestamp": "2025-05-13T10:16:00Z" - } - ``` - -Para enviar un mensaje: -1. En el panel "Message", envía: - ```json - { - "type": "DIRECT_CHAT", - "payload": { - "content": "Hola, este es un mensaje de prueba", - "roomID": "chat-id-123456", - "type": "text" - }, - "timestamp": "2025-05-13T10:17:00Z" - } - ``` - -## Desarrollo - -Para añadir nuevas funcionalidades: - -1. Crea los modelos necesarios en `internal/models/` -2. Implementa la lógica de negocio en `internal/services/` -3. Crea los repositorios en caso de ser necesarios en `internal/repositories/` -4. Crea los manejadores HTTP en `internal/handlers/` -5. Registra las rutas en `internal/routes/router.go` -6. Registra los proveedores en `cmd/api/main.go` +**Enviar mensaje**: + +```json +{ + "type": "DIRECT_CHAT", + "payload": { + "content": "Hola", + "roomID": "chat-id-123456", + "type": "text" + }, + "timestamp": "2025-05-13T10:17:00Z" +} +``` + +--- + +## 🚧 Desarrollo + +Pasos para añadir nuevas funcionalidades: + +1. 📦 Modelos → `internal/models/` +2. 🔧 Servicios → `internal/services/` +3. 🗃 Repositorios → `internal/repositories/` +4. 🧩 Manejadores HTTP → `internal/handlers/` +5. 🌐 Rutas → `internal/routes/router.go` +6. 🧬 Proveedores → `cmd/api/main.go` From 74215575a06460a70c39490d94ffda63d759114c Mon Sep 17 00:00:00 2001 From: Joan Manuel Jaramillo Avila <89425013+LifeRIP@users.noreply.github.com> Date: Tue, 24 Jun 2025 22:00:09 -0500 Subject: [PATCH 5/9] fix: Corrects index formatting in README for consistency --- README.md | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 259b1e2..6aefdf0 100644 --- a/README.md +++ b/README.md @@ -6,22 +6,22 @@ ## 📚 Índice -* [🧰 Tecnologías principales](#tecnologías-principales) -* [🗂️ Estructura del Proyecto](#️estructura-del-proyecto) -* [✅ Requisitos](#requisitos) -* [⚙️ Configuración](#configuración) -* [▶️ Ejecución](#️ejecución) -* [📖 Documentación API (Swagger)](#documentación-api-swagger) -* [🔐 Endpoints](#endpoints) +* [🧰 Tecnologías principales](#-tecnologías-principales) +* [🗂️ Estructura del Proyecto](#-estructura-del-proyecto) +* [✅ Requisitos](#-requisitos) +* [⚙️ Configuración](#-configuración) +* [▶️ Ejecución](#-ejecución) +* [📖 Documentación API (Swagger)](#-documentación-api-swagger) +* [🔐 Endpoints](#-endpoints) * [🟢 Públicos](#-públicos) - * [🔒 Protegidos (requieren token JWT)](#protegidos-requieren-token-jwt) -* [⚙️ Implementación técnica](#implementación-técnica) + * [🔒 Protegidos (requieren token JWT)](#-protegidos-requieren-token-jwt) +* [⚙️ Implementación técnica](#-implementación-técnica) - * [🧩 Firestore](#firestore) -* [🌐 WebSockets](#websockets) -* [💬 Flujo de Chat Directo](#flujo-de-chat-directo) -* [🚧 Desarrollo](#desarrollo) + * [🧩 Firestore](#-firestore) +* [🌐 WebSockets](#-websockets) +* [💬 Flujo de Chat Directo](#-flujo-de-chat-directo) +* [🚧 Desarrollo](#-desarrollo) --- From 1909343bb6288f70830b5ccefe6e020cabe6307b Mon Sep 17 00:00:00 2001 From: Joan Manuel Jaramillo Avila <89425013+LifeRIP@users.noreply.github.com> Date: Tue, 24 Jun 2025 22:18:45 -0500 Subject: [PATCH 6/9] refactor: Comment out user permission check in JoinRoom method --- internal/services/room_service.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/internal/services/room_service.go b/internal/services/room_service.go index e763b0d..b81ed13 100644 --- a/internal/services/room_service.go +++ b/internal/services/room_service.go @@ -69,10 +69,10 @@ func (s *RoomService) GetAllRooms() ([]models.Room, error) { // JoinRoom permite a un usuario unirse a una sala si tiene permiso func (s *RoomService) JoinRoom(roomID string, userID string) error { // Verificar si el usuario puede unirse a la sala - canJoin := s.RoomRepo.CanJoinRoomWebSocket(roomID, userID) - if !canJoin { - return fmt.Errorf("user is not allowed to join this room") - } + // canJoin := s.RoomRepo.CanJoinRoomWebSocket(roomID, userID) + // if !canJoin { + // return fmt.Errorf("user is not allowed to join this room") + // } // Añadir usuario a la sala return s.RoomRepo.AddMemberToRoom(roomID, userID) From ed7972ed0b899e1ae56e451d415c771b0df1d0cf Mon Sep 17 00:00:00 2001 From: Joan Manuel Jaramillo Avila <89425013+LifeRIP@users.noreply.github.com> Date: Tue, 24 Jun 2025 22:28:04 -0500 Subject: [PATCH 7/9] refactor: Comment out private room check in AddMemberToRoom method --- internal/repositories/room_repository.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/repositories/room_repository.go b/internal/repositories/room_repository.go index b13a85d..4d04d7b 100644 --- a/internal/repositories/room_repository.go +++ b/internal/repositories/room_repository.go @@ -244,9 +244,9 @@ func (r *RoomRepository) AddMemberToRoom(roomID string, userID string) error { } // Si la sala es privada, no puede unirse - if room.IsPrivate { - return fmt.Errorf("room is private, user cannot join") - } + // if room.IsPrivate { + // return fmt.Errorf("room is private, user cannot join") + // } // Añadir el usuario como miembro y actualizar la fecha de modificación _, err = r.FirestoreClient.Client.Collection("rooms").Doc(roomID).Update(ctx, []firestore.Update{ From d6ad5205be1dc31bdea724c9fb3c8aa79f3584de Mon Sep 17 00:00:00 2001 From: Joan Manuel Jaramillo Avila <89425013+LifeRIP@users.noreply.github.com> Date: Tue, 24 Jun 2025 22:49:14 -0500 Subject: [PATCH 8/9] refactor: Comment out user permission check in ReadPump method --- internal/pkg/websocket/websocket.go | 40 ++++++++++++++--------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/internal/pkg/websocket/websocket.go b/internal/pkg/websocket/websocket.go index 21ce3c7..cb147ef 100644 --- a/internal/pkg/websocket/websocket.go +++ b/internal/pkg/websocket/websocket.go @@ -256,26 +256,26 @@ func (c *Client) ReadPump() { } // Verificar si el usuario tiene permiso para unirse a la sala - if c.hub.roomRepo.CanJoinRoomWebSocket(roomID, c.userID) { - c.rooms[roomID] = true - log.Printf("User %s joined room %s", c.userID, roomID) - // Enviar mensaje de confirmación al usuario - // successMsg := "Successfully joined room" - // successPayload, _ := json.Marshal(successMsg) - // c.send <- WebSocketMessage{ - // Type: MessageTypeSuccess, - // Payload: successPayload, - // Timestamp: time.Now(), - // } - } else { - errMsg := "No permission to join this room" - errorPayload, _ := json.Marshal(errMsg) - c.send <- WebSocketMessage{ - Type: MessageTypeError, - Payload: errorPayload, - Timestamp: time.Now(), - } - } + // if c.hub.roomRepo.CanJoinRoomWebSocket(roomID, c.userID) { + // c.rooms[roomID] = true + // log.Printf("User %s joined room %s", c.userID, roomID) + // // Enviar mensaje de confirmación al usuario + // // successMsg := "Successfully joined room" + // // successPayload, _ := json.Marshal(successMsg) + // // c.send <- WebSocketMessage{ + // // Type: MessageTypeSuccess, + // // Payload: successPayload, + // // Timestamp: time.Now(), + // // } + // } else { + // errMsg := "No permission to join this room" + // errorPayload, _ := json.Marshal(errMsg) + // c.send <- WebSocketMessage{ + // Type: MessageTypeError, + // Payload: errorPayload, + // Timestamp: time.Now(), + // } + // } // Manejar cuando un usuario quiere escuchar un chat directo case MessageTypeJoinDirectChat: From 49ded004b19d31a3295eee392635bef216c44c37 Mon Sep 17 00:00:00 2001 From: Joan Manuel Jaramillo Avila <89425013+LifeRIP@users.noreply.github.com> Date: Tue, 24 Jun 2025 23:09:46 -0500 Subject: [PATCH 9/9] refactor: Uncomment room join logic in ReadPump method --- internal/pkg/websocket/websocket.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/pkg/websocket/websocket.go b/internal/pkg/websocket/websocket.go index cb147ef..85404b0 100644 --- a/internal/pkg/websocket/websocket.go +++ b/internal/pkg/websocket/websocket.go @@ -257,8 +257,8 @@ func (c *Client) ReadPump() { // Verificar si el usuario tiene permiso para unirse a la sala // if c.hub.roomRepo.CanJoinRoomWebSocket(roomID, c.userID) { - // c.rooms[roomID] = true - // log.Printf("User %s joined room %s", c.userID, roomID) + c.rooms[roomID] = true + log.Printf("User %s joined room %s", c.userID, roomID) // // Enviar mensaje de confirmación al usuario // // successMsg := "Successfully joined room" // // successPayload, _ := json.Marshal(successMsg)