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

u-wave / core / 11980840475

22 Nov 2024 10:04PM UTC coverage: 78.492% (-1.7%) from 80.158%
11980840475

Pull #637

github

goto-bus-stop
ci: add node 22
Pull Request #637: Switch to a relational database

757 of 912 branches covered (83.0%)

Branch coverage included in aggregate %.

2001 of 2791 new or added lines in 52 files covered. (71.69%)

9 existing lines in 7 files now uncovered.

8666 of 11093 relevant lines covered (78.12%)

70.72 hits per line

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

49.2
/src/controllers/users.js
1
import {
1✔
2
  HTTPError,
1✔
3
  PermissionError,
1✔
4
  UserNotFoundError,
1✔
5
} from '../errors/index.js';
1✔
6
import skipIfCurrentDJ from '../utils/skipIfCurrentDJ.js';
1✔
7
import removeFromWaitlist from '../utils/removeFromWaitlist.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 beautifyDuplicateKeyError from '../utils/beautifyDuplicateKeyError.js';
1✔
13
import { muteUser, unmuteUser } from './chat.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) {
×
50
  const { users } = req.uwave;
×
51
  const { id: userID } = req.params;
×
52

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

×
58
  return toItemResponse(user, {
×
59
    url: req.fullUrl,
×
60
  });
×
61
}
×
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) {
×
97
  const { user: moderator } = req;
×
98
  const { id, role } = req.params;
×
99
  const { acl, users } = req.uwave;
×
100

×
NEW
101
  const selfHasRole = moderator.roles.includes('*') || moderator.roles.includes(role);
×
102
  if (!selfHasRole) {
×
103
    throw new PermissionError({ requiredRole: role });
×
104
  }
×
105

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

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

×
113
  return toItemResponse({}, {
×
114
    url: req.fullUrl,
×
115
  });
×
116
}
×
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) {
×
128
  const { user: moderator } = req;
×
129
  const { id, role } = req.params;
×
130
  const { acl, users } = req.uwave;
×
131

×
NEW
132
  const selfHasRole = moderator.roles.includes('*') || moderator.roles.includes(role);
×
133
  if (!selfHasRole) {
×
134
    throw new PermissionError({ requiredRole: role });
×
135
  }
×
136

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

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

×
144
  return toItemResponse({}, {
×
145
    url: req.fullUrl,
×
146
  });
×
147
}
×
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) {
×
161
  const { user: moderator } = req;
×
162
  const { id } = req.params;
×
163
  const { username } = req.body;
×
164
  const { users } = req.uwave;
×
165

×
166
  try {
×
167
    const user = await users.updateUser(
×
168
      id,
×
169
      { username },
×
170
      { moderator },
×
171
    );
×
172

×
173
    return toItemResponse(user);
×
174
  } catch (error) {
×
175
    throw beautifyDuplicateKeyError(error);
×
176
  }
×
177
}
×
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
 * @param {import('../Uwave.js').default} uw
1✔
188
 * @param {UserID} userID
1✔
189
 */
1✔
190
async function disconnectUser(uw, userID) {
×
191
  await skipIfCurrentDJ(uw, userID);
×
192

×
193
  try {
×
194
    await removeFromWaitlist(uw, userID);
×
195
  } catch {
×
196
    // Ignore
×
197
  }
×
198

×
NEW
199
  await uw.redis.lrem('users', 0, userID);
×
200

×
NEW
201
  uw.publish('user:leave', { userID });
×
202
}
×
203

1✔
204
/**
1✔
205
 * @typedef {object} GetHistoryParams
1✔
206
 * @prop {UserID} id
1✔
207
 */
1✔
208

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

×
220
  const user = await uw.users.getUser(id);
×
221
  if (!user) {
×
222
    throw new UserNotFoundError({ id });
×
223
  }
×
224

×
225
  const history = await uw.history.getUserHistory(user, pagination);
×
226

×
227
  return toPaginatedResponse(history, {
×
228
    baseUrl: req.fullUrl,
×
229
    included: {
×
230
      media: ['media.media'],
×
231
      user: ['user'],
×
232
    },
×
233
  });
×
234
}
×
235

1✔
236
export {
1✔
237
  getUsers,
1✔
238
  getUser,
1✔
239
  getUserRoles,
1✔
240
  addUserRole,
1✔
241
  removeUserRole,
1✔
242
  changeUsername,
1✔
243
  changeAvatar,
1✔
244
  disconnectUser,
1✔
245
  getHistory,
1✔
246
  muteUser,
1✔
247
  unmuteUser,
1✔
248
};
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