server for refapp and refbot and other stuff
0

Configure Feed

Select the types of activity you want to include in your feed.

feat: delete single + bulk players, bulk link/unlink to event

alpine (Jul 16, 2026, 8:09 AM +0200) 55ca45a6 e6516e45

+110
+46
src/routes/players.ts
··· 157 157 next(err); 158 158 } 159 159 }); 160 + 161 + // Delete a single player by discordId. 162 + playersRouter.delete('/players/:discordId', requireAuth('referee'), async (req, res, next) => { 163 + try { 164 + const discordId = param(req, 'discordId'); 165 + const doc = await Player.findOneAndDelete({ discordId }); 166 + if (!doc) throw new HttpError(404, 'not_found', 'player not found'); 167 + res.json({ deleted: 1 }); 168 + } 169 + catch (err) { 170 + next(err); 171 + } 172 + }); 173 + 174 + // Bulk-delete players by discordId. 175 + playersRouter.post('/players/bulk-delete', requireAuth('referee'), async (req, res, next) => { 176 + try { 177 + const { discordIds } = req.body as { discordIds?: string[] }; 178 + if (!discordIds?.length) throw new HttpError(400, 'missing_ids', 'discordIds is required'); 179 + const result = await Player.deleteMany({ discordId: { $in: discordIds } }); 180 + res.json({ deleted: result.deletedCount }); 181 + } 182 + catch (err) { 183 + next(err); 184 + } 185 + }); 186 + 187 + // Bulk link/unlink players to the active (or given) event. 188 + playersRouter.post('/players/bulk-event', requireAuth('referee'), async (req, res, next) => { 189 + try { 190 + const { discordIds, eventId, action } = req.body as { discordIds?: string[]; eventId?: string; action?: string }; 191 + if (!discordIds?.length) throw new HttpError(400, 'missing_ids', 'discordIds is required'); 192 + if (action !== 'link' && action !== 'unlink') throw new HttpError(400, 'invalid_action', "action must be 'link' or 'unlink'"); 193 + if (!eventId || !Types.ObjectId.isValid(eventId)) throw new HttpError(400, 'invalid_event_id', 'eventId must be a valid ObjectId'); 194 + 195 + const eventObjId = new Types.ObjectId(eventId); 196 + const update = action === 'link' 197 + ? { $addToSet: { events: eventObjId } } 198 + : { $pull: { events: eventObjId } }; 199 + const result = await Player.updateMany({ discordId: { $in: discordIds } }, update); 200 + res.json({ modified: result.modifiedCount }); 201 + } 202 + catch (err) { 203 + next(err); 204 + } 205 + });
+64
src/routes/routes.test.ts
··· 438 438 assert.equal(res.status, 400); 439 439 }); 440 440 441 + test('DELETE /v1/players/:discordId removes a player', async () => { 442 + await request(app) 443 + .put('/v1/players/998000111/events/0000000000000000000000a1') 444 + .set('Authorization', auth(refereeApiKey)); 445 + const del = await request(app) 446 + .delete('/v1/players/998000111') 447 + .set('Authorization', auth(refereeApiKey)); 448 + assert.equal(del.status, 200); 449 + assert.equal(del.body.deleted, 1); 450 + 451 + const missing = await request(app) 452 + .delete('/v1/players/998000111') 453 + .set('Authorization', auth(refereeApiKey)); 454 + assert.equal(missing.status, 404); 455 + }); 456 + 457 + test('POST /v1/players/bulk-delete removes many players', async () => { 458 + await request(app).put('/v1/players/997000111/events/0000000000000000000000a2').set('Authorization', auth(refereeApiKey)); 459 + await request(app).put('/v1/players/997000222/events/0000000000000000000000a2').set('Authorization', auth(refereeApiKey)); 460 + 461 + const res = await request(app) 462 + .post('/v1/players/bulk-delete') 463 + .set('Authorization', auth(refereeApiKey)) 464 + .send({ discordIds: ['997000111', '997000222'] }); 465 + assert.equal(res.status, 200); 466 + assert.equal(res.body.deleted, 2); 467 + 468 + const empty = await request(app) 469 + .post('/v1/players/bulk-delete') 470 + .set('Authorization', auth(refereeApiKey)) 471 + .send({ discordIds: [] }); 472 + assert.equal(empty.status, 400); 473 + }); 474 + 475 + test('POST /v1/players/bulk-event links and unlinks many players', async () => { 476 + const evt = await request(app) 477 + .post('/v1/events') 478 + .set('Authorization', auth(adminApiKey)) 479 + .send({ name: 'Bulk Event Target' }); 480 + const eventId = evt.body._id; 481 + 482 + await request(app).put('/v1/players/996000111/events/0000000000000000000000a3').set('Authorization', auth(refereeApiKey)); 483 + await request(app).put('/v1/players/996000222/events/0000000000000000000000a3').set('Authorization', auth(refereeApiKey)); 484 + 485 + const link = await request(app) 486 + .post('/v1/players/bulk-event') 487 + .set('Authorization', auth(refereeApiKey)) 488 + .send({ discordIds: ['996000111', '996000222'], eventId, action: 'link' }); 489 + assert.equal(link.status, 200); 490 + assert.ok(link.body.modified >= 2); 491 + 492 + const unlink = await request(app) 493 + .post('/v1/players/bulk-event') 494 + .set('Authorization', auth(refereeApiKey)) 495 + .send({ discordIds: ['996000111', '996000222'], eventId, action: 'unlink' }); 496 + assert.equal(unlink.status, 200); 497 + 498 + const bad = await request(app) 499 + .post('/v1/players/bulk-event') 500 + .set('Authorization', auth(refereeApiKey)) 501 + .send({ discordIds: ['996000111'], eventId, action: 'frobnicate' }); 502 + assert.equal(bad.status, 400); 503 + }); 504 + 441 505 // ---------- Check-in / Approval ---------- 442 506 443 507 test('POST /v1/matches/:id/check-in marks a player as checked in', async () => {