-
Notifications
You must be signed in to change notification settings - Fork 7
Minor fixes #38
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Minor fixes #38
Changes from all commits
20befe4
6860584
4c3d7f5
eeddab1
8f23aea
f90dd5b
cd1342f
f5e2a6d
75ab289
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -46,7 +46,7 @@ export const createAMember = | |||||||||||||||||||||||||||||||
| (supabase: SupabaseClient) => async (req: Request, res: Response) => { | ||||||||||||||||||||||||||||||||
| const {email, name, password, passoutYear, provider} = req.body; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| if (!email || !name || !password || !passoutYear) { | ||||||||||||||||||||||||||||||||
| if (!email || !name || !password || !passoutYear || !provider) { | ||||||||||||||||||||||||||||||||
| throw new ApiError("Required fields absent", 400); | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
Comment on lines
+49
to
51
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Verification agent 🧩 Analysis chainDon't require password for OAuth signups; refine validation to depend on provider. Requiring Apply this diff: - if (!email || !name || !password || !passoutYear || !provider) {
+ if (!email || !name || !passoutYear || !provider || (provider === "credentials" && !password)) {
throw new ApiError("Required fields absent", 400);
}Run to verify no callers use unsupported provider values (e.g., "local") and that tests align with status code 400: 🏁 Script executed: #!/bin/bash
# Check for unsupported provider literals like "local"
rg -n "provider\\s*:\\s*['\"]local['\"]" -S --glob '!node_modules' .
# Check for tests expecting status 402 for 'Required fields absent'
rg -n "Required fields absent" --glob '!node_modules' tests | sed 's/:/ /' | while read -r f l _; do sed -n "${l}p" "$f"; doneLength of output: 276 🏁 Script executed: #!/bin/bash
# Search for any code comparing provider to "local"
rg -nE 'provider\s*===?\s*["'"'"']local["'"'"']' -S --glob '!node_modules' .
# Show tests that reference the ApiError message "Required fields absent" with context
rg -n -A2 "Required fields absent" -S --glob '!node_modules' tests/Length of output: 395 Refine password validation and align test expectations Update the signup validation so that • In - if (!email || !name || !password || !passoutYear || !provider) {
+ if (
+ !email ||
+ !name ||
+ !passoutYear ||
+ !provider ||
+ (provider === "credentials" && !password)
+ ) {
throw new ApiError("Required fields absent", 400);
}• In - await expect(handler(req, res)).rejects.toThrow(new ApiError('Required fields absent', 402));
+ await expect(handler(req, res)).rejects.toThrow(new ApiError('Required fields absent', 400));No references to a 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
|
|
@@ -89,7 +89,8 @@ export const updateAMember = | |||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| if (imageUrl) parsedBody.profilePhoto = imageUrl; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| await memberService.updateMember(memberId, parsedBody); | ||||||||||||||||||||||||||||||||
| if(parsedBody.password) await memberService.updatePassword(memberId, parsedBody.password); | ||||||||||||||||||||||||||||||||
| else await memberService.updateMember(memberId, parsedBody); | ||||||||||||||||||||||||||||||||
|
Comment on lines
+92
to
+93
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Password-only branch discards other field updates; update both. If payload includes Apply this diff: - if(parsedBody.password) await memberService.updatePassword(memberId, parsedBody.password);
- else await memberService.updateMember(memberId, parsedBody);
+ const { password, ...rest } = parsedBody;
+ if (password) {
+ await memberService.updatePassword(memberId, password);
+ }
+ if (Object.keys(rest).length) {
+ await memberService.updateMember(memberId, rest);
+ }For stronger consistency, consider wrapping both updates in a single service method using a Prisma transaction to make the operation atomic. 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| const updatedData = await memberService.getDetails(memberId); | ||||||||||||||||||||||||||||||||
| res | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -66,33 +66,35 @@ export const createMember = async ( | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export const updateMember = async ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| id: string, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| payload: UpdateMemberPayload, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| payload: UpdateMemberPayload | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+69
to
70
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Verification agent 🧩 Analysis chainConfirm UpdateMemberPayload shape across modules. Ensure 🏁 Script executed: #!/bin/bash
# Inspect UpdateMemberPayload for a 'password' property
rg -n "interface\\s+UpdateMemberPayload" -A 20 src --glob '!node_modules'Length of output: 771 🏁 Script executed: #!/bin/bash
# Show the updateMember method and how it handles the payload
rg -n "updateMember" -C 5 src/services/member.service.tsLength of output: 246 Remove
This guarantees that 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const { name, ...rest } = payload; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const member = await prisma.member.findUnique({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| where: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| id: id, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!member) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| throw new ApiError("Member not found", 404); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const dataToUpdate = Object.fromEntries( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Object.entries(rest).filter(([_, v]) => v !== undefined), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Object.entries({ name, ...rest }).filter(([_, v]) => v !== undefined) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (JSON.stringify(dataToUpdate) === "{}") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| throw new ApiError("No fields passed", 400); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (JSON.stringify(dataToUpdate) === "{}") throw new ApiError("No fields passed", 400); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return await prisma.member.update({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| where: { id }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| data: dataToUpdate, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export const updatePassword = async(id: string, password: string) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const account = await prisma.account.findFirst({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| where: { memberId: id }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!account) throw new ApiError("Associated account not found", 404); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return await prisma.account.update({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| where: { id: account.id }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| data: { password }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+85
to
+96
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Verification agent 🧩 Analysis chainHash passwords and block password updates for non-credentials accounts.
Apply this diff to enforce provider and hash the password: export const updatePassword = async(id: string, password: string) => {
const account = await prisma.account.findFirst({
where: { memberId: id },
});
if (!account) throw new ApiError("Associated account not found", 404);
- return await prisma.account.update({
+ if (account.provider !== "credentials") {
+ throw new ApiError("Password updates not allowed for OAuth accounts", 400);
+ }
+
+ const hashed = await bcrypt.hash(password, 12);
+ return await prisma.account.update({
where: { id: account.id },
- data: { password },
+ data: { password: hashed },
});
}Add the import (outside this hunk): import bcrypt from "bcryptjs";To ensure no other code paths write plaintext passwords, search for raw 🏁 Script executed: #!/bin/bash
rg -n "password\\s*:\\s*password\\b" --glob '!node_modules' .
rg -n "argon2|bcrypt" --glob '!node_modules' .Length of output: 109 🏁 Script executed: #!/bin/bash
# Find all prisma.account.create/update calls to see where password fields may be set
rg -n 'prisma\.account\.(create|update)' --glob '!node_modules' -A3 .Length of output: 563 🏁 Script executed: #!/bin/bash
# Show full prisma.account.create call to check for password assignments
rg -n 'prisma\.account\.create' --glob '!node_modules' -A10 src/services/member.service.tsLength of output: 346 Enforce password hashing at creation and restrict updates to credentials-only accounts The current implementation stores plaintext passwords on signup and allows password changes for OAuth users. To address both issues: • Add bcrypt import at the top of src/services/member.service.ts Diff for src/services/member.service.ts: --- a/src/services/member.service.ts
+++ b/src/services/member.service.ts
@@
+ import bcrypt from "bcryptjs";
// ...signup or createMember function...
- await prisma.account.create({
- data: {
- provider,
- providerAccountId: email,
- password: provider === "credentials" ? password : null,
- memberId: newMember.id,
- },
- });
+ const hashedPassword = provider === "credentials"
+ ? await bcrypt.hash(password, 12)
+ : null;
+ await prisma.account.create({
+ data: {
+ provider,
+ providerAccountId: email,
+ password: hashedPassword,
+ memberId: newMember.id,
+ },
+ });
// ...later in the file...
export const updatePassword = async (id: string, password: string) => {
const account = await prisma.account.findFirst({
where: { memberId: id },
});
if (!account) throw new ApiError("Associated account not found", 404);
-
- return await prisma.account.update({
- where: { id: account.id },
- data: { password },
- });
+ if (account.provider !== "credentials") {
+ throw new ApiError("Password updates not allowed for OAuth accounts", 400);
+ }
+
+ const newHashed = await bcrypt.hash(password, 12);
+ return await prisma.account.update({
+ where: { id: account.id },
+ data: { password: newHashed },
+ });
};This ensures no plaintext passwords are stored and prevents OAuth accounts from updating passwords. 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export const unapprovedMembers = async () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return await prisma.member.findMany({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| where: { isApproved: false }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -15,5 +15,6 @@ declare global { | |
| leetcode?: string; | ||
| codechef?: string; | ||
| codeforces?: string; | ||
| password?: string | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Enhanced validation looks good.
The addition of
providervalidation is consistent with the existing pattern and strengthens the member creation flow.Verify that all API clients and tests include the
providerfield when calling this endpoint:🏁 Script executed:
Length of output: 6644
Tests and API docs must be updated for the new
providervalidationThe added
provider—and previously existingpassoutYear—fields now break thecreateAMembertests and out-of-date JSDoc. Please:• tests/Member.test.ts
– Include both
passoutYearandproviderin thereq.bodyfixtures for your createAMember tests.– Example diff:
it('should respond with 201 and created member', async () => { - const req = { - body: { - email: 'shruti@example.com', - name: 'Shruti', - password: 'password123', - } - } as Request; + const req = { + body: { + email: 'shruti@example.com', + name: 'Shruti', + password: 'password123', + passoutYear: 2025, + provider: 'local', + } + } as Request;• src/routes/members.ts
– Update your route JSDoc to surface the new parameters:
* @apiBody {String} [name] Full name of the member. + * @apiBody {Number} passoutYear Graduation year of the member. + * @apiBody {String} provider Authentication provider for the member. * @apiBody {File} [file] Profile photo file (field name: "file").Once tests and docs are aligned with the controller’s required fields, the build will pass and the validation enhancement remains effective.
📝 Committable suggestion
🤖 Prompt for AI Agents
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@shrutiiiyet
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.