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
7 changes: 4 additions & 3 deletions app/api/auth/register/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { eq } from "drizzle-orm";
import { generateSessionToken, createSession } from "@/lib/server/session";
import { setSessionTokenCookie } from "@/lib/server/cookies";
import { hashPassword } from "@/lib/password";
import { generateUsername } from "@/lib/username";

export async function POST(request: NextRequest) {
try {
Expand All @@ -25,15 +26,14 @@ export async function POST(request: NextRequest) {

const hashedPassword = await hashPassword(validatedData.password);

const nameId = await generateUsername(validatedData.email);
const [user] = await db
.insert(users)
.values({
email: validatedData.email,
hashedPassword: hashedPassword,
name: validatedData.name,

// TODO: Use a proper readable slug for nameId
nameId: validatedData.email,
nameId,
})
.returning();

Expand All @@ -45,6 +45,7 @@ export async function POST(request: NextRequest) {
_id: user.id,
email: user.email,
name: user.name,
nameId: user.nameId,
// role: user.role,
});
} catch (error) {
Expand Down
121 changes: 111 additions & 10 deletions app/api/orgs/[orgId]/contests/[contestId]/participants/route.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,130 @@
import { NextRequest, NextResponse } from "next/server";
import { z } from "zod";
import { NextRequest } from "next/server";
import { createParticipantSchema } from "@/lib/validations";
import { createParticipantSchema, NameIdSchema } from "@/lib/validations";
import * as participantService from "./service";
import { IdSchema } from "@/app/api/types";
import { getOrgIdFromNameId, getContestIdFromNameId } from "@/app/api/service";

export async function POST(
request: NextRequest,
{ params }: { params: { contestId: string } },
{ params }: { params: { orgId: string; contestId: string } },
) {
try {
const contestId = IdSchema.parse(params.contestId);
// Validate nameIds first
const orgNameId = NameIdSchema.parse(params.orgId);
const contestNameId = NameIdSchema.parse(params.contestId);

// Then get numeric IDs
const orgId = await getOrgIdFromNameId(orgNameId);
const contestId = await getContestIdFromNameId(orgId, contestNameId);
const data = createParticipantSchema.parse(await request.json());

const participant = await participantService.registerParticipant(
orgId,
contestId,
data.email,
);

return NextResponse.json(participant, { status: 201 });
} catch (error) {
if (error instanceof z.ZodError) {
return NextResponse.json({ error: error.errors }, { status: 400 });
}
if (error instanceof Error) {
if (
error.message === "Organization not found" ||
error.message === "Contest not found"
) {
return NextResponse.json({ error: error.message }, { status: 404 });
}
if (error.message === "User not found") {
return NextResponse.json({ error: error.message }, { status: 404 });
}
if (
error.message === "User already registered" ||
error.message === "Registration closed" ||
error.message === "User not allowed" ||
error.message === "User disallowed"
) {
return NextResponse.json({ error: error.message }, { status: 403 });
}
}
return NextResponse.json({ error: "Registration failed" }, { status: 500 });
}
}

export async function GET(
_req: NextRequest,
{ params }: { params: { orgId: string; contestId: string } },
) {
try {
// Validate nameIds first
const orgNameId = NameIdSchema.parse(params.orgId);
const contestNameId = NameIdSchema.parse(params.contestId);

// Then get numeric IDs
const orgId = await getOrgIdFromNameId(orgNameId);
const contestId = await getContestIdFromNameId(orgId, contestNameId);

const participants = await participantService.getContestParticipants(
orgId,
contestId,
data.userId,
);
return NextResponse.json(participants);
} catch (error) {
if (error instanceof z.ZodError) {
return NextResponse.json({ error: error.errors }, { status: 400 });
}
if (error instanceof Error) {
if (
error.message === "Organization not found" ||
error.message === "Contest not found"
) {
return NextResponse.json({ error: error.message }, { status: 404 });
}
}
return NextResponse.json(
{ error: "Failed to fetch participants" },
{ status: 500 },
);
}
}

return Response.json(participant, { status: 201 });
export async function DELETE(
request: NextRequest,
{ params }: { params: { orgId: string; contestId: string } },
) {
try {
// Validate nameIds first
const orgNameId = NameIdSchema.parse(params.orgId);
const contestNameId = NameIdSchema.parse(params.contestId);
const data = createParticipantSchema.parse(await request.json());

// Then get numeric IDs
const orgId = await getOrgIdFromNameId(orgNameId);
const contestId = await getContestIdFromNameId(orgId, contestNameId);

await participantService.removeParticipant(orgId, contestId, data.email);
return new Response(null, { status: 204 });
} catch (error) {
if (error instanceof z.ZodError) {
return Response.json({ error: error.errors }, { status: 400 });
return NextResponse.json({ error: error.errors }, { status: 400 });
}
if (error instanceof Error) {
if (
error.message === "Organization not found" ||
error.message === "Contest not found"
) {
return NextResponse.json({ error: error.message }, { status: 404 });
}
if (
error.message === "User not found" ||
error.message === "User not registered"
) {
return NextResponse.json({ error: error.message }, { status: 404 });
}
}
return Response.json(
{ error: error instanceof Error ? error.message : "Registration failed" },
return NextResponse.json(
{ error: "Failed to remove participant" },
{ status: 500 },
);
}
Expand Down
104 changes: 82 additions & 22 deletions app/api/orgs/[orgId]/contests/[contestId]/problems/route.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,50 @@
import * as problemService from "./service";
import { IdSchema } from "@/app/api/types";
import { addProblemSchema } from "@/lib/validations";

// /app/api/orgs/[orgId]/contests/[contestId]/problems/route.ts
import { NextRequest, NextResponse } from "next/server";
import { getOrgIdFromNameId, getContestIdFromNameId } from "@/app/api/service";
import { NameIdSchema } from "@/app/api/types";
import { z } from "zod";
import * as problemService from "./service";
import { getOrgIdFromNameId, getContestIdFromNameId } from "@/app/api/service";
import { addProblemSchema, NameIdSchema } from "@/lib/validations";

export async function POST(
request: NextRequest,
{ params }: { params: { contestId: string; orgId: string } },
{ params }: { params: { orgId: string; contestId: string } },
) {
try {
const contestId = IdSchema.parse(params.contestId);
// Validate nameIds first
const orgNameId = NameIdSchema.parse(params.orgId);
const contestNameId = NameIdSchema.parse(params.contestId);

// Then get numeric IDs
const orgId = await getOrgIdFromNameId(orgNameId);
const contestId = await getContestIdFromNameId(orgId, contestNameId);
const data = addProblemSchema.parse(await request.json());

const problem = await problemService.addProblemToContest(
orgId,
contestId,
data.problemId,
data.problemCode,
data.order ?? 0,
);

return Response.json(problem, { status: 201 });
return NextResponse.json(problem, { status: 201 });
} catch (error) {
if (error instanceof z.ZodError) {
return Response.json({ error: error.errors }, { status: 400 });
return NextResponse.json({ error: error.errors }, { status: 400 });
}
return Response.json(
if (error instanceof Error) {
if (
error.message === "Organization not found" ||
error.message === "Contest not found"
) {
return NextResponse.json({ error: error.message }, { status: 404 });
}
if (error.message === "Problem not found") {
return NextResponse.json({ error: error.message }, { status: 404 });
}
if (error.message === "Problem already added to contest") {
return NextResponse.json({ error: error.message }, { status: 409 });
}
}
return NextResponse.json(
{ error: "Failed to add problem to contest" },
{ status: 500 },
);
Expand All @@ -47,21 +64,64 @@ export async function GET(
const orgId = await getOrgIdFromNameId(orgNameId);
const contestId = await getContestIdFromNameId(orgId, contestNameId);

const problems = await problemService.getContestProblems(contestId);
const problems = await problemService.getContestProblems(orgId, contestId);
return NextResponse.json(problems);
} catch (error) {
if (error instanceof z.ZodError) {
return NextResponse.json({ errors: error.errors }, { status: 400 });
return NextResponse.json({ error: error.errors }, { status: 400 });
}
if (error instanceof Error) {
if (
error.message === "Organization not found" ||
error.message === "Contest not found"
) {
return NextResponse.json({ error: error.message }, { status: 404 });
}
}
return NextResponse.json(
{ error: "Failed to fetch contest problems" },
{ status: 500 },
);
}
}

export async function DELETE(
request: NextRequest,
{
params,
}: { params: { orgId: string; contestId: string; problemId: string } },
) {
try {
// Validate nameIds first
const orgNameId = NameIdSchema.parse(params.orgId);
const contestNameId = NameIdSchema.parse(params.contestId);
const problemCode = NameIdSchema.parse(params.problemId);

// Then get numeric IDs
const orgId = await getOrgIdFromNameId(orgNameId);
const contestId = await getContestIdFromNameId(orgId, contestNameId);

await problemService.removeProblemFromContest(
orgId,
contestId,
problemCode,
);
return new Response(null, { status: 204 });
} catch (error) {
if (error instanceof z.ZodError) {
return NextResponse.json({ error: error.errors }, { status: 400 });
}
if (
error instanceof Error &&
(error.message === "Organization not found" ||
error.message === "Contest not found")
) {
return NextResponse.json({ message: error.message }, { status: 404 });
if (error instanceof Error) {
if (
error.message === "Organization not found" ||
error.message === "Contest not found" ||
error.message === "Problem not found"
) {
return NextResponse.json({ error: error.message }, { status: 404 });
}
}
return NextResponse.json(
{ message: "Failed to fetch contest problems" },
{ error: "Failed to remove problem from contest" },
{ status: 500 },
);
}
Expand Down
Loading
Loading