mensajes Table¶
Relevant source files * src/router.js * src/sockets/socketHandler.js
Purpose and Scope¶
The mensajes table stores all chat messages exchanged in the support chat system between users and administrators. This table serves as the persistence layer for the real-time messaging functionality, recording every message sent through Socket.IO WebSocket connections and providing historical message retrieval through HTTP API endpoints.
For information about the real-time WebSocket layer that writes to this table, see Real-time Communication System. For details about the HTTP endpoints that query this table, see API Endpoints. For the related user authentication table, see usuarios Table.
Sources: src/router.js L229-L315
src/sockets/socketHandler.js L55-L62
Table Schema¶
The mensajes table has the following structure:
| Column | Type | Constraints | Description |
|---|---|---|---|
de_usuario |
VARCHAR | NOT NULL, FK → usuarios.usuario |
Username of the message sender |
para_usuario |
VARCHAR | NOT NULL, FK → usuarios.usuario |
Username of the message recipient |
mensaje |
TEXT | NOT NULL | Message content |
fecha |
DATETIME | DEFAULT CURRENT_TIMESTAMP | Timestamp when message was created |
Note: The table does not have an explicit primary key defined in the queries, though it likely has an auto-increment ID column for internal indexing.
Sources: src/router.js L237-L269
src/sockets/socketHandler.js L55
Entity Relationship Diagram¶
#mermaid-1rs7o028btr{font-family:ui-sans-serif,-apple-system,system-ui,Segoe UI,Helvetica;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-1rs7o028btr .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-1rs7o028btr .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-1rs7o028btr .error-icon{fill:#dddddd;}#mermaid-1rs7o028btr .error-text{fill:#222222;stroke:#222222;}#mermaid-1rs7o028btr .edge-thickness-normal{stroke-width:1px;}#mermaid-1rs7o028btr .edge-thickness-thick{stroke-width:3.5px;}#mermaid-1rs7o028btr .edge-pattern-solid{stroke-dasharray:0;}#mermaid-1rs7o028btr .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-1rs7o028btr .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-1rs7o028btr .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-1rs7o028btr .marker{fill:#999;stroke:#999;}#mermaid-1rs7o028btr .marker.cross{stroke:#999;}#mermaid-1rs7o028btr svg{font-family:ui-sans-serif,-apple-system,system-ui,Segoe UI,Helvetica;font-size:16px;}#mermaid-1rs7o028btr p{margin:0;}#mermaid-1rs7o028btr .entityBox{fill:#ffffff;stroke:#dddddd;}#mermaid-1rs7o028btr .relationshipLabelBox{fill:#dddddd;opacity:0.7;background-color:#dddddd;}#mermaid-1rs7o028btr .relationshipLabelBox rect{opacity:0.5;}#mermaid-1rs7o028btr .labelBkg{background-color:rgba(221, 221, 221, 0.5);}#mermaid-1rs7o028btr .edgeLabel .label{fill:#dddddd;font-size:14px;}#mermaid-1rs7o028btr .label{font-family:ui-sans-serif,-apple-system,system-ui,Segoe UI,Helvetica;color:#333;}#mermaid-1rs7o028btr .edge-pattern-dashed{stroke-dasharray:8,8;}#mermaid-1rs7o028btr .node rect,#mermaid-1rs7o028btr .node circle,#mermaid-1rs7o028btr .node ellipse,#mermaid-1rs7o028btr .node polygon{fill:#ffffff;stroke:#dddddd;stroke-width:1px;}#mermaid-1rs7o028btr .relationshipLine{stroke:#999;stroke-width:1;fill:none;}#mermaid-1rs7o028btr .marker{fill:none!important;stroke:#999!important;stroke-width:1;}#mermaid-1rs7o028btr :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}sends (de_usuario)receives (para_usuario)usuariosvarcharusuarioPKvarcharnombrevarcharrolvarcharpassvarcharimagenmensajesvarcharde_usuarioFKvarcharpara_usuarioFKtextmensajedatetimefecha
Diagram: Relationship between usuarios and mensajes tables. Each message has two foreign key relationships to the usuarios table: one for the sender (de_usuario) and one for the recipient (para_usuario).
Sources: src/router.js L237-L242
src/sockets/socketHandler.js L55
Column Descriptions¶
de_usuario¶
The de_usuario column stores the username of the message sender. This is a foreign key reference to usuarios.usuario. The value is extracted from the authenticated JWT token in WebSocket connections.
Populated by: src/sockets/socketHandler.js L46-L56
para_usuario¶
The para_usuario column stores the username of the message recipient. This is a foreign key reference to usuarios.usuario. In the WebSocket handler, this value comes from the para field of the mensaje_privado event payload.
Populated by: src/sockets/socketHandler.js L45-L56
mensaje¶
The mensaje column stores the actual text content of the message as a TEXT field, allowing for messages of arbitrary length. This content is passed directly from the client through the WebSocket mensaje_privado event.
Populated by: src/sockets/socketHandler.js L45-L56
fecha¶
The fecha column is a DATETIME field that records when the message was created. This column likely uses DEFAULT CURRENT_TIMESTAMP to automatically set the timestamp during insertion, though it is not explicitly set in the INSERT query.
Implicitly set by: src/sockets/socketHandler.js L55-L56
Sources: src/sockets/socketHandler.js L45-L62
Data Operations¶
Message Insertion (WebSocket)¶
Messages are inserted into the mensajes table when a user sends a message through the Socket.IO WebSocket connection. The insertion occurs in the mensaje_privado event handler.
sequenceDiagram
participant Client
participant Socket.IO Server
participant socketHandler.js
participant MySQL mensajes table
Client->>Socket.IO Server: "emit mensaje_privado {para, mensaje}"
Socket.IO Server->>socketHandler.js: "mensaje_privado event"
note over socketHandler.js: "Extract 'de' from JWT
socketHandler.js->>MySQL mensajes table: "INSERT INTO mensajes
socketHandler.js->>Socket.IO Server: (de_usuario, para_usuario, mensaje)"
socketHandler.js->>Socket.IO Server: "emit mensaje_recibido to recipient"
MySQL mensajes table-->>socketHandler.js: "emit mensaje_recibido to admins (if sender not admin)"
note over socketHandler.js: "Log: 💾 Mensaje guardado"
Diagram: Message insertion flow through WebSocket connection
The actual SQL insertion code:
INSERT INTO mensajes (de_usuario, para_usuario, mensaje) VALUES (?, ?, ?)
Sources: src/sockets/socketHandler.js L45-L63
Message Retrieval (HTTP API)¶
Three HTTP API endpoints query the mensajes table to retrieve historical messages:
1. GET /api/mensajes (Admin Only)¶
This endpoint retrieves all messages for a specific user conversation. It requires the verifyAdmin middleware and accepts a query parameter ?con=username.
SELECT de_usuario, para_usuario, mensaje, fecha
FROM mensajes
WHERE
(de_usuario = ? OR para_usuario = ?)
ORDER BY fecha ASC
Sources: src/router.js L229-L253
2. GET /api/mensajes/mios (Authenticated Users)¶
This endpoint retrieves all messages for the currently authenticated user. The username is extracted from the JWT token via the verifyToken middleware.
SELECT de_usuario, para_usuario, mensaje, fecha
FROM mensajes
WHERE
(de_usuario = ? OR para_usuario = ?)
ORDER BY fecha ASC
Sources: src/router.js L256-L280
3. GET /api/usuarios-conversaciones (Admin Only)¶
This endpoint retrieves a list of all users who have had conversations with administrators. It uses a complex UNION query to find all non-admin users who have either sent messages to admins or received messages from admins.
SELECT DISTINCT usuario
FROM (
SELECT de_usuario AS usuario FROM mensajes
WHERE para_usuario IN (SELECT usuario FROM usuarios WHERE rol = 'admin')
UNION
SELECT para_usuario AS usuario FROM mensajes
WHERE de_usuario IN (SELECT usuario FROM usuarios WHERE rol = 'admin')
) AS conversaciones
WHERE usuario NOT IN (SELECT usuario FROM usuarios WHERE rol = 'admin')
This query:
- Finds all non-admin senders who sent to admins (
de_usuario) - Finds all non-admin recipients who received from admins (
para_usuario) - Combines results with UNION to eliminate duplicates
- Filters out any admin users from the final list
Sources: src/router.js L283-L315
Data Access Patterns¶
flowchart TD
WS["Socket.IO WebSocket<br>mensaje_privado event"]
WSHandler["socketHandler.js:45-63"]
API1["GET /api/mensajes?con=user<br>verifyAdmin"]
API2["GET /api/mensajes/mios<br>verifyToken"]
API3["GET /api/usuarios-conversaciones<br>verifyAdmin"]
Router["router.js:229-315"]
MensajesTable["mensajes table<br>de_usuario, para_usuario,<br>mensaje, fecha"]
WSHandler --> MensajesTable
Router --> MensajesTable
subgraph Database ["Database"]
MensajesTable
end
subgraph subGraph1 ["Read Operations"]
API1
API2
API3
Router
API1 --> Router
API2 --> Router
API3 --> Router
end
subgraph subGraph0 ["Write Operations"]
WS
WSHandler
WS --> WSHandler
end
Diagram: Write and read access patterns for the mensajes table
Sources: src/router.js L229-L315
src/sockets/socketHandler.js L45-L63
Query Patterns and Performance Considerations¶
Bidirectional Message Queries¶
Both /api/mensajes and /api/mensajes/mios endpoints use the same query pattern to retrieve bidirectional conversations:
WHERE (de_usuario = ? OR para_usuario = ?)
This pattern matches messages where the user is either the sender or recipient, enabling a complete conversation view. For optimal performance, the table should have composite indexes on:
(de_usuario, fecha)(para_usuario, fecha)
This allows efficient retrieval of chronologically ordered messages for any user.
Chronological Ordering¶
All message retrieval queries use ORDER BY fecha ASC to return messages in chronological order, which is essential for displaying conversation history correctly.
Sources: src/router.js L241-L268
Admin Conversation Discovery¶
The /api/usuarios-conversaciones endpoint uses a complex subquery pattern to discover all users who have communicated with administrators. This query performs multiple table scans and subqueries, making it potentially expensive for large datasets. Consider caching this result or maintaining a denormalized table if performance becomes an issue.
Sources: src/router.js L292-L304
Usage in Real-time Messaging Flow¶
sequenceDiagram
participant User Client
participant Admin Client
participant Socket.IO Server
participant socketHandler.js
participant mensajes table
participant router.js API
note over User Client,Admin Client: "Initial page load"
User Client->>router.js API: "GET /api/mensajes/mios"
Admin Client->>router.js API: "GET /api/usuarios-conversaciones"
router.js API->>mensajes table: "SELECT historical messages"
mensajes table-->>router.js API: "Previous messages"
router.js API-->>User Client: "JSON: message history"
router.js API-->>Admin Client: "JSON: user list"
note over User Client,Admin Client: "Real-time messaging"
User Client->>Socket.IO Server: "mensaje_privado {para: 'admin1', mensaje: '...'}"
Socket.IO Server->>socketHandler.js: "Event received"
socketHandler.js->>mensajes table: "INSERT INTO mensajes"
socketHandler.js->>User Client: "mensaje_recibido (confirmation)"
socketHandler.js->>Admin Client: "mensaje_recibido (to admins room)"
Admin Client->>Socket.IO Server: "mensaje_privado {para: 'user1', mensaje: '...'}"
Socket.IO Server->>socketHandler.js: "Event received"
socketHandler.js->>mensajes table: "INSERT INTO mensajes"
socketHandler.js->>Admin Client: "mensaje_recibido (confirmation)"
socketHandler.js->>User Client: "mensaje_recibido (to user:user1 room)"
Diagram: Complete message flow showing both historical retrieval (HTTP) and real-time messaging (WebSocket) interactions with the mensajes table
Sources: src/router.js L256-L315
src/sockets/socketHandler.js L45-L63
Error Handling¶
Insertion Errors¶
When message insertion fails in the WebSocket handler, an error is logged to the console but the client is not explicitly notified. The message delivery to recipients proceeds regardless of database persistence success.
Sources: src/sockets/socketHandler.js L57-L61
Query Errors¶
All HTTP API endpoints that query the mensajes table implement error handling:
- Return HTTP 500 status code with JSON error message
- Log errors to console with "❌" prefix
- Generic error messages to avoid leaking database information
Sources: src/router.js L245-L310
Security Considerations¶
Authorization Enforcement¶
Access to message data is protected by middleware:
/api/mensajesrequiresverifyAdminmiddleware - only administrators can query other users' messages/api/mensajes/miosrequiresverifyTokenmiddleware - users can only access their own messages- WebSocket insertions require JWT authentication via Socket.IO middleware
Sources: src/router.js L229-L283
src/sockets/socketHandler.js L6-L32
SQL Injection Prevention¶
All database queries use parameterized queries with ? placeholders, preventing SQL injection attacks:
db.query(sql, [usuario, usuario], (err, results) => { ... })
db.query(sql, [de, para, mensaje], (err) => { ... })
Sources: src/router.js L244-L271
src/sockets/socketHandler.js L56
Data Exposure¶
The queries select only necessary columns (de_usuario, para_usuario, mensaje, fecha) and do not expose internal IDs or other sensitive metadata.
Sources: src/router.js L237-L264