diff --git a/scripts/seed.mjs b/scripts/seed.mjs index a7a6249..ffca0d2 100644 --- a/scripts/seed.mjs +++ b/scripts/seed.mjs @@ -8,10 +8,10 @@ const client = createClient({ //Users have username: testUser and testUser2, with passswords: ABC@1234, DEF@1234 respectively (async () => { await client.batch([ - "INSERT OR IGNORE INTO user (id, username, password, time_added, time_updated, points, roles) VALUES ('hwurhw64luqmednj5xqh5suwzm', 'testUser', '$2a$10$F3TH05I6LGRLq3Ws6.NPlOwbXsbjtXCcBedjMuewuUVcmjLEMU1LW', 1738639335, 1738639335, 0, 'admin')", - "INSERT OR IGNORE INTO user (id, username, password, time_added, time_updated, points, roles) VALUES ('n4cpqnzaf4cdmxzuwwlxmberpq', 'testUser2', '$2a$10$zi2/N.Ry3euMsMviLp9IvurB/jjf8nkuTUePmPFCNWTl4OZYkohla', 1738639335, 1738639335, 0, 'admin')", - "INSERT OR IGNORE INTO personal_info (user_id, first_name, last_name, email, phone, area_code) VALUES ('hwurhw64luqmednj5xqh5suwzm', 'Test', 'User', 'testuser@gmail.com', 1234567890, 123)", - "INSERT OR IGNORE INTO personal_info (user_id, first_name, last_name, email, phone, area_code) VALUES ('n4cpqnzaf4cdmxzuwwlxmberpq', 'Test', 'User2', 'testuser2@gmail.com', 1112223333, 111)", + "INSERT OR IGNORE INTO user (id, username, password, time_added, time_updated, points, roles, email) VALUES ('hwurhw64luqmednj5xqh5suwzm', 'testUser', '$2a$10$F3TH05I6LGRLq3Ws6.NPlOwbXsbjtXCcBedjMuewuUVcmjLEMU1LW', 1738639335, 1738639335, 0, 'admin', 'testuser@gmail.com')", + "INSERT OR IGNORE INTO user (id, username, password, time_added, time_updated, points, roles, email) VALUES ('n4cpqnzaf4cdmxzuwwlxmberpq', 'testUser2', '$2a$10$zi2/N.Ry3euMsMviLp9IvurB/jjf8nkuTUePmPFCNWTl4OZYkohla', 1738639335, 1738639335, 0, 'admin', 'testuser2@gmail.com')", + "INSERT OR IGNORE INTO personal_info (user_id, first_name, last_name, phone, area_code) VALUES ('hwurhw64luqmednj5xqh5suwzm', 'Test', 'User', 1234567890, 123)", + "INSERT OR IGNORE INTO personal_info (user_id, first_name, last_name, phone, area_code) VALUES ('n4cpqnzaf4cdmxzuwwlxmberpq', 'Test', 'User2', 1112223333, 111)", "INSERT OR IGNORE INTO professional_info (user_id, resume_path, linkedin, portfolio, majors, minors, graduation_semester) VALUES ('hwurhw64luqmednj5xqh5suwzm', 'ufsase.com', 'linkedin.com', 'github.com', 'Computer Science', 'Mathematics', 'Spring 2025')", "INSERT OR IGNORE INTO professional_info (user_id, resume_path, linkedin, portfolio, majors, minors, graduation_semester) VALUES ('n4cpqnzaf4cdmxzuwwlxmberpq', 'ufsase.com', 'linkedin.com', 'github.com', 'Computer Science', 'Statistics', 'Spring 2027')", "INSERT OR IGNORE INTO sase_info (user_id, events_attended, groups) VALUES ('hwurhw64luqmednj5xqh5suwzm', 'eventID', 'SASE')", diff --git a/src/client/components/UserForm.tsx b/src/client/components/UserForm.tsx index 140a0f7..e49b42c 100644 --- a/src/client/components/UserForm.tsx +++ b/src/client/components/UserForm.tsx @@ -10,6 +10,7 @@ export const UserForm = () => { password: "", points: 0, roles: "user", + email: "", }); // TODO: Get id from user context @@ -25,6 +26,7 @@ export const UserForm = () => { password: "", points: 0, roles: "user", + email: "", }); }, }); diff --git a/src/server/api/auth.ts b/src/server/api/auth.ts index 6f80ec9..756a05d 100644 --- a/src/server/api/auth.ts +++ b/src/server/api/auth.ts @@ -13,6 +13,8 @@ authRoutes.post("/auth/signup", async (c) => { const formData = await c.req.json(); const formUsername = formData["username"]; const formPassword = formData["password"]; + const formEmail = formData["email"]; + const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/; //validate username if (!formUsername || typeof formUsername !== "string") { @@ -20,12 +22,19 @@ authRoutes.post("/auth/signup", async (c) => { status: 400, }); } - + //validate password if (!formPassword || typeof formPassword !== "string") { return new Response("Invalid password!", { status: 400, }); } + //validate email + // add 3rd validation for email using regular expressions + if (!formEmail || typeof formEmail !== "string" || !emailRegex.test(formEmail)) { + return new Response("Invalid email!", { + status: 400, + }); + } const passSalt = await genSalt(10); const formPasswordHash = await hash(formPassword, passSalt); @@ -37,6 +46,7 @@ authRoutes.post("/auth/signup", async (c) => { id: userId, username: formUsername, password: formPasswordHash, + email: formEmail, }); return new Response("User successfully created!", { diff --git a/src/server/db/tables.ts b/src/server/db/tables.ts index 9482720..406cee4 100644 --- a/src/server/db/tables.ts +++ b/src/server/db/tables.ts @@ -11,6 +11,7 @@ export const users = sqliteTable("user", { .$defaultFn(() => generateIdFromEntropySize(10)), username: text("username").notNull().unique(), password: text("password").notNull(), + email: text("email").notNull().unique(), // TODO: password_hash // password_hash: text("password_hash").notNull(), @@ -41,7 +42,6 @@ export const personalInfo = sqliteTable("personal_info", { .references(() => users.id), first_name: text("first_name").notNull(), last_name: text("last_name").notNull(), - email: text("email").notNull().unique(), phone: blob("phone", { mode: "bigint" }).unique(), // NOTE: Phone is bigint area_code: integer("area_code"), }); diff --git a/src/shared/schema/personalInfoSchema.ts b/src/shared/schema/personalInfoSchema.ts index ab5b95a..2d04987 100644 --- a/src/shared/schema/personalInfoSchema.ts +++ b/src/shared/schema/personalInfoSchema.ts @@ -5,7 +5,6 @@ export const personalInfoSchema = z.object({ user_id: z.string().min(1, "User ID is required."), first_name: z.string().min(1, "First name is required."), last_name: z.string().min(1, "Last name is required."), - email: z.string().email("Invalid email address."), phone: z.bigint().optional(), area_code: z.number().int().min(0).optional(), }); diff --git a/src/shared/schema/userSchema.ts b/src/shared/schema/userSchema.ts index 67e5371..6140762 100644 --- a/src/shared/schema/userSchema.ts +++ b/src/shared/schema/userSchema.ts @@ -4,6 +4,7 @@ export const userSchema = z.object({ id: z.string().min(1, "User ID is required."), username: z.string().min(1, "Username is required."), password: z.string().min(1, "Password is required."), + email: z.string().email("Invalid email address."), time_added: z.number().int().min(0, "Time added must be a valid timestamp."), time_updated: z.number().int().min(0, "Time updated must be a valid timestamp."), points: z.number().int().min(0).optional(),