1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
|
import express from 'express';
import * as roomCtrl from '../controllers/roomController.js';
import { authenticate } from '../middleware/auth.js';
const router = express.Router();
// Apply auth to all room routes
router.use(authenticate);
const NAME_MAX = 100;
const DEFAULT_LIMIT = 50;
const MAX_LIMIT = 200;
function isValidName(name) {
if (name === undefined || name === null) {
return true;
}
if (typeof name !== 'string') {
return false;
}
return name.length <= NAME_MAX;
}
function isValidType(type) {
if (type === undefined || type === null) {
return true;
}
if (type !== 'group' && type !== 'direct') {
return false;
}
return true;
}
function isValidMembers(members) {
if (members === undefined || members === null) {
return true;
}
if (!Array.isArray(members)) {
return false;
}
for (let i = 0; i < members.length; i++) {
if (typeof members[i] !== 'string') {
return false;
}
}
return true;
}
// Search rooms by name
router.get('/search', async function(req, res) {
const q = req.query.q;
if (typeof q !== 'string' || q.length < 1) {
return res.status(400).json({ error: 'invalid_query' });
}
const results = await roomCtrl.searchRooms({ q: q });
res.json(results);
});
// List rooms with pagination
router.get('/', async function(req, res) {
let limit = parseInt(req.query.limit, 10);
let offset = parseInt(req.query.offset, 10);
if (isNaN(limit) || limit < 1) {
limit = DEFAULT_LIMIT;
}
if (limit > MAX_LIMIT) {
limit = MAX_LIMIT;
}
if (isNaN(offset) || offset < 0) {
offset = 0;
}
const filters = {};
if (req.query.type) {
filters.type = req.query.type;
}
if (req.query.memberId) {
filters.memberId = req.query.memberId;
}
const rooms = await roomCtrl.listRooms({ limit: limit, offset: offset, filters: filters });
res.json(rooms);
});
// Get room by id
router.get('/:id', async function(req, res) {
const room = await roomCtrl.getRoomById(req.params.id);
if (!room) {
return res.status(404).json({ error: 'not_found' });
}
res.json(room);
});
// Get room messages (chat history)
router.get('/:id/messages', async function(req, res) {
let limit = parseInt(req.query.limit, 10);
let offset = parseInt(req.query.offset, 10);
if (isNaN(limit) || limit < 1) {
limit = DEFAULT_LIMIT;
}
if (limit > MAX_LIMIT) {
limit = MAX_LIMIT;
}
if (isNaN(offset) || offset < 0) {
offset = 0;
}
const room = await roomCtrl.getRoomById(req.params.id);
if (!room) {
return res.status(404).json({ error: 'room_not_found' });
}
const messages = await roomCtrl.getRoomMessages(req.params.id, { limit: limit, offset: offset });
res.json(messages);
});
// Create room
router.post('/', async function(req, res) {
const name = req.body.name;
const type = req.body.type;
const members = req.body.members;
if (!isValidName(name)) {
return res.status(400).json({ error: 'invalid_name' });
}
if (!isValidType(type)) {
return res.status(400).json({ error: 'invalid_type' });
}
if (!isValidMembers(members)) {
return res.status(400).json({ error: 'invalid_members' });
}
const result = await roomCtrl.createRoom({ name: name, type: type, members: members });
if (result.error === 'member_not_found') {
return res.status(400).json({ error: 'member_not_found', memberId: result.memberId });
}
res.status(201).json(result);
});
// Update room
router.put('/:id', async function(req, res) {
const name = req.body.name;
const type = req.body.type;
if (!isValidName(name)) {
return res.status(400).json({ error: 'invalid_name' });
}
if (!isValidType(type)) {
return res.status(400).json({ error: 'invalid_type' });
}
const updates = {};
if (name !== undefined) {
updates.name = name;
}
if (type !== undefined) {
updates.type = type;
}
const room = await roomCtrl.updateRoom(req.params.id, updates);
if (!room) {
return res.status(404).json({ error: 'not_found' });
}
res.json(room);
});
// Add member to room
router.post('/:id/members', async function(req, res) {
const userId = req.body.userId;
if (typeof userId !== 'string' || userId.length < 1) {
return res.status(400).json({ error: 'invalid_user_id' });
}
const room = await roomCtrl.addMember(req.params.id, userId);
if (!room) {
return res.status(404).json({ error: 'not_found' });
}
res.json(room);
});
// Remove member from room
router.delete('/:id/members/:userId', async function(req, res) {
const room = await roomCtrl.removeMember(req.params.id, req.params.userId);
if (!room) {
return res.status(404).json({ error: 'not_found' });
}
res.json(room);
});
// Delete room
router.delete('/:id', async function(req, res) {
const ok = await roomCtrl.deleteRoom(req.params.id);
if (!ok) {
return res.status(404).json({ error: 'not_found' });
}
res.status(204).send();
});
export default router;
|