fix: align request payloads with backend DTOs ahead of forbidNonWhitelisted#594
Conversation
…listed The boundless-nestjs backend (PR #187) is flipping its global ValidationPipe from forbidNonWhitelisted: false to true. Today any unknown body field is silently stripped; after the flip, sending such a field produces a 400. This commit fixes every front-end -> back-end payload the audit flagged. 1. Team posts (CreateTeamPostModal, ContactTeamModal, team-detail page). - lib/api/hackathons/teams.ts: replaced `contactInfo: string` and `contactMethod` with `contactInfo?: TeamContactInfo` matching the backend shape `{ telegram?, discord?, email? }`. Added a readTeamContact() helper that flattens the sparse object back to `{ method, value }` for UI display. - CreateTeamPostModal: form schema keeps the `{ method, value }` UX, but onSubmit projects to `{ contactInfo: { [method]: value } }` before calling the api. github and other were removed from the method enum because the backend cannot store them. Edit-mode initial-data hydrates from readTeamContact(). - ContactTeamModal: replaced direct string access with readTeamContact() and used contact.method/value throughout. The github icon case was dropped since github is no longer a valid method. - team-detail page: same readTeamContact() flattening for the Contact card. 2. Judging score (lib/api/hackathons/judging.ts, useScoreForm). The submitJudgingScore request used `comment` for the global per-judge note, but the backend ScoreSubmissionDto names the field `notes`. Renamed the interface field and the call site. Per-criterion `comment` stays because CriterionScoreDto does accept it. 3. Submission update (hooks/hackathon/use-submission.ts). Added a pickUpdateSubmissionFields() whitelist mapping to the backend UpdateSubmissionDto. The update path now strips: - participationType, teamId, teamName (Create-only) - organizationId, hackathonId, participantId (server-derived) - per-entry email on teamMembers (TeamMemberDto has no email) ...before PATCH /hackathons/submissions/:id. The form continues to send the full shape; the hook filters at the API boundary so future callers are also protected. 4. User profile (lib/api/auth.ts). Removed the `preferences` block from UpdateUserProfileRequest. The backend UpdateProfileDto has no such field; users use the dedicated /users/settings/* endpoints for appearance / language / timezone / notification toggles. The wide type was the same kind of unknown- field door the audit is closing. 5. Hackathon update (lib/api/hackathons.ts). UpdateHackathonRequest was `Partial<Hackathon> & { rewards? }`, which let any Hackathon-shape field (id, organizationId, status, creatorId, ...) leak into PUT /hackathons/:id. Narrowed to just `{ rewards?: HackathonRewards }` since that is the only field actually sent today (JudgingResultsTable's rank-override save). Out of scope (separate pre-existing bugs to file): - PUT /hackathons/:id has no matching backend route; the rank- override save in JudgingResultsTable is broken regardless of #187. - POST /users/earnings/claim does not match backend /users/earnings/ withdraw. - POST /organizations/:id/invite does not match backend /invitations. Backend #188 (global ThrottlerGuard + 429 handling): no frontend changes needed. lib/api/api.ts already honours Retry-After and exponential backoff (1s, 2s, 4s) for three retries before surfacing a RATE_LIMIT_EXCEEDED error code. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Warning Review limit reached
More reviews will be available in 52 minutes and 53 seconds. Learn how PR review limits work. Your organization has run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (9)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
…o shape
Found during code review of the previous commit on this branch.
The strip on teamMembers only removed `email`, but the frontend
SubmissionFormData also lets each entry carry `avatar` — and the
backend TeamMemberDto only has { userId, name, username?, role }.
Sending `avatar` would 400 once forbidNonWhitelisted lands.
Switched from a deny-list strip (`{ email: _email, ...rest }`) to an
explicit project-down to the four backend fields. Same defense-in-
depth pattern as the outer pickUpdateSubmissionFields whitelist.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Companion to backend PR boundlessfi/boundless-nestjs#187, which flips the global
ValidationPipefromforbidNonWhitelisted: falsetotrue. Today any unknown body field is silently stripped; after the flip, sending such a field returns a 400 with class-validator'sproperty X should not exist.This PR fixes every front-end -> back-end payload an audit flagged.
Changes
1. Team posts —
contactInfoshape +contactMethoddroppedBackend
CreateTeamDto/UpdateTeamDtoexpectcontactInfoas a sparse object{ telegram?, discord?, email? }; the channel is implicit in the populated key. There is no separatecontactMethodfield.lib/api/hackathons/teams.ts: introducesTeamContactInfotype andreadTeamContact(info)helper that flattens the sparse object back to{ method, value }for UI display.Team.contactInfoandCreateTeamRequest.contactInfoswitch to the new shape;contactMethodis removed from both.CreateTeamPostModal.tsx: keeps the form UX (single channel selector + string input), but the submit handler projects to{ contactInfo: { [method]: value } }before calling the API. Edit-mode initial data hydrates viareadTeamContact().githubandotherwere removed from the method enum because the backend cannot store them.ContactTeamModal.tsx: replacedcontactInfo/contactMethoddirect access withreadTeamContact()+contact.method/contact.value. The github icon branch was dropped.app/(landing)/hackathons/[slug]/teams/[teamId]/page.tsx: samereadTeamContact()flattening on the team detail page.2. Judging score —
comment->notesSubmitJudgingScoreRequest.commentwas renamed tonotesto match the backendScoreSubmissionDto.notes. Per-criterioncommentstays becauseCriterionScoreDtodoes accept it.lib/api/hackathons/judging.ts: field rename.components/organization/cards/GradeSubmissionModal/useScoreForm.ts: call site updated.3. Submission update — whitelist filter at the hook layer
hooks/hackathon/use-submission.tsnow exposes apickUpdateSubmissionFields()mapping to the backendUpdateSubmissionDto. The update path strips:participationType,teamId,teamName(Create-only)organizationId,hackathonId,participantId(server-derived)emailonteamMembers(TeamMemberDtohas noemailfield)before
PATCH /hackathons/submissions/:id. The form continues to send the full shape; the hook filters at the API boundary so future callers are also protected.4. User profile — removed
preferencesblockUpdateUserProfileRequest.preferenceshad no counterpart on backendUpdateProfileDto. The frontend already uses the dedicated/users/settings/*endpoints for appearance / language / timezone / notifications, so this was an unused typed door. Closed it.5. Hackathon update — narrowed
UpdateHackathonRequestWas
Partial<Hackathon> & { rewards? }, which let any Hackathon-shape field (id,organizationId,status,creatorId, ...) leak intoPUT /hackathons/:id. Narrowed to just{ rewards?: HackathonRewards }since that's the only field actually sent today (the rank-override save inJudgingResultsTable).PublishHackathonRequest extends Hackathonwas left alone because the publish flow legitimately sends a full hackathon shape.Out of scope (separate pre-existing bugs)
The audit also surfaced URL mismatches between frontend and backend that are not whitelist issues:
PUT /hackathons/:idhas no matching backend route. The rank-override save inJudgingResultsTableis broken regardless of feat: Add 'What Makes Boundless Different' Section #187.POST /users/earnings/claimdoesn't match backend/users/earnings/withdraw.POST /organizations/:id/invitedoesn't match backend/invitations.Worth filing as separate frontend tickets.
Backend PR #188 (global ThrottlerGuard + per-route 429 limits)
No frontend changes needed.
lib/api/api.tsalready honoursRetry-Afterand exponential backoff (1s / 2s / 4s) for three retries before surfacing aRATE_LIMIT_EXCEEDEDerror code. Verified during the audit.Test plan
npm run type-check— cleannpm run lint— cleanprettier --check .— cleannpm run build— pre-commit build passespreferencesregression)Coordination
Merge after backend PR #187 lands on staging so dev / staging do not 400 in the interim window. The changes here are backward-compatible with the current
forbidNonWhitelisted: falsebackend.🤖 Generated with Claude Code