• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

u-wave / core / 12197793619

06 Dec 2024 11:12AM UTC coverage: 84.385% (+0.03%) from 84.36%
12197793619

Pull #682

github

goto-bus-stop
lint
Pull Request #682: Improve session handling

911 of 1089 branches covered (83.65%)

Branch coverage included in aggregate %.

85 of 122 new or added lines in 10 files covered. (69.67%)

50 existing lines in 2 files now uncovered.

9881 of 11700 relevant lines covered (84.45%)

92.82 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

75.46
/src/controllers/users.js
1
import {
1✔
2
  HTTPError,
1✔
3
  PermissionError,
1✔
4
  UserNotFoundError,
1✔
5
  UserNotInWaitlistError,
1✔
6
} from '../errors/index.js';
1✔
7
import skipIfCurrentDJ from '../utils/skipIfCurrentDJ.js';
1✔
8
import getOffsetPagination from '../utils/getOffsetPagination.js';
1✔
9
import toItemResponse from '../utils/toItemResponse.js';
1✔
10
import toListResponse from '../utils/toListResponse.js';
1✔
11
import toPaginatedResponse from '../utils/toPaginatedResponse.js';
1✔
12
import { muteUser, unmuteUser } from './chat.js';
1✔
13
import { REDIS_ACTIVE_SESSIONS } from '../SocketServer.js';
1✔
14

1✔
15
/**
1✔
16
 * @typedef {import('../schema').UserID} UserID
1✔
17
 */
1✔
18

1✔
19
/**
1✔
20
 * @typedef {object} GetUsersQuery
1✔
21
 * @prop {string} filter
1✔
22
 */
1✔
23

1✔
24
/**
1✔
25
 * @type {import('../types.js').AuthenticatedController<{}, GetUsersQuery>}
1✔
26
 */
1✔
27
async function getUsers(req) {
1✔
28
  const { filter } = req.query;
1✔
29
  const pagination = getOffsetPagination(req.query, {
1✔
30
    defaultSize: 50,
1✔
31
  });
1✔
32
  const { users } = req.uwave;
1✔
33

1✔
34
  const userList = await users.getUsers(filter, pagination);
1✔
35

1✔
36
  return toPaginatedResponse(userList, {
1✔
37
    baseUrl: req.fullUrl,
1✔
38
  });
1✔
39
}
1✔
40

1✔
41
/**
1✔
42
 * @typedef {object} GetUserParams
1✔
43
 * @prop {UserID} id
1✔
44
 */
1✔
45

1✔
46
/**
1✔
47
 * @type {import('../types.js').Controller<GetUserParams>}
1✔
48
 */
1✔
49
async function getUser(req) {
4✔
50
  const { users } = req.uwave;
4✔
51
  const { id: userID } = req.params;
4✔
52

4✔
53
  const user = await users.getUser(userID);
4✔
54
  if (!user) {
4!
55
    throw new UserNotFoundError({ id: userID });
×
56
  }
×
57

4✔
58
  return toItemResponse(user, {
4✔
59
    url: req.fullUrl,
4✔
60
  });
4✔
61
}
4✔
62

1✔
63
/**
1✔
64
 * @typedef {object} GetUserRolesParams
1✔
65
 * @prop {UserID} id
1✔
66
 */
1✔
67

1✔
68
/**
1✔
69
 * @type {import('../types.js').Controller<GetUserRolesParams>}
1✔
70
 */
1✔
71
async function getUserRoles(req) {
×
72
  const { acl, users } = req.uwave;
×
73
  const { id } = req.params;
×
74

×
75
  const user = await users.getUser(id);
×
76
  if (!user) {
×
77
    throw new UserNotFoundError({ id });
×
78
  }
×
79

×
80
  const roles = await acl.getAllPermissions(user);
×
81

×
82
  return toListResponse(roles, {
×
83
    url: req.fullUrl,
×
84
  });
×
85
}
×
86

1✔
87
/**
1✔
88
 * @typedef {object} AddUserRoleParams
1✔
89
 * @prop {UserID} id
1✔
90
 * @prop {string} role
1✔
91
 */
1✔
92

1✔
93
/**
1✔
94
 * @type {import('../types.js').AuthenticatedController<AddUserRoleParams>}
1✔
95
 */
1✔
96
async function addUserRole(req) {
3✔
97
  const { user: moderator } = req;
3✔
98
  const { id, role } = req.params;
3✔
99
  const { acl, users } = req.uwave;
3✔
100

3✔
101
  const canModifyRoles = moderator.roles.includes('admin');
3✔
102
  if (!canModifyRoles) {
3!
103
    throw new PermissionError({ requiredRole: 'admin' });
×
104
  }
×
105

3✔
106
  const user = await users.getUser(id);
3✔
107
  if (!user) {
3✔
108
    throw new UserNotFoundError({ id });
1✔
109
  }
1✔
110

2✔
111
  await acl.allow(user, [role]);
2✔
112

1✔
113
  return toItemResponse({}, {
1✔
114
    url: req.fullUrl,
1✔
115
  });
1✔
116
}
3✔
117

1✔
118
/**
1✔
119
 * @typedef {object} RemoveUserRoleParams
1✔
120
 * @prop {UserID} id
1✔
121
 * @prop {string} role
1✔
122
 */
1✔
123

1✔
124
/**
1✔
125
 * @type {import('../types.js').AuthenticatedController<RemoveUserRoleParams>}
1✔
126
 */
1✔
127
async function removeUserRole(req) {
2✔
128
  const { user: moderator } = req;
2✔
129
  const { id, role } = req.params;
2✔
130
  const { acl, users } = req.uwave;
2✔
131

2✔
132
  const canModifyRoles = moderator.roles.includes('admin');
2✔
133
  if (!canModifyRoles) {
2!
134
    throw new PermissionError({ requiredRole: 'admin' });
×
135
  }
×
136

2✔
137
  const user = await users.getUser(id);
2✔
138
  if (!user) {
2✔
139
    throw new UserNotFoundError({ id });
1✔
140
  }
1✔
141

1✔
142
  await acl.disallow(user, [role]);
1✔
143

1✔
144
  return toItemResponse({}, {
1✔
145
    url: req.fullUrl,
1✔
146
  });
1✔
147
}
2✔
148

1✔
149
/**
1✔
150
 * @typedef {object} ChangeUsernameParams
1✔
151
 * @prop {UserID} id
1✔
152
 * @typedef {object} ChangeUsernameBody
1✔
153
 * @prop {string} username
1✔
154
 */
1✔
155

1✔
156
/**
1✔
157
 * @type {import('../types.js').AuthenticatedController<
1✔
158
 *     ChangeUsernameParams, {}, ChangeUsernameBody>}
1✔
159
 */
1✔
160
async function changeUsername(req) {
5✔
161
  const { user: moderator } = req;
5✔
162
  const { id } = req.params;
5✔
163
  const { username } = req.body;
5✔
164
  const { users } = req.uwave;
5✔
165

5✔
166
  if (id !== moderator.id) {
5✔
167
    throw new PermissionError();
1✔
168
  }
1✔
169

4✔
170
  const user = await users.updateUser(
4✔
171
    id,
4✔
172
    { username },
4✔
173
    { moderator },
4✔
174
  );
4✔
175

3✔
176
  return toItemResponse(user);
3✔
177
}
5✔
178

1✔
179
/**
1✔
180
 * @returns {Promise<import('type-fest').JsonObject>}
1✔
181
 */
1✔
182
async function changeAvatar() {
×
183
  throw new HTTPError(500, 'Not implemented');
×
184
}
×
185

1✔
186
/**
1✔
187
 * Remove the user ID from the online users list.
1✔
188
 *
1✔
189
 * @param {import('../Uwave.js').default} uw
1✔
190
 * @param {UserID} userID
1✔
191
 */
1✔
192
async function disconnectUser(uw, userID) {
×
193
  await skipIfCurrentDJ(uw, userID);
×
194

×
195
  try {
×
196
    await uw.waitlist.removeUser(userID);
×
197
  } catch (err) {
×
198
    // It's expected that the user would not be in the waitlist
×
199
    if (!(err instanceof UserNotInWaitlistError)) {
×
200
      throw err;
×
201
    }
×
202
  }
×
203

×
NEW
204
  await uw.redis.lrem(REDIS_ACTIVE_SESSIONS, 0, userID);
×
205

×
206
  uw.publish('user:leave', { userID });
×
207
}
×
208

1✔
209
/**
1✔
210
 * @typedef {object} GetHistoryParams
1✔
211
 * @prop {UserID} id
1✔
212
 */
1✔
213

1✔
214
/**
1✔
215
 * @type {import('../types.js').Controller<GetHistoryParams>}
1✔
216
 */
1✔
217
async function getHistory(req) {
×
218
  const { id } = req.params;
×
219
  const pagination = getOffsetPagination(req.query, {
×
220
    defaultSize: 25,
×
221
    maxSize: 100,
×
222
  });
×
223
  const uw = req.uwave;
×
224

×
225
  const user = await uw.users.getUser(id);
×
226
  if (!user) {
×
227
    throw new UserNotFoundError({ id });
×
228
  }
×
229

×
230
  const history = await uw.history.getUserHistory(user, pagination);
×
231

×
232
  return toPaginatedResponse(history, {
×
233
    baseUrl: req.fullUrl,
×
234
    included: {
×
235
      media: ['media.media'],
×
236
      user: ['user'],
×
237
    },
×
238
  });
×
239
}
×
240

1✔
241
export {
1✔
242
  getUsers,
1✔
243
  getUser,
1✔
244
  getUserRoles,
1✔
245
  addUserRole,
1✔
246
  removeUserRole,
1✔
247
  changeUsername,
1✔
248
  changeAvatar,
1✔
249
  disconnectUser,
1✔
250
  getHistory,
1✔
251
  muteUser,
1✔
252
  unmuteUser,
1✔
253
};
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc