-
Notifications
You must be signed in to change notification settings - Fork 180
[Feat] pro users testimonials page with redis caching #262
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: main
Are you sure you want to change the base?
Conversation
…d renderer and styling.
|
@huamanraj is attempting to deploy a commit to the AJEET PRATAP SINGH's projects Team on Vercel. A member of the Team first needs to authorize it. |
WalkthroughAdds a Testimonial feature: Prisma model and relation, TRPC testimonial router, server-side avatar validator, Redis utilities and types, frontend list and submit pages (react-hook-form + zod), subscription/refetch retry logic, plus package/config updates. Changes
Sequence Diagram(s)sequenceDiagram
actor User
participant Web as Web Client
participant API as API Server
participant DB as Prisma/DB
participant Auth as Subscription Service
participant Validator as Avatar Validator
User->>Web: Open testimonials page
Web->>API: TRPC testimonials.getAll
API->>DB: query Testimonial (order by createdAt desc)
DB-->>API: return records
API-->>Web: return testimonials
Web->>Web: merge with local imageTestimonials & render
sequenceDiagram
actor User
participant Web as Web Client
participant API as API Server
participant Auth as Subscription Service
participant Validator as Avatar Validator
participant DB as Prisma/DB
User->>Web: Click "Submit Testimonial" (form validated client-side)
Web->>API: TRPC testimonials.submit (protected)
API->>Auth: check subscription status (is paid)
alt not paid
Auth-->>API: not paid
API-->>Web: TRPC FORBIDDEN
else paid
API->>Validator: validateAvatarUrl(avatarUrl) (HEAD + allowlist)
alt avatar invalid
Validator-->>API: validation error
API-->>Web: TRPC BAD_REQUEST
else valid
API->>DB: create Testimonial (error if exists)
DB-->>API: created record
API-->>Web: return created testimonial
Web->>Web: navigate to /testimonials
end
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Pre-merge checks and finishing touches❌ Failed checks (2 warnings)
✅ Passed checks (1 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: 8
🧹 Nitpick comments (17)
apps/web/src/app/(main)/dashboard/pro/dashboard/page.tsx (1)
113-127: Verify the new button follows design system and accessibility guidelines.The addition of the "Submit Testimonial" button follows the existing pattern and uses design system colors (
bg-brand-purple,hover:bg-brand-purple-light). However, consider the following:
Consistency: The "Join Slack" button has a disabled state, but the "Submit Testimonial" button does not. Should it also be disabled while joining or have its own loading state?
Accessibility: Consider adding
aria-labelattributes for screen readers, especially since the buttons are in a flex container.As per coding guidelines, the use of Tailwind design token classes is correct.
Optional enhancement:
<div className="flex flex-wrap gap-4 justify-center"> <button onClick={handleJoinSlack} disabled={isJoining} + aria-label="Join our Slack community" className="px-4 py-2 bg-brand-purple hover:bg-brand-purple-light text-text-primary font-medium rounded-lg transition-colors duration-200 disabled:opacity-50 disabled:cursor-not-allowed text-sm" > {isJoining ? "Joining..." : "Join Slack"} </button> <button onClick={() => router.push("/testimonials/submit")} + aria-label="Submit your testimonial" className="px-4 py-2 bg-brand-purple hover:bg-brand-purple-light text-text-primary font-medium rounded-lg transition-colors duration-200 text-sm" > Submit Testimonial </button> </div>apps/web/src/data/testimonials.ts (2)
1-1: Remove unnecessary comment.Per coding guidelines, code should be self-documenting. The type and constant names clearly indicate this is image testimonials data.
-// Image testimonials data export type ImageTestimonial = {
9-9: Use UPPER_SNAKE_CASE for constants.Per coding guidelines, constants should use UPPER_SNAKE_CASE.
-export const imageTestimonials: ImageTestimonial[] = [ +export const IMAGE_TESTIMONIALS: ImageTestimonial[] = [Note: Update the import in
apps/web/src/app/(main)/testimonials/page.tsxaccordingly.apps/api/prisma/schema.prisma (1)
48-57: Consider adding an index oncreatedAtfor query performance.The
getAllendpoint orders testimonials bycreatedAt desc. As the testimonials table grows, an index would optimize this query.user User @relation(fields: [userId], references: [id], onDelete: Cascade) + + @@index([createdAt(sort: Desc)]) }apps/web/src/app/(main)/testimonials/page.tsx (3)
37-37: Replace hardcoded hex values with design tokens.Per coding guidelines, never use hardcoded hex values directly in components. Use semantic color classes from the design token system instead.
- <div className="mb-4 break-inside-avoid rounded-xl border border-[#252525] bg-neutral-900/50 overflow-hidden hover:border-neutral-700 transition-colors"> + <div className="mb-4 break-inside-avoid rounded-xl border border-neutral-800 bg-neutral-900/50 overflow-hidden hover:border-neutral-700 transition-colors">Apply similar changes to other occurrences on lines 52, 124, 127, 146, 166.
Also applies to: 52-52
54-61: Addsizesattribute to Image withfillprop.When using
fillprop, providing asizesattribute helps Next.js optimize image loading and prevents layout shift warnings.<div className="relative w-10 h-10 rounded-full overflow-hidden border border-white/10"> <Image src={item.user.avatar} alt={item.user.name} fill + sizes="40px" className="object-cover" /> </div>
34-34: Use const with arrow functions for component definitions.Per coding guidelines, prefer
constwith arrow functions instead of function declarations, and define a type for const functions.-const TestimonialCard = ({ item }: { item: Testimonial }) => { +type TestimonialCardProps = { + item: Testimonial; +}; + +const TestimonialCard: React.FC<TestimonialCardProps> = ({ item }) => {-const TestimonialsPage = () => { +const TestimonialsPage: React.FC = () => {Also applies to: 78-78
apps/api/src/routers/testimonial.ts (2)
17-17: Add context to log statements for debugging.Per coding guidelines, log errors with context (userId, endpoint, timestamp) for debugging. Consider using structured logging.
- console.log("Testimonials served from cache"); + console.log("[testimonials:getAll] cache HIT");- console.log("Testimonials fetched from database and cached"); + console.log("[testimonials:getAll] fetched from database and cached");- console.log("Testimonials cache invalidated after submission"); + console.log("[testimonials:submit] cache invalidated", { userId });Also applies to: 20-20, 31-31, 92-92
15-15: Use a specific type instead ofany[]for cached data.Define a proper type for the cached testimonials to maintain type safety throughout the codebase.
+type CachedTestimonial = { + id: string; + userId: string; + content: string; + name: string; + avatar: string; + createdAt: Date; + updatedAt: Date; +}; + export const testimonialRouter = router({ getAll: publicProcedure.query(async ({ ctx }: any) => { // try to get from cache first - const cached = await redisCache.get<any[]>(TESTIMONIALS_CACHE_KEY); + const cached = await redisCache.get<CachedTestimonial[]>(TESTIMONIALS_CACHE_KEY);Alternatively, import the generated Prisma type for
Testimonialif available.packages/shared/types/redis.ts (4)
3-8: Variable shadowing is intentional but can be clarified.The static analysis tool flags line 8 as a redeclaration of
redis. This pattern is intentional—global.redispersists across HMR in development, while the module-levelredisholds the reference for the current process. Consider renaming the module-level variable toredisInstanceto make the distinction clearer and silence the linter.-let redis: Redis | undefined; +let redisInstance: Redis | undefined;Then update references at lines 44, 47, 48, 56, 62 accordingly.
45-59: Production Redis config is inconsistent with development.The development configuration includes
lazyConnect: true, but the production configuration omits it. This inconsistency may cause different connection behaviors between environments.if (!redis) { redis = new Redis(redisUrl, { maxRetriesPerRequest: 3, retryStrategy(times) { const delay = Math.min(times * 50, 2000); return delay; }, + lazyConnect: true, });
93-93: Avoidanytype for thevalueparameter.Per coding guidelines, avoid using
any. Use a generic orunknowntype instead to maintain type safety.- async set(key: string, value: any, ttl?: number): Promise<boolean> { + async set<T>(key: string, value: T, ttl?: number): Promise<boolean> {
137-141:KEYScommand can block Redis in production.The
KEYScommand scans the entire keyspace and blocks the server, which is problematic with large datasets. Consider usingSCANfor production-safe iteration.async delPattern(pattern: string): Promise<boolean> { const client = getRedisClient(); if (!client) return false; try { - const keys = await client.keys(pattern); - if (keys.length > 0) { - await client.del(...keys); - } + let cursor = "0"; + do { + const [nextCursor, keys] = await client.scan(cursor, "MATCH", pattern, "COUNT", 100); + cursor = nextCursor; + if (keys.length > 0) { + await client.del(...keys); + } + } while (cursor !== "0"); return true; } catch (error) {apps/web/src/app/(main)/testimonials/submit/page.tsx (4)
59-61: Replacealert()with a toast notification or inline error display.Using
alert()blocks the UI and provides poor UX. Consider using a toast component for non-blocking error feedback.onError: (error: any) => { - alert("Error submitting testimonial: " + error.message); + // use a toast notification library or inline error state + // e.g., toast.error("Error submitting testimonial: " + error.message); },
117-123: Use design tokens instead of hardcoded hex values.Per coding guidelines, never use hardcoded hex values like
#101010and#252525directly in components. Reference colors from the design token system using Tailwind classes.Replace hardcoded colors with the appropriate semantic Tailwind classes from your design system. For example, if
#101010maps to a background token, usebg-backgroundor the equivalent defined indesign-tokens.ts.Also applies to: 129-129, 166-166, 201-201
98-108: Replacealert()with consistent error handling.Line 100 uses
alert()for missing avatar validation. For consistency and better UX, use the same error notification pattern as other errors in this component.
238-241: ImageonErrorhandler bypasses Next.js optimization.Directly setting
srcon the image element bypasses Next.js Image optimization. Consider using a state variable to handle fallback images properly.// add state for avatar error const [avatarError, setAvatarError] = React.useState(false); // in the Image component: <Image src={avatarError ? `https://i.pravatar.cc/150?u=${session?.user?.email}` : displayAvatar} alt="Profile Picture" fill className="object-cover" onError={() => setAvatarError(true)} />
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (15)
apps/api/.env.example(1 hunks)apps/api/package.json(1 hunks)apps/api/prisma/schema.prisma(1 hunks)apps/api/src/routers/_app.ts(2 hunks)apps/api/src/routers/testimonial.ts(1 hunks)apps/api/tsconfig.json(1 hunks)apps/web/next.config.js(1 hunks)apps/web/package.json(2 hunks)apps/web/src/app/(main)/dashboard/pro/dashboard/page.tsx(1 hunks)apps/web/src/app/(main)/testimonials/page.tsx(1 hunks)apps/web/src/app/(main)/testimonials/submit/page.tsx(1 hunks)apps/web/src/data/testimonials.ts(1 hunks)packages/shared/package.json(1 hunks)packages/shared/types/index.ts(1 hunks)packages/shared/types/redis.ts(1 hunks)
🧰 Additional context used
📓 Path-based instructions (10)
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.cursorrules)
**/*.{ts,tsx,js,jsx}: Always use lowercase when writing comments
Avoid unnecessary comments; code should be self-documenting when possible
Use comments to explain 'why', not 'what'
Remove unused imports
Use UPPER_SNAKE_CASE for constants
Use camelCase for functions and variables
Files:
apps/web/src/data/testimonials.tsapps/api/src/routers/testimonial.tsapps/api/src/routers/_app.tspackages/shared/types/redis.tsapps/web/src/app/(main)/testimonials/submit/page.tsxpackages/shared/types/index.tsapps/web/src/app/(main)/dashboard/pro/dashboard/page.tsxapps/web/next.config.jsapps/web/src/app/(main)/testimonials/page.tsx
apps/web/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursorrules)
apps/web/src/**/*.{ts,tsx}: Always follow the design system defined inapps/web/src/lib/design-tokens.tsandapps/web/tailwind.config.ts
NEVER use hardcoded hex values (e.g.,#5519f7) directly in components; ALWAYS reference colors from the design token system using Tailwind classes
Use semantic color names that describe purpose, not appearance
Usefont-sansfor standard UI text (Geist Sans) andfont-monofor code, technical content, or monospace needs (DM Mono)
Follow Tailwind's spacing scale (0.25rem increments); for section padding use mobilep-4(1rem) and desktopp-[60px]
Use appropriate border radius: small elementsrounded-lg, mediumrounded-xl, largerounded-2xl, buttonsrounded-[16px]
Use animation durations: fastduration-100(0.1s), normalduration-300(0.3s), slowduration-600(0.6s)
Files:
apps/web/src/data/testimonials.tsapps/web/src/app/(main)/testimonials/submit/page.tsxapps/web/src/app/(main)/dashboard/pro/dashboard/page.tsxapps/web/src/app/(main)/testimonials/page.tsx
**/*.{tsx,ts}
📄 CodeRabbit inference engine (.cursorrules)
**/*.{tsx,ts}: Prefer functional components with TypeScript and use proper TypeScript types, avoidany
Extract reusable logic into custom hooks
Use descriptive prop names and define prop types using TypeScript interfaces or types
Prefer controlled components over uncontrolled
Use zustand for global state (located insrc/store/)
Use absolute imports from@/prefix when available
Include proper aria labels for accessibility
Ensure keyboard navigation works
Maintain proper heading hierarchy
Provide alt text for images
Avoid unnecessary re-renders
Files:
apps/web/src/data/testimonials.tsapps/api/src/routers/testimonial.tsapps/api/src/routers/_app.tspackages/shared/types/redis.tsapps/web/src/app/(main)/testimonials/submit/page.tsxpackages/shared/types/index.tsapps/web/src/app/(main)/dashboard/pro/dashboard/page.tsxapps/web/src/app/(main)/testimonials/page.tsx
**/*.{tsx,ts,jsx,js}
📄 CodeRabbit inference engine (.cursorrules)
Organize imports: react → third-party → local components → utils → types
Files:
apps/web/src/data/testimonials.tsapps/api/src/routers/testimonial.tsapps/api/src/routers/_app.tspackages/shared/types/redis.tsapps/web/src/app/(main)/testimonials/submit/page.tsxpackages/shared/types/index.tsapps/web/src/app/(main)/dashboard/pro/dashboard/page.tsxapps/web/next.config.jsapps/web/src/app/(main)/testimonials/page.tsx
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursorrules)
**/*.{ts,tsx}: Use PascalCase for types and interfaces with descriptive names
Use dynamic imports for code splitting when appropriate
Memoize expensive computations
Files:
apps/web/src/data/testimonials.tsapps/api/src/routers/testimonial.tsapps/api/src/routers/_app.tspackages/shared/types/redis.tsapps/web/src/app/(main)/testimonials/submit/page.tsxpackages/shared/types/index.tsapps/web/src/app/(main)/dashboard/pro/dashboard/page.tsxapps/web/src/app/(main)/testimonials/page.tsx
apps/web/src/**/*.{tsx,ts}
📄 CodeRabbit inference engine (.cursorrules)
Optimize images using next/image
apps/web/src/**/*.{tsx,ts}: Use Zustand for global state, located insrc/store/
Use PascalCase for types and interfaces with descriptive names
Use dynamic imports for code splitting when appropriate
Optimize images using next/image
Memoize expensive computations
Define a type when defining const functions
Files:
apps/web/src/data/testimonials.tsapps/web/src/app/(main)/testimonials/submit/page.tsxapps/web/src/app/(main)/dashboard/pro/dashboard/page.tsxapps/web/src/app/(main)/testimonials/page.tsx
**/*.{js,jsx,ts,tsx,py,java,go,rb,php}
📄 CodeRabbit inference engine (.cursor/rules/general_rules.mdc)
**/*.{js,jsx,ts,tsx,py,java,go,rb,php}: Always use lowercase when writing comments
Avoid unnecessary comments; code should be self-documenting when possible
Use comments to explain 'why', not 'what'
Files:
apps/web/src/data/testimonials.tsapps/api/src/routers/testimonial.tsapps/api/src/routers/_app.tspackages/shared/types/redis.tsapps/web/src/app/(main)/testimonials/submit/page.tsxpackages/shared/types/index.tsapps/web/src/app/(main)/dashboard/pro/dashboard/page.tsxapps/web/next.config.jsapps/web/src/app/(main)/testimonials/page.tsx
apps/web/src/**/*.{tsx,ts,jsx,js}
📄 CodeRabbit inference engine (apps/web/.cursor/rules/frontend_rules.mdc)
apps/web/src/**/*.{tsx,ts,jsx,js}: Organize imports in order: React → third-party → local components → utils → types
Use absolute imports from@/prefix when available
Remove unused imports
Use UPPER_SNAKE_CASE for constants
Use camelCase for functions and variables
Always use Tailwind classes for styling HTML elements; avoid using CSS or style tags
Use descriptive variable and function names; name event functions with a 'handle' prefix (e.g., handleClick, handleKeyDown)
Use const with arrow functions instead of function declarations (e.g., 'const toggle = () =>')
Files:
apps/web/src/data/testimonials.tsapps/web/src/app/(main)/testimonials/submit/page.tsxapps/web/src/app/(main)/dashboard/pro/dashboard/page.tsxapps/web/src/app/(main)/testimonials/page.tsx
apps/api/src/**/*.{js,ts}
📄 CodeRabbit inference engine (apps/api/.cursor/rules/backend_rules.mdc)
apps/api/src/**/*.{js,ts}: Log errors with context (userId, endpoint, timestamp) for debugging
Always await async operations; never forget to handle promise rejections
Never log sensitive information (passwords, tokens, credit card numbers)
Files:
apps/api/src/routers/testimonial.tsapps/api/src/routers/_app.ts
apps/api/src/**/*.ts
📄 CodeRabbit inference engine (apps/api/.cursor/rules/backend_rules.mdc)
Avoid
anytype; useunknownfor truly dynamic data and narrow with type guards
Files:
apps/api/src/routers/testimonial.tsapps/api/src/routers/_app.ts
🧠 Learnings (15)
📚 Learning: 2025-11-25T07:34:58.984Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/api/.cursor/rules/backend_rules.mdc:0-0
Timestamp: 2025-11-25T07:34:58.984Z
Learning: Applies to apps/api/src/**/{routes,controllers,handlers,middleware}/*.{js,ts} : Use zod or similar for runtime validation of incoming data
Applied to files:
apps/api/package.jsonapps/api/src/routers/_app.ts
📚 Learning: 2025-11-25T07:34:58.984Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/api/.cursor/rules/backend_rules.mdc:0-0
Timestamp: 2025-11-25T07:34:58.984Z
Learning: Document all required environment variables in `.env.example`
Applied to files:
apps/api/.env.example
📚 Learning: 2025-11-25T07:34:30.473Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-25T07:34:30.473Z
Learning: Applies to **/*.{tsx,ts} : Provide alt text for images
Applied to files:
apps/web/src/data/testimonials.ts
📚 Learning: 2025-11-25T07:35:19.071Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/web/.cursor/rules/frontend_rules.mdc:0-0
Timestamp: 2025-11-25T07:35:19.071Z
Learning: Applies to apps/web/src/components/**/*.{tsx,ts,jsx,js} : Provide alt text for images
Applied to files:
apps/web/src/data/testimonials.ts
📚 Learning: 2025-11-25T07:34:58.984Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/api/.cursor/rules/backend_rules.mdc:0-0
Timestamp: 2025-11-25T07:34:58.984Z
Learning: Applies to apps/api/src/**/{routes,middleware}/*.{js,ts} : Use middleware for auth checks; never duplicate auth logic in route handlers
Applied to files:
apps/api/src/routers/_app.ts
📚 Learning: 2025-11-25T07:34:58.984Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/api/.cursor/rules/backend_rules.mdc:0-0
Timestamp: 2025-11-25T07:34:58.984Z
Learning: Export types from shared package for consistency across apps
Applied to files:
packages/shared/types/index.ts
📚 Learning: 2025-11-25T07:35:19.071Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/web/.cursor/rules/frontend_rules.mdc:0-0
Timestamp: 2025-11-25T07:35:19.071Z
Learning: Applies to apps/web/src/**/*.{tsx,ts,jsx,js} : Remove unused imports
Applied to files:
packages/shared/types/index.tsapps/api/tsconfig.json
📚 Learning: 2025-11-25T07:34:30.473Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-25T07:34:30.473Z
Learning: Applies to apps/web/src/**/*.{tsx,ts} : Optimize images using next/image
Applied to files:
apps/web/next.config.js
📚 Learning: 2025-11-25T07:34:58.984Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/api/.cursor/rules/backend_rules.mdc:0-0
Timestamp: 2025-11-25T07:34:58.984Z
Learning: Applies to apps/api/src/**/{logging,middleware,utils}/*.{js,ts} : Use structured logging (JSON format) for easier parsing and analysis
Applied to files:
apps/api/tsconfig.json
📚 Learning: 2025-11-25T07:34:30.473Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-25T07:34:30.473Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Avoid unnecessary comments; code should be self-documenting when possible
Applied to files:
apps/api/tsconfig.json
📚 Learning: 2025-11-25T07:34:58.984Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/api/.cursor/rules/backend_rules.mdc:0-0
Timestamp: 2025-11-25T07:34:58.984Z
Learning: Applies to apps/api/src/**/{config,environment,setup}/*.{js,ts} : Use type-safe environment variable access with proper defaults
Applied to files:
apps/api/tsconfig.json
📚 Learning: 2025-11-25T07:34:30.473Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-25T07:34:30.473Z
Learning: Applies to **/*.{tsx,ts,jsx,js} : Organize imports: react → third-party → local components → utils → types
Applied to files:
apps/api/tsconfig.json
📚 Learning: 2025-11-25T07:34:30.473Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-25T07:34:30.473Z
Learning: Applies to **/*.{tsx,ts} : Use descriptive prop names and define prop types using TypeScript interfaces or types
Applied to files:
apps/api/tsconfig.json
📚 Learning: 2025-11-25T07:35:19.071Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/web/.cursor/rules/frontend_rules.mdc:0-0
Timestamp: 2025-11-25T07:35:19.071Z
Learning: Applies to apps/web/src/**/*.{tsx,ts,jsx,js} : Organize imports in order: React → third-party → local components → utils → types
Applied to files:
apps/api/tsconfig.json
📚 Learning: 2025-11-25T07:34:30.473Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-25T07:34:30.473Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Use UPPER_SNAKE_CASE for constants
Applied to files:
apps/api/tsconfig.json
🧬 Code graph analysis (2)
apps/api/src/routers/testimonial.ts (3)
apps/api/src/trpc.ts (3)
router(38-38)publicProcedure(39-39)protectedProcedure(40-40)packages/shared/types/redis.ts (1)
redisCache(72-148)apps/api/src/services/user.service.ts (1)
userService(5-89)
apps/api/src/routers/_app.ts (3)
apps/api/src/routers/testimonial.ts (1)
testimonialRouter(12-96)apps/web/src/app/(main)/(landing)/pricing/page.tsx (4)
testimonial(623-635)testimonials(496-640)testimonial(610-619)testimonial(594-606)apps/web/src/components/landing-sections/testimonials.tsx (1)
div(45-82)
🪛 Biome (2.1.2)
packages/shared/types/redis.ts
[error] 8-8: Shouldn't redeclare 'redis'. Consider to delete it or rename it.
'redis' is defined here:
(lint/suspicious/noRedeclare)
🪛 dotenv-linter (4.0.0)
apps/api/.env.example
[warning] 34-34: [ExtraBlankLine] Extra blank line detected
(ExtraBlankLine)
[warning] 35-35: [EndingBlankLine] No blank line at the end of the file
(EndingBlankLine)
🔇 Additional comments (9)
apps/api/package.json (1)
42-42: No functional changes detected.This is a formatting-only change with no impact on dependencies or behavior.
apps/api/tsconfig.json (1)
38-38: No functional changes detected.This is a formatting-only change with no impact on TypeScript configuration.
packages/shared/package.json (1)
11-15: LGTM!The added dependencies are appropriate for Redis support:
ioredisis the standard Redis client for Node.js@types/nodeprovides necessary type definitions for Node.js APIsThese align with the new Redis utilities introduced in
packages/shared/types/redis.ts.packages/shared/types/index.ts (1)
2-3: LGTM!The addition of the Redis export is well-organized and follows the established pattern. The semicolon addition ensures consistency across export statements.
Based on learnings, this properly exports types from the shared package for consistency across apps.
apps/web/package.json (1)
13-47: Dependencies are compatible with the project.@hookform/resolvers 5.2.2, react-hook-form 7.68.0, and zod 4.1.9 are fully compatible with each other and with Next.js 15.5.3. These versions are appropriate for form validation in the testimonials feature.
apps/api/src/routers/_app.ts (1)
7-7: LGTM!The testimonial router is correctly imported and registered following the existing pattern for other routers.
Also applies to: 25-25
apps/web/src/app/(main)/testimonials/submit/page.tsx (3)
1-19: Import organization looks good.Imports are correctly organized: React → third-party libraries → local components → utils/hooks. This follows the coding guidelines.
23-34: Form validation with zod is well implemented.The schema validation with character limits and the integration with react-hook-form using
zodResolveris clean and follows best practices.Also applies to: 64-76
84-96: Effect handles form population correctly.The useEffect properly resets form values when existing testimonial data or session changes, with correct dependencies in the array.
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: 2
🧹 Nitpick comments (1)
apps/api/src/utils/avatar-validator.ts (1)
164-171: Consider adding error logging for debugging.The function silently returns
falseon all errors, which is appropriate for Zod refinement. However, logging failed validations would aid debugging without exposing details to the client.As per coding guidelines for
apps/api/src/**/*.{js,ts}, consider adding structured logging:export async function avatarUrlRefinement(url: string): Promise<boolean> { try { await validateAvatarUrl(url); return true; } catch (error) { + // log error with context for debugging (don't expose to client) + console.error('Avatar URL validation failed in refinement', { + url: url.substring(0, 50), // truncate for privacy + error: error instanceof TRPCError ? error.message : 'Unknown error' + }); return false; } }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
apps/api/src/routers/testimonial.ts(1 hunks)apps/api/src/trpc.ts(2 hunks)apps/api/src/utils/avatar-validator.ts(1 hunks)apps/web/src/app/(main)/testimonials/page.tsx(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- apps/web/src/app/(main)/testimonials/page.tsx
- apps/api/src/routers/testimonial.ts
🧰 Additional context used
📓 Path-based instructions (10)
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.cursorrules)
**/*.{ts,tsx,js,jsx}: Always use lowercase when writing comments
Avoid unnecessary comments; code should be self-documenting when possible
Use comments to explain 'why', not 'what'
Remove unused imports
Use UPPER_SNAKE_CASE for constants
Use camelCase for functions and variables
Files:
apps/api/src/utils/avatar-validator.tsapps/api/src/trpc.ts
**/*.{tsx,ts}
📄 CodeRabbit inference engine (.cursorrules)
**/*.{tsx,ts}: Prefer functional components with TypeScript and use proper TypeScript types, avoidany
Extract reusable logic into custom hooks
Use descriptive prop names and define prop types using TypeScript interfaces or types
Prefer controlled components over uncontrolled
Use zustand for global state (located insrc/store/)
Use absolute imports from@/prefix when available
Include proper aria labels for accessibility
Ensure keyboard navigation works
Maintain proper heading hierarchy
Provide alt text for images
Avoid unnecessary re-renders
Files:
apps/api/src/utils/avatar-validator.tsapps/api/src/trpc.ts
**/*.{tsx,ts,jsx,js}
📄 CodeRabbit inference engine (.cursorrules)
Organize imports: react → third-party → local components → utils → types
Files:
apps/api/src/utils/avatar-validator.tsapps/api/src/trpc.ts
**/src/{lib,utils}/**
📄 CodeRabbit inference engine (.cursorrules)
Use kebab-case or camelCase for utility file names
Files:
apps/api/src/utils/avatar-validator.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursorrules)
**/*.{ts,tsx}: Use PascalCase for types and interfaces with descriptive names
Use dynamic imports for code splitting when appropriate
Memoize expensive computations
Files:
apps/api/src/utils/avatar-validator.tsapps/api/src/trpc.ts
**/*.{js,jsx,ts,tsx,py,java,go,rb,php}
📄 CodeRabbit inference engine (.cursor/rules/general_rules.mdc)
**/*.{js,jsx,ts,tsx,py,java,go,rb,php}: Always use lowercase when writing comments
Avoid unnecessary comments; code should be self-documenting when possible
Use comments to explain 'why', not 'what'
Files:
apps/api/src/utils/avatar-validator.tsapps/api/src/trpc.ts
apps/api/src/**/*.{js,ts}
📄 CodeRabbit inference engine (apps/api/.cursor/rules/backend_rules.mdc)
apps/api/src/**/*.{js,ts}: Log errors with context (userId, endpoint, timestamp) for debugging
Always await async operations; never forget to handle promise rejections
Never log sensitive information (passwords, tokens, credit card numbers)
Files:
apps/api/src/utils/avatar-validator.tsapps/api/src/trpc.ts
apps/api/src/**/*.ts
📄 CodeRabbit inference engine (apps/api/.cursor/rules/backend_rules.mdc)
Avoid
anytype; useunknownfor truly dynamic data and narrow with type guards
Files:
apps/api/src/utils/avatar-validator.tsapps/api/src/trpc.ts
apps/api/src/**/{middleware,logging,utils}/*.{js,ts}
📄 CodeRabbit inference engine (apps/api/.cursor/rules/backend_rules.mdc)
Include correlation IDs in logs for request tracing across services
Files:
apps/api/src/utils/avatar-validator.ts
apps/api/src/**/{logging,middleware,utils}/*.{js,ts}
📄 CodeRabbit inference engine (apps/api/.cursor/rules/backend_rules.mdc)
Use structured logging (JSON format) for easier parsing and analysis
Files:
apps/api/src/utils/avatar-validator.ts
🧠 Learnings (3)
📚 Learning: 2025-11-25T07:34:58.984Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/api/.cursor/rules/backend_rules.mdc:0-0
Timestamp: 2025-11-25T07:34:58.984Z
Learning: Applies to apps/api/src/**/{routes,controllers,handlers,middleware}/*.{js,ts} : Always validate user authentication before processing protected routes
Applied to files:
apps/api/src/trpc.ts
📚 Learning: 2025-11-25T07:34:58.984Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/api/.cursor/rules/backend_rules.mdc:0-0
Timestamp: 2025-11-25T07:34:58.984Z
Learning: Applies to apps/api/src/**/{routes,middleware}/*.{js,ts} : Use middleware for auth checks; never duplicate auth logic in route handlers
Applied to files:
apps/api/src/trpc.ts
📚 Learning: 2025-11-25T07:34:58.984Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/api/.cursor/rules/backend_rules.mdc:0-0
Timestamp: 2025-11-25T07:34:58.984Z
Learning: Applies to apps/api/src/**/*.ts : Avoid `any` type; use `unknown` for truly dynamic data and narrow with type guards
Applied to files:
apps/api/src/trpc.ts
🔇 Additional comments (3)
apps/api/src/utils/avatar-validator.ts (2)
1-16: LGTM!The imports and allowlist configuration are appropriate. The trusted hosts cover major OAuth providers and CDN services.
18-32: LGTM!The validation limits and private IP ranges are comprehensive, covering IPv4/IPv6 private networks, localhost, and link-local addresses.
apps/api/src/trpc.ts (1)
5-8: LGTM!The
ProtectedContexttype accurately represents the context shape after authentication, improving type clarity for protected procedures.
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: 5
♻️ Duplicate comments (2)
apps/api/src/utils/avatar-validator.ts (1)
102-109: SSRF protection applied correctly.The addition of
redirect: "error"at line 105 properly addresses the SSRF vulnerability flagged in the previous review. This prevents attackers from bypassing private IP checks via HTTP redirects.apps/api/src/trpc.ts (1)
5-44: Type safety improvements look good.The removal of
as anyfrom theprotectedProcedureexport (line 44) properly addresses the previous review concern. The new approach usingProtectedContexttype and typed middleware is a significant improvement. The remaining cast at line 32 is acceptable since it's narrowing the context type after runtime validation confirms the user exists.Based on learnings and coding guidelines, this avoids the
anytype and uses proper TypeScript types as required.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
apps/api/src/trpc.ts(3 hunks)apps/api/src/utils/avatar-validator.ts(1 hunks)
🧰 Additional context used
📓 Path-based instructions (10)
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.cursorrules)
**/*.{ts,tsx,js,jsx}: Always use lowercase when writing comments
Avoid unnecessary comments; code should be self-documenting when possible
Use comments to explain 'why', not 'what'
Remove unused imports
Use UPPER_SNAKE_CASE for constants
Use camelCase for functions and variables
Files:
apps/api/src/utils/avatar-validator.tsapps/api/src/trpc.ts
**/*.{tsx,ts}
📄 CodeRabbit inference engine (.cursorrules)
**/*.{tsx,ts}: Prefer functional components with TypeScript and use proper TypeScript types, avoidany
Extract reusable logic into custom hooks
Use descriptive prop names and define prop types using TypeScript interfaces or types
Prefer controlled components over uncontrolled
Use zustand for global state (located insrc/store/)
Use absolute imports from@/prefix when available
Include proper aria labels for accessibility
Ensure keyboard navigation works
Maintain proper heading hierarchy
Provide alt text for images
Avoid unnecessary re-renders
Files:
apps/api/src/utils/avatar-validator.tsapps/api/src/trpc.ts
**/*.{tsx,ts,jsx,js}
📄 CodeRabbit inference engine (.cursorrules)
Organize imports: react → third-party → local components → utils → types
Files:
apps/api/src/utils/avatar-validator.tsapps/api/src/trpc.ts
**/src/{lib,utils}/**
📄 CodeRabbit inference engine (.cursorrules)
Use kebab-case or camelCase for utility file names
Files:
apps/api/src/utils/avatar-validator.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursorrules)
**/*.{ts,tsx}: Use PascalCase for types and interfaces with descriptive names
Use dynamic imports for code splitting when appropriate
Memoize expensive computations
Files:
apps/api/src/utils/avatar-validator.tsapps/api/src/trpc.ts
**/*.{js,jsx,ts,tsx,py,java,go,rb,php}
📄 CodeRabbit inference engine (.cursor/rules/general_rules.mdc)
**/*.{js,jsx,ts,tsx,py,java,go,rb,php}: Always use lowercase when writing comments
Avoid unnecessary comments; code should be self-documenting when possible
Use comments to explain 'why', not 'what'
Files:
apps/api/src/utils/avatar-validator.tsapps/api/src/trpc.ts
apps/api/src/**/*.{js,ts}
📄 CodeRabbit inference engine (apps/api/.cursor/rules/backend_rules.mdc)
apps/api/src/**/*.{js,ts}: Log errors with context (userId, endpoint, timestamp) for debugging
Always await async operations; never forget to handle promise rejections
Never log sensitive information (passwords, tokens, credit card numbers)
Files:
apps/api/src/utils/avatar-validator.tsapps/api/src/trpc.ts
apps/api/src/**/*.ts
📄 CodeRabbit inference engine (apps/api/.cursor/rules/backend_rules.mdc)
Avoid
anytype; useunknownfor truly dynamic data and narrow with type guards
Files:
apps/api/src/utils/avatar-validator.tsapps/api/src/trpc.ts
apps/api/src/**/{middleware,logging,utils}/*.{js,ts}
📄 CodeRabbit inference engine (apps/api/.cursor/rules/backend_rules.mdc)
Include correlation IDs in logs for request tracing across services
Files:
apps/api/src/utils/avatar-validator.ts
apps/api/src/**/{logging,middleware,utils}/*.{js,ts}
📄 CodeRabbit inference engine (apps/api/.cursor/rules/backend_rules.mdc)
Use structured logging (JSON format) for easier parsing and analysis
Files:
apps/api/src/utils/avatar-validator.ts
🧠 Learnings (8)
📚 Learning: 2025-11-25T07:34:58.984Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/api/.cursor/rules/backend_rules.mdc:0-0
Timestamp: 2025-11-25T07:34:58.984Z
Learning: Applies to apps/api/src/**/{routes,middleware}/*.{js,ts} : Use middleware for auth checks; never duplicate auth logic in route handlers
Applied to files:
apps/api/src/trpc.ts
📚 Learning: 2025-11-25T07:34:58.984Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/api/.cursor/rules/backend_rules.mdc:0-0
Timestamp: 2025-11-25T07:34:58.984Z
Learning: Applies to apps/api/src/**/{routes,controllers,handlers,middleware}/*.{js,ts} : Always validate user authentication before processing protected routes
Applied to files:
apps/api/src/trpc.ts
📚 Learning: 2025-11-25T07:34:58.984Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/api/.cursor/rules/backend_rules.mdc:0-0
Timestamp: 2025-11-25T07:34:58.984Z
Learning: Applies to apps/api/src/**/*.ts : Avoid `any` type; use `unknown` for truly dynamic data and narrow with type guards
Applied to files:
apps/api/src/trpc.ts
📚 Learning: 2025-11-25T07:35:19.071Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/web/.cursor/rules/frontend_rules.mdc:0-0
Timestamp: 2025-11-25T07:35:19.071Z
Learning: Applies to apps/web/src/components/**/*.{tsx,ts} : Use proper TypeScript types and avoid using `any` type
Applied to files:
apps/api/src/trpc.ts
📚 Learning: 2025-11-25T07:34:30.473Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-25T07:34:30.473Z
Learning: Applies to **/*.{tsx,ts} : Prefer functional components with TypeScript and use proper TypeScript types, avoid `any`
Applied to files:
apps/api/src/trpc.ts
📚 Learning: 2025-11-25T07:34:58.984Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/api/.cursor/rules/backend_rules.mdc:0-0
Timestamp: 2025-11-25T07:34:58.984Z
Learning: Applies to apps/api/src/**/{routes,controllers,handlers,middleware}/*.{js,ts} : Use zod or similar for runtime validation of incoming data
Applied to files:
apps/api/src/trpc.ts
📚 Learning: 2025-11-25T07:34:58.984Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/api/.cursor/rules/backend_rules.mdc:0-0
Timestamp: 2025-11-25T07:34:58.984Z
Learning: Applies to apps/api/src/**/{routes,controllers,handlers,types}/*.ts : Define strict TypeScript interfaces for request/response bodies
Applied to files:
apps/api/src/trpc.ts
📚 Learning: 2025-11-25T07:34:58.984Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/api/.cursor/rules/backend_rules.mdc:0-0
Timestamp: 2025-11-25T07:34:58.984Z
Learning: Applies to apps/api/src/**/{database,models,services}/*.{js,ts} : Always use parameterized queries or ORM methods to prevent SQL injection
Applied to files:
apps/api/src/trpc.ts
🧬 Code graph analysis (2)
apps/api/src/utils/avatar-validator.ts (1)
apps/api/test-ddos.js (1)
response(28-28)
apps/api/src/trpc.ts (1)
apps/api/src/context.ts (1)
Context(26-26)
|
@apsinghdev fixed it! optional social link and icon based on link input
button invisible after testimonial get submitted
also payment issue fixed |
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
♻️ Duplicate comments (3)
apps/api/src/routers/testimonial.ts (2)
14-14: Removeanytype annotations; let tRPC infer types.Per coding guidelines, avoid
anytype. tRPC should correctly infer context types from procedure definitions.- getAll: publicProcedure.query(async ({ ctx }: any) => { + getAll: publicProcedure.query(async ({ ctx }) => {- getMyTestimonial: protectedProcedure.query(async ({ ctx }: any) => { + getMyTestimonial: protectedProcedure.query(async ({ ctx }) => {- .mutation(async ({ ctx, input }: any) => { + .mutation(async ({ ctx, input }) => {Also applies to: 37-37, 81-81
43-48: Misleading error message for read operation.The error message says "submit testimonials" but this is
getMyTestimonial(a read operation).if (!isPaidUser) { throw new TRPCError({ code: "FORBIDDEN", - message: "Only premium users can submit testimonials", + message: "Only premium users can view their testimonials", }); }apps/web/src/app/(main)/testimonials/submit/page.tsx (1)
72-74: Remove unnecessaryanytype casts on trpc.The tRPC client is properly typed with
AppRouter. These casts bypass type checking and violate coding guidelines.- const { data, isLoading: isDataLoading } = ( - trpc as any - ).testimonial.getMyTestimonial.useQuery(undefined, { + const { data, isLoading: isDataLoading } = trpc.testimonial.getMyTestimonial.useQuery(undefined, {- const submitMutation = (trpc as any).testimonial.submit.useMutation({ + const submitMutation = trpc.testimonial.submit.useMutation({Also applies to: 89-89
🧹 Nitpick comments (8)
apps/web/src/app/(main)/testimonials/page.tsx (3)
6-6: Consider using project's custom icon components.The project has custom Twitter and Youtube icons in
apps/web/src/components/icons/icons.tsx. Consider using these for consistency across the codebase, unless lucide-react icons are preferred project-wide.
28-32: Remove duplicate type definition.
ImageTestimonialis already defined and exported fromapps/web/src/data/testimonials.ts. Import it instead of redefining.+import { imageTestimonials, ImageTestimonial } from "@/data/testimonials"; -import { imageTestimonials } from "@/data/testimonials"; -type ImageTestimonial = TestimonialBase & { - type: "image"; - imageUrl: string; - alt: string; -};
61-61: Replace hardcoded hex values with design tokens.Per coding guidelines, avoid hardcoded hex values like
#252525and#101010. Use semantic color classes from the design token system (e.g.,bg-surface-primary,border-border-primary).Example locations to update:
- Line 61:
border-[#252525]→ use border token- Line 80:
border-[#252525]→ use border token- Line 170:
bg-[#101010]→ use background tokenAlso applies to: 80-80, 170-170
apps/api/src/routers/testimonial.ts (2)
18-21: Consider using structured logging instead of console.log.For production observability, consider using a structured logger with context (e.g., request ID, timestamp) instead of raw
console.log. Per coding guidelines: "Log errors with context (userId, endpoint, timestamp) for debugging."Also applies to: 32-32
91-93: Remove extra blank lines.if (!isPaidUser) { throw new TRPCError({ code: "FORBIDDEN", message: "Only premium users can submit testimonials", }); } - - // Check if testimonial already exists - prevent updatesapps/web/src/app/(main)/testimonials/submit/page.tsx (3)
96-98: Type the error parameter properly.Avoid
anyfor the error parameter. Use tRPC's error type for proper type safety.+import { TRPCClientError } from "@trpc/client"; - onError: (error: any) => { + onError: (error: TRPCClientError<any>) => { setError(error.message || "Error submitting testimonial"); },
289-292: Consider using a local fallback image.The fallback uses an external service (
pravatar.cc). Consider using a local placeholder image for better reliability and privacy.
155-155: Replace hardcoded hex values with design tokens.Similar to the testimonials list page, replace hardcoded colors like
bg-[#101010]andborder-[#252525]with semantic design token classes.Also applies to: 167-167, 253-253
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (7)
apps/api/prisma/schema.prisma(1 hunks)apps/api/src/routers/testimonial.ts(1 hunks)apps/web/src/app/(main)/dashboard/pro/dashboard/page.tsx(3 hunks)apps/web/src/app/(main)/testimonials/page.tsx(1 hunks)apps/web/src/app/(main)/testimonials/submit/page.tsx(1 hunks)apps/web/src/components/checkout/CheckoutWrapper.tsx(1 hunks)apps/web/src/hooks/useSubscription.ts(2 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- apps/api/prisma/schema.prisma
- apps/web/src/app/(main)/dashboard/pro/dashboard/page.tsx
🧰 Additional context used
📓 Path-based instructions (13)
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.cursorrules)
**/*.{ts,tsx,js,jsx}: Always use lowercase when writing comments
Avoid unnecessary comments; code should be self-documenting when possible
Use comments to explain 'why', not 'what'
Remove unused imports
Use UPPER_SNAKE_CASE for constants
Use camelCase for functions and variables
Files:
apps/web/src/hooks/useSubscription.tsapps/api/src/routers/testimonial.tsapps/web/src/app/(main)/testimonials/page.tsxapps/web/src/app/(main)/testimonials/submit/page.tsxapps/web/src/components/checkout/CheckoutWrapper.tsx
apps/web/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursorrules)
apps/web/src/**/*.{ts,tsx}: Always follow the design system defined inapps/web/src/lib/design-tokens.tsandapps/web/tailwind.config.ts
NEVER use hardcoded hex values (e.g.,#5519f7) directly in components; ALWAYS reference colors from the design token system using Tailwind classes
Use semantic color names that describe purpose, not appearance
Usefont-sansfor standard UI text (Geist Sans) andfont-monofor code, technical content, or monospace needs (DM Mono)
Follow Tailwind's spacing scale (0.25rem increments); for section padding use mobilep-4(1rem) and desktopp-[60px]
Use appropriate border radius: small elementsrounded-lg, mediumrounded-xl, largerounded-2xl, buttonsrounded-[16px]
Use animation durations: fastduration-100(0.1s), normalduration-300(0.3s), slowduration-600(0.6s)
Files:
apps/web/src/hooks/useSubscription.tsapps/web/src/app/(main)/testimonials/page.tsxapps/web/src/app/(main)/testimonials/submit/page.tsxapps/web/src/components/checkout/CheckoutWrapper.tsx
**/*.{tsx,ts}
📄 CodeRabbit inference engine (.cursorrules)
**/*.{tsx,ts}: Prefer functional components with TypeScript and use proper TypeScript types, avoidany
Extract reusable logic into custom hooks
Use descriptive prop names and define prop types using TypeScript interfaces or types
Prefer controlled components over uncontrolled
Use zustand for global state (located insrc/store/)
Use absolute imports from@/prefix when available
Include proper aria labels for accessibility
Ensure keyboard navigation works
Maintain proper heading hierarchy
Provide alt text for images
Avoid unnecessary re-renders
Files:
apps/web/src/hooks/useSubscription.tsapps/api/src/routers/testimonial.tsapps/web/src/app/(main)/testimonials/page.tsxapps/web/src/app/(main)/testimonials/submit/page.tsxapps/web/src/components/checkout/CheckoutWrapper.tsx
**/*.{tsx,ts,jsx,js}
📄 CodeRabbit inference engine (.cursorrules)
Organize imports: react → third-party → local components → utils → types
Files:
apps/web/src/hooks/useSubscription.tsapps/api/src/routers/testimonial.tsapps/web/src/app/(main)/testimonials/page.tsxapps/web/src/app/(main)/testimonials/submit/page.tsxapps/web/src/components/checkout/CheckoutWrapper.tsx
**/*[A-Z]*.{tsx,ts}
📄 CodeRabbit inference engine (.cursorrules)
Use PascalCase for component file names (e.g.,
UserProfile.tsx)
Files:
apps/web/src/hooks/useSubscription.tsapps/web/src/components/checkout/CheckoutWrapper.tsx
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursorrules)
**/*.{ts,tsx}: Use PascalCase for types and interfaces with descriptive names
Use dynamic imports for code splitting when appropriate
Memoize expensive computations
Files:
apps/web/src/hooks/useSubscription.tsapps/api/src/routers/testimonial.tsapps/web/src/app/(main)/testimonials/page.tsxapps/web/src/app/(main)/testimonials/submit/page.tsxapps/web/src/components/checkout/CheckoutWrapper.tsx
apps/web/src/**/*.{tsx,ts}
📄 CodeRabbit inference engine (.cursorrules)
Optimize images using next/image
apps/web/src/**/*.{tsx,ts}: Use Zustand for global state, located insrc/store/
Use PascalCase for types and interfaces with descriptive names
Use dynamic imports for code splitting when appropriate
Optimize images using next/image
Memoize expensive computations
Define a type when defining const functions
Files:
apps/web/src/hooks/useSubscription.tsapps/web/src/app/(main)/testimonials/page.tsxapps/web/src/app/(main)/testimonials/submit/page.tsxapps/web/src/components/checkout/CheckoutWrapper.tsx
**/*.{js,jsx,ts,tsx,py,java,go,rb,php}
📄 CodeRabbit inference engine (.cursor/rules/general_rules.mdc)
**/*.{js,jsx,ts,tsx,py,java,go,rb,php}: Always use lowercase when writing comments
Avoid unnecessary comments; code should be self-documenting when possible
Use comments to explain 'why', not 'what'
Files:
apps/web/src/hooks/useSubscription.tsapps/api/src/routers/testimonial.tsapps/web/src/app/(main)/testimonials/page.tsxapps/web/src/app/(main)/testimonials/submit/page.tsxapps/web/src/components/checkout/CheckoutWrapper.tsx
apps/web/src/**/*.{tsx,ts,jsx,js}
📄 CodeRabbit inference engine (apps/web/.cursor/rules/frontend_rules.mdc)
apps/web/src/**/*.{tsx,ts,jsx,js}: Organize imports in order: React → third-party → local components → utils → types
Use absolute imports from@/prefix when available
Remove unused imports
Use UPPER_SNAKE_CASE for constants
Use camelCase for functions and variables
Always use Tailwind classes for styling HTML elements; avoid using CSS or style tags
Use descriptive variable and function names; name event functions with a 'handle' prefix (e.g., handleClick, handleKeyDown)
Use const with arrow functions instead of function declarations (e.g., 'const toggle = () =>')
Files:
apps/web/src/hooks/useSubscription.tsapps/web/src/app/(main)/testimonials/page.tsxapps/web/src/app/(main)/testimonials/submit/page.tsxapps/web/src/components/checkout/CheckoutWrapper.tsx
apps/api/src/**/*.{js,ts}
📄 CodeRabbit inference engine (apps/api/.cursor/rules/backend_rules.mdc)
apps/api/src/**/*.{js,ts}: Log errors with context (userId, endpoint, timestamp) for debugging
Always await async operations; never forget to handle promise rejections
Never log sensitive information (passwords, tokens, credit card numbers)
Files:
apps/api/src/routers/testimonial.ts
apps/api/src/**/*.ts
📄 CodeRabbit inference engine (apps/api/.cursor/rules/backend_rules.mdc)
Avoid
anytype; useunknownfor truly dynamic data and narrow with type guards
Files:
apps/api/src/routers/testimonial.ts
apps/web/src/components/**/*.{tsx,ts,jsx,js}
📄 CodeRabbit inference engine (apps/web/.cursor/rules/frontend_rules.mdc)
apps/web/src/components/**/*.{tsx,ts,jsx,js}: Never use hardcoded hex values directly in components; always reference colors from the design token system using Tailwind classes
Use semantic color names from the design token system that describe purpose, not appearance (e.g., bg-brand-purple, bg-surface-primary, text-text-primary)
Use font-sans for standard UI text (Geist Sans) and font-mono for code, technical content, or monospace needs (DM Mono)
Follow Tailwind's spacing scale for section padding: p-4 (1rem) on mobile, p-[60px] on desktop
Use rounded-lg (0.5rem) for small elements, rounded-xl (1rem) for medium elements, rounded-2xl (1.5rem) for large elements, and rounded-[16px] for buttons
Use duration-100 (0.1s) for fast transitions, duration-300 (0.3s) for normal transitions, and duration-600 (0.6s) for slow transitions
Use available custom animations: animate-accordion-down, animate-accordion-up, animate-scrollRight, animate-scrollLeft, animate-customspin, animate-spin-slow, animate-spin-slow-reverse, animate-marquee, animate-marquee-vertical, animate-shine
Prefer functional components with TypeScript
Extract reusable logic into custom hooks
Prefer controlled components over uncontrolled
Include proper aria labels for accessibility
Ensure keyboard navigation works in interactive components
Maintain proper heading hierarchy in page components
Provide alt text for images
Use 'class:' instead of the ternary operator in class tags whenever possible
Implement accessibility features on interactive elements (e.g., tabindex='0', aria-label, onClick, onKeyDown)
Always follow the design system defined inapps/web/src/lib/design-tokens.tsandapps/web/tailwind.config.ts
Files:
apps/web/src/components/checkout/CheckoutWrapper.tsx
apps/web/src/components/**/*.{tsx,ts}
📄 CodeRabbit inference engine (apps/web/.cursor/rules/frontend_rules.mdc)
apps/web/src/components/**/*.{tsx,ts}: Use proper TypeScript types and avoid usinganytype
Use descriptive prop names and define prop types using TypeScript interfaces or types
Name components using PascalCase (e.g., UserProfile.tsx)
Files:
apps/web/src/components/checkout/CheckoutWrapper.tsx
🧠 Learnings (11)
📚 Learning: 2025-11-25T07:35:19.071Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/web/.cursor/rules/frontend_rules.mdc:0-0
Timestamp: 2025-11-25T07:35:19.071Z
Learning: Applies to apps/web/src/components/**/*.{tsx,ts} : Use proper TypeScript types and avoid using `any` type
Applied to files:
apps/api/src/routers/testimonial.tsapps/web/src/app/(main)/testimonials/submit/page.tsx
📚 Learning: 2025-11-25T07:34:58.984Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/api/.cursor/rules/backend_rules.mdc:0-0
Timestamp: 2025-11-25T07:34:58.984Z
Learning: Applies to apps/api/src/**/*.ts : Avoid `any` type; use `unknown` for truly dynamic data and narrow with type guards
Applied to files:
apps/api/src/routers/testimonial.tsapps/web/src/app/(main)/testimonials/submit/page.tsx
📚 Learning: 2025-11-25T07:34:30.473Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-25T07:34:30.473Z
Learning: Applies to **/*.{tsx,ts} : Prefer functional components with TypeScript and use proper TypeScript types, avoid `any`
Applied to files:
apps/api/src/routers/testimonial.tsapps/web/src/app/(main)/testimonials/submit/page.tsx
📚 Learning: 2025-11-25T07:34:58.984Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/api/.cursor/rules/backend_rules.mdc:0-0
Timestamp: 2025-11-25T07:34:58.984Z
Learning: Applies to apps/api/src/**/{routes,controllers,handlers,types}/*.ts : Define strict TypeScript interfaces for request/response bodies
Applied to files:
apps/api/src/routers/testimonial.tsapps/web/src/app/(main)/testimonials/submit/page.tsx
📚 Learning: 2025-11-25T07:34:58.984Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/api/.cursor/rules/backend_rules.mdc:0-0
Timestamp: 2025-11-25T07:34:58.984Z
Learning: Applies to apps/api/src/**/{routes,controllers,handlers,middleware}/*.{js,ts} : Use zod or similar for runtime validation of incoming data
Applied to files:
apps/api/src/routers/testimonial.tsapps/web/src/app/(main)/testimonials/submit/page.tsx
📚 Learning: 2025-11-25T07:35:19.071Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/web/.cursor/rules/frontend_rules.mdc:0-0
Timestamp: 2025-11-25T07:35:19.071Z
Learning: Applies to apps/web/src/**/*.{tsx,ts} : Use PascalCase for types and interfaces with descriptive names
Applied to files:
apps/api/src/routers/testimonial.tsapps/web/src/app/(main)/testimonials/submit/page.tsx
📚 Learning: 2025-11-25T07:34:58.984Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/api/.cursor/rules/backend_rules.mdc:0-0
Timestamp: 2025-11-25T07:34:58.984Z
Learning: Applies to apps/api/src/**/{services,repositories}/*.{js,ts} : Avoid N+1 queries; use eager loading or batch queries when fetching related data
Applied to files:
apps/api/src/routers/testimonial.ts
📚 Learning: 2025-11-25T07:34:58.984Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/api/.cursor/rules/backend_rules.mdc:0-0
Timestamp: 2025-11-25T07:34:58.984Z
Learning: Applies to apps/api/src/**/*.{js,ts} : Always await async operations; never forget to handle promise rejections
Applied to files:
apps/api/src/routers/testimonial.ts
📚 Learning: 2025-11-25T07:34:30.473Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-25T07:34:30.473Z
Learning: Applies to **/*.{tsx,ts} : Use descriptive prop names and define prop types using TypeScript interfaces or types
Applied to files:
apps/api/src/routers/testimonial.tsapps/web/src/app/(main)/testimonials/submit/page.tsx
📚 Learning: 2025-11-25T07:35:19.071Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/web/.cursor/rules/frontend_rules.mdc:0-0
Timestamp: 2025-11-25T07:35:19.071Z
Learning: Applies to apps/web/src/components/**/*.{tsx,ts} : Use descriptive prop names and define prop types using TypeScript interfaces or types
Applied to files:
apps/api/src/routers/testimonial.tsapps/web/src/app/(main)/testimonials/submit/page.tsx
📚 Learning: 2025-11-25T07:34:30.473Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-25T07:34:30.473Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Avoid unnecessary comments; code should be self-documenting when possible
Applied to files:
apps/web/src/app/(main)/testimonials/submit/page.tsx
🧬 Code graph analysis (4)
apps/api/src/routers/testimonial.ts (4)
apps/api/src/trpc.ts (3)
router(42-42)publicProcedure(43-43)protectedProcedure(44-44)packages/shared/types/redis.ts (1)
redisCache(72-148)apps/api/src/services/user.service.ts (1)
userService(5-89)apps/api/src/utils/avatar-validator.ts (1)
validateAvatarUrl(46-159)
apps/web/src/app/(main)/testimonials/page.tsx (4)
apps/web/src/data/testimonials.ts (2)
ImageTestimonial(2-7)imageTestimonials(9-40)apps/web/src/components/icons/icons.tsx (2)
Youtube(200-202)apps/web/src/lib/trpc.ts (1)
trpc(4-4)apps/web/src/components/ui/skeleton.tsx (1)
Skeleton(15-15)
apps/web/src/app/(main)/testimonials/submit/page.tsx (7)
apps/api/src/trpc.ts (1)
router(42-42)apps/web/src/lib/auth/config.ts (1)
session(43-49)apps/web/src/hooks/useSubscription.ts (1)
useSubscription(11-79)apps/web/src/lib/trpc.ts (1)
trpc(4-4)apps/web/src/components/ui/label.tsx (1)
Label(26-26)apps/web/src/components/ui/input.tsx (1)
Input(22-22)apps/web/src/components/ui/textarea.tsx (1)
Textarea(22-22)
apps/web/src/components/checkout/CheckoutWrapper.tsx (1)
apps/web/src/hooks/useSubscription.ts (1)
useSubscription(11-79)
🔇 Additional comments (12)
apps/web/src/app/(main)/testimonials/page.tsx (4)
37-56: LGTM!The
getSocialIconhelper correctly parses URLs and handles errors gracefully with a null fallback.
58-115: Well-structured component with proper type narrowing.Good use of discriminated union for type narrowing and proper accessibility attributes on the social link.
123-167: LGTM!The interleaving logic is correct and
useMemoappropriately memoizes the computation with the correct dependency.
220-236: LGTM! Empty state logic is now correct.The conditions are properly mutually exclusive - the grid renders only when
allTestimonials.length > 0, and the empty state only whenlength === 0.apps/api/src/routers/testimonial.ts (2)
59-80: LGTM! Comprehensive input validation.Good use of zod with detailed constraints. The avatar URL is properly validated via
validateAvatarUrlwhich implements SSRF protections.
94-104: LGTM! Testimonial immutability properly enforced.The check for existing testimonial with clear error message correctly implements the PR requirement that testimonials cannot be edited once submitted.
apps/web/src/hooks/useSubscription.ts (1)
28-28: LGTM! Exposing refetch is appropriate.The addition of
refetchto the hook's return value is a clean change that enables consumers (like CheckoutWrapper) to manually trigger subscription status refresh.Also applies to: 77-77
apps/web/src/components/checkout/CheckoutWrapper.tsx (2)
15-29: LGTM! Retry logic is well-implemented.Good use of cleanup to clear timeout, defensive optional chaining on
refetch, and reasonable retry parameters (3 attempts, 2s intervals).
32-40: LGTM! Good loading state UX.The dynamic message ("Loading..." vs "Verifying payment...") provides appropriate feedback during different phases.
apps/web/src/app/(main)/testimonials/submit/page.tsx (3)
45-62: LGTM! Client-side validation mirrors server schema.Good practice to have consistent validation on both client and server. The schema correctly validates name length, content bounds, and social link platforms.
133-146: LGTM! Submit handler with proper validation.Good check for avatar existence before submission with clear error message.
164-199: Well-structured access control states.Good UX with clear messaging for unauthenticated, non-premium, and already-submitted states. Each includes appropriate CTAs.
Also applies to: 201-236, 254-273
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 (5)
apps/api/src/services/payment.service.ts (5)
58-68: Add context to error logging.The error log on line 65 lacks context such as
planIdand timestamp. This makes debugging production issues difficult.As per coding guidelines, errors should be logged with context (userId, endpoint, timestamp) for debugging.
Apply this diff to improve error logging:
} catch (error) { - console.error("Error fetching plan:", error); + console.error("error fetching plan:", { + planId, + error: error instanceof Error ? error.message : error, + timestamp: new Date().toISOString(), + }); throw new Error("Failed to fetch plan");
73-105: Add timeout for external API call and avoidanytype.This method has several issues:
- The Razorpay API call on line 77 has no timeout, which can cause hanging requests if Razorpay is slow or unresponsive.
- Line 85 uses
anytype instead ofunknown.- Missing logging for successful order creation, which is a critical operation.
As per coding guidelines, implement timeouts for external API calls and avoid
anytype.Consider:
- Wrapping the Razorpay call with a timeout mechanism or configuring timeout at the client level.
- Changing
error: anytoerror: unknownand using type guards.- Adding structured logging for successful order creation.
- } catch (error: any) { + } catch (error: unknown) { - if (error.error) { + if (error && typeof error === 'object' && 'error' in error) { return { - error: error.error, + error: (error as any).error, } as RazorpayError; } // Handle unexpected errors return { error: { code: "INTERNAL_ERROR", - description: error.message || "An unexpected error occurred", + description: error instanceof Error ? error.message : "an unexpected error occurred",
110-138: Add input validation for security-critical signature verification.This security-critical method lacks input validation. Empty or invalid inputs could lead to incorrect verification results. Additionally, successful signature verifications should be logged for audit trails.
As per coding guidelines, log all critical operations (auth attempts, payment processing, data mutations).
Apply this diff:
verifyPaymentSignature( orderId: string, paymentId: string, signature: string ): boolean { try { + // validate inputs + if (!orderId || !paymentId || !signature) { + console.error("signature verification failed: missing required parameters"); + return false; + } + const keySecret = process.env.RAZORPAY_KEY_SECRET; if (!keySecret) { throw new Error("RAZORPAY_KEY_SECRET not configured"); } // Create the expected signature const generatedSignatureHex = crypto .createHmac("sha256", keySecret) .update(`${orderId}|${paymentId}`) .digest("hex"); const a = Buffer.from(signature, "hex"); const b = Buffer.from(generatedSignatureHex, "hex"); if (a.length !== b.length) return false; // Compare signatures securely - return crypto.timingSafeEqual(a, b); + const isValid = crypto.timingSafeEqual(a, b); + if (isValid) { + console.log("payment signature verified successfully", { + orderId, + timestamp: new Date().toISOString(), + }); + } + return isValid; } catch (error) { - console.error("Signature verification error:", error); + console.error("signature verification error:", { + error: error instanceof Error ? error.message : error, + timestamp: new Date().toISOString(), + }); return false;
143-174: Replaceanyreturn type and add logging for critical operation.This method has several issues:
- Return type is
anyon line 146 instead of a specific Prisma type.- Error logging lacks context (userId, paymentId).
- Missing logging for successful payment creation, which is a critical operation.
As per coding guidelines, avoid
anytype and log all critical operations.Apply this diff:
async createPaymentRecord( userId: string, paymentData: PaymentData - ): Promise<any> { + ): Promise<import('@prisma/client').Payment> { try { // Check if payment already exists (idempotency) const existingPayment = await prisma.payment.findUnique({ where: { razorpayPaymentId: paymentData.razorpayPaymentId }, }); if (existingPayment) { + console.log("payment record already exists (idempotent)", { + userId, + paymentId: existingPayment.id, + timestamp: new Date().toISOString(), + }); return existingPayment; } // Create new payment record const payment = await prisma.payment.create({ data: { userId, razorpayPaymentId: paymentData.razorpayPaymentId, razorpayOrderId: paymentData.razorpayOrderId, amount: paymentData.amount, // Amount in paise (smallest currency unit) currency: paymentData.currency, status: PAYMENT_STATUS.CAPTURED, }, }); + console.log("payment record created successfully", { + userId, + paymentId: payment.id, + amount: payment.amount, + timestamp: new Date().toISOString(), + }); + return payment; } catch (error) { - console.error("Error creating payment record:", error); + console.error("error creating payment record:", { + userId, + razorpayPaymentId: paymentData.razorpayPaymentId, + error: error instanceof Error ? error.message : error, + timestamp: new Date().toISOString(), + }); throw new Error("Failed to create payment record");
179-256: Wrap subscription creation and payment update in a transaction.This method has a critical data consistency issue: the subscription creation (line 234) and payment update (line 246) are two separate database operations without a transaction wrapper. If the payment update fails after subscription creation, the database will be in an inconsistent state with an orphaned subscription.
Additional issues:
- Return type is
any(line 183) instead of a specific Prisma type.- Error logging lacks context (userId, planId, paymentId).
- Missing logging for successful subscription creation (critical operation).
As per coding guidelines, wrap database transactions in try-catch blocks with proper rollback on failure.
Apply this diff:
async createSubscription( userId: string, planId: string, paymentId: string - ): Promise<any> { + ): Promise<import('@prisma/client').Subscription> { try { // Get plan details const plan = await prisma.plan.findUnique({ where: { id: planId }, }); if (!plan) { throw new Error("Plan not found"); } // Calculate end date - Currently only yearly plan is supported const startDate = new Date(); const endDate = new Date(startDate); // Set subscription for 1 year (yearly plan) endDate.setFullYear(endDate.getFullYear() + 1); // Future plan intervals (commented out for now): // switch (plan.interval.toLowerCase()) { // case "monthly": // endDate.setMonth(endDate.getMonth() + 1); // break; // case "quarterly": // endDate.setMonth(endDate.getMonth() + 3); // break; // case "yearly": // case "annual": // endDate.setFullYear(endDate.getFullYear() + 1); // break; // default: // endDate.setFullYear(endDate.getFullYear() + 1); // } // Check if user already has an active subscription for this payment const existingSubscription = await prisma.subscription.findFirst({ where: { userId, payments: { some: { id: paymentId, }, }, }, }); if (existingSubscription) { + console.log("subscription already exists for payment (idempotent)", { + userId, + subscriptionId: existingSubscription.id, + timestamp: new Date().toISOString(), + }); return existingSubscription; } - // Create subscription - const subscription = await prisma.subscription.create({ - data: { - userId, - planId, - status: SUBSCRIPTION_STATUS.ACTIVE, - startDate, - endDate, - autoRenew: true, - }, - }); - - // Link payment to subscription - await prisma.payment.update({ - where: { id: paymentId }, - data: { subscriptionId: subscription.id }, + // use transaction to ensure data consistency + const subscription = await prisma.$transaction(async (tx) => { + // create subscription + const newSubscription = await tx.subscription.create({ + data: { + userId, + planId, + status: SUBSCRIPTION_STATUS.ACTIVE, + startDate, + endDate, + autoRenew: true, + }, + }); + + // link payment to subscription + await tx.payment.update({ + where: { id: paymentId }, + data: { subscriptionId: newSubscription.id }, + }); + + return newSubscription; }); + console.log("subscription created successfully", { + userId, + subscriptionId: subscription.id, + planId, + timestamp: new Date().toISOString(), + }); + return subscription; } catch (error) { - console.error("Error creating subscription:", error); + console.error("error creating subscription:", { + userId, + planId, + paymentId, + error: error instanceof Error ? error.message : error, + timestamp: new Date().toISOString(), + }); throw new Error("Failed to create subscription");
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
apps/api/src/services/payment.service.ts(1 hunks)apps/web/src/app/(main)/(landing)/pitch/page.tsx(19 hunks)
✅ Files skipped from review due to trivial changes (1)
- apps/web/src/app/(main)/(landing)/pitch/page.tsx
🧰 Additional context used
📓 Path-based instructions (16)
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.cursorrules)
**/*.{ts,tsx,js,jsx}: Always use lowercase when writing comments
Avoid unnecessary comments; code should be self-documenting when possible
Use comments to explain 'why', not 'what'
Remove unused imports
Use UPPER_SNAKE_CASE for constants
Use camelCase for functions and variables
Files:
apps/api/src/services/payment.service.ts
**/*.{tsx,ts}
📄 CodeRabbit inference engine (.cursorrules)
**/*.{tsx,ts}: Prefer functional components with TypeScript and use proper TypeScript types, avoidany
Extract reusable logic into custom hooks
Use descriptive prop names and define prop types using TypeScript interfaces or types
Prefer controlled components over uncontrolled
Use zustand for global state (located insrc/store/)
Use absolute imports from@/prefix when available
Include proper aria labels for accessibility
Ensure keyboard navigation works
Maintain proper heading hierarchy
Provide alt text for images
Avoid unnecessary re-renders
Files:
apps/api/src/services/payment.service.ts
**/*.{tsx,ts,jsx,js}
📄 CodeRabbit inference engine (.cursorrules)
Organize imports: react → third-party → local components → utils → types
Files:
apps/api/src/services/payment.service.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursorrules)
**/*.{ts,tsx}: Use PascalCase for types and interfaces with descriptive names
Use dynamic imports for code splitting when appropriate
Memoize expensive computations
Files:
apps/api/src/services/payment.service.ts
**/*.{js,jsx,ts,tsx,py,java,go,rb,php}
📄 CodeRabbit inference engine (.cursor/rules/general_rules.mdc)
**/*.{js,jsx,ts,tsx,py,java,go,rb,php}: Always use lowercase when writing comments
Avoid unnecessary comments; code should be self-documenting when possible
Use comments to explain 'why', not 'what'
Files:
apps/api/src/services/payment.service.ts
apps/api/src/**/{services,models,database}/*.{js,ts}
📄 CodeRabbit inference engine (apps/api/.cursor/rules/backend_rules.mdc)
Encrypt sensitive data (passwords, tokens, API keys) before storing in database
Files:
apps/api/src/services/payment.service.ts
apps/api/src/**/*.{js,ts}
📄 CodeRabbit inference engine (apps/api/.cursor/rules/backend_rules.mdc)
apps/api/src/**/*.{js,ts}: Log errors with context (userId, endpoint, timestamp) for debugging
Always await async operations; never forget to handle promise rejections
Never log sensitive information (passwords, tokens, credit card numbers)
Files:
apps/api/src/services/payment.service.ts
apps/api/src/**/{database,services}/*.{js,ts}
📄 CodeRabbit inference engine (apps/api/.cursor/rules/backend_rules.mdc)
Handle database connection failures gracefully with retry logic
Files:
apps/api/src/services/payment.service.ts
apps/api/src/**/{database,models,services}/*.{js,ts}
📄 CodeRabbit inference engine (apps/api/.cursor/rules/backend_rules.mdc)
Always use parameterized queries or ORM methods to prevent SQL injection
Files:
apps/api/src/services/payment.service.ts
apps/api/src/**/{services,database}/*.{js,ts}
📄 CodeRabbit inference engine (apps/api/.cursor/rules/backend_rules.mdc)
Wrap database transactions in try-catch blocks with proper rollback on failure
Files:
apps/api/src/services/payment.service.ts
apps/api/src/**/{services,repositories}/*.{js,ts}
📄 CodeRabbit inference engine (apps/api/.cursor/rules/backend_rules.mdc)
Avoid N+1 queries; use eager loading or batch queries when fetching related data
Files:
apps/api/src/services/payment.service.ts
apps/api/src/**/{services,models}/*.{js,ts}
📄 CodeRabbit inference engine (apps/api/.cursor/rules/backend_rules.mdc)
Validate data against schema before database operations
Files:
apps/api/src/services/payment.service.ts
apps/api/src/**/*.ts
📄 CodeRabbit inference engine (apps/api/.cursor/rules/backend_rules.mdc)
Avoid
anytype; useunknownfor truly dynamic data and narrow with type guards
Files:
apps/api/src/services/payment.service.ts
apps/api/src/**/{database,clients,services}/*.{js,ts}
📄 CodeRabbit inference engine (apps/api/.cursor/rules/backend_rules.mdc)
Use connection pooling for database and external service clients
Files:
apps/api/src/services/payment.service.ts
apps/api/src/**/{services,clients,handlers}/*.{js,ts}
📄 CodeRabbit inference engine (apps/api/.cursor/rules/backend_rules.mdc)
Implement timeouts for external API calls to prevent hanging requests
Files:
apps/api/src/services/payment.service.ts
apps/api/src/**/{middleware,services,routes,controllers}/*.{js,ts}
📄 CodeRabbit inference engine (apps/api/.cursor/rules/backend_rules.mdc)
Log all critical operations (auth attempts, payment processing, data mutations)
Files:
apps/api/src/services/payment.service.ts
🔇 Additional comments (1)
apps/api/src/services/payment.service.ts (1)
18-31: LGTM!The interface export enables external modules to import and use this type, which aligns with the broader API surface expansion in this PR.
|
@huamanraj everything looks fine but i don't know why am not getting to the ref Screen.Recording.2025-12-18.at.11.05.39.PM.mov
|
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
♻️ Duplicate comments (4)
apps/api/src/routers/testimonial.ts (4)
8-8: Avoidanytype; use proper typing for context.This issue was already flagged in a previous review. Per coding guidelines, avoid
anytype. TRPC should infer the context type correctly from the procedure definition.As per coding guidelines and learnings retrieved from long-term memory.
19-19: Avoidanytype; use proper typing for context.This issue was already flagged in a previous review. Per coding guidelines, avoid
anytype. TRPC should infer the context type correctly from the procedure definition.As per coding guidelines and learnings retrieved from long-term memory.
25-30: Misleading error message in read operation.This issue was already flagged in a previous review. The error message says "submit testimonials" but this is the
getMyTestimonialendpoint (read operation). The message should reflect access/viewing restrictions instead.
63-63: Avoidanytype; use proper typing for context.This issue was already flagged in a previous review. Per coding guidelines, avoid
anytype. TRPC should infer the context type correctly from the procedure and input definitions.As per coding guidelines and learnings retrieved from long-term memory.
🧹 Nitpick comments (1)
apps/api/src/routers/testimonial.ts (1)
74-75: Remove unnecessary blank lines.Two consecutive empty lines here serve no purpose and reduce code readability.
🔎 Proposed fix
} - - // Check if testimonial already exists - prevent updates
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
apps/api/src/routers/testimonial.tsapps/api/src/routers/user.tsapps/web/src/app/(main)/testimonials/submit/page.tsxapps/web/src/components/checkout/checkout-confirmation.tsx
✅ Files skipped from review due to trivial changes (2)
- apps/web/src/components/checkout/checkout-confirmation.tsx
- apps/api/src/routers/user.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- apps/web/src/app/(main)/testimonials/submit/page.tsx
🧰 Additional context used
📓 Path-based instructions (7)
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.cursorrules)
**/*.{ts,tsx,js,jsx}: Always use lowercase when writing comments
Avoid unnecessary comments; code should be self-documenting when possible
Use comments to explain 'why', not 'what'
Remove unused imports
Use UPPER_SNAKE_CASE for constants
Use camelCase for functions and variables
Files:
apps/api/src/routers/testimonial.ts
**/*.{tsx,ts}
📄 CodeRabbit inference engine (.cursorrules)
**/*.{tsx,ts}: Prefer functional components with TypeScript and use proper TypeScript types, avoidany
Extract reusable logic into custom hooks
Use descriptive prop names and define prop types using TypeScript interfaces or types
Prefer controlled components over uncontrolled
Use zustand for global state (located insrc/store/)
Use absolute imports from@/prefix when available
Include proper aria labels for accessibility
Ensure keyboard navigation works
Maintain proper heading hierarchy
Provide alt text for images
Avoid unnecessary re-renders
Files:
apps/api/src/routers/testimonial.ts
**/*.{tsx,ts,jsx,js}
📄 CodeRabbit inference engine (.cursorrules)
Organize imports: react → third-party → local components → utils → types
Files:
apps/api/src/routers/testimonial.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursorrules)
**/*.{ts,tsx}: Use PascalCase for types and interfaces with descriptive names
Use dynamic imports for code splitting when appropriate
Memoize expensive computations
Files:
apps/api/src/routers/testimonial.ts
**/*.{js,jsx,ts,tsx,py,java,go,rb,php}
📄 CodeRabbit inference engine (.cursor/rules/general_rules.mdc)
**/*.{js,jsx,ts,tsx,py,java,go,rb,php}: Always use lowercase when writing comments
Avoid unnecessary comments; code should be self-documenting when possible
Use comments to explain 'why', not 'what'
Files:
apps/api/src/routers/testimonial.ts
apps/api/src/**/*.{js,ts}
📄 CodeRabbit inference engine (apps/api/.cursor/rules/backend_rules.mdc)
apps/api/src/**/*.{js,ts}: Log errors with context (userId, endpoint, timestamp) for debugging
Always await async operations; never forget to handle promise rejections
Never log sensitive information (passwords, tokens, credit card numbers)
Files:
apps/api/src/routers/testimonial.ts
apps/api/src/**/*.ts
📄 CodeRabbit inference engine (apps/api/.cursor/rules/backend_rules.mdc)
Avoid
anytype; useunknownfor truly dynamic data and narrow with type guards
Files:
apps/api/src/routers/testimonial.ts
🧠 Learnings (10)
📚 Learning: 2025-11-25T07:35:19.071Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/web/.cursor/rules/frontend_rules.mdc:0-0
Timestamp: 2025-11-25T07:35:19.071Z
Learning: Applies to apps/web/src/components/**/*.{tsx,ts} : Use proper TypeScript types and avoid using `any` type
Applied to files:
apps/api/src/routers/testimonial.ts
📚 Learning: 2025-11-25T07:34:58.984Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/api/.cursor/rules/backend_rules.mdc:0-0
Timestamp: 2025-11-25T07:34:58.984Z
Learning: Applies to apps/api/src/**/*.ts : Avoid `any` type; use `unknown` for truly dynamic data and narrow with type guards
Applied to files:
apps/api/src/routers/testimonial.ts
📚 Learning: 2025-11-25T07:34:30.473Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-25T07:34:30.473Z
Learning: Applies to **/*.{tsx,ts} : Prefer functional components with TypeScript and use proper TypeScript types, avoid `any`
Applied to files:
apps/api/src/routers/testimonial.ts
📚 Learning: 2025-11-25T07:34:58.984Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/api/.cursor/rules/backend_rules.mdc:0-0
Timestamp: 2025-11-25T07:34:58.984Z
Learning: Applies to apps/api/src/**/{routes,controllers,handlers,types}/*.ts : Define strict TypeScript interfaces for request/response bodies
Applied to files:
apps/api/src/routers/testimonial.ts
📚 Learning: 2025-11-25T07:34:58.984Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/api/.cursor/rules/backend_rules.mdc:0-0
Timestamp: 2025-11-25T07:34:58.984Z
Learning: Applies to apps/api/src/**/{routes,controllers,handlers,middleware}/*.{js,ts} : Use zod or similar for runtime validation of incoming data
Applied to files:
apps/api/src/routers/testimonial.ts
📚 Learning: 2025-11-25T07:35:19.071Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/web/.cursor/rules/frontend_rules.mdc:0-0
Timestamp: 2025-11-25T07:35:19.071Z
Learning: Applies to apps/web/src/**/*.{tsx,ts} : Use PascalCase for types and interfaces with descriptive names
Applied to files:
apps/api/src/routers/testimonial.ts
📚 Learning: 2025-11-25T07:34:58.984Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/api/.cursor/rules/backend_rules.mdc:0-0
Timestamp: 2025-11-25T07:34:58.984Z
Learning: Applies to apps/api/src/**/{services,repositories}/*.{js,ts} : Avoid N+1 queries; use eager loading or batch queries when fetching related data
Applied to files:
apps/api/src/routers/testimonial.ts
📚 Learning: 2025-11-25T07:34:58.984Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/api/.cursor/rules/backend_rules.mdc:0-0
Timestamp: 2025-11-25T07:34:58.984Z
Learning: Applies to apps/api/src/**/*.{js,ts} : Always await async operations; never forget to handle promise rejections
Applied to files:
apps/api/src/routers/testimonial.ts
📚 Learning: 2025-11-25T07:34:30.473Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-25T07:34:30.473Z
Learning: Applies to **/*.{tsx,ts} : Use descriptive prop names and define prop types using TypeScript interfaces or types
Applied to files:
apps/api/src/routers/testimonial.ts
📚 Learning: 2025-11-25T07:35:19.071Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/web/.cursor/rules/frontend_rules.mdc:0-0
Timestamp: 2025-11-25T07:35:19.071Z
Learning: Applies to apps/web/src/components/**/*.{tsx,ts} : Use descriptive prop names and define prop types using TypeScript interfaces or types
Applied to files:
apps/api/src/routers/testimonial.ts
🧬 Code graph analysis (1)
apps/api/src/routers/testimonial.ts (3)
apps/api/src/trpc.ts (3)
router(42-42)publicProcedure(43-43)protectedProcedure(44-44)apps/api/src/services/user.service.ts (1)
userService(5-89)apps/api/src/utils/avatar-validator.ts (1)
validateAvatarUrl(46-159)
🔇 Additional comments (1)
apps/api/src/routers/testimonial.ts (1)
88-89: Good implementation of avatar URL validation.The addition of
validateAvatarUrlproperly addresses the server-side security validation concerns raised in previous reviews. The validation function (shown in relevant code snippets) implements HTTPS-only, IP blocking, domain allowlist, HEAD request validation, and content-type/size checks.
| socialLink: z.string().url("Invalid social link URL").refine((url) => { | ||
| const supportedPlatforms = [ | ||
| 'twitter.com', | ||
| 'x.com', | ||
| 'linkedin.com', | ||
| 'instagram.com', | ||
| 'youtube.com', | ||
| 'youtu.be', | ||
| ]; | ||
| try { | ||
| const parsedUrl = new URL(url); | ||
| return supportedPlatforms.some(platform => parsedUrl.hostname.includes(platform)); | ||
| } catch { | ||
| return false; | ||
| } | ||
| }, "Only Twitter/X, LinkedIn, Instagram, and YouTube links are supported").optional().or(z.literal('')), |
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.
Fix insecure hostname validation that allows malicious domains.
The hostname.includes(platform) check is vulnerable to subdomain attacks. For example, malicious-twitter.com or evil-linkedin.com.attacker.net would pass validation because their hostnames contain the platform strings.
🔎 Proposed fix using exact hostname matching
- socialLink: z.string().url("Invalid social link URL").refine((url) => {
- const supportedPlatforms = [
- 'twitter.com',
- 'x.com',
- 'linkedin.com',
- 'instagram.com',
- 'youtube.com',
- 'youtu.be',
- ];
- try {
- const parsedUrl = new URL(url);
- return supportedPlatforms.some(platform => parsedUrl.hostname.includes(platform));
- } catch {
- return false;
- }
- }, "Only Twitter/X, LinkedIn, Instagram, and YouTube links are supported").optional().or(z.literal('')),
+ socialLink: z.string().url("Invalid social link URL").refine((url) => {
+ const supportedPlatforms = [
+ 'twitter.com',
+ 'x.com',
+ 'linkedin.com',
+ 'instagram.com',
+ 'youtube.com',
+ 'youtu.be',
+ ];
+ try {
+ const parsedUrl = new URL(url);
+ const hostname = parsedUrl.hostname;
+ // exact match or subdomain of allowed platform
+ return supportedPlatforms.some(platform =>
+ hostname === platform || hostname.endsWith(`.${platform}`)
+ );
+ } catch {
+ return false;
+ }
+ }, "Only Twitter/X, LinkedIn, Instagram, and YouTube links are supported").optional().or(z.literal('')),🤖 Prompt for AI Agents
In apps/api/src/routers/testimonial.ts around lines 46 to 61, the hostname
validation uses hostname.includes(platform) which allows malicious hosts
containing the platform string; change it to exact-match or proper subdomain
matching: parse the URL and compare parsedUrl.hostname === platform OR
parsedUrl.hostname.endsWith('.' + platform) (this covers valid subdomains like
"www.twitter.com" or "mobile.twitter.com"), apply the same logic for each
supportedPlatforms entry (including handling short hosts like "youtu.be"), keep
the try/catch, and return false on parse errors so only exact platform hostnames
or their subdomains are allowed.



UI examples
Summary by CodeRabbit
New Features
Performance
Security / UX
Chores
✏️ Tip: You can customize this high-level summary in your review settings.