Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 103 additions & 0 deletions src/hasura/contest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@
* @param {string} contest_id
* @returns {string} contest_name
*/
export const get_contest_name: any = async (contest_id: string) => {

Check warning on line 22 in src/hasura/contest.ts

View workflow job for this annotation

GitHub Actions / test

Unexpected any. Specify a different type
const query_contest_name: any = await client.request(

Check warning on line 23 in src/hasura/contest.ts

View workflow job for this annotation

GitHub Actions / test

Unexpected any. Specify a different type
gql`
query get_contest_name($contest_id: uuid!) {
contest(where: { id: { _eq: $contest_id } }) {
Expand All @@ -40,8 +40,8 @@
* @param {string} contest_name
* @returns {string} contest_id
*/
export const get_contest_id: any = async (contest_name: string) => {

Check warning on line 43 in src/hasura/contest.ts

View workflow job for this annotation

GitHub Actions / test

Unexpected any. Specify a different type
const query_contest_id: any = await client.request(

Check warning on line 44 in src/hasura/contest.ts

View workflow job for this annotation

GitHub Actions / test

Unexpected any. Specify a different type
gql`
query get_contest_id($contest_name: String!) {
contest(where: { name: { _eq: $contest_name } }) {
Expand All @@ -61,8 +61,8 @@
* @param {string} contest_id
* @returns {object} {arena_switch, code_upload_switch, playback_switch, playground_switch, stream_switch, team_switch}
*/
export const get_contest_settings: any = async (contest_id: string) => {

Check warning on line 64 in src/hasura/contest.ts

View workflow job for this annotation

GitHub Actions / test

Unexpected any. Specify a different type
const query_contest_settings: any = await client.request(

Check warning on line 65 in src/hasura/contest.ts

View workflow job for this annotation

GitHub Actions / test

Unexpected any. Specify a different type
gql`
query get_contest_settings($contest_id: uuid!) {
contest(where: { id: { _eq: $contest_id } }) {
Expand Down Expand Up @@ -99,11 +99,11 @@
* @param {string} contest_id
* @returns {string} team_id
*/
export const get_team_from_user: any = async (

Check warning on line 102 in src/hasura/contest.ts

View workflow job for this annotation

GitHub Actions / test

Unexpected any. Specify a different type
user_uuid: string,
contest_id: string,
) => {
const query_team_id: any = await client.request(

Check warning on line 106 in src/hasura/contest.ts

View workflow job for this annotation

GitHub Actions / test

Unexpected any. Specify a different type
gql`
query get_team_id($user_uuid: uuid!, $contest_id: uuid!) {
contest_team_member(
Expand All @@ -126,7 +126,7 @@
if (query_team_id.contest_team_member.length !== 0) {
return query_team_id.contest_team_member[0]?.team_id ?? null;
}
const query_team_from_leader: any = await client.request(

Check warning on line 129 in src/hasura/contest.ts

View workflow job for this annotation

GitHub Actions / test

Unexpected any. Specify a different type
gql`
query get_team_from_leader($user_uuid: uuid!, $contest_id: uuid!) {
contest_team(
Expand Down Expand Up @@ -156,7 +156,7 @@
* @param {string} team_id
* @returns {boolean} true if user is in the team, false otherwise
*/
export const check_user_team: any = async (

Check warning on line 159 in src/hasura/contest.ts

View workflow job for this annotation

GitHub Actions / test

Unexpected any. Specify a different type
user_uuid: string,
team_id: string,
) => {
Expand Down Expand Up @@ -1479,6 +1479,109 @@
);
};

export const add_team_software_final: any = async (
contest_id: string,
team_id: string,
code_url: string,
) => {
await client.request(
gql`
mutation add_team_software_final(
$url: String!
$team_id: uuid!
$contest_id: uuid!
) {
insert_contest_team_software_final(
objects: { URL: $url, team_id: $team_id, contest_id: $contest_id }
) {
returning {
created_at
}
}
}
`,
{
team_id: team_id,
url: code_url,
contest_id: contest_id,
},
);
};

export const update_team_software_final: any = async (
team_id: string,
code_url: string,
submission_count: number,
) => {
await client.request(
gql`
mutation change_team_software_final(
$url: String!
$teamId: uuid!
$count: Int!
) {
update_contest_team_software_final(
where: { team_id: { _eq: $teamId } }
_set: { URL: $url, submission_count: $count, updated_at: "now()" }
) {
affected_rows
}
}
`,
{
teamId: team_id,
url: code_url,
count: submission_count,
},
);
};

export const get_team_software_final_one: any = async (team_id: string) => {
const query_team_software_final: any = await client.request(
gql`
query get_team_software_final($team_id: uuid!) {
contest_team_software_final(where: { team_id: { _eq: $team_id } }) {
URL
updated_at
submission_count
}
}
`,
{
team_id: team_id,
},
);
return query_team_software_final.contest_team_software_final.map(
(code: any) => ({
URL: code.URL,
updated_at: code.updated_at,
submission_count: code.submission_count,
}),
)[0];
};

export const get_team_software_final_submission_count: any = async (
team_id: string,
) => {
const query_team_software_final_submission_count: any = await client.request(
gql`
query team_software_final_submission_count($teamId: uuid!) {
contest_team_software_final(where: { team_id: { _eq: $teamId } }) {
submission_count
}
}
`,
{
teamId: team_id,
},
);
return (
query_team_software_final_submission_count.contest_team_software_final.map(
(code: any) => code.submission_count,
)[0] ?? 0
);
};

/**
*
* @param {uuid} contest_id ID
Expand Down
191 changes: 191 additions & 0 deletions src/routes/competition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1359,6 +1359,197 @@ router.post("/get_team_software_code_one", authenticate(), async (req, res) => {
}
});

/**
* Update team software code
* @param {string} team_id - Team ID
* @param {string} code_url - Code URL
* @param {number} submission_count - Current submission count
*/
router.post("/update_team_software_final", authenticate(), async (req, res) => {
try {
const user_uuid = req.auth.user.uuid;
const { team_id, code_url } = req.body;

// Input validation
if (!team_id || !code_url) {
return res
.status(400)
.json({ error: "400 Bad Request: Missing required parameters" });
}

// Validate UUID format
const uuidRegex =
/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
if (!uuidRegex.test(team_id)) {
return res
.status(400)
.json({ error: "400 Bad Request: Invalid UUID format" });
}

// Validate URL format
try {
new URL(code_url);
} catch {
return res
.status(400)
.json({ error: "400 Bad Request: Invalid URL format" });
}

// Deadline check: verify current time is before contest deadline
const contest_id = await ContHasFunc.get_contest_id_from_team_id(team_id);
const deadline =
await ContHasFunc.get_software_contest_deadline(contest_id);
const isBeforeDeadline = deadline ? new Date() < new Date(deadline) : false;
if (!isBeforeDeadline) {
return res.status(403).json({
error: "403 Forbidden: Contest submission deadline has passed",
});
}

// Permission check: verify user is a member of the team
const is_member = await ContHasFunc.check_user_team(user_uuid, team_id);
if (!is_member) {
return res
.status(403)
.json({ error: "403 Forbidden: You are not a member of this team" });
}

const submission_count: number =
(await ContHasFunc.get_team_software_final_submission_count(team_id)) + 1;

// Update team software final code
await ContHasFunc.update_team_software_final(
team_id,
code_url,
submission_count,
);
res
.status(200)
.json({ message: "Team software final code updated successfully" });
} catch (err: any) {
console.error(err);
res.status(500).json({
error: "500 Internal Server Error",
message: err.message,
stack: process.env.NODE_ENV === "development" ? err.stack : undefined,
});
}
});

/**
* Get team software code by team ID
* @param {string} team_id - Team ID
*/
router.post(
"/get_team_software_final_one",
authenticate(),
async (req, res) => {
try {
const user_uuid = req.auth.user.uuid;
const { team_id } = req.body;

// Input validation
if (!team_id) {
return res
.status(400)
.json({ error: "400 Bad Request: Missing team_id" });
}

// Validate UUID format
const uuidRegex =
/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
if (!uuidRegex.test(team_id)) {
return res
.status(400)
.json({ error: "400 Bad Request: Invalid UUID format" });
}

// Permission check: verify user is a member of the team or a manager
// For now, we'll allow team members to view their own code
//const teams = await ContHasFunc.get_team_from_user(user_uuid, team_id);
const isMember = await ContHasFunc.check_user_team(user_uuid, team_id);
if (!isMember) {
return res.status(403).json({
error:
"403 Forbidden: You are not authorized to view this team's code",
});
}

// Get team software final code
const code = await ContHasFunc.get_team_software_final_one(team_id);
res.status(200).json({ data: code });
} catch (err: any) {
console.error(err);
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_software_final", authenticate(), async (req, res) => {
try {
const user_uuid = req.auth.user.uuid;
const { contest_id, team_id, code_url } = req.body;

// Input validation
if (!contest_id || !team_id || !code_url) {
return res
.status(400)
.json({ error: "400 Bad Request: Missing required parameters" });
}

// Validate UUID format
const uuidRegex =
/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
if (!uuidRegex.test(contest_id) || !uuidRegex.test(team_id)) {
return res
.status(400)
.json({ error: "400 Bad Request: Invalid UUID format" });
}

// Validate URL format
try {
new URL(code_url);
} catch {
return res
.status(400)
.json({ error: "400 Bad Request: Invalid URL format" });
}
// Deadline check: verify current time is before contest deadline
const deadline =
await ContHasFunc.get_software_contest_deadline(contest_id);
const isBeforeDeadline = deadline ? new Date() < new Date(deadline) : false;
if (!isBeforeDeadline) {
return res.status(403).json({
error: "403 Forbidden: Contest submission deadline has passed",
});
}
// Permission check: verify user is a member of the team
const userTeam = await ContHasFunc.check_user_team(user_uuid, team_id);
if (!userTeam) {
return res
.status(403)
.json({ error: "403 Forbidden: You are not a member of this team" });
}

// Add team software final code
await ContHasFunc.add_team_software_final(contest_id, team_id, code_url);
res
.status(200)
.json({ message: "Team software final code added successfully" });
} catch (err: any) {
console.error(err);
res.status(500).json({
error: "500 Internal Server Error",
message: err.message,
stack: process.env.NODE_ENV === "development" ? err.stack : undefined,
});
}
});

/**
* Get all team software code by contest ID
* @param {string} contest_id - Contest ID
Expand Down
Loading