@@ -33,6 +33,7 @@ import { generateDocument } from "../../lib/document";
3333import { generateSnowflake } from "../../lib/snowflake" ;
3434import { useValidation } from "../../middlewares/useValidation" ;
3535import {
36+ hasContestPermission as requestHasContestPermission ,
3637 hasOrganisationPermission ,
3738 mustHaveContestPermission ,
3839 mustHaveCurrentOrganisationPermission ,
@@ -430,88 +431,106 @@ ContestHandler.get("/members/self", async (req, res) => {
430431 ) ;
431432} ) ;
432433
433- ContestHandler . get ( "/:contest_id/leaderboard" , async ( req , res ) => {
434- const contest = await extractContest ( req ) ;
434+ const LeaderboardQuerySchema = Type . Object ( {
435+ show_all_users : Type . Optional ( Type . Union ( [ Type . Literal ( "true" ) , Type . Literal ( "false" ) ] ) ) ,
436+ } ) ;
435437
436- if (
437- ( ! contest . show_leaderboard_during_contest || ! isContestRunning ( contest ) ) &&
438- ! isContestOver ( contest )
439- ) {
440- await mustHaveContestPermission ( req , ContestMemberPermissions . VIEW_PRIVATE , contest . id ) ;
441- }
438+ ContestHandler . get (
439+ "/:contest_id/leaderboard" ,
440+ useValidation ( LeaderboardQuerySchema , { query : true } ) ,
441+ async ( req , res ) => {
442+ const contest = await extractContest ( req ) ;
442443
443- const _contestMembers = await Database . selectFrom ( "contest_members" , "*" , {
444- contest_id : contest . id ,
445- } ) ;
446-
447- const users = (
448- await Promise . all (
449- R . chunk ( _contestMembers , 100 ) . map ( ( chunk ) => {
450- return Database . selectFrom ( "users" , "*" , {
451- id : eqIn ( ...chunk . map ( ( it ) => it . user_id ) ) ,
452- } ) ;
453- } )
454- )
455- ) . flat ( ) ;
444+ if (
445+ ( ! contest . show_leaderboard_during_contest || ! isContestRunning ( contest ) ) &&
446+ ! isContestOver ( contest )
447+ ) {
448+ await mustHaveContestPermission ( req , ContestMemberPermissions . VIEW_PRIVATE , contest . id ) ;
449+ }
456450
457- // if for every contestMember doesn't exist a corresponding user
458- if ( ! _contestMembers . every ( ( it ) => users . some ( ( user ) => user . id === it . user_id ) ) )
459- throw new SafeError ( StatusCodes . INTERNAL_SERVER_ERROR ) ;
451+ const _contestMembers = await Database . selectFrom ( "contest_members" , "*" , {
452+ contest_id : contest . id ,
453+ } ) ;
460454
461- const contestMembers = _contestMembers . filter (
462- ( it , _ , __ , user = users . find ( ( user ) => user . id === it . user_id ) ! ) =>
463- ! hasContestPermission (
464- it . contest_permissions ,
465- ContestMemberPermissions . VIEW_PRIVATE ,
466- user . permissions
455+ const users = (
456+ await Promise . all (
457+ R . chunk ( _contestMembers , 100 ) . map ( ( chunk ) => {
458+ return Database . selectFrom ( "users" , "*" , {
459+ id : eqIn ( ...chunk . map ( ( it ) => it . user_id ) ) ,
460+ } ) ;
461+ } )
467462 )
468- ) ;
463+ ) . flat ( ) ;
469464
470- const eduUsers = (
471- await Promise . all (
472- R . chunk ( contestMembers , 100 ) . map ( ( chunk ) => {
473- return Database . selectFrom ( "edu_users" , "*" , {
474- id : eqIn ( ...chunk . map ( ( it ) => it . user_id ) ) ,
475- } ) ;
476- } )
477- )
478- ) . flat ( ) ;
465+ // if for every contestMember doesn't exist a corresponding user
466+ if ( ! _contestMembers . every ( ( it ) => users . some ( ( user ) => user . id === it . user_id ) ) )
467+ throw new SafeError ( StatusCodes . INTERNAL_SERVER_ERROR ) ;
479468
480- const organisationMembers = await Database . selectFrom (
481- "organisation_members" ,
482- "*" ,
483- {
484- organisation_id : contest . organisation_id ,
485- } ,
486- "ALLOW FILTERING"
487- ) ;
488-
489- return respond (
490- res ,
491- StatusCodes . OK ,
492- contestMembers
493- . map (
494- (
495- it ,
496- _ ,
497- __ ,
498- user = users . find ( ( user ) => user . id === it . user_id ) ! ,
499- eduUser = eduUsers . find ( ( user ) => user . id === it . user_id )
500- ) => ( {
501- ...it ,
502- ...R . pick (
503- organisationMembers . find ( ( member ) => member . user_id === it . user_id ) ! ,
504- [ "elo" ]
505- ) ,
506- full_name :
507- ( contest . require_edu_verification && eduUser ?. full_name ) || user . full_name ,
508- email_domain : user . email . split ( "@" ) . at ( - 1 ) ,
509- edu_mail_domain : eduUser ?. email . split ( "@" ) . at ( - 1 ) ,
469+ const showAll =
470+ req . query . show_all_users === "true" &&
471+ ( await requestHasContestPermission (
472+ req ,
473+ ContestMemberPermissions . VIEW_PRIVATE ,
474+ contest . id
475+ ) . catch ( ( ) => false ) ) ;
476+
477+ const contestMembers = _contestMembers . filter (
478+ ( it , _ , __ , user = users . find ( ( user ) => user . id === it . user_id ) ! ) =>
479+ showAll ||
480+ ! hasContestPermission (
481+ it . contest_permissions ,
482+ ContestMemberPermissions . VIEW_PRIVATE ,
483+ user . permissions
484+ )
485+ ) ;
486+
487+ const eduUsers = (
488+ await Promise . all (
489+ R . chunk ( contestMembers , 100 ) . map ( ( chunk ) => {
490+ return Database . selectFrom ( "edu_users" , "*" , {
491+ id : eqIn ( ...chunk . map ( ( it ) => it . user_id ) ) ,
492+ } ) ;
510493 } )
511494 )
512- . map ( ( it ) => ( { ...it , score : it . score ?? { } } ) )
513- ) ;
514- } ) ;
495+ ) . flat ( ) ;
496+
497+ const organisationMembers = await Database . selectFrom (
498+ "organisation_members" ,
499+ "*" ,
500+ {
501+ organisation_id : contest . organisation_id ,
502+ } ,
503+ "ALLOW FILTERING"
504+ ) ;
505+
506+ return respond (
507+ res ,
508+ StatusCodes . OK ,
509+ contestMembers
510+ . map (
511+ (
512+ it ,
513+ _ ,
514+ __ ,
515+ user = users . find ( ( user ) => user . id === it . user_id ) ! ,
516+ eduUser = eduUsers . find ( ( user ) => user . id === it . user_id )
517+ ) => ( {
518+ ...it ,
519+ ...R . pick (
520+ organisationMembers . find ( ( member ) => member . user_id === it . user_id ) ! ,
521+ [ "elo" ]
522+ ) ,
523+ full_name :
524+ ( contest . require_edu_verification && eduUser ?. full_name ) ||
525+ user . full_name ,
526+ email_domain : user . email . split ( "@" ) . at ( - 1 ) ,
527+ edu_mail_domain : eduUser ?. email . split ( "@" ) . at ( - 1 ) ,
528+ } )
529+ )
530+ . map ( ( it ) => ( { ...it , score : it . score ?? { } } ) )
531+ ) ;
532+ }
533+ ) ;
515534
516535ContestHandler . get ( "/:contest_id" , async ( req , res ) => {
517536 const contest = await extractContest ( req ) ;
0 commit comments