Skip to content
Open
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
16 changes: 1 addition & 15 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

88 changes: 88 additions & 0 deletions src/lib/registration.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
export type RegistrationForm = {
firstName: string;
lastName: string;

school: string;

major: string;
gradYear: string;

email: string;
dob: string;

allergies: string[];
allergiesOther: string;

tshirtSize: string;

projectIdea: string;
};

export type ValidationResult = {
ok: boolean;
errors: Record<string, string>;
data?: RegistrationForm;
};


export function countWords(text: string) {
return text.trim().split(/\s+/).filter(Boolean).length;
}

export function validateRegistration(payload: RegistrationForm): ValidationResult {
const errors: Record<string, string> = {};

const reqStr = (key: keyof RegistrationForm) => {
const v = payload?.[key];
if (typeof v !== "string" || v.trim().length === 0) errors[String(key)] = "Required";
};

reqStr("firstName");
reqStr("lastName");
reqStr("email");
reqStr("dob");
Copy link

Copilot AI Dec 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The date of birth field lacks validation to ensure users are of appropriate age for the hackathon. There's no check to prevent future dates or verify minimum age requirements. Consider adding validation to ensure the DOB is in the past and meets any age eligibility requirements.

Copilot uses AI. Check for mistakes.
reqStr("major");
reqStr("gradYear");
reqStr("school");
reqStr("tshirtSize");
reqStr("projectIdea");

if (typeof payload?.email === "string" && !/^\S+@\S+\.\S+$/.test(payload.email)) {
errors.email = "Invalid email";
}
Comment on lines +50 to +52
Copy link

Copilot AI Dec 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The email validation regex is overly simplistic and will accept many invalid email addresses (e.g., "a@b.c" or emails with invalid characters). Consider using a more robust email validation pattern or a dedicated email validation library to ensure proper email format validation.

Copilot uses AI. Check for mistakes.

if (payload?.school === "__OTHER__") {
errors.schoolOther = "Please type your school";
}
Comment on lines +54 to +56
Copy link

Copilot AI Dec 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The validation error message "Please type your school" will always trigger when the school value is "OTHER", but this check should verify that the user has provided an actual school name when "Other" is selected. This validation doesn't check the otherSchoolName field that the user fills in. The validation logic should check a separate field or the actual school name after the frontend sets it.

Copilot uses AI. Check for mistakes.

if (payload?.allergies != null) {
if (!Array.isArray(payload.allergies) || payload.allergies.some((x: any) => typeof x !== "string")) {
errors.allergies = "Invalid allergies";
}
}

if (typeof payload?.projectIdea === "string") {
const words = countWords(payload.projectIdea);
if (words > 250) errors.projectIdea = `Too long (${words}/250 words)`;
}

if (Object.keys(errors).length > 0) {
return { ok: false, errors };
}

const data: RegistrationForm = {
firstName: payload.firstName.trim(),
lastName: payload.lastName.trim(),
school: payload.school.trim(),
major: payload.major.trim(),
gradYear: payload.gradYear.trim(),
email: payload.email.trim(),
dob: payload.dob.trim(),
allergies: Array.isArray(payload.allergies) ? payload.allergies : [],
allergiesOther: (payload.allergiesOther ?? "").trim(),
tshirtSize: payload.tshirtSize.trim(),
projectIdea: payload.projectIdea.trim()
};

return { ok: true, errors: {}, data };
}
34 changes: 34 additions & 0 deletions src/routes/api/register/+server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { json } from "@sveltejs/kit";
import type { RequestHandler } from "./$types";
import { validateRegistration } from "$lib/registration";

Copy link

Copilot AI Dec 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Storing submissions in an in-memory array will lose all data when the server restarts. This is not suitable for production use. Consider implementing persistent storage using a database or file system, or at minimum, add a comment documenting that this is temporary/for development only.

Suggested change
// NOTE: In-memory store for development/debugging only. Not suitable for production.
// In production, replace this with persistent storage (e.g., database or filesystem).

Copilot uses AI. Check for mistakes.
const submissions: any[] = [];

export const POST: RequestHandler = async ({ request }) => {
Copy link

Copilot AI Dec 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no rate limiting or authentication on this endpoint. Without proper protection, the API is vulnerable to spam submissions and abuse. Consider implementing rate limiting (e.g., by IP address) and/or authentication to prevent malicious actors from flooding the system with registrations.

Suggested change
export const POST: RequestHandler = async ({ request }) => {
const RATE_LIMIT_WINDOW_MS = 60_000; // 1 minute
const RATE_LIMIT_MAX_REQUESTS = 10; // max registrations per IP per window
type RateLimitEntry = {
count: number;
firstRequestTime: number;
};
const rateLimitMap = new Map<string, RateLimitEntry>();
function isRateLimited(ip: string): boolean {
const now = Date.now();
const existing = rateLimitMap.get(ip);
if (!existing) {
rateLimitMap.set(ip, { count: 1, firstRequestTime: now });
return false;
}
if (now - existing.firstRequestTime > RATE_LIMIT_WINDOW_MS) {
// window has passed; reset counter
rateLimitMap.set(ip, { count: 1, firstRequestTime: now });
return false;
}
existing.count += 1;
rateLimitMap.set(ip, existing);
return existing.count > RATE_LIMIT_MAX_REQUESTS;
}
export const POST: RequestHandler = async ({ request, getClientAddress }) => {
const clientIp = typeof getClientAddress === "function" ? getClientAddress() : null;
if (clientIp && isRateLimited(clientIp)) {
return json({ ok: false, error: "Too many requests" }, { status: 429 });
}

Copilot uses AI. Check for mistakes.
let body: any;
Comment on lines +5 to +8
Copy link

Copilot AI Dec 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The 'any' type removes type safety. Consider creating a proper type for the submission object or using 'unknown' with proper type guards. This would help catch potential type-related bugs at compile time.

Suggested change
const submissions: any[] = [];
export const POST: RequestHandler = async ({ request }) => {
let body: any;
type RegistrationData = ReturnType<typeof validateRegistration> extends { ok: true; data: infer D }
? D
: never;
type Submission = RegistrationData & {
id: string;
createdAt: string;
};
const submissions: Submission[] = [];
export const POST: RequestHandler = async ({ request }) => {
let body: unknown;

Copilot uses AI. Check for mistakes.
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only valid thing, but we r in web. Types here are basically non existent u little piece of scrap

Copy link

Copilot AI Dec 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The 'any' type removes type safety. Since the request body should conform to the RegistrationForm type, consider typing this as 'unknown' initially and letting the validation function handle type checking, or explicitly type it as 'any' only in the catch block.

Suggested change
let body: any;
let body: unknown;

Copilot uses AI. Check for mistakes.
try {
body = await request.json();
} catch {
return json({ ok: false, error: "Invalid JSON" }, { status: 400 });
Comment on lines +11 to +12
Copy link

Copilot AI Dec 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error message "Invalid JSON" is vague and doesn't help users understand what went wrong. Consider providing more context, such as "Request body must be valid JSON" or including the parsing error details in development mode.

Suggested change
} catch {
return json({ ok: false, error: "Invalid JSON" }, { status: 400 });
} catch (err: unknown) {
let message = "Request body must be valid JSON.";
if (process.env.NODE_ENV === "development" && err instanceof Error && err.message) {
message += ` Parsing error: ${err.message}`;
}
return json({ ok: false, error: message }, { status: 400 });

Copilot uses AI. Check for mistakes.
}

const result = validateRegistration(body);
if (!result.ok) {
return json({ ok: false, errors: result.errors }, { status: 400 });
}

const submission = {
...result.data,
id: crypto.randomUUID(),
createdAt: new Date().toISOString()
};

submissions.push(submission);

return json({ ok: true, id: submission.id });
};

export const GET: RequestHandler = async () => {
// return last 20 for debugging
return json({ ok: true, count: submissions.length, submissions: submissions.slice(-20).reverse() });
Comment on lines +32 to +33
Copy link

Copilot AI Dec 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The GET endpoint exposes all submission data without any authentication or authorization. This is a security concern as it allows anyone to view potentially sensitive user information (emails, DOB, etc.). Consider adding authentication or removing this endpoint in production, or at minimum add a comment that it's for debugging only.

Suggested change
// return last 20 for debugging
return json({ ok: true, count: submissions.length, submissions: submissions.slice(-20).reverse() });
// Debugging endpoint: returns last 20 submissions.
// This is disabled in production to avoid exposing potentially sensitive data.
if (process.env.NODE_ENV === "production") {
return json({ ok: false, error: "Not available in production" }, { status: 404 });
}
return json({
ok: true,
count: submissions.length,
submissions: submissions.slice(-20).reverse()
});

Copilot uses AI. Check for mistakes.
};
Loading