refactor: migrate to Clerk Show component, postgres-js driver, and rename middleware#62
Conversation
…base driver, and update project dependencies
📝 WalkthroughWalkthroughDependencies are updated (Clerk 6.30 → 7.3, React pinned to 19.2.5, Zod upgraded). Auth component usage across three pages switches from Clerk's Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 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. Review rate limit: 0/1 reviews remaining, refill in 60 minutes.Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/app/_components/navbar.tsx`:
- Around line 100-104: The Sign In control uses <SignInButton> with a nested
<Button className='...' asChild> which renders a non-semantic span; remove the
asChild prop from the Button so it renders as a native <button> element to
preserve keyboard and screen-reader accessibility. Update the JSX where
SignInButton and Button are used (the SignInButton fallback block) to drop
asChild and keep the existing className and inner <span> text content so the
element remains visually identical but semantic and accessible.
In `@src/components/shared/chapter-page.tsx`:
- Around line 31-33: The chapter content is being injected via
dangerouslySetInnerHTML (see <Show> rendering chapter.content) without runtime
sanitization while admin flows call markdownToHtml(..., allowDangerousHtml:
true) (see updateChapterContent) which permits stored XSS; fix this by
sanitizing HTML either at render time or before persisting: 1) In components
that use dangerouslySetInnerHTML (chapter-page.tsx rendering chapter.content,
novel page richText, admin form preview components) import a vetted sanitizer
(e.g., DOMPurify or sanitize-html) and run chapter.content (or the preview HTML)
through the sanitizer immediately before passing it to dangerouslySetInnerHTML;
2) In admin mutation/action updateChapterContent (and any markdown conversion
paths) disable allowDangerousHtml or sanitize the output of markdownToHtml
before saving to DB so dangerous tags/attributes are removed server-side; and 3)
apply the same sanitizer to the create/edit preview components
(create-chapter-form.tsx and form.tsx) to ensure both preview and persisted
content are safe.
In `@src/lib/db/index.ts`:
- Line 12: The postgres client is created with postgres(connectionString) which
uses prepared statements by default and breaks with Supabase's PgBouncer in
transaction mode; update the client creation (the const client variable produced
by postgres(...)) to pass the option prepare: false (e.g.,
postgres(connectionString, { prepare: false })) so prepared statements are
disabled and the connection pooler is compatible.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: f10dc940-87da-4f9f-8cc8-33c51017d1e4
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (6)
package.jsonsrc/app/(main)/translate/page.tsxsrc/app/_components/navbar.tsxsrc/components/shared/chapter-page.tsxsrc/lib/db/index.tssrc/proxy.ts
| <Show when="signed-in" fallback={ | ||
| <SignInButton> | ||
| <Button className='hover:cursor-pointer font-semibold' asChild> | ||
| <span>Sign In</span> | ||
| </Button> |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, let's find and examine the navbar.tsx file
fd navbar.tsxRepository: Shashank3736/quaslation
Length of output: 98
🏁 Script executed:
# Check the file size and read the specific lines
wc -l src/app/_components/navbar.tsxRepository: Shashank3736/quaslation
Length of output: 102
🏁 Script executed:
# Read the relevant section around lines 100-104
sed -n '95,110p' src/app/_components/navbar.tsxRepository: Shashank3736/quaslation
Length of output: 570
🏁 Script executed:
# Also find the Button component to understand asChild behavior
fd -type f -name "*.tsx" -o -name "*.ts" | head -20
rg "export.*Button" --type tsx --type ts -A 5 | head -50Repository: Shashank3736/quaslation
Length of output: 266
🏁 Script executed:
# Search for asChild usage patterns in the codebase
rg "asChild" --type tsx --type ts -B 2 -A 2Repository: Shashank3736/quaslation
Length of output: 95
🏁 Script executed:
# Search for Button component import and definition
rg "Button" src/app/_components/navbar.tsx -B 5 -A 2Repository: Shashank3736/quaslation
Length of output: 2067
🏁 Script executed:
# Find Button component definition
fd Button -type f | head -20Repository: Shashank3736/quaslation
Length of output: 238
🏁 Script executed:
# Look for UI component library files
fd -type f -name "*.tsx" -o -name "*.ts" | grep -i button | head -20Repository: Shashank3736/quaslation
Length of output: 238
🏁 Script executed:
# Check imports at top of navbar.tsx
head -30 src/app/_components/navbar.tsxRepository: Shashank3736/quaslation
Length of output: 874
🏁 Script executed:
# Search for asChild prop usage documentation in the codebase
rg "asChild" -B 3 -A 3 | head -40Repository: Shashank3736/quaslation
Length of output: 2432
🏁 Script executed:
# Verify the Button component's default rendering when asChild is not used
cat src/components/ui/button.tsxRepository: Shashank3736/quaslation
Length of output: 3773
🏁 Script executed:
# Check if SignInButton from Clerk provides keyboard handlers
rg "SignInButton" src/app/_components/navbar.tsx -B 2 -A 5Repository: Shashank3736/quaslation
Length of output: 929
Remove asChild to preserve semantic button role.
The asChild prop converts the Button into a <span> element, which lacks native keyboard accessibility for this authentication action. Screen readers won't identify it as a button, and keyboard users can't activate it without additional event handling. Let the Button render as a semantic <button> element instead.
Accessibility-safe fix
<Show when="signed-in" fallback={
<SignInButton>
- <Button className='hover:cursor-pointer font-semibold' asChild>
- <span>Sign In</span>
- </Button>
+ <Button className='hover:cursor-pointer font-semibold'>
+ Sign In
+ </Button>
</SignInButton>
}>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/app/_components/navbar.tsx` around lines 100 - 104, The Sign In control
uses <SignInButton> with a nested <Button className='...' asChild> which renders
a non-semantic span; remove the asChild prop from the Button so it renders as a
native <button> element to preserve keyboard and screen-reader accessibility.
Update the JSX where SignInButton and Button are used (the SignInButton fallback
block) to drop asChild and keep the existing className and inner <span> text
content so the element remains visually identical but semantic and accessible.
| <Show when="signed-in"> | ||
| <article className='space-y-2 prose lg:prose-xl dark:prose-invert max-w-none chapter-content' dangerouslySetInnerHTML={{__html: chapter.content}} /> | ||
| </SignedIn> | ||
| <SignedOut> | ||
| </Show> |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify whether chapter HTML is sanitized before render.
rg -n -C3 --type=ts --type=tsx 'dangerouslySetInnerHTML|chapter\.content|sanitize|sanitize-html|DOMPurify'Repository: Shashank3736/quaslation
Length of output: 95
🏁 Script executed:
#!/bin/bash
# First, let's see what file types ripgrep supports
rg --type-list | grep -i "javascript\|typescript"Repository: Shashank3736/quaslation
Length of output: 49
🏁 Script executed:
#!/bin/bash
# Search without type flags - just look for key patterns in the codebase
rg -n 'dangerouslySetInnerHTML|sanitize|DOMPurify|sanitize-html' --max-count=20Repository: Shashank3736/quaslation
Length of output: 2446
🏁 Script executed:
#!/bin/bash
# Read the specific file mentioned in the review
cat -n src/components/shared/chapter-page.tsxRepository: Shashank3736/quaslation
Length of output: 4350
🏁 Script executed:
#!/bin/bash
# Check if sanitize-html is in dependencies
cat package.json | grep -i "sanitize"Repository: Shashank3736/quaslation
Length of output: 49
🏁 Script executed:
#!/bin/bash
# Search for where chapter object/content is defined or processed
rg -n 'chapter.*content|chapter.*=|chapter\s*:' --max-count=30Repository: Shashank3736/quaslation
Length of output: 20130
🏁 Script executed:
#!/bin/bash
# Find the getChapterBySlug function
rg -n "getChapterBySlug|function.*getChapterBySlug" --max-count=10Repository: Shashank3736/quaslation
Length of output: 1126
🏁 Script executed:
#!/bin/bash
# Read the lib/db/query.ts file to see getChapterBySlug implementation
wc -l src/lib/db/query.tsRepository: Shashank3736/quaslation
Length of output: 91
🏁 Script executed:
#!/bin/bash
# Check package.json for sanitization dependencies
cat package.jsonRepository: Shashank3736/quaslation
Length of output: 3065
🏁 Script executed:
#!/bin/bash
# Search for where chapter content is set or sanitized in database operations
rg -n "content.*=|sanitize" src/lib/db/ --max-count=20Repository: Shashank3736/quaslation
Length of output: 49
🏁 Script executed:
#!/bin/bash
# Read the _getChapterBySlug function implementation
sed -n '174,250p' src/lib/db/query.tsRepository: Shashank3736/quaslation
Length of output: 2375
🏁 Script executed:
#!/bin/bash
# Also check the richText schema to understand content structure
rg -n "richText|content.*field|html" src/lib/db/schema.ts -A 2 -B 2 | head -50Repository: Shashank3736/quaslation
Length of output: 2078
🏁 Script executed:
#!/bin/bash
# Check the kakuyomu-fetch.ts to see if chapter content is sanitized before storage
sed -n '1,130p' scripts/translation/kakuyomu-fetch.ts | head -80Repository: Shashank3736/quaslation
Length of output: 3172
🏁 Script executed:
#!/bin/bash
# Check the sanitizeHtml function implementation
sed -n '37,80p' scripts/translation/utils/convert.tsRepository: Shashank3736/quaslation
Length of output: 1622
🏁 Script executed:
#!/bin/bash
# Search for all places where richText HTML content is created/inserted
rg -n "richText|html.*=|insert.*richText" src/lib/db/ -A 3 | head -40Repository: Shashank3736/quaslation
Length of output: 2436
🏁 Script executed:
#!/bin/bash
# See more of kakuyomu-fetch.ts to find where chapter HTML is processed
sed -n '95,150p' scripts/translation/kakuyomu-fetch.tsRepository: Shashank3736/quaslation
Length of output: 2093
🏁 Script executed:
#!/bin/bash
# Check the upload.ts to see how richText is inserted into database
sed -n '1,200p' scripts/gemini/upload.ts | tail -80Repository: Shashank3736/quaslation
Length of output: 2730
🏁 Script executed:
#!/bin/bash
# Search for where richText HTML is actually inserted/created
rg -n "richText|html:" scripts/ -B 2 -A 2 | grep -A 4 -B 4 "html"Repository: Shashank3736/quaslation
Length of output: 8672
🏁 Script executed:
#!/bin/bash
# Check the admin chapter edit form to see if it sanitizes before update
sed -n '1,160p' src/app/admin/chapters/edit/\[id\]/form.tsx | tail -50Repository: Shashank3736/quaslation
Length of output: 1789
🏁 Script executed:
#!/bin/bash
# Check the form actions to see if updateChapterContent sanitizes
sed -n '1,80p' src/app/admin/chapters/edit/\[id\]/actions.tsRepository: Shashank3736/quaslation
Length of output: 2298
🏁 Script executed:
#!/bin/bash
# Find and read the markdownToHtml utility function
rg -n "markdownToHtml|function.*markdown" src/lib/utils.ts -A 10 | head -40Repository: Shashank3736/quaslation
Length of output: 469
🏁 Script executed:
#!/bin/bash
# Check if there's sanitization in the utils
rg -n "sanitize|DOMPurify|escapeHtml" src/lib/utils.tsRepository: Shashank3736/quaslation
Length of output: 49
Sanitize chapter.content before HTML injection.
Line 32 injects raw HTML via dangerouslySetInnerHTML without runtime sanitization. While the import pipeline sanitizes content during fetch, an admin editing a chapter through the form bypasses this protection: the updateChapterContent action (line 34 in src/app/admin/chapters/edit/[id]/actions.ts) calls markdownToHtml() with allowDangerousHtml: true, allowing dangerous HTML to be stored directly in the database. This creates an XSS path for authenticated users with editing privileges.
Additionally, multiple other components use dangerouslySetInnerHTML without sanitization:
src/app/(main)/novels/[slug]/page.tsx:187(novel richText)src/app/admin/chapters/edit/[id]/form.tsx:125(form preview)src/app/admin/chapters/[novelId]/create/create-chapter-form.tsx:158(create form preview)
Apply sanitization either at render time using a library like sanitize-html or DOMPurify, or prevent dangerous HTML from being stored by disabling allowDangerousHtml in the markdown-to-HTML conversion pipeline.
🧰 Tools
🪛 ast-grep (0.42.1)
[warning] 31-31: Usage of dangerouslySetInnerHTML detected. This bypasses React's built-in XSS protection. Always sanitize HTML content using libraries like DOMPurify before injecting it into the DOM to prevent XSS attacks.
Context: dangerouslySetInnerHTML
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml
- https://cwe.mitre.org/data/definitions/79.html
(react-unsafe-html-injection)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/components/shared/chapter-page.tsx` around lines 31 - 33, The chapter
content is being injected via dangerouslySetInnerHTML (see <Show> rendering
chapter.content) without runtime sanitization while admin flows call
markdownToHtml(..., allowDangerousHtml: true) (see updateChapterContent) which
permits stored XSS; fix this by sanitizing HTML either at render time or before
persisting: 1) In components that use dangerouslySetInnerHTML (chapter-page.tsx
rendering chapter.content, novel page richText, admin form preview components)
import a vetted sanitizer (e.g., DOMPurify or sanitize-html) and run
chapter.content (or the preview HTML) through the sanitizer immediately before
passing it to dangerouslySetInnerHTML; 2) In admin mutation/action
updateChapterContent (and any markdown conversion paths) disable
allowDangerousHtml or sanitize the output of markdownToHtml before saving to DB
so dangerous tags/attributes are removed server-side; and 3) apply the same
sanitizer to the create/edit preview components (create-chapter-form.tsx and
form.tsx) to ensure both preview and persisted content are safe.
| throw new Error('Missing database connection string. Set POSTGRES_URL or DIRECT_URL in your environment.'); | ||
| } | ||
|
|
||
| const client = postgres(connectionString); |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
For postgres-js used with Supabase transaction pooler/PgBouncer, do official docs recommend postgres(connectionString, { prepare: false })?
💡 Result:
Yes, official Supabase documentation recommends using postgres(connectionString, { prepare: false }) when connecting postgres.js to the transaction pooler (Supavisor/PgBouncer in transaction mode, port 6543). This is because transaction mode does not support prepared statements, which postgres.js enables by default. The postgres.js quickstart docs show the basic connection without the option, but the troubleshooting guide on disabling prepared statements explicitly lists postgres(connectionString, { prepare: false }) for Drizzle (which uses postgres.js underneath). The connecting guide states "Transaction mode does not support prepared statements. To avoid errors, turn off prepared statements for your connection library." Multiple other official pages (Drizzle guide, Prisma troubleshooting) confirm this requirement for transaction pooling.
Citations:
- 1: https://supabase.com/docs/guides/troubleshooting/disabling-prepared-statements-qL8lEL
- 2: https://supabase.com/docs/guides/database/connecting-to-postgres/serverless-drivers
- 3: https://supabase.com/docs/guides/database/drizzle
- 4: https://supabase.com/docs/guides/database/postgres-js
🏁 Script executed:
cat -n src/lib/db/index.tsRepository: Shashank3736/quaslation
Length of output: 661
Add prepare: false for Supabase transaction pooler compatibility.
Official Supabase documentation requires disabling prepared statements when using the transaction pooler (PgBouncer in transaction mode). Transaction mode does not support prepared statements, and postgres-js enables them by default, causing intermittent query errors. Line 12 needs this option to prevent pooling issues.
Proposed fix
-const client = postgres(connectionString);
+const client = postgres(connectionString, { prepare: false });📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const client = postgres(connectionString); | |
| const client = postgres(connectionString, { prepare: false }); |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/lib/db/index.ts` at line 12, The postgres client is created with
postgres(connectionString) which uses prepared statements by default and breaks
with Supabase's PgBouncer in transaction mode; update the client creation (the
const client variable produced by postgres(...)) to pass the option prepare:
false (e.g., postgres(connectionString, { prepare: false })) so prepared
statements are disabled and the connection pooler is compatible.
Changes
Summary by CodeRabbit
Release Notes
Dependencies
Improvements