Skip to content

Commit 0572397

Browse files
committed
Direct-to-Landlord Chat with Archiving (#39)
1 parent b194901 commit 0572397

File tree

5 files changed

+106
-0
lines changed

5 files changed

+106
-0
lines changed

src/controllers/chat.controller.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
const chatService = require('../services/chat.service');
2+
3+
class ChatController {
4+
async sendMessage(req, res) {
5+
try {
6+
const { receiverId, content } = req.body;
7+
const senderId = req.user.id;
8+
const message = await chatService.sendMessage(senderId, receiverId, content);
9+
res.status(201).json(message);
10+
} catch (err) {
11+
res.status(500).json({ error: err.message });
12+
}
13+
}
14+
15+
async listMessages(req, res) {
16+
try {
17+
const otherPartyId = req.params.id;
18+
const userId = req.user.id;
19+
const messages = await chatService.listMessages(userId, otherPartyId);
20+
res.json(messages);
21+
} catch (err) {
22+
res.status(500).json({ error: err.message });
23+
}
24+
}
25+
}
26+
27+
module.exports = new ChatController();

src/routes/chat.routes.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
const express = require('express');
2+
const router = express.Router();
3+
const chatController = require('../controllers/chat.controller');
4+
const { authMiddleware } = require('../services/auth.service');
5+
6+
router.post('/chat/message', authMiddleware, (req, res) => chatController.sendMessage(req, res));
7+
router.get('/chat/messages/:id', authMiddleware, (req, res) => chatController.listMessages(req, res));
8+
9+
module.exports = router;

src/services/chat.service.js

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
const db = require('../db');
2+
const crypto = require('crypto');
3+
4+
class ChatService {
5+
async sendMessage(senderId, receiverId, content) {
6+
const hash = crypto.createHash('sha256').update(content + Date.now()).digest('hex');
7+
8+
const [message] = await db('chat_messages')
9+
.insert({
10+
senderId,
11+
receiverId,
12+
content,
13+
hash,
14+
createdAt: new Date()
15+
})
16+
.returning('*');
17+
18+
return message;
19+
}
20+
21+
async listMessages(userId, otherPartyId) {
22+
return db('chat_messages')
23+
.where(function () {
24+
this.where({ senderId: userId, receiverId: otherPartyId })
25+
.orWhere({ senderId: otherPartyId, receiverId: userId });
26+
})
27+
.orderBy('createdAt', 'asc');
28+
}
29+
}
30+
31+
module.exports = new ChatService();

src/websockets/chat.gateway.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
const { Server } = require('socket.io');
2+
const chatService = require('../services/chat.service');
3+
4+
let io;
5+
6+
function initChatGateway(server) {
7+
io = new Server(server, { path: '/ws/chat' });
8+
9+
io.on('connection', (socket) => {
10+
socket.on('send_message', async ({ senderId, receiverId, content }) => {
11+
const message = await chatService.sendMessage(senderId, receiverId, content);
12+
io.to(receiverId).emit('new_message', message);
13+
io.to(senderId).emit('message_sent', message);
14+
});
15+
});
16+
}
17+
18+
module.exports = { initChatGateway };

tests/chat.e2e-spec.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
const request = require('supertest');
2+
const app = require('../index');
3+
4+
describe('Direct-to-Landlord Chat API', () => {
5+
it('POST /chat/message archives and hashes message', async () => {
6+
const res = await request(app)
7+
.post('/chat/message')
8+
.set('Authorization', 'Bearer tenantToken')
9+
.send({ receiverId: 'landlord-123', content: 'Hello landlord' });
10+
expect(res.status).toBe(201);
11+
expect(res.body).toHaveProperty('hash');
12+
});
13+
14+
it('GET /chat/messages/:id returns full conversation', async () => {
15+
const res = await request(app)
16+
.get('/chat/messages/landlord-123')
17+
.set('Authorization', 'Bearer tenantToken');
18+
expect(res.status).toBe(200);
19+
expect(Array.isArray(res.body)).toBe(true);
20+
});
21+
});

0 commit comments

Comments
 (0)