-
Notifications
You must be signed in to change notification settings - Fork 182
Fix: prevent PRO-only listing data exposure to non-PRO users #1296
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
base: staging
Are you sure you want to change the base?
Fix: prevent PRO-only listing data exposure to non-PRO users #1296
Conversation
…s based on viewer's pro status
|
@mitgajera is attempting to deploy a commit to the Superteam Team on Vercel. A member of the Team first needs to authorize it. |
WalkthroughThis PR implements server-side access control for PRO-only listing data by integrating Privy-based authentication to sanitize sensitive fields (description, eligibility, requirements, etc.) from API responses when viewers are non-PRO. Additionally, it enhances the payout submission UI with transaction link visualization for multi-tranche projects. Changes
Sequence DiagramsequenceDiagram
participant Client
participant Page as Listing Page
participant API as API Route
participant Privy
participant DB as Prisma/DB
participant Sanitizer as Data Sanitizer
Client->>Page: Request listing page (with auth context)
Page->>Privy: getPrivyToken(context.req)
Privy-->>Page: privyDid token
Page->>DB: Query user isPro flag (via privyDid)
DB-->>Page: { isPro: boolean }
Page->>API: fetch listing details + { viewerIsPro }
API->>DB: Fetch full listing data
DB-->>API: Complete listing payload
API->>Sanitizer: sanitizeListingForViewer(listing, viewerIsPro)
alt viewerIsPro = true
Sanitizer-->>API: Return full listing
else viewerIsPro = false
Sanitizer-->>API: Mask sensitive fields (description, eligibility, requirements, etc.)
end
API-->>Page: Sanitized listing response
Page-->>Client: Render listing (data filtered by Pro status)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes
Possibly related PRs
Poem
Pre-merge checks and finishing touches❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✨ 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 |
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.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
src/pages/api/listings/details/[slug].ts (1)
135-186: Rate limiting has been removed from this public endpoint.Per retrieved learnings, public API endpoints should implement rate limiting to prevent abuse. The AI summary indicates rate-limit checks were removed. For a publicly accessible endpoint that performs database queries, this could enable:
- Enumeration attacks on listing slugs
- Resource exhaustion via repeated requests
Consider re-adding rate limiting or confirming this is intentional and handled elsewhere (e.g., at the infrastructure/CDN layer).
Based on learnings, implement rate limiting and proper authorization checks for public API endpoints to enhance security and prevent abuse.
src/app/api/server-time/route.ts (1)
5-48: Implement rate limiting for this public endpoint to prevent abuse.A
serverTimeRateLimiterexists in the codebase (30 requests per minute) but is not being used in this route. Public endpoints require rate limiting per the app-api-guidelines and to protect against denial-of-service attacks. Import and applyserverTimeRateLimiterto the GET handler:import { serverTimeRateLimiter } from '@/lib/ratelimit'; export async function GET(request: NextRequest) { const { success } = await serverTimeRateLimiter.limit(request.ip || '127.0.0.1'); if (!success) { return Response.json({ error: 'Rate limit exceeded' }, { status: 429 }); } // ... rest of handler }src/features/sponsor-dashboard/components/Submissions/PayoutSection.tsx (1)
247-303: Fix logic error: payout button shown for paid submissions when transaction links are hidden.When
submission.isPaidistrueandshouldHideTxLinksistrue, the first condition at line 247 fails, causing the code to fall through to line 282. SinceshouldHideTxLinksimplieslistingStatus === 'Completed', bothbounty.isWinnersAnnouncedand!isFndnToPayare true, incorrectly displaying thePayoutButtonfor an already-paid submission.Apply this diff to fix the fallthrough logic:
<TableCell> <div className="flex items-center gap-2"> {submission.isPaid && !shouldHideTxLinks ? ( isProject && hasMultipleTranches ? ( <div className="flex cursor-pointer items-center gap-1 text-sm font-medium text-slate-600 hover:text-slate-900" onClick={() => toggleExpandRow(submission.id)} > <span>View transaction links</span> <ChevronDown className={cn( 'h-4 w-4 text-slate-400 transition-transform duration-300 ease-in-out', isExpanded ? 'rotate-180' : 'rotate-0', )} /> </div> ) : ( <div className="flex cursor-pointer items-center text-sm font-medium text-slate-600 hover:text-slate-900" onClick={() => { const txId = submission.paymentDetails?.[0]?.txId; if (txId) { window.open( `https://solscan.io/tx/${txId}?cluster=${process.env.NEXT_PUBLIC_PAYMENT_CLUSTER}`, '_blank', ); } }} > {submission.paymentDetails?.[0]?.txId && truncatePublicKey( submission.paymentDetails[0].txId, 5, )} <ExternalLink className="ml-1 h-4 w-4" /> </div> ) - ) : bounty.isWinnersAnnounced && !isFndnToPay ? ( + ) : !submission.isPaid && bounty.isWinnersAnnounced && !isFndnToPay ? ( <div className="flex items-center"> <PayoutButton bounty={bounty} submission={submission} /> {hasMultipleTranches && ( <span onClick={() => toggleExpandRow(submission.id)} > <ChevronDown className={cn( 'ml-8 h-4 w-4 text-slate-400 transition-transform duration-300 ease-in-out', isExpanded ? 'rotate-180' : 'rotate-0', )} /> </span> )} </div> ) : ( <></> )} </div> </TableCell>
♻️ Duplicate comments (3)
src/pages/listing/[slug]/index.tsx (1)
94-107: DuplicateviewerIsProresolution pattern.Same code block exists in
winner.tsx(lines 106-117) and the API handler. See the refactoring suggestion in the other file's review.src/pages/api/listings/details/[slug].ts (1)
152-163: DuplicateviewerIsProresolution pattern.This is the third occurrence of the same pattern. Extracting to a shared helper would improve maintainability.
src/features/sponsor-dashboard/components/Submissions/SubmissionPanel.tsx (1)
50-52: Duplicate logic with PayoutSection.tsx.The same
shouldHideTxLinkscomputation logic is duplicated here and inPayoutSection.tsx(lines 126-130). See the review comment on PayoutSection.tsx for the suggested refactor.
🧹 Nitpick comments (6)
src/pages/listing/[slug]/winner.tsx (1)
106-117: Consider extracting theviewerIsProresolution logic into a shared helper.This pattern of resolving
privyDid→ querying user → extractingisProis duplicated insrc/pages/listing/[slug]/index.tsx(lines 94-105) andsrc/pages/api/listings/details/[slug].ts(lines 152-163). Extract to a reusable utility to reduce duplication.Example helper in
@/features/auth/utils/:export async function getViewerIsPro(req: NextApiRequest | IncomingMessage): Promise<boolean> { const privyDid = await getPrivyToken(req); if (!privyDid) return false; const viewer = await prisma.user.findUnique({ where: { privyDid }, select: { isPro: true }, }); return viewer?.isPro ?? false; }src/pages/api/listings/details/[slug].ts (1)
69-72: Consider a more specific return type thanPromise<any>.Per coding guidelines, top-level module functions should have declared return types. Consider returning
Promise<ListingWithProFlag | null>or a union type that reflects the sanitized structure.export async function getListingDetailsBySlug( slug: string, options?: { viewerIsPro?: boolean }, -): Promise<any> { +): Promise<ListingWithProFlag | null> {src/app/api/server-time/route.ts (1)
8-13: Remove unnecessaryAcceptheader from HEAD request.The
Accept: application/jsonheader has no effect on HEAD requests since they do not return a response body. TheDateheader you're extracting is returned regardless of this header.const response = await fetch('https://httpbin.org/get', { method: 'HEAD', - headers: { - Accept: 'application/json', - }, });src/layouts/Grants.tsx (3)
71-87: Consider moving function outside component and adding JSDoc.The
getRewardTextfunction is defined inside the component body, which means it's recreated on every render. For better performance, define it outside the component or memoize it if it needs to access component state. Additionally, per coding guidelines, add a JSDoc comment to document the function's behavior.Apply this diff to move the function outside and add JSDoc:
+/** + * Generates reward text for grant metadata based on min/max reward values. + * Returns "between $X" if minReward is valid, "up to $X" otherwise, or empty string if no maxReward. + */ +const getRewardText = (grant: GrantWithApplicationCount | null): string => { + if (!grant?.maxReward) return ''; + + const { minReward, maxReward } = grant; + const amount = grantAmount({ + minReward: minReward ?? 0, + maxReward, + }); + + const hasValidMinReward = minReward && minReward > 0; + + if (hasValidMinReward) { + return `between $${amount}`; + } + + return `up to $${amount.replace('Up to ', '')}`; +}; + export function GrantPageLayout({ grant: initialGrant, children, }: GrantPageProps) { const [grant] = useState<typeof initialGrant>(initialGrant); const encodedTitle = encodeURIComponent(initialGrant?.title || ''); // ... rest of component - const getRewardText = (): string => { - if (!initialGrant?.maxReward) return ''; - - const { minReward, maxReward } = initialGrant; - const amount = grantAmount({ - minReward: minReward ?? 0, - maxReward, - }); - - const hasValidMinReward = minReward && minReward > 0; - - if (hasValidMinReward) { - return `between $${amount}`; - } - - return `up to $${amount.replace('Up to ', '')}`; - }; - - const rewardText = getRewardText(); + const rewardText = getRewardText(initialGrant);
76-76: Inconsistent handling of minReward = 0.Line 76 passes
minReward ?? 0tograntAmount, meaning ifminRewardisnullorundefined, it becomes0. However, line 80 checksminReward && minReward > 0to determine validity. This creates an inconsistency: ifminRewardis explicitly0, it's passed tograntAmountbut then treated as invalid for text generation. Consider making the handling consistent—either treat0as a valid minimum (and pass it along) or treat it as invalid (and passundefined).Apply this diff to make the handling consistent:
const { minReward, maxReward } = initialGrant; const amount = grantAmount({ - minReward: minReward ?? 0, + minReward: minReward && minReward > 0 ? minReward : undefined, maxReward, }); const hasValidMinReward = minReward && minReward > 0;Alternatively, if
grantAmountrequires a number and0is semantically valid, adjust the validation logic instead.Also applies to: 80-80
86-86: Fragile string manipulation depends on grantAmount output format.Line 86 uses
amount.replace('Up to ', '')which assumesgrantAmountreturns a string with the exact prefix "Up to " whenminRewardis not provided. While this assumption is currently correct (the function returns"Up to " + formatNumberWithSuffix(maxReward)in that case), this creates a hidden dependency on the implementation details ofgrantAmount. If the formatting logic changes, this replacement will silently fail or produce incorrect text.Consider refactoring
grantAmountto accept a parameter controlling output format (e.g.,includeLabel: boolean), or extract the raw numeric amount into a separate return value to eliminate the need for string manipulation.
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (36)
src/app/api/grants/route.ts(0 hunks)src/app/api/hackathon/route.ts(0 hunks)src/app/api/homepage/feed/route.ts(0 hunks)src/app/api/homepage/recent-earners/route.ts(0 hunks)src/app/api/homepage/sponsor-count/route.ts(0 hunks)src/app/api/homepage/stats/route.ts(0 hunks)src/app/api/homepage/user-count/route.ts(0 hunks)src/app/api/image/sign/route.ts(0 hunks)src/app/api/listings/count/route.ts(0 hunks)src/app/api/listings/route.ts(0 hunks)src/app/api/og/get/route.ts(0 hunks)src/app/api/report-listing/route.ts(0 hunks)src/app/api/server-time/route.ts(1 hunks)src/app/api/wallet/price/route.ts(0 hunks)src/features/sponsor-dashboard/components/Submissions/PayoutSection.tsx(3 hunks)src/features/sponsor-dashboard/components/Submissions/SubmissionPanel.tsx(3 hunks)src/layouts/Grants.tsx(2 hunks)src/pages/api/comment/[id]/index.ts(0 hunks)src/pages/api/comment/create.ts(0 hunks)src/pages/api/feed/[type]/[id]/get.ts(0 hunks)src/pages/api/feed/get.ts(0 hunks)src/pages/api/grants/[slug].ts(0 hunks)src/pages/api/grants/live.ts(0 hunks)src/pages/api/hackathon/public-stats.ts(0 hunks)src/pages/api/listings/[listingId]/related.ts(0 hunks)src/pages/api/listings/details/[slug].ts(3 hunks)src/pages/api/listings/live.ts(0 hunks)src/pages/api/pow/create.ts(0 hunks)src/pages/api/pow/get.ts(0 hunks)src/pages/api/search/[query].ts(0 hunks)src/pages/api/sponsors/check-name.ts(0 hunks)src/pages/api/sponsors/check-slug.ts(0 hunks)src/pages/listing/[slug]/index.tsx(2 hunks)src/pages/listing/[slug]/winner.tsx(3 hunks)src/utils/getClientIP.ts(0 hunks)src/utils/getClientIPPages.ts(0 hunks)
💤 Files with no reviewable changes (29)
- src/app/api/homepage/sponsor-count/route.ts
- src/pages/api/comment/create.ts
- src/pages/api/feed/get.ts
- src/pages/api/sponsors/check-name.ts
- src/pages/api/pow/create.ts
- src/pages/api/listings/live.ts
- src/utils/getClientIP.ts
- src/pages/api/grants/live.ts
- src/app/api/homepage/recent-earners/route.ts
- src/app/api/homepage/feed/route.ts
- src/app/api/homepage/user-count/route.ts
- src/pages/api/pow/get.ts
- src/pages/api/comment/[id]/index.ts
- src/pages/api/grants/[slug].ts
- src/pages/api/listings/[listingId]/related.ts
- src/app/api/hackathon/route.ts
- src/app/api/og/get/route.ts
- src/utils/getClientIPPages.ts
- src/app/api/grants/route.ts
- src/pages/api/feed/[type]/[id]/get.ts
- src/pages/api/search/[query].ts
- src/app/api/listings/route.ts
- src/app/api/listings/count/route.ts
- src/app/api/report-listing/route.ts
- src/app/api/image/sign/route.ts
- src/app/api/homepage/stats/route.ts
- src/pages/api/sponsors/check-slug.ts
- src/pages/api/hackathon/public-stats.ts
- src/app/api/wallet/price/route.ts
🧰 Additional context used
📓 Path-based instructions (10)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/any-inside-generic-functions.mdc)
**/*.{ts,tsx}: When building generic functions in TypeScript where the return type depends on the input type parameter, useanytype assertion when TypeScript cannot match runtime logic to type logic (e.g., conditional return types)
Outside of generic functions, useanytype extremely sparingly
**/*.{ts,tsx}: Proactively use discriminated unions to model data that can be in one of a few different shapes, such as when sending events between environments
Use switch statements to handle the results of discriminated unions
Use discriminated unions to prevent the 'bag of optionals' problem when describing state with multiple mutually exclusive conditions (e.g., fetching state with status, data, and error fields)
**/*.{ts,tsx}: Do not introduce new enums into the codebase. Retain existing enums.
If you require enum-like behaviour, use anas constobject instead of enums
Understand that numeric enums produce reverse mappings, resulting in double the keys compared to the number of members defined
**/*.{ts,tsx}: Use import type whenever you are importing a type
Prefer top-levelimport typeover inlineimport { type ... }syntax
**/*.{ts,tsx}: Prefer interfaces when modelling inheritance in TypeScript
Avoid using the&(intersection) operator in TypeScript; only use it whereinterface extendsis not possible due to performance concerns
**/*.{ts,tsx}: Use UpperCamelCase (PascalCase) for classes, types, and interfaces (e.g.,MyClass,MyInterface)
Inside generic types, functions or classes, prefix type parameters withT(e.g.,TKey,TValue)Be aware that with
noUncheckedIndexedAccessenabled intsconfig.json, indexing into objects and arrays returnsType | undefinedinstead of justType. Ensure null/undefined checks are performed when accessing object properties or array elements.Use
property: Type | undefinedinstead ofproperty?: Typefor TypeScript type definitions to force explicit property passing and prevent bugs from...
Files:
src/layouts/Grants.tsxsrc/pages/listing/[slug]/winner.tsxsrc/features/sponsor-dashboard/components/Submissions/SubmissionPanel.tsxsrc/app/api/server-time/route.tssrc/features/sponsor-dashboard/components/Submissions/PayoutSection.tsxsrc/pages/api/listings/details/[slug].tssrc/pages/listing/[slug]/index.tsx
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/default-exports.mdc)
Do not use default exports unless explicitly required by the framework. Use named exports instead (e.g.,
export function myFunction()rather thanexport default function myFunction())Import motion components from 'motion/react' instead of 'framer-motion'
**/*.{js,jsx,ts,tsx}: PostHog event names should follow the 'action_location' convention with lowercase words separated by underscores (e.g., 'getting started_sponsor resources', 'login_navbar')
Be specific when naming PostHog events: the location should clearly identify the UI section (e.g., sponsor resources, navbar, sidebar, banner)
Use action verbs in PostHog event names to describe what the user is doing (e.g., getting started, rate card, view profile, open)
Keep PostHog event names concise using short, descriptive phrases
Implement PostHog event tracking using posthog.capture('action_location') for link clicks, button clicks, and programmatic events
**/*.{js,jsx,ts,tsx}: Use updater functions (functional form) for state updates when multiple updates occur sequentially or when the new state depends on the previous state value
Avoid using stale state values in asynchronous operations; always use the updater function pattern (e.g.,setPending(p => p + 1)) instead of direct state references (e.g.,setPending(pending + 1))
Include all reactive values (props, state variables, and component-scoped variables) used inside a useEffect in its dependency array
Never suppress the exhaustive-deps linter warning; instead, refactor the Effect to fix the underlying dependency issue using updater functions, Effect Events, or moving logic outside the Effect
Use useEffectEvent (when available) to read non-reactive values inside Effects without triggering re-runs when those values change
Calculate derived values during rendering instead of using Effects for computations; reserve Effects for synchronizing with external systems
Handle event-driven logic directly in event handlers rather than using Effects with condit...
Files:
src/layouts/Grants.tsxsrc/pages/listing/[slug]/winner.tsxsrc/features/sponsor-dashboard/components/Submissions/SubmissionPanel.tsxsrc/app/api/server-time/route.tssrc/features/sponsor-dashboard/components/Submissions/PayoutSection.tsxsrc/pages/api/listings/details/[slug].tssrc/pages/listing/[slug]/index.tsx
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.cursor/rules/jsdoc-comments.mdc)
**/*.{ts,tsx,js,jsx}: Use JSDoc comments to annotate functions and types
Be concise in JSDoc comments, and only provide JSDoc comments if the function's behaviour is not self-evident
Use the JSDoc inline@linktag to link to other functions and types within the same file
Files:
src/layouts/Grants.tsxsrc/pages/listing/[slug]/winner.tsxsrc/features/sponsor-dashboard/components/Submissions/SubmissionPanel.tsxsrc/app/api/server-time/route.tssrc/features/sponsor-dashboard/components/Submissions/PayoutSection.tsxsrc/pages/api/listings/details/[slug].tssrc/pages/listing/[slug]/index.tsx
**/*
📄 CodeRabbit inference engine (.cursor/rules/naming-conventions.mdc)
Use kebab-case for file names (e.g.,
my-component.ts)
Files:
src/layouts/Grants.tsxsrc/pages/listing/[slug]/winner.tsxsrc/features/sponsor-dashboard/components/Submissions/SubmissionPanel.tsxsrc/app/api/server-time/route.tssrc/features/sponsor-dashboard/components/Submissions/PayoutSection.tsxsrc/pages/api/listings/details/[slug].tssrc/pages/listing/[slug]/index.tsx
**/*.{js,ts,jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/naming-conventions.mdc)
**/*.{js,ts,jsx,tsx}: Use camelCase for variables and function names (e.g.,myVariable,myFunction())
Use ALL_CAPS for constants and enum values (e.g.,MAX_COUNT,Color.RED)
Files:
src/layouts/Grants.tsxsrc/pages/listing/[slug]/winner.tsxsrc/features/sponsor-dashboard/components/Submissions/SubmissionPanel.tsxsrc/app/api/server-time/route.tssrc/features/sponsor-dashboard/components/Submissions/PayoutSection.tsxsrc/pages/api/listings/details/[slug].tssrc/pages/listing/[slug]/index.tsx
**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/return-types.mdc)
React components returning JSX do not require explicit return type declarations
Files:
src/layouts/Grants.tsxsrc/pages/listing/[slug]/winner.tsxsrc/features/sponsor-dashboard/components/Submissions/SubmissionPanel.tsxsrc/features/sponsor-dashboard/components/Submissions/PayoutSection.tsxsrc/pages/listing/[slug]/index.tsx
**/*.{tsx,ts}
📄 CodeRabbit inference engine (.cursor/rules/we-might-not-need-an-effect.mdc)
**/*.{tsx,ts}: Avoid using Effects for data transformations during rendering; calculate values directly during render instead of using useState and useEffect
Use useMemo instead of Effects for caching expensive calculations
Use the key prop to reset component state when props change, rather than using Effects with setState
Calculate derived state during render based on props rather than adjusting state in Effects
Place event-specific logic in event handlers rather than Effects
Avoid chains of Effects that trigger state updates; calculate all derived state at once in event handlers or during render
Show notifications and post-render feedback in event handlers rather than Effects
When fetching data in Effects, implement proper cleanup to prevent race conditions by ignoring stale responses
Use custom Hooks to encapsulate data fetching and subscription logic instead of implementing Effects directly in components
Use Effects only for synchronizing with external systems such as browser APIs, third-party libraries, WebSocket connections, or non-React UI widgets
Files:
src/layouts/Grants.tsxsrc/pages/listing/[slug]/winner.tsxsrc/features/sponsor-dashboard/components/Submissions/SubmissionPanel.tsxsrc/app/api/server-time/route.tssrc/features/sponsor-dashboard/components/Submissions/PayoutSection.tsxsrc/pages/api/listings/details/[slug].tssrc/pages/listing/[slug]/index.tsx
src/app/api/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/app-api-guidelines.mdc)
src/app/api/**/*.{ts,tsx}: Use NextResponse.json() with async GET/POST handlers as the standard structure for Next.js App Router API endpoints
Route handlers should accept request parameters with async props destructuring for accessing dynamic segments (e.g.,props: { params: Promise<{ id: string }> })
Use getUserSession(await headers()) and getSponsorSession(await headers()) from @/features/auth/utils for session authentication in API routes
Check session.error and session.data before using session data, returning NextResponse.json with appropriate error status
Use checkGrantSponsorAuth() to validate sponsor authorization and return NextResponse.json with error details when authorization fails
Extract URL query parameters using new URL(request.url) and searchParams.get() for request handling
Parse request body using await request.json() and log it using safeStringify() for debugging
Return success responses with NextResponse.json({ data, message: 'Success' }, { status: 200 })
Return error responses with NextResponse.json({ error, message: userFriendlyMessage }, { status: errorCode })
Use logger from @/lib/logger for all logging with appropriate levels (debug, info, warn, error)
Wrap main API route logic in try-catch block and log errors using logger.error()
Use waitUntil() from @vercel/functions for non-blocking background task operations with error logging
Use Zod schemas with safeParse() to validate request data and return validation error details on failure
Implement response caching with Cache-Control headers (e.g., 'public, s-maxage=60, stale-while-revalidate=30') for GET endpoints
Use maxDuration export to configure route segment timeout limits (e.g., export const maxDuration = 300)
Organize API route files with imports first, followed by type definitions, then route handler exports
Keep route handlers focused and single-responsibility, avoiding multiple unrelated operations
Always validate input data in API endpoints before processing
Use appropriate HTT...
Files:
src/app/api/server-time/route.ts
**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/return-types.mdc)
Declare return types for top-level module functions in TypeScript (exception: React components returning JSX)
Files:
src/app/api/server-time/route.tssrc/pages/api/listings/details/[slug].ts
src/pages/api/**/*
📄 CodeRabbit inference engine (.cursor/rules/pages-api-guidelines.mdc)
src/pages/api/**/*: UsewithAuthHOC for basic user authentication andwithSponsorAuthHOC for sponsor-specific routes in Next.js API endpoints
Type protected requests with appropriate interfaces (NextApiRequestWithUserorNextApiRequestWithSponsor) from '@/features/auth/types'
Always use the logger from '@/lib/logger' for consistent logging in API endpoints
Use debug logs for request data, info logs for successful operations, warning logs for handled issues, and error logs for exceptions
Always wrap main logic in try-catch blocks and log errors using safeStringify before returning 500 status
Use HTTP status codes: 200 for success, 400 for validation errors, 401 for unauthorized (no auth token), 403 for forbidden (insufficient permissions), 404 for not found, 500 for internal server error
Use Zod schemas for request validation with safeParse and return 403 status with validation errors on failure
Return success responses with format { data: result, message: 'Operation successful' } and error responses with format { error: errorMessage, message: userFriendlyMessage }
Use Prisma client from '@/prisma' for database operations in Next.js API endpoints
Always use TypeScript types for request/response objects, specifically importing NextApiResponse from 'next'
Organize API endpoint files with: imports first, then type imports, then handler function, then export with auth wrapper
Use setCacheHeaders utility for setting appropriate caching headers on responses in Next.js API endpoints
Files:
src/pages/api/listings/details/[slug].ts
🧠 Learnings (9)
📓 Common learnings
Learnt from: CR
Repo: SuperteamDAO/earn PR: 0
File: .cursor/rules/pages-api-guidelines.mdc:0-0
Timestamp: 2025-06-24T03:45:32.567Z
Learning: Implement rate limiting and proper authorization checks for public API endpoints to enhance security and prevent abuse.
📚 Learning: 2025-11-27T08:11:43.002Z
Learnt from: CR
Repo: SuperteamDAO/earn PR: 0
File: .cursor/rules/pages-api-guidelines.mdc:0-0
Timestamp: 2025-11-27T08:11:43.002Z
Learning: Applies to src/pages/api/**/* : Use Prisma client from '@/prisma' for database operations in Next.js API endpoints
Applied to files:
src/pages/listing/[slug]/winner.tsxsrc/pages/api/listings/details/[slug].tssrc/pages/listing/[slug]/index.tsx
📚 Learning: 2025-11-27T08:11:43.002Z
Learnt from: CR
Repo: SuperteamDAO/earn PR: 0
File: .cursor/rules/pages-api-guidelines.mdc:0-0
Timestamp: 2025-11-27T08:11:43.002Z
Learning: Applies to src/pages/api/**/* : Type protected requests with appropriate interfaces (`NextApiRequestWithUser` or `NextApiRequestWithSponsor`) from '@/features/auth/types'
Applied to files:
src/pages/listing/[slug]/winner.tsxsrc/pages/api/listings/details/[slug].ts
📚 Learning: 2025-11-27T08:10:14.867Z
Learnt from: CR
Repo: SuperteamDAO/earn PR: 0
File: .cursor/rules/app-api-guidelines.mdc:0-0
Timestamp: 2025-11-27T08:10:14.867Z
Learning: Applies to src/app/api/**/*.{ts,tsx} : Use maxDuration export to configure route segment timeout limits (e.g., export const maxDuration = 300)
Applied to files:
src/app/api/server-time/route.ts
📚 Learning: 2025-11-27T08:10:14.867Z
Learnt from: CR
Repo: SuperteamDAO/earn PR: 0
File: .cursor/rules/app-api-guidelines.mdc:0-0
Timestamp: 2025-11-27T08:10:14.867Z
Learning: Applies to src/app/api/**/*.{ts,tsx} : Keep route handlers focused and single-responsibility, avoiding multiple unrelated operations
Applied to files:
src/app/api/server-time/route.ts
📚 Learning: 2025-11-27T08:10:14.867Z
Learnt from: CR
Repo: SuperteamDAO/earn PR: 0
File: .cursor/rules/app-api-guidelines.mdc:0-0
Timestamp: 2025-11-27T08:10:14.867Z
Learning: Applies to src/app/api/**/*.{ts,tsx} : Implement rate limiting where necessary for API endpoints
Applied to files:
src/app/api/server-time/route.ts
📚 Learning: 2025-11-27T08:10:14.867Z
Learnt from: CR
Repo: SuperteamDAO/earn PR: 0
File: .cursor/rules/app-api-guidelines.mdc:0-0
Timestamp: 2025-11-27T08:10:14.867Z
Learning: Applies to src/app/api/**/*.{ts,tsx} : Use appropriate HTTP methods (GET, POST, etc.) for API route handlers matching the operation semantics
Applied to files:
src/app/api/server-time/route.ts
📚 Learning: 2025-11-27T08:11:43.002Z
Learnt from: CR
Repo: SuperteamDAO/earn PR: 0
File: .cursor/rules/pages-api-guidelines.mdc:0-0
Timestamp: 2025-11-27T08:11:43.002Z
Learning: Use proper rate limiting for public Next.js API endpoints
Applied to files:
src/app/api/server-time/route.tssrc/pages/api/listings/details/[slug].ts
📚 Learning: 2025-05-30T08:55:12.723Z
Learnt from: a20hek
Repo: SuperteamDAO/earn PR: 1089
File: src/features/credits/components/CreditLog.tsx:226-227
Timestamp: 2025-05-30T08:55:12.723Z
Learning: CREDIT_REFUND entries in CreditLog.tsx should remain linkable (not included in isNonLinkableEntry check) even though they relate to unpublished listings.
Applied to files:
src/features/sponsor-dashboard/components/Submissions/PayoutSection.tsx
🧬 Code graph analysis (4)
src/features/sponsor-dashboard/components/Submissions/SubmissionPanel.tsx (1)
src/features/listings/utils/status.ts (1)
getListingStatus(26-79)
src/features/sponsor-dashboard/components/Submissions/PayoutSection.tsx (1)
src/components/ui/table.tsx (2)
TableRow(110-110)TableCell(106-106)
src/pages/api/listings/details/[slug].ts (3)
src/utils/safeStringify.ts (1)
convertDatesToISO(18-32)src/features/auth/utils/getPrivyToken.ts (1)
getPrivyToken(9-57)src/prisma.ts (1)
prisma(60-60)
src/pages/listing/[slug]/index.tsx (3)
src/pages/api/listings/details/[slug].ts (1)
getListingDetailsBySlug(69-133)src/features/auth/utils/getPrivyToken.ts (1)
getPrivyToken(9-57)src/prisma.ts (1)
prisma(60-60)
🔇 Additional comments (9)
src/pages/listing/[slug]/winner.tsx (1)
121-141: Verify whether winner submission data should also be sanitized for non-PRO users.The PR objective is to prevent PRO-only listing data exposure. Currently, if
bountyData.idexists (even for PRO listings viewed by non-PRO users), winner submissions are still fetched and returned. Confirm that exposing winner names/photos for PRO listings is intentional for non-PRO viewers.src/pages/api/listings/details/[slug].ts (2)
31-38: Verify all sensitive fields from the security issue are included.Cross-reference with issue #1295 requirements:
- ✅
description(full listing description)- ✅
eligibility(eligibility fields)- ✅
requirements(application/submission requirements)- ✅
applicationLink(submission details)- ✅
references- ✅
commitmentDateConfirm no additional sensitive fields (e.g.,
submissionRequirements,skills,pocSocials, internal IDs) should be redacted per your security requirements.
40-67: Sanitization logic is sound.The guard at line 44 correctly short-circuits when:
- Listing is null
- Listing is not PRO-only (
isPro !== true)- Viewer has PRO access
Using
nullinstead ofundefinedfor stable JSON shape is a good practice.src/pages/listing/[slug]/index.tsx (1)
78-81: No action needed.DescriptionUIalready handlesnulldescription gracefully through two mechanisms: (1) whenisPro && !user?.isPro, it displays a PRO paywall UI (lines 186-259); (2) for normal rendering, thedescription ?? ''fallback on line 85 convertsnullto an empty string, preventing any crashes or "null" text from appearing. The component safely renders empty content when description is null.src/layouts/Grants.tsx (2)
97-97: LGTM: Proper conditional inclusion of reward text in metadata.The Meta description correctly uses optional chaining and conditional rendering to include reward text only when available. The template string construction handles missing values gracefully.
104-107: LGTM: Improved Open Graph image alt text for accessibility.The dynamic alt text provides better context for the Open Graph image, using appropriate fallbacks for missing data. This enhances SEO and accessibility.
src/features/sponsor-dashboard/components/Submissions/PayoutSection.tsx (2)
307-318: Conditional rendering is consistent with transaction link hiding.The expanded payment details row is correctly gated by
!shouldHideTxLinks, which aligns with the feature's intent to hide transaction information when the foundation handles payments and the listing is completed.
126-130: Verify alignment with PR objectives.The PR title indicates this change should "prevent PRO-only listing data exposure to non-PRO users" with server-side access control enforcement. However, the changes in this file hide transaction links based on foundation payment status (
isFndnPayingandlistingStatus === 'Completed'), which appears unrelated to PRO membership or access control.Please confirm:
- Are these transaction-hiding changes intentionally part of the PRO access control fix, or were they bundled separately?
- Should transaction links be hidden when the foundation pays regardless of user PRO status?
- If these are separate concerns, consider splitting them into separate PRs for clearer change tracking.
src/features/sponsor-dashboard/components/Submissions/SubmissionPanel.tsx (1)
111-130: Conditional gating is correct and consistent.The addition of
!shouldHideTxLinksat line 115 correctly prevents the "View Payment Tx" button from displaying when transaction links should be hidden. The condition appropriately uses optional chaining forselectedSubmissionand aligns with the transaction-hiding logic inPayoutSection.tsx.
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.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/features/sponsor-dashboard/components/Submissions/PayoutSection.tsx (1)
279-424: Critical: File has severe syntax errors and will not compile.The static analysis reports 12 parse errors in this region, and manual inspection confirms the JSX structure is broken:
- Line 279: Orphaned
)}after closing</TableCell>from Prize column- Lines 280-321: A
TableCellblock appears with transaction link logic- Lines 322-334:
% Paidcolumn logic appears mid-stream- Lines 335-409: Duplicate transaction link logic nearly identical to lines 280-321
- Lines 420-424: Duplicate closing tags (
</React.Fragment>,</TableBody>,</Table>)This appears to be a corrupted merge or incomplete edit. The intended change is to add
shouldHideTxLinkschecks, but the file structure is invalid.The file needs to be restructured. The correct flow after the Prize
<TableCell>(ending at line 278) should be:</TableCell> {isProject && !isFndnToPay && ( <TableCell> {/* % Paid progress bar */} </TableCell> )} {!isFndnToPay && ( <TableCell> <div className="flex items-center gap-2"> {submission.isPaid && !shouldHideTxLinks ? ( // Transaction link logic (single or multi-tranche) ) : bounty.isWinnersAnnounced && !isFndnToPay ? ( // PayoutButton logic ) : ( <></> )} </div> </TableCell> )} </TableRow> {isExpanded && isProject && hasMultipleTranches && !shouldHideTxLinks && ( <TableRow> <TableCell /> <PaymentDetailsRow ... /> </TableRow> )} </React.Fragment>Please review the file diff carefully and ensure all opening/closing tags match. Run a local build to verify the fix compiles correctly.
♻️ Duplicate comments (1)
src/features/sponsor-dashboard/components/Submissions/PayoutSection.tsx (1)
145-149: Extract duplicatedshouldHideTxLinkslogic to a shared utility.This computation (
bounty.isFndnPaying && listingStatus === 'Completed') is duplicated inSubmissionPanel.tsx. Consider extracting tosrc/features/listings/utils/status.tsas suggested in the previous review.
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
src/app/api/wallet/price/route.ts(0 hunks)src/features/sponsor-dashboard/components/Submissions/PayoutSection.tsx(4 hunks)src/pages/listing/[slug]/winner.tsx(3 hunks)
💤 Files with no reviewable changes (1)
- src/app/api/wallet/price/route.ts
🧰 Additional context used
📓 Path-based instructions (7)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/any-inside-generic-functions.mdc)
**/*.{ts,tsx}: When building generic functions in TypeScript where the return type depends on the input type parameter, useanytype assertion when TypeScript cannot match runtime logic to type logic (e.g., conditional return types)
Outside of generic functions, useanytype extremely sparingly
**/*.{ts,tsx}: Proactively use discriminated unions to model data that can be in one of a few different shapes, such as when sending events between environments
Use switch statements to handle the results of discriminated unions
Use discriminated unions to prevent the 'bag of optionals' problem when describing state with multiple mutually exclusive conditions (e.g., fetching state with status, data, and error fields)
**/*.{ts,tsx}: Do not introduce new enums into the codebase. Retain existing enums.
If you require enum-like behaviour, use anas constobject instead of enums
Understand that numeric enums produce reverse mappings, resulting in double the keys compared to the number of members defined
**/*.{ts,tsx}: Use import type whenever you are importing a type
Prefer top-levelimport typeover inlineimport { type ... }syntax
**/*.{ts,tsx}: Prefer interfaces when modelling inheritance in TypeScript
Avoid using the&(intersection) operator in TypeScript; only use it whereinterface extendsis not possible due to performance concerns
**/*.{ts,tsx}: Use UpperCamelCase (PascalCase) for classes, types, and interfaces (e.g.,MyClass,MyInterface)
Inside generic types, functions or classes, prefix type parameters withT(e.g.,TKey,TValue)Be aware that with
noUncheckedIndexedAccessenabled intsconfig.json, indexing into objects and arrays returnsType | undefinedinstead of justType. Ensure null/undefined checks are performed when accessing object properties or array elements.Use
property: Type | undefinedinstead ofproperty?: Typefor TypeScript type definitions to force explicit property passing and prevent bugs from...
Files:
src/pages/listing/[slug]/winner.tsxsrc/features/sponsor-dashboard/components/Submissions/PayoutSection.tsx
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/default-exports.mdc)
Do not use default exports unless explicitly required by the framework. Use named exports instead (e.g.,
export function myFunction()rather thanexport default function myFunction())Import motion components from 'motion/react' instead of 'framer-motion'
**/*.{js,jsx,ts,tsx}: PostHog event names should follow the 'action_location' convention with lowercase words separated by underscores (e.g., 'getting started_sponsor resources', 'login_navbar')
Be specific when naming PostHog events: the location should clearly identify the UI section (e.g., sponsor resources, navbar, sidebar, banner)
Use action verbs in PostHog event names to describe what the user is doing (e.g., getting started, rate card, view profile, open)
Keep PostHog event names concise using short, descriptive phrases
Implement PostHog event tracking using posthog.capture('action_location') for link clicks, button clicks, and programmatic events
**/*.{js,jsx,ts,tsx}: Use updater functions (functional form) for state updates when multiple updates occur sequentially or when the new state depends on the previous state value
Avoid using stale state values in asynchronous operations; always use the updater function pattern (e.g.,setPending(p => p + 1)) instead of direct state references (e.g.,setPending(pending + 1))
Include all reactive values (props, state variables, and component-scoped variables) used inside a useEffect in its dependency array
Never suppress the exhaustive-deps linter warning; instead, refactor the Effect to fix the underlying dependency issue using updater functions, Effect Events, or moving logic outside the Effect
Use useEffectEvent (when available) to read non-reactive values inside Effects without triggering re-runs when those values change
Calculate derived values during rendering instead of using Effects for computations; reserve Effects for synchronizing with external systems
Handle event-driven logic directly in event handlers rather than using Effects with condit...
Files:
src/pages/listing/[slug]/winner.tsxsrc/features/sponsor-dashboard/components/Submissions/PayoutSection.tsx
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.cursor/rules/jsdoc-comments.mdc)
**/*.{ts,tsx,js,jsx}: Use JSDoc comments to annotate functions and types
Be concise in JSDoc comments, and only provide JSDoc comments if the function's behaviour is not self-evident
Use the JSDoc inline@linktag to link to other functions and types within the same file
Files:
src/pages/listing/[slug]/winner.tsxsrc/features/sponsor-dashboard/components/Submissions/PayoutSection.tsx
**/*
📄 CodeRabbit inference engine (.cursor/rules/naming-conventions.mdc)
Use kebab-case for file names (e.g.,
my-component.ts)
Files:
src/pages/listing/[slug]/winner.tsxsrc/features/sponsor-dashboard/components/Submissions/PayoutSection.tsx
**/*.{js,ts,jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/naming-conventions.mdc)
**/*.{js,ts,jsx,tsx}: Use camelCase for variables and function names (e.g.,myVariable,myFunction())
Use ALL_CAPS for constants and enum values (e.g.,MAX_COUNT,Color.RED)
Files:
src/pages/listing/[slug]/winner.tsxsrc/features/sponsor-dashboard/components/Submissions/PayoutSection.tsx
**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/return-types.mdc)
React components returning JSX do not require explicit return type declarations
Files:
src/pages/listing/[slug]/winner.tsxsrc/features/sponsor-dashboard/components/Submissions/PayoutSection.tsx
**/*.{tsx,ts}
📄 CodeRabbit inference engine (.cursor/rules/we-might-not-need-an-effect.mdc)
**/*.{tsx,ts}: Avoid using Effects for data transformations during rendering; calculate values directly during render instead of using useState and useEffect
Use useMemo instead of Effects for caching expensive calculations
Use the key prop to reset component state when props change, rather than using Effects with setState
Calculate derived state during render based on props rather than adjusting state in Effects
Place event-specific logic in event handlers rather than Effects
Avoid chains of Effects that trigger state updates; calculate all derived state at once in event handlers or during render
Show notifications and post-render feedback in event handlers rather than Effects
When fetching data in Effects, implement proper cleanup to prevent race conditions by ignoring stale responses
Use custom Hooks to encapsulate data fetching and subscription logic instead of implementing Effects directly in components
Use Effects only for synchronizing with external systems such as browser APIs, third-party libraries, WebSocket connections, or non-React UI widgets
Files:
src/pages/listing/[slug]/winner.tsxsrc/features/sponsor-dashboard/components/Submissions/PayoutSection.tsx
🧠 Learnings (3)
📚 Learning: 2025-11-27T08:11:43.002Z
Learnt from: CR
Repo: SuperteamDAO/earn PR: 0
File: .cursor/rules/pages-api-guidelines.mdc:0-0
Timestamp: 2025-11-27T08:11:43.002Z
Learning: Applies to src/pages/api/**/* : Use Prisma client from '@/prisma' for database operations in Next.js API endpoints
Applied to files:
src/pages/listing/[slug]/winner.tsx
📚 Learning: 2025-11-27T08:11:43.002Z
Learnt from: CR
Repo: SuperteamDAO/earn PR: 0
File: .cursor/rules/pages-api-guidelines.mdc:0-0
Timestamp: 2025-11-27T08:11:43.002Z
Learning: Applies to src/pages/api/**/* : Type protected requests with appropriate interfaces (`NextApiRequestWithUser` or `NextApiRequestWithSponsor`) from '@/features/auth/types'
Applied to files:
src/pages/listing/[slug]/winner.tsx
📚 Learning: 2025-05-30T08:55:12.723Z
Learnt from: a20hek
Repo: SuperteamDAO/earn PR: 1089
File: src/features/credits/components/CreditLog.tsx:226-227
Timestamp: 2025-05-30T08:55:12.723Z
Learning: CREDIT_REFUND entries in CreditLog.tsx should remain linkable (not included in isNonLinkableEntry check) even though they relate to unpublished listings.
Applied to files:
src/features/sponsor-dashboard/components/Submissions/PayoutSection.tsx
🧬 Code graph analysis (1)
src/pages/listing/[slug]/winner.tsx (6)
src/features/auth/utils/getPrivyToken.ts (1)
getPrivyToken(9-57)src/prisma.ts (1)
prisma(60-60)src/pages/api/listings/details/[slug].ts (1)
getListingDetailsBySlug(69-133)src/pages/api/listings/[listingId]/winners.ts (2)
getWinningSubmissionsByListingId(8-35)submission(37-63)src/features/listing-builder/constants/index.ts (1)
BONUS_REWARD_POSITION(3-3)src/interface/submission.ts (1)
SubmissionWithUser(43-43)
🪛 Biome (2.1.2)
src/features/sponsor-dashboard/components/Submissions/PayoutSection.tsx
[error] 279-279: Unexpected token. Did you mean {'}'} or }?
(parse)
[error] 318-318: Expected corresponding JSX closing tag for 'div'.
Opening tag
closing tag
(parse)
[error] 280-280: Expected corresponding JSX closing tag for 'TableCell'.
Opening tag
closing tag
(parse)
[error] 351-351: expected : but instead found }
Remove }
(parse)
[error] 355-355: Unexpected token. Did you mean {'}'} or }?
(parse)
[error] 359-359: expected ) but instead found {
Remove {
(parse)
[error] 359-359: expected , but instead found &&
Remove &&
(parse)
[error] 360-360: expected , but instead found isProject
Remove isProject
(parse)
[error] 360-360: expected , but instead found &&
Remove &&
(parse)
[error] 361-361: expected , but instead found hasMultipleTranches
Remove hasMultipleTranches
(parse)
[error] 361-361: expected , but instead found &&
Remove &&
(parse)
[error] 362-362: expected , but instead found !
Remove !
(parse)
[error] 420-420: Expected an expression but instead found '<'.
Expected an expression here.
(parse)
🔇 Additional comments (5)
src/features/sponsor-dashboard/components/Submissions/PayoutSection.tsx (1)
148-149:shouldHideTxLinkslogic looks correct.The flag correctly derives from
bounty.isFndnPayingand completed status to determine when transaction links should be hidden.src/pages/listing/[slug]/winner.tsx (4)
9-9: LGTM! Necessary imports for PRO access control.These imports support the server-side PRO status determination, which is the core security fix for this PR.
Also applies to: 13-13
101-103: Good defensive handling of slug parameter.The array-safe extraction prevents potential issues with Next.js query parameter formats.
112-125: Excellent implementation of server-side PRO access control.The logic correctly:
- Retrieves and verifies the Privy token from the request
- Looks up the user's PRO status from the database
- Defaults to non-PRO (secure default) when no authentication is present
- Passes the PRO status to the listing details function for proper sanitization
This addresses the core PR objective of preventing PRO-only data exposure at the backend level.
127-147: Good defensive guard for winner processing.The conditional check ensures winner submissions are only fetched when a valid listing ID exists, preventing errors if the listing doesn't exist or was sanitized for non-PRO viewers. The existing winner processing logic is correctly preserved.
|
Check chat in #1296 first please🙂 |
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.
Actionable comments posted: 1
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/features/sponsor-dashboard/components/Submissions/PayoutSection.tsx(1 hunks)
🧰 Additional context used
📓 Path-based instructions (7)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/any-inside-generic-functions.mdc)
**/*.{ts,tsx}: When building generic functions in TypeScript where the return type depends on the input type parameter, useanytype assertion when TypeScript cannot match runtime logic to type logic (e.g., conditional return types)
Outside of generic functions, useanytype extremely sparingly
**/*.{ts,tsx}: Proactively use discriminated unions to model data that can be in one of a few different shapes, such as when sending events between environments
Use switch statements to handle the results of discriminated unions
Use discriminated unions to prevent the 'bag of optionals' problem when describing state with multiple mutually exclusive conditions (e.g., fetching state with status, data, and error fields)
**/*.{ts,tsx}: Do not introduce new enums into the codebase. Retain existing enums.
If you require enum-like behaviour, use anas constobject instead of enums
Understand that numeric enums produce reverse mappings, resulting in double the keys compared to the number of members defined
**/*.{ts,tsx}: Use import type whenever you are importing a type
Prefer top-levelimport typeover inlineimport { type ... }syntax
**/*.{ts,tsx}: Prefer interfaces when modelling inheritance in TypeScript
Avoid using the&(intersection) operator in TypeScript; only use it whereinterface extendsis not possible due to performance concerns
**/*.{ts,tsx}: Use UpperCamelCase (PascalCase) for classes, types, and interfaces (e.g.,MyClass,MyInterface)
Inside generic types, functions or classes, prefix type parameters withT(e.g.,TKey,TValue)Be aware that with
noUncheckedIndexedAccessenabled intsconfig.json, indexing into objects and arrays returnsType | undefinedinstead of justType. Ensure null/undefined checks are performed when accessing object properties or array elements.Use
property: Type | undefinedinstead ofproperty?: Typefor TypeScript type definitions to force explicit property passing and prevent bugs from...
Files:
src/features/sponsor-dashboard/components/Submissions/PayoutSection.tsx
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/default-exports.mdc)
Do not use default exports unless explicitly required by the framework. Use named exports instead (e.g.,
export function myFunction()rather thanexport default function myFunction())Import motion components from 'motion/react' instead of 'framer-motion'
**/*.{js,jsx,ts,tsx}: PostHog event names should follow the 'action_location' convention with lowercase words separated by underscores (e.g., 'getting started_sponsor resources', 'login_navbar')
Be specific when naming PostHog events: the location should clearly identify the UI section (e.g., sponsor resources, navbar, sidebar, banner)
Use action verbs in PostHog event names to describe what the user is doing (e.g., getting started, rate card, view profile, open)
Keep PostHog event names concise using short, descriptive phrases
Implement PostHog event tracking using posthog.capture('action_location') for link clicks, button clicks, and programmatic events
**/*.{js,jsx,ts,tsx}: Use updater functions (functional form) for state updates when multiple updates occur sequentially or when the new state depends on the previous state value
Avoid using stale state values in asynchronous operations; always use the updater function pattern (e.g.,setPending(p => p + 1)) instead of direct state references (e.g.,setPending(pending + 1))
Include all reactive values (props, state variables, and component-scoped variables) used inside a useEffect in its dependency array
Never suppress the exhaustive-deps linter warning; instead, refactor the Effect to fix the underlying dependency issue using updater functions, Effect Events, or moving logic outside the Effect
Use useEffectEvent (when available) to read non-reactive values inside Effects without triggering re-runs when those values change
Calculate derived values during rendering instead of using Effects for computations; reserve Effects for synchronizing with external systems
Handle event-driven logic directly in event handlers rather than using Effects with condit...
Files:
src/features/sponsor-dashboard/components/Submissions/PayoutSection.tsx
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.cursor/rules/jsdoc-comments.mdc)
**/*.{ts,tsx,js,jsx}: Use JSDoc comments to annotate functions and types
Be concise in JSDoc comments, and only provide JSDoc comments if the function's behaviour is not self-evident
Use the JSDoc inline@linktag to link to other functions and types within the same file
Files:
src/features/sponsor-dashboard/components/Submissions/PayoutSection.tsx
**/*
📄 CodeRabbit inference engine (.cursor/rules/naming-conventions.mdc)
Use kebab-case for file names (e.g.,
my-component.ts)
Files:
src/features/sponsor-dashboard/components/Submissions/PayoutSection.tsx
**/*.{js,ts,jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/naming-conventions.mdc)
**/*.{js,ts,jsx,tsx}: Use camelCase for variables and function names (e.g.,myVariable,myFunction())
Use ALL_CAPS for constants and enum values (e.g.,MAX_COUNT,Color.RED)
Files:
src/features/sponsor-dashboard/components/Submissions/PayoutSection.tsx
**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/return-types.mdc)
React components returning JSX do not require explicit return type declarations
Files:
src/features/sponsor-dashboard/components/Submissions/PayoutSection.tsx
**/*.{tsx,ts}
📄 CodeRabbit inference engine (.cursor/rules/we-might-not-need-an-effect.mdc)
**/*.{tsx,ts}: Avoid using Effects for data transformations during rendering; calculate values directly during render instead of using useState and useEffect
Use useMemo instead of Effects for caching expensive calculations
Use the key prop to reset component state when props change, rather than using Effects with setState
Calculate derived state during render based on props rather than adjusting state in Effects
Place event-specific logic in event handlers rather than Effects
Avoid chains of Effects that trigger state updates; calculate all derived state at once in event handlers or during render
Show notifications and post-render feedback in event handlers rather than Effects
When fetching data in Effects, implement proper cleanup to prevent race conditions by ignoring stale responses
Use custom Hooks to encapsulate data fetching and subscription logic instead of implementing Effects directly in components
Use Effects only for synchronizing with external systems such as browser APIs, third-party libraries, WebSocket connections, or non-React UI widgets
Files:
src/features/sponsor-dashboard/components/Submissions/PayoutSection.tsx
🧠 Learnings (1)
📚 Learning: 2025-05-30T08:55:12.723Z
Learnt from: a20hek
Repo: SuperteamDAO/earn PR: 1089
File: src/features/credits/components/CreditLog.tsx:226-227
Timestamp: 2025-05-30T08:55:12.723Z
Learning: CREDIT_REFUND entries in CreditLog.tsx should remain linkable (not included in isNonLinkableEntry check) even though they relate to unpublished listings.
Applied to files:
src/features/sponsor-dashboard/components/Submissions/PayoutSection.tsx
🧬 Code graph analysis (1)
src/features/sponsor-dashboard/components/Submissions/PayoutSection.tsx (2)
src/components/ui/table.tsx (1)
TableCell(106-106)src/features/sponsor-dashboard/components/Submissions/PayoutButton.tsx (1)
PayoutButton(58-517)
🪛 Biome (2.1.2)
src/features/sponsor-dashboard/components/Submissions/PayoutSection.tsx
[error] 279-279: Unexpected token. Did you mean {'}'} or }?
(parse)
[error] 318-318: Expected corresponding JSX closing tag for 'div'.
Opening tag
closing tag
(parse)
[error] 281-281: Expected corresponding JSX closing tag for 'div'.
Opening tag
closing tag
(parse)
[error] 280-280: Expected corresponding JSX closing tag for 'TableCell'.
Opening tag
closing tag
(parse)
🔇 Additional comments (1)
src/features/sponsor-dashboard/components/Submissions/PayoutSection.tsx (1)
335-398: Transaction link rendering logic looks good.The implementation correctly:
- Gates transaction link visibility behind
isPaid && !shouldHideTxLinks(line 338)- Handles both multi-tranche projects (toggle with chevron) and single-payment cases (direct link)
- Uses
truncatePublicKeyfor display andExternalLinkicon for visual affordance- Ensures expanded
PaymentDetailsRowrespectsshouldHideTxLinksat line 403- Aligns with the PR objective to conditionally hide transaction data
src/features/sponsor-dashboard/components/Submissions/PayoutSection.tsx
Outdated
Show resolved
Hide resolved
Removed transaction link display logic for paid submissions.
Summary
Problem
Although the UI blocks access to PRO-only listings, the backend API returned the full listing payload regardless of user membership, leading to information disclosure.
Solution
Changes Included
Related Issue
Closes #1295 : PRO-only listing data exposed to non-PRO users via Network tab (information disclosure)
Summary by CodeRabbit
✏️ Tip: You can customize this high-level summary in your review settings.