From d92c95eb3b18531f15ca0661f82c781a16f9760e Mon Sep 17 00:00:00 2001 From: JamesCai <160403721+JamesCai-QwQ@users.noreply.github.com> Date: Sun, 13 Oct 2024 22:32:05 +0800 Subject: [PATCH] feat: Update contest team and manage routes (#1449) * Add graphql functions in hasura/contest.ts and Add routes in routes/contest.ts offerred to frontend * add annotations for hasura/contest.ts * change start/end data type (from string to Date) * update routes/contests.ts, add response info (message and stack) * delete inappropriate route and classify routes: 1.contest management 2.contest team * delete useless routes in team.ts * fix errors: delete all defined but never used imports * fix contest.ts * update contest.ts ( fix return expressions ) * delete 'AuthenticationError' * add authenticate params in src/routes/team.ts as well as manage.ts * update routes and gqls to make params in update operations optional * fix errors in contest.ts * fix error in dynamic params and improved several points * fix errors in /manage.ts --- src/app.ts | 8 +- src/hasura/contest.ts | 976 +++++++++++++++++++++++++++++++++++++++++- src/routes/manage.ts | 342 +++++++++++++++ src/routes/team.ts | 205 +++++++++ 4 files changed, 1524 insertions(+), 7 deletions(-) create mode 100644 src/routes/manage.ts create mode 100644 src/routes/team.ts diff --git a/src/app.ts b/src/app.ts index e1d330f5..8ae23e66 100644 --- a/src/app.ts +++ b/src/app.ts @@ -9,8 +9,10 @@ import docsRouter from "./routes/docs"; import applicationRouter from "./routes/application"; import fileRouter from "./routes/files"; import codeRouter from "./routes/code"; -// import roomRouter from "./routes/room"; // import contestRouter from "./routes/contest"; +// import roomRouter from "./routes/room"; +import teamRouter from "./routes/team"; +import manageRouter from "./routes/manage"; import arenaRouter from "./routes/arena"; import competitionRouter from "./routes/competition"; import notificationRouter from "./routes/notification"; @@ -46,8 +48,10 @@ app.use("/docs", docsRouter); app.use("/application", applicationRouter); app.use("/files",fileRouter); app.use("/code", codeRouter); -// app.use("/room", roomRouter); // app.use("/contest", contestRouter); +// app.use("/room", roomRouter); +app.use("/team", teamRouter); +app.use("/manage", manageRouter); app.use("/arena", arenaRouter); app.use("/competition", competitionRouter); app.use("/notification", notificationRouter); diff --git a/src/hasura/contest.ts b/src/hasura/contest.ts index 67195394..434468db 100644 --- a/src/hasura/contest.ts +++ b/src/hasura/contest.ts @@ -1,8 +1,6 @@ import { gql } from "graphql-request"; import { client } from ".."; - - /** ============================================================================ ============================ QUERY FUNCTIONS =============================== @@ -777,17 +775,314 @@ export const insert_room_teams: any = async (room_id: string, team_ids: Array} + */ +export const add_contest_map:any = async(contest_id:string, name:string, filename:string, team_labels:string) => { + const add_contest_map:any = await client.request( + gql` + mutation AddContestMap( + $contest_id: uuid! + $name: String! + $filename: String! + $team_labels: String! + ) { + insert_contest_map_one( + object: { + contest_id: $contest_id + name: $name + filename: $filename + team_labels: $team_labels + } + ) { + map_id + } + } + `, + { + contest_id: contest_id, + name: name, + filename: filename, + team_labels: team_labels + }); + return add_contest_map.insert_contest_map_one?.map_id ?? undefined; +} +/** + * + * @param {string} title + * @param {string} content + * @param {string} files + * @param {string} contest_id + * @returns {Promise} + */ +export const add_contest_notice:any = async(title:string,content:string,files:string,contest_id:string) => { + const add_contest_notice:any = await client.request( + gql` + mutation AddContestNotice( + $title: String! + $content: String! + $files: String + $contest_id: uuid! + ) { + insert_contest_notice_one( + object: { + title: $title + content: $content + files: $files + contest_id: $contest_id + } + ) { + id + } + } + `, + { + title:title, + content:content, + files:files, + contest_id:contest_id + } + ) + return add_contest_notice.insert_contest_notice_one?.id?? undefined; +} +/** + * + * @param {string} contest_id + * @param {string} team_label + * @param {string} player_label + * @param {string} roles_available + * @returns {Promise} + */ +export const add_contest_player:any = async(contest_id:string,team_label:string,player_label:string,roles_available:string) =>{ + const add_contest_player:any = await client.request( + gql` + mutation AddContestPlayer( + $contest_id: uuid! + $team_label: String! + $player_label: String! + $roles_available: String! + ) { + insert_contest_player_one( + object: { + contest_id: $contest_id + team_label: $team_label + player_label: $player_label + roles_available: $roles_available + } + ) { + team_label + } + } + `, + { + contest_id: contest_id, + team_label: team_label, + player_label: player_label, + roles_available: roles_available + }); + return add_contest_player.insert_contest_player_one?.team_label?? undefined; +} +/** + * + * @param {string} contest_id ID + * @param {string} name + * @param {string} map_id ID + * @returns {Promise} ID + */ +export const add_contest_round:any = async(contest_id:string,name:string,map_id:string)=>{ + const add_contest_round:any = await client.request( + gql` + mutation AddContestRound($contest_id: uuid!, $name: String!, $map_id: uuid) { + insert_contest_round_one( + object: { contest_id: $contest_id, name: $name, map_id: $map_id } + ) { + round_id + } + } + `, + { + contest_id: contest_id, + name: name, + map_id: map_id + }); + return add_contest_round.insert_contest_round_one?.round_id?? undefined; +} +/** + * + * @param {string} team_id ID + * @param {string} code_name + * @param {string} language + * @param {string} compile_status + * @returns {Promise} ID + */ +export const add_team_code:any = async(team_id:string,code_name:string,language:string,compile_status:string) =>{ + const add_team_code:any = await client.request( + gql` + mutation AddTeamCode( + $team_id: uuid! + $code_name: String! + $language: String! + $compile_status: String + ) { + insert_contest_team_code_one( + object: { + team_id: $team_id + code_name: $code_name + language: $language + compile_status: $compile_status + } + ) { + code_id + } + } + `, + { + team_id: team_id, + code_name: code_name, + compile_status: compile_status, + }); + return add_team_code.insert_contest_team_code_one?.code_id?? undefined; + } +/** + * + * @param {string} team_id ID + * @param {string} player + * @returns {Promise} + */ +export const add_team_player:any = async(team_id:string,player:string) =>{ + const add_team_player:any = await client.request( + gql` + mutation AddTeamPlayer($team_id: uuid!, $player: String!) { + insert_contest_team_player_one( + object: { team_id: $team_id, player: $player } + ) { + player + } + } + `, + { + team_id:team_id, + player:player + }); + return add_team_player.insert_contest_team_player_one?.player?? undefined; +} +/** + * + * @param {string} team_name + * @param {string} team_intro + * @param {string} team_leader_uuid UUID + * @param {string} invited_code + * @param {string} contest_id ID + * @returns {Promise} ID + */ +export const add_team:any = async(team_name:string,team_intro:string,team_leader_uuid:string,invited_code:string,contest_id:string) =>{ + const add_team:any = await client.request( + gql` + mutation AddTeam( + $team_name: String! + $team_intro: String = "" # 此处的intro可以为NULL + $team_leader_uuid: uuid! # team_leader的uuid + $invited_code: String! + $contest_id: uuid! # 是必填的项 + ) { + insert_contest_team_one( + object: { + team_name: $team_name + team_intro: $team_intro + team_leader_uuid: $team_leader_uuid + invited_code: $invited_code + contest_id: $contest_id + contest_team_members: { data: { user_uuid: $team_leader_uuid } } + } + ) { + team_id + } + }`, + { + team_name: team_name, + team_intro: team_intro, + team_leader_uuid: team_leader_uuid, + invited_code: invited_code, + contest_id: contest_id + }); + return add_team.insert_contest_team_one?.team_id ?? undefined; + } +/** + * + * @param {string} team_id ID + * @param {string} user_uuid UUID + * @returns {Promise} ID + */ +export const add_team_member:any = async(team_id:string,user_uuid:string) =>{ + const add_team_member:any = await client.request( + gql` + mutation AddTeamMember($team_id: uuid!, $user_uuid: uuid!) { + insert_contest_team_member_one( + object: { team_id: $team_id, user_uuid: $user_uuid } + ) { + team_id + } + } + `, + { + team_id: team_id, + user_uuid: user_uuid + }); + return add_team_member.insert_contest_team_member_one?.team_id?? undefined; + } - - - +/** + * + * @param {uuid} contest_id ID + * @param {string} event + * @param {timestamptz} start + * @param {timestamptz} end + * @param {string} description + * @returns {Promise} + */ +export const add_contest_time:any = async(contest_id:string,event:string,start:Date,end:Date,description:string) =>{ + const add_contest_time:any = await client.request( + gql` + mutation AddContestTime( + $contest_id: uuid! + $event: String! + $start: timestamptz! + $end: timestamptz! + $description: String + ) { + insert_contest_time_one( + object: { + contest_id: $contest_id + event: $event + start: $start + end: $end + description: $description + } + ) { + event + } + }`, + { + contest_id: contest_id, + event: event, + start: new Date(start), + end: new Date(end), + description: description + }); + return add_contest_time.insert_contest_time_one?.event?? undefined; + } @@ -966,17 +1261,474 @@ export const update_room_created_at: any = async (room_id: string, created_at: s return update_room_created_at.update_contest_room_by_pk.created_at; } +/** + * Updates the contest information. + * + * @param {string} contest_id The ID of the contest to update. + * @param {string} fullname The new full name of the contest. + * @param {string} description The new description of the contest. + * @param {Date} start_date The new start date of the contest (timestamp). + * @param {Date} end_date The new end date of the contest (timestamp). + * @returns {string} The ID of the updated contest. + */ +export const update_contest_info:any = async(contest_id: string, updateFields:Partial<{fullname: string; description: string; start_date: Date;end_date: Date}>) => { + + const setFields:{[key:string]:any} = {}; + if (updateFields.fullname) setFields.fullname = updateFields.fullname; + if (updateFields.description) setFields.description = updateFields.description; + if (updateFields.start_date) setFields.start_date = updateFields.start_date; + if (updateFields.end_date) setFields.end_date = updateFields.end_date; + + if (Object.keys(setFields).length === 0) { + console.error("At least update one feature"); + return undefined; + } + + const setString = Object.keys(setFields) + .map(key => `${key}: $${key}`) + .join(', '); + + const variableString = Object.keys(setFields) + .map(key => `$${key}: String`) + .join(', '); + + const mutation = gql` + mutation UpdateContest($contest_id: uuid!, ${variableString}) { + update_contest_by_pk(pk_columns: { id: $contest_id }, _set: { ${setString} }) { + id + fullname + description + start_date + end_date + } + } + `; + + const variables:{[key:string]:any} = { + contest_id:contest_id + } + if(setFields.fullname) variables.fullname = setFields.fullname; + if(setFields.description) variables.description = setFields.description; + if(setFields.start_date) variables.start_date = setFields.start_date; + if(setFields.end_date) variables.end_date = setFields.end_date; + + try { + const response: any = await client.request(mutation, variables); + return response.update_contest_by_pk?.id ?? undefined; + } catch (error) { + console.error('Error updating contest info', error); + throw error; + } +} + +/** + * Updates the contest switches. + * + * @param {string} contest_id The ID of the contest to update. + * @param {boolean} team_switch The new state of the team switch. + * @param {boolean} code_upload_switch The new state of the code upload switch. + * @param {boolean} arena_switch The new state of the arena switch. + * @param {boolean} playground_switch The new state of the playground switch. + * @param {boolean} stream_switch The new state of the stream switch. + * @param {boolean} playback_switch The new state of the playback switch. + * @returns {string} The ID of the updated contest. + */ +export const update_contest_switch:any = async(contest_id: string, team_switch: boolean, code_upload_switch: boolean, arena_switch: boolean, playground_switch: boolean, stream_switch: boolean, playback_switch:boolean) => { + const update_contest_switch:any = await client.request( + gql` + mutation UpdateContestSwitch( + $contest_id: uuid! + $team_switch: Boolean! + $code_upload_switch: Boolean! + $arena_switch: Boolean! + $playground_switch: Boolean! + $stream_switch: Boolean! + $playback_switch: Boolean! + ) { + update_contest_by_pk( + pk_columns: { id: $contest_id } + _set: { + team_switch: $team_switch + code_upload_switch: $code_upload_switch + arena_switch: $arena_switch + playground_switch: $playground_switch + stream_switch: $stream_switch + playback_switch: $playback_switch + } + ) { + id + } + } + `, + { + contest_id: contest_id, + team_switch: team_switch, + code_upload_switch: code_upload_switch, + arena_switch: arena_switch, + playground_switch: playground_switch, + stream_switch: stream_switch, + playback_switch: playback_switch + } + ); + + return update_contest_switch.update_contest_by_pk?.id?? undefined; +} + +/** + * Updates the contest map. + * + * @param {string} map_id The ID of the map to update. + * @param {string} name The new name of the map. + * @param {string} filename The new filename of the map. + * @param {string} team_labels The new team labels of the map. + * @returns {string} The ID of the updated map. + */ +export const update_contest_map:any = async(map_id:string, updateFields: Partial<{ name: string; filename: string; team_labels: string }>) => { + const setFields: any = {}; + if(updateFields.name) setFields.name = updateFields.name; + if(updateFields.filename) setFields.filename = updateFields.filename; + if(updateFields.team_labels) setFields.team_labels = updateFields.team_labels; + + if(Object.keys(setFields).length === 0 ){ + console.error("At least update one feature"); + return undefined; + } + + const setString = Object.keys(setFields) + .map(key => `${key}: $${key}`) + .join(', '); + + const variableString = Object.keys(setFields) + .map(key => `$${key}: String`) + .join(', '); + + const mutation = gql` + mutation UpdateContestMap($map_id: uuid!, ${variableString}) { + update_contest_map_by_pk(pk_columns: { map_id: $map_id }, _set: { ${setString} }) { + map_id + name + filename + team_labels + } + } + `; + +/* const variables = { + map_id, + ...setFields + } + 本以为这样可以,但是不能,因为setFields是一个对象,而variables是一个数组 + */ + + const variables:{[key:string]:any} = { + map_id: map_id + } + + if(setFields.name) variables.name = setFields.name; + if(setFields.filename) variables.filename = setFields.filename; + if(setFields.team_labels) variables.team_labels = setFields.team_labels; + + try { + const response:any = await client.request(mutation,variables); + return response.update_contest_map_by_pk?.map_id?? undefined; + }catch(error){ + console.error('Error updating contest map', error); + throw(error); + } +} + +/** + * Updates the contest notice. + * + * @param {string} id The ID of the notice to update. + * @param {string} title Optional The new title of the notice. + * @param {string} content Optional The new content of the notice. + * @param {string} files Optional The new files of the notice. + * @returns {string} The ID of the updated notice. + */ +export const update_contest_notice: any = async (id: string, updateFields: Partial<{ title: string; content: string; files: string }>) => { + const setFields: any = {}; + if (updateFields.title) setFields.title = updateFields.title; + if (updateFields.content) setFields.content = updateFields.content; + if (updateFields.files) setFields.files = updateFields.files; + + if (Object.keys(setFields).length === 0) { + console.error("At least update one feature"); + return undefined; + } + + const variableString = Object.keys(setFields) + .map(key=>`$${key}`) + .join(', '); + + const setString = Object.keys(setFields) + .map(key => `${key}: $${key}`) + .join(', '); + + const mutation = gql` + mutation UpdateContestNotice($id: uuid!, ${variableString}) { + update_contest_notice_by_pk(pk_columns: { id: $id }, _set: { ${setString} }) { + id + title + content + files + } + } + `; + + const variables:{[key:string]:any} = { + id:id + } + if(setFields.title) variables.title = setFields.title; + if(setFields.content) variables.content = setFields.content; + if(setFields.files) variables.files = setFields.files; + + + try { + const response: any = await client.request(mutation, variables); + return response.update_contest_notice_by_pk?.id ?? undefined; + } catch (error) { + console.error('Error updating contest notice', error); + throw error; + } +}; + +/** + * Updates the contest player. + * + * @param {string} contest_id The ID of the contest to update. + * @param {string} team_label The new team label of the player. + * @param {string} player_label Optional The new player label. + * @param {string} roles_available Optional The new roles available for the player. + * @returns {string} The team label of the updated player. + */ +export const update_contest_player: any = async (contest_id: string, player_label: string, team_label:string,updateFields: Partial<{roles_available: string }>) => { + const setFields: any = {}; + if (updateFields.roles_available) setFields.roles_available = updateFields.roles_available; + + if (Object.keys(setFields).length === 0) { + console.error("At least update one feature"); + return undefined; + } + + const variableString = Object.keys(setFields) + .map(key => `$${key}: String`) + .join(', '); + + const setString = Object.keys(setFields) + .map(key => `${key}: $${key}`) + .join(', '); + + const mutation = gql` + mutation UpdateContestPlayer($contest_id: uuid!, $player_label: String!, $team_label:String!,${variableString}) { + update_contest_player_by_pk(pk_columns: { contest_id: $contest_id, player_label: $player_label ,team_label:$team_label} _set: { ${setString} }) { + team_label + player_label + roles_available + } + } + `; + + const variables:{[key:string]:any} = { + contest_id:contest_id, + player_label:player_label, + team_label:team_label + } + + if(setFields.roles_available) variables.roles_available = setFields.roles_available; + + try { + const response: any = await client.request(mutation, variables); + return response.update_contest_player_by_pk?.team_label ?? undefined; + } catch (error) { + console.error('Error updating contest player', error); + throw error; + } +}; + +/** + * Updates the contest round name. + * + * @param {string} round_id The ID of the round to update. + * @param {string} name The new name of the round. + * @returns {string} The ID of the updated round. + */ +export const update_contest_round_name:any = async(round_id:string,name:string) => { + const update_contest_round_name:any = await client.request( + gql` + mutation UpdateContestRoundName($round_id: uuid!, $name: String!) { + update_contest_round_by_pk( + pk_columns: { round_id: $round_id } + _set: { name: $name } + ) { + round_id + } + } + `, + { + round_id: round_id, + name: name + } + ); + + return update_contest_round_name.update_contest_round_by_pk?.round_id?? undefined; +} + +/** + * Updates the team code name. + * + * @param {string} code_id The ID of the code to update. + * @param {string} code_name The new name of the code. + * @returns {string} The ID of the updated code. + */ +export const update_team_code_name:any = async(code_id:string,code_name:string) => { + const update_team_code_name:any = await client.request( + gql` + mutation UpdateTeamCodeName($code_id: uuid!, $code_name: String!) { + update_contest_team_code_by_pk( + pk_columns: { code_id: $code_id } + _set: { code_name: $code_name } + ) { + code_id + } + } + `, + { + code_id: code_id, + code_name: code_name + } + ); + return update_team_code_name.update_contest_team_code_by_pk?.code_id?? undefined; +} +/** + * Update a team player's information + * @param {string} team_id The ID of the team + * @param {string} player The player to be updated + * @param {string} code_id The code ID associated with the player + * @param {string} role The role of the player + * @returns {Promise} The updated player's information + */ +export const update_team_player:any = async(team_id:string,player:string,code_id:string,role:string) =>{ + const update_team_player:any = await client.request( + gql` + mutation UpdateTeamPlayer( + $team_id: uuid! + $player: String! + $code_id: uuid + $role: String + ) { + update_contest_team_player_by_pk( + pk_columns: { team_id: $team_id, player: $player } + _set: { code_id: $code_id, role: $role } + ) { + player + } + } + `, + { + team_id: team_id, + player: player, + code_id: code_id, + role: role + }); + return update_team_player.update_contest_team_player_by_pk?.player?? undefined; + } +/** + * Update a team's information + * @param {string} team_id The ID of the team + * @param {string} team_name The name of the team + * @param {string} team_intro The introduction of the team + * @returns {Promise} The updated team's ID + */ +export const update_team:any = async(team_id:string,updateFields:Partial<{ team_name: string; team_intro: string}>) =>{ + const setFields: any = {}; + if (updateFields.team_name) setFields.team_name = updateFields.team_name; + if (updateFields.team_intro) setFields.team_intro = updateFields.team_intro; + if (Object.keys(setFields).length === 0) { + console.error("At least update one feature"); + return undefined; + } + const variableString = Object.keys(setFields) + .map(key => `$${key}: String`) + .join(',') + + const setString = Object.keys(setFields) + .map(key => `${key}: $${key}`) + .join(',') + + const mutation = gql` + mutation UpdateTeam( + $team_id: uuid!, + ${variableString} + ) { + update_contest_team_by_pk( + pk_columns: { team_id: $team_id } + _set: { ${setString} } + ) { + team_id + } + } + `; + const variables:{[key:string]:any}= { + team_id:team_id + } + if(setFields.team_name) variables.team_name = setFields.team_name; + if(setFields.team_intro) variables.team_intro = setFields.team_intro; + try { + const response: any = await client.request(mutation, variables); + return response.update_contest_team_by_pk?.team_id ?? undefined; + } catch (error) { + console.error('Error updating contest player', error); + throw error; + } +} +/** + * Update contest time information + * @param {string} contest_id The ID of the contest + * @param {string} event The event to be updated + * @param {Date} start The start time of the event + * @param {Date} end The end time of the event + * @param {string} description The description of the event + * @returns {Promise} The updated event information + */ +export const update_contest_time:any = async(contest_id:string,event:string,start:Date,end:Date,description:string) =>{ + const update_contest_time:any = await client.request( + gql` + mutation UpdateContestTime( + $contest_id: uuid! + $event: String! + $start: timestamptz! + $end: timestamptz! + $description: String + ) { + update_contest_time_by_pk( + pk_columns: { contest_id: $contest_id, event: $event } + _set: { start: $start, end: $end, description: $description } + ) { + event + } + } + `, + { + contest_id: contest_id, + event: event, + start: new Date(start), + end: new Date(end), + description: description + }); + return update_contest_time.update_contest_time_by_pk?.event?? undefined; + } @@ -1050,3 +1802,217 @@ export const delete_room_team: any = async (room_id: string) => { return delete_room_team.delete_contest_room_team.affected_rows; } + +/** + * Delete a contest + * @param {string} contest_id The ID of the contest to be deleted + * @returns {Promise} The number of affected rows + */ +export const delete_contest:any = async (contest_id: string) => { + const delete_contest: any = await client.request( + gql` + mutation delete_contest($contest_id: uuid!) { + delete_contest_by_pk(id: $contest_id) { + id + } + } + `, + { + contest_id: contest_id + } + ); + + return delete_contest.delete_contest?.affected_rows?? undefined; +} + +/** + * Delete a contest map + * @param {string} map_id The ID of the map to be deleted + * @returns {Promise} The ID of the deleted map + */ + +export const delete_contest_map:any = async (map_id: string) => { + const delete_contest_map: any = await client.request( + gql` + mutation delete_contest_map($map_id: uuid!) { + delete_contest_map_by_pk(map_id: $map_id) { + map_id + } + } + `, + { + map_id: map_id + } + ); + + return delete_contest_map.delete_contest_map_by_pk?.map_id?? undefined; +} + +/** + * Delete a contest notice + * @param {string} id The ID of the notice to be deleted + * @returns {Promise} The ID of the deleted notice + */ + +export const delete_contest_notice:any = async(id:string) =>{ + const delete_contest_notice:any = await client.request( + gql` + mutation DeleteContestNotice($id: uuid!) { + delete_contest_notice_by_pk(id: $id) { + id + } + } + `, + { + id:id + } + ); + return delete_contest_notice.delete_contest_notice_by_pk?.id?? undefined; +} + +/** + * Delete a contest player + * @param {string} contest_id The ID of the contest + * @param {string} team_label The label of the team + * @param {string} player_label The label of the player + * @returns {Promise} The label of the deleted team + */ + +export const delete_contest_player:any = async(contest_id:string,team_label:string,player_label:string) =>{ + const delete_contest_player:any = await client.request( + gql` + mutation DeleteContestPlayer( + $contest_id: uuid! + $team_label: String! + $player_label: String! + ) { + delete_contest_player_by_pk( + contest_id: $contest_id + team_label: $team_label + player_label: $player_label + ) { + team_label + } + } + `, + { + contest_id: contest_id, + team_label: team_label, + player_label: player_label + }); + return delete_contest_player.delete_contest_player_by_pk?.team_label?? undefined; +} + + +/** + * Delete a contest round + * @param {string} round_id The ID of the round to be deleted + * @returns {Promise} The ID of the deleted round + */ + +export const delete_contest_round:any = async(round_id:string) => { + const delete_contest_round:any = await client.request( + gql` + mutation DeleteContestRound($round_id: uuid!) { + delete_contest_round_by_pk(round_id: $round_id) { + round_id + } + } + `, + { + round_id: round_id + }); + return delete_contest_round.delete_contest_round_by_pk?.round_id?? undefined; +} + +/** + * Delete a team code + * @param {string} code_id The ID of the code to be deleted + * @returns {Promise} The ID of the deleted code + */ + +export const delete_team_code:any = async(code_id:string)=>{ + const delete_team_code:any = await client.request( + gql` + mutation DeleteTeamCode($code_id: uuid!) { + delete_contest_team_code_by_pk(code_id: $code_id) { + code_id + } + } + `, + { + code_id: code_id + + }); + return delete_team_code.delete_contest_team_code_by_pk?.code_id?? undefined; +} + + +/** + * Delete a team + * @param {string} team_id The ID of the team to be deleted + * @returns {Promise} The ID of the deleted team + */ + + +export const delete_team:any = async(team_id:string) => { + const delete_team:any = await client.request( + gql` + mutation DeleteTeam($team_id: uuid!) { + delete_contest_team_by_pk(team_id: $team_id) { + team_id + } + } + `, + { + team_id: team_id + }); + return delete_team.delete_contest_team_by_pk?.team_id?? undefined; +} + + +/** + * Delete a team member + * @param {string} user_uuid The UUID of the user + * @param {string} team_id The ID of the team + * @returns {Promise} The ID of the team + */ + +export const delete_team_member:any = async(user_uuid:string,team_id:string) => { + const delete_team_member:any = await client.request( + gql` + mutation DeleteTeamMember($user_uuid: uuid!, $team_id: uuid!) { + delete_contest_team_member_by_pk(user_uuid: $user_uuid, team_id: $team_id) { + team_id + } + } + `, + { + user_uuid: user_uuid, + team_id: team_id + }); + return delete_team_member.delete_contest_team_member_by_pk?.team_id?? undefined; +} + +/** + * Delete contest time + * @param {string} contest_id The ID of the contest + * @param {string} event The event to be deleted + * @returns {Promise} The event of the deleted contest time + */ + +export const delete_contest_time:any = async(contest_id:string,event:string) => { + const delete_contest_time:any = await client.request( + gql` + mutation DeleteContestTime($contest_id: uuid!, $event: String!) { + delete_contest_time_by_pk(contest_id: $contest_id, event: $event) { + event + } + } + `, + { + contest_id: contest_id, + event: event + }); + return delete_contest_time.delete_contest_time_by_pk?.event?? undefined; + } \ No newline at end of file diff --git a/src/routes/manage.ts b/src/routes/manage.ts new file mode 100644 index 00000000..2ea692a0 --- /dev/null +++ b/src/routes/manage.ts @@ -0,0 +1,342 @@ +import express from "express"; +import authenticate from "../middlewares/authenticate"; + +import * as ContHasFunc from "../hasura/contest" + +const router = express.Router(); + +// used in uploadmap.tsx +router.post("/add_contest_map", authenticate(["counselor"]), async (req, res) => { + try { + const { contest_id, name, filename, team_labels } = req.body; + if (!contest_id || !name || !filename || !team_labels) { + return res.status(400).json({ error: "400 Bad Request: Missing required parameters" }); + } + const map_id = await ContHasFunc.add_contest_map(contest_id, name, filename, team_labels); + res.status(200).json({ map_id:map_id,message:"Contest Map Added Successfully" }); + } catch (err:any) { + res.status(500).json({ + error: "500 Internal Server Error", + message: err.message, + stack: process.env.NODE_ENV === 'development' ? err.stack : undefined + }); + } +}); + +// used in noticepage.tsx +router.post("/add_contest_notice", authenticate(["counselor"]), async (req, res) => { + try { + const { title, content, files, contest_id } = req.body; + if (!title || !content || !contest_id) { + return res.status(400).json({ error: "400 Bad Request: Missing required parameters" }); + } + const id = await ContHasFunc.add_contest_notice(title, content, files, contest_id); + res.status(200).json({ id:id,message:"Contest Notice Added Successfully" }); + } catch (err:any) { + + res.status(500).json({ + error: "500 Internal Server Error", + message: err.message, + stack: process.env.NODE_ENV === 'development' ? err.stack : undefined + }); + } +}); + +router.post("/add_contest_player", authenticate(["counselor"]), async (req, res) => { + try { + const { contest_id, team_label, player_label, roles_available } = req.body; + if (!contest_id || !team_label || !player_label || !roles_available) { + return res.status(400).json({ error: "400 Bad Request: Missing required parameters" }); + } + const team_label_result = await ContHasFunc.add_contest_player(contest_id, team_label, player_label, roles_available); + res.json({ team_label: team_label_result }); + } catch (err:any) { + res.status(500).json({ + error: "500 Internal Server Error", + message: err.message, + stack: process.env.NODE_ENV === 'development' ? err.stack : undefined + }); + } +}); + + +// used in Competition.tsx +router.post("/add_contest_round", authenticate(["counselor"]), async (req, res) => { + try { + const { contest_id, name, map_id } = req.body; + if (!contest_id || !name || !map_id) { + return res.status(400).json({ error: "400 Bad Request: Missing required parameters" }); + } + const round_id = await ContHasFunc.add_contest_round(contest_id, name, map_id); + res.status(200).json({ round_id:round_id,message:"Contest Round Added Successfully" }); + } catch (err:any) { + + res.status(500).json({ + error: "500 Internal Server Error", + message: err.message, + stack: process.env.NODE_ENV === 'development' ? err.stack : undefined + }); + } +}); + +// used in edittimeline.tsx +router.post("/add_contest_time", authenticate(["counselor"]), async (req, res) => { + try { + const { contest_id, event, start, end, description } = req.body; + if (!contest_id || !event || !start || !end) { + return res.status(400).json({ error: "400 Bad Request: Missing required parameters" }); + } + const event_result = await ContHasFunc.add_contest_time(contest_id, event, start, end, description); + res.status(200).json({ event: event_result,message:"Contest Time Added Successfully" }); + } catch (err:any) { + res.status(500).json({ + error: "500 Internal Server Error", + message: err.message, + stack: process.env.NODE_ENV === 'development' ? err.stack : undefined + }); + } +}); + + +// used in editinfo.tsx +router.post("/update_contest_info", authenticate(["counselor"]), async (req, res) => { + + try{ + const {contest_id, ...updateFields} = req.body + if(!contest_id){ + return res.status(400).json({error: "400 Bad Request: Missing required parameters (Contest_id)" }); + } + const update_contest_info = await ContHasFunc.update_contest_info(contest_id, updateFields); + res.status(200).json({ id: update_contest_info.id,message:"Contest Map updated successfully" }); + }catch (err:any) { + res.status(500).json({ + error: "500 Internal Server Error", + message: err.message, + stack: process.env.NODE_ENV === 'development' ? err.stack : undefined + }); + } +}); + +// used in setting.tsx +router.post("/update_contest_switch", authenticate(["counselor"]), async (req, res) => { + try { + const { contest_id, team_switch, code_upload_switch, arena_switch, playground_switch, stream_switch, playback_switch } = req.body; + if (!contest_id || team_switch === undefined || code_upload_switch === undefined || arena_switch === undefined || playground_switch === undefined || stream_switch === undefined || playback_switch === undefined) { + return res.status(400).json({ error: "400 Bad Request: Missing required parameters" }); + } + const update_contest_switch = await ContHasFunc.update_contest_switch(contest_id, team_switch, code_upload_switch, arena_switch, playground_switch, stream_switch, playback_switch); + res.status(200).json({ id: update_contest_switch.id, message:"Contest Switch Updated Successfully"}); + } catch (err:any) { + res.status(500).json({ + error: "500 Internal Server Error", + message: err.message, + stack: process.env.NODE_ENV === 'development' ? err.stack : undefined + }); + } +}); + +router.post("/update_contest_map", authenticate(["counselor"]), async (req, res) => { + try { + const { map_id, ...updateFields } = req.body; + if (!map_id) { + return res.status(400).json({ error: "400 Bad Request: Missing required parameters (Map_ID)" }); + } + const update_contest_map = await ContHasFunc.update_contest_map(map_id, updateFields); + res.status(200).json({ map_id: update_contest_map.map_id,message:"Contest Map updated successfully" }); + } catch (err:any) { + res.status(500).json({ + error: "500 Internal Server Error", + message: err.message, + stack: process.env.NODE_ENV === 'development' ? err.stack : undefined + }); + } +}); + + +//used in noticepage.tsx +router.post("/update_contest_notice", authenticate(["counselor"]), async (req, res) => { + try { + const { id, ...updateFields } = req.body; + if (!id) { + return res.status(400).json({ error: "400 Bad Request: Missing required parameters (Contest_ID)" }); + } + const update_contest_notice = await ContHasFunc.update_contest_notice(id, updateFields); + res.status(200).json({ id: update_contest_notice.id, message:"Contest Notice Updated Successfully" }); + } catch (err:any) { + res.status(500).json({ + error: "500 Internal Server Error", + message: err.message, + stack: process.env.NODE_ENV === 'development' ? err.stack : undefined + }); + } +}); + +router.post("/update_contest_player", authenticate(["counselor"]), async (req, res) => { + try { + const { contest_id, player_label, team_label,...updateFields } = req.body; + if (!contest_id || !player_label || !team_label) { + return res.status(400).json({ error: "400 Bad Request: Missing required parameters (contest_id or player_lable or team_label)" }); + } + const update_contest_player = await ContHasFunc.update_contest_player(contest_id, player_label, team_label,updateFields); + res.status(200).json({ team_label: update_contest_player.team_label, message:"Contest player added successfully" }); + } catch (err: any) { + res.status(500).json({ + error: "500 Internal Server Error", + message: err.message, + stack: process.env.NODE_ENV === 'development' ? err.stack : undefined + }); + } +}); + +router.post("/update_contest_round_name", authenticate(["counselor"]), async (req, res) => { + try { + const { round_id, name } = req.body; + if (!round_id || !name) { + return res.status(400).json({ error: "400 Bad Request: Missing required parameters" }); + } + const update_contest_round_name = await ContHasFunc.update_contest_round_name(round_id, name); + res.json({ round_id: update_contest_round_name.round_id }); + } catch (err: any) { + + res.status(500).json({ + error: "500 Internal Server Error", + message: err.message, + stack: process.env.NODE_ENV === 'development' ? err.stack : undefined + }); + } +}); + +router.post("/update_contest_time", authenticate(["counselor"]), async (req, res) => { + try { + const { contest_id, event, start, end, description } = req.body; + if (!contest_id || !event || !start || !end || !description) { + return res.status(400).json({ error: "400 Bad Request: Missing required parameters" }); + } + const update_contest_time = await ContHasFunc.update_contest_time(contest_id, event, start, end, description); + res.json({ event: update_contest_time.event }); + } catch (err: any) { + + res.status(500).json({ + error: "500 Internal Server Error", + message: err.message, + stack: process.env.NODE_ENV === 'development' ? err.stack : undefined + }); + } +}); + +router.post("/delete_contest", authenticate(["counselor"]), async (req, res) => { + try { + const { contest_id } = req.body; + if (!contest_id) { + return res.status(400).json({ error: "400 Bad Request: Missing required parameters" }); + } + const delete_contest = await ContHasFunc.delete_contest(contest_id); + res.json({ affected_rows: delete_contest }); + } catch (err: any) { + + res.status(500).json({ + error: "500 Internal Server Error", + message: err.message, + stack: process.env.NODE_ENV === 'development' ? err.stack : undefined + }); + } +}); + +// used in uploadmap.tsx + +router.post("/delete_contest_map", authenticate(["counselor"]), async (req, res) => { + try { + const { map_id } = req.body; + if (!map_id) { + return res.status(400).json({ error: "400 Bad Request: Missing required parameters" }); + } + const delete_contest_map = await ContHasFunc.delete_contest_map(map_id); + res.status(200).json({ map_id: delete_contest_map, message:"Contest Map Deleted Successfully" }); + } catch (err: any) { + + res.status(500).json({ + error: "500 Internal Server Error", + message: err.message, + stack: process.env.NODE_ENV === 'development' ? err.stack : undefined + }); + } +}); + + +// used in noticepage.tsx +router.post("/delete_contest_notice", authenticate(["counselor"]), async (req, res) => { + try { + const { id } = req.body; + if (!id) { + return res.status(400).json({ error: "400 Bad Request: Missing required parameters" }); + } + const delete_contest_notice = await ContHasFunc.delete_contest_notice(id); + res.status(200).json({ id: delete_contest_notice, message:"Contest Notice Deleted Successfully" }); + } catch (err: any) { + + res.status(500).json({ + error: "500 Internal Server Error", + message: err.message, + stack: process.env.NODE_ENV === 'development' ? err.stack : undefined + }); + } +}); + +router.post("/delete_contest_player", authenticate(["counselor"]), async (req, res) => { + try { + const { contest_id, team_label, player_label } = req.body; + if (!contest_id || !team_label || !player_label) { + return res.status(400).json({ error: "400 Bad Request: Missing required parameters" }); + } + const delete_contest_player = await ContHasFunc.delete_contest_player(contest_id, team_label, player_label); + res.json({ team_label: delete_contest_player }); + } catch (err: any) { + + res.status(500).json({ + error: "500 Internal Server Error", + message: err.message, + stack: process.env.NODE_ENV === 'development' ? err.stack : undefined + }); + } +}); + + +router.post("/delete_contest_round", authenticate(["counselor"]), async (req, res) => { + try { + const { round_id } = req.body; + if (!round_id) { + return res.status(400).send("400 Bad Request: Missing required parameters"); + } + const delete_contest_round = await ContHasFunc.delete_contest_round(round_id); + res.json({ round_id: delete_contest_round }); + } catch (err: any) { + + res.status(500).json({ + error: "500 Internal Server Error", + message: err.message, + stack: process.env.NODE_ENV === 'development' ? err.stack : undefined + }); + } +}); + + +router.post("/delete_contest_time", authenticate(["counselor"]), async (req, res) => { + try { + const { contest_id, event } = req.body; + if (!contest_id || !event) { + return res.status(400).send("400 Bad Request: Missing required parameters"); + } + const delete_contest_time = await ContHasFunc.delete_contest_time(contest_id, event); + res.json({ event: delete_contest_time }); + } catch (err: any) { + + res.status(500).json({ + error: "500 Internal Server Error", + message: err.message, + stack: process.env.NODE_ENV === 'development' ? err.stack : undefined + }); + } +}); + + +export default router; \ No newline at end of file diff --git a/src/routes/team.ts b/src/routes/team.ts new file mode 100644 index 00000000..9a39b2e9 --- /dev/null +++ b/src/routes/team.ts @@ -0,0 +1,205 @@ +import express from "express"; +import authenticate from "../middlewares/authenticate"; +import * as ContHasFunc from "../hasura/contest" + +const router = express.Router(); + +// used in codepage.tsx +router.post("/add_team_code", authenticate(["student"]), async (req, res) => { + try { + const { team_id, code_name, language, compile_status } = req.body; + if (!team_id || !code_name || !language || !compile_status) { + return res.status(400).json({ error: "400 Bad Request: Missing required parameters" }); + } + const code_id = await ContHasFunc.add_team_code(team_id, code_name, language, compile_status); + res.status(200).json({ code_id:code_id,message:"Code added successfully" }); + } catch (err:any) { + res.status(500).json({ + error: "500 Internal Server Error", + message: err.message, + stack: process.env.NODE_ENV === 'development' ? err.stack : undefined + }); + } +}); + + +// used in joinpage.tsx +router.post("/add_team_player", authenticate(["student"]), async (req, res) => { + try { + const { team_id, player } = req.body; + if (!team_id || !player) { + return res.status(400).json({ error: "400 Bad Request: Missing required parameters" }); + } + const player_result = await ContHasFunc.add_team_player(team_id, player); + res.status(200).json({ player: player_result,message:"Team Player Added Successfully" }); + } catch (err:any) { + + res.status(500).json({ + error: "500 Internal Server Error", + message: err.message, + stack: process.env.NODE_ENV === 'development' ? err.stack : undefined + }); + } +}); + +router.post("/add_team", authenticate(["student"]), async (req, res) => { + try { + const { team_name, team_intro, team_leader_uuid, invited_code, contest_id } = req.body; + if (!team_name || !team_intro || !invited_code || !contest_id) { + return res.status(400).json({ error: "400 Bad Request: Missing required parameters" }); + } + else if(!team_leader_uuid){ + return res.status(400).json({ error: "400 Bad Request: Missing Team Leader UUID" }); + } + // else if(!isValid(contest_id)){ + //} + const team_id = await ContHasFunc.add_team(team_name, team_intro, team_leader_uuid, invited_code, contest_id); + res.status(200).json({ team_id: team_id,message:"Team Added Successfully" }); + } catch (err:any) { + + res.status(500).json({ + error: "500 Internal Server Error", + message: err.message, + stack: process.env.NODE_ENV === 'development' ? err.stack : undefined + }); + } +}); + +router.post("/add_team_member", authenticate(["student"]), async (req, res) => { + try { + const { team_id, user_uuid } = req.body; + if (!team_id || !user_uuid) { + return res.status(400).json({ error: "400 Bad Request: Missing required parameters" }); + } + const team_id_result = await ContHasFunc.add_team_member(team_id, user_uuid); + res.status(200).json({ message:"Team Member Added Successfully",team_id: team_id_result }); + } catch (err:any) { + + res.status(500).json({ + error: "500 Internal Server Error", + message: err.message, + stack: process.env.NODE_ENV === 'development' ? err.stack : undefined + }); + } +}); + + +// used in codepage.tsx +router.post("/update_team_code_name", authenticate(["student"]), async (req, res) => { + try { + const { code_id, code_name } = req.body; + if (!code_id || !code_name) { + return res.status(400).json({ error: "400 Bad Request: Missing required parameters" }); + } + const update_team_code_name = await ContHasFunc.update_team_code_name(code_id, code_name); + res.status(200).json({ code_id: update_team_code_name.code_id,message:"Code Name Updated Successfully" }); + } catch (err: any) { + + res.status(500).json({ + error: "500 Internal Server Error", + message: err.message, + stack: process.env.NODE_ENV === 'development' ? err.stack : undefined + }); + } +}); + + +// used in codepage.tsx +router.post("/update_team_player", authenticate(["student"]), async (req, res) => { + try { + const { team_id, player, code_id, role } = req.body; + if (!team_id || !player || !code_id || !role) { + return res.status(400).json({ error: "400 Bad Request: Missing required parameters" }); + } + const update_team_player = await ContHasFunc.update_team_player(team_id, player, code_id, role); + res.status(200).json({ player: update_team_player.player,message:"Player Updated Successfully" }); + } catch (err: any) { + + res.status(500).json({ + error: "500 Internal Server Error", + message: err.message, + stack: process.env.NODE_ENV === 'development' ? err.stack : undefined + }); + } +}); + +// used in managepage.tsx +router.post("/update_team", authenticate(["student"]), async (req, res) => { + try { + const { team_id, ...update_Fields } = req.body; + if (!team_id) { + return res.status(400).json({ error: "400 Bad Request: Missing required parameters(team_id)" }); + } + const update_team = await ContHasFunc.update_team(team_id, update_Fields); + res.status(200).json({ message:"Team Updated Successfully",team_id: update_team.team_id }); + } catch (err: any) { + res.status(500).json({ + error: "500 Internal Server Error", + message: err.message, + stack: process.env.NODE_ENV === 'development' ? err.stack : undefined + }); + } +}); + + + +//used in codepage.tsx + +router.post("/delete_team_code", authenticate(["student"]), async (req, res) => { + try { + const { code_id } = req.body; + if (!code_id) { + return res.status(400).send("400 Bad Request: Missing required parameters"); + } + const delete_team_code = await ContHasFunc.delete_team_code(code_id); + res.status(200).json({ code_id: delete_team_code,message:"Code Deleted Successfully" }); + } catch (err: any) { + + res.status(500).json({ + error: "500 Internal Server Error", + message: err.message, + stack: process.env.NODE_ENV === 'development' ? err.stack : undefined + }); + } +}); + +// used in managepage.tsx + +router.post("/delete_team", authenticate(["student"]), async (req, res) => { + try { + const { team_id } = req.body; + if (!team_id) { + return res.status(400).send("400 Bad Request: Missing required parameters"); + } + const delete_team = await ContHasFunc.delete_team(team_id); + res.status(200).json({ team_id: delete_team, message:"Team Deleted Successfully" }); + } catch (err: any) { + + res.status(500).json({ + error: "500 Internal Server Error", + message: err.message, + stack: process.env.NODE_ENV === 'development' ? err.stack : undefined + }); + } +}); + +// used in managepage.tsx +router.post("/delete_team_member", authenticate(["student"]), async (req, res) => { + try { + const { user_uuid, team_id } = req.body; + if (!user_uuid || !team_id) { + return res.status(400).send("400 Bad Request: Missing required parameters"); + } + const delete_team_member = await ContHasFunc.delete_team_member(user_uuid, team_id); + res.status(200).json({ team_id: delete_team_member,message:"Team Member Deleted Successfully" }); + } catch (err: any) { + + res.status(500).json({ + error: "500 Internal Server Error", + message: err.message, + stack: process.env.NODE_ENV === 'development' ? err.stack : undefined + }); + } +}); + +export default router; \ No newline at end of file