Skip to content

refactor: migrate to Clerk Show component, postgres-js driver, and rename middleware#62

Merged
Shashank3736 merged 2 commits into
masterfrom
v3.2/dependency-update
May 1, 2026
Merged

refactor: migrate to Clerk Show component, postgres-js driver, and rename middleware#62
Shashank3736 merged 2 commits into
masterfrom
v3.2/dependency-update

Conversation

@Shashank3736

@Shashank3736 Shashank3736 commented May 1, 2026

Copy link
Copy Markdown
Owner

Changes

  • Migrated from Clerk SignedIn/SignedOut to the new Show API (Clerk v7)
  • Switched database driver from @vercel/postgres to postgres-js for Supabase compatibility
  • Updated project dependencies for Next.js 16 compatibility
  • Renamed middleware.ts to proxy.ts

Summary by CodeRabbit

Release Notes

  • Dependencies

    • Updated core dependencies to latest stable versions, including framework libraries, authentication services, and database connectors for improved security and performance.
  • Improvements

    • Refactored internal authentication and database connectivity components to enhance system reliability and responsiveness.

@coderabbitai

coderabbitai Bot commented May 1, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

Dependencies 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 SignedIn/SignedOut to a Show component. Database client migrates from Vercel's postgres integration to the postgres package with drizzle-orm.

Changes

Cohort / File(s) Summary
Dependency Updates
package.json
Clerk bumped from ^6.30.0 to ^7.3.0; React and react-dom pinned to 19.2.5; Zod upgraded to ^3.25.76; @types/react and @types/react-dom patched to 19.2.3; overrides section aligned.
Auth Component Refactoring
src/app/(main)/translate/page.tsx, src/app/_components/navbar.tsx, src/components/shared/chapter-page.tsx
Replaces Clerk's <SignedIn>/<SignedOut> components with <Show> conditional rendering driven by when="signed-in" and when="signed-out" props; corresponding imports updated across all three files.
Database Client Migration
src/lib/db/index.ts
Switches from Vercel's @vercel/postgres sql integration to postgres package client with drizzle-orm/postgres-js; db initialization now derives connection string from POSTGRES_URL or DIRECT_URL environment variables with runtime validation.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 Hop, hop! Dependencies dance and climb,
Clerk and React sync in perfect time,
Show takes the stage where SignedIn once stood,
Postgres migration—all well and good!
Refactored and ready, this code's fresh and bright!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately captures the main refactoring changes: migrating from Clerk SignedIn/SignedOut to Show component, switching database drivers, and renaming middleware—all primary objectives of this PR.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch v3.2/dependency-update

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.

❤️ Share
Review rate limit: 0/1 reviews remaining, refill in 60 minutes.

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between b83dc1f and fbb4c92.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (6)
  • package.json
  • src/app/(main)/translate/page.tsx
  • src/app/_components/navbar.tsx
  • src/components/shared/chapter-page.tsx
  • src/lib/db/index.ts
  • src/proxy.ts

Comment on lines +100 to 104
<Show when="signed-in" fallback={
<SignInButton>
<Button className='hover:cursor-pointer font-semibold' asChild>
<span>Sign In</span>
</Button>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, let's find and examine the navbar.tsx file
fd navbar.tsx

Repository: Shashank3736/quaslation

Length of output: 98


🏁 Script executed:

# Check the file size and read the specific lines
wc -l src/app/_components/navbar.tsx

Repository: Shashank3736/quaslation

Length of output: 102


🏁 Script executed:

# Read the relevant section around lines 100-104
sed -n '95,110p' src/app/_components/navbar.tsx

Repository: 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 -50

Repository: 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 2

Repository: 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 2

Repository: Shashank3736/quaslation

Length of output: 2067


🏁 Script executed:

# Find Button component definition
fd Button -type f | head -20

Repository: 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 -20

Repository: Shashank3736/quaslation

Length of output: 238


🏁 Script executed:

# Check imports at top of navbar.tsx
head -30 src/app/_components/navbar.tsx

Repository: Shashank3736/quaslation

Length of output: 874


🏁 Script executed:

# Search for asChild prop usage documentation in the codebase
rg "asChild" -B 3 -A 3 | head -40

Repository: 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.tsx

Repository: 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 5

Repository: 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.

Comment on lines +31 to +33
<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>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 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=20

Repository: 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.tsx

Repository: 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=30

Repository: Shashank3736/quaslation

Length of output: 20130


🏁 Script executed:

#!/bin/bash
# Find the getChapterBySlug function
rg -n "getChapterBySlug|function.*getChapterBySlug" --max-count=10

Repository: 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.ts

Repository: Shashank3736/quaslation

Length of output: 91


🏁 Script executed:

#!/bin/bash
# Check package.json for sanitization dependencies
cat package.json

Repository: 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=20

Repository: Shashank3736/quaslation

Length of output: 49


🏁 Script executed:

#!/bin/bash
# Read the _getChapterBySlug function implementation
sed -n '174,250p' src/lib/db/query.ts

Repository: 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 -50

Repository: 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 -80

Repository: Shashank3736/quaslation

Length of output: 3172


🏁 Script executed:

#!/bin/bash
# Check the sanitizeHtml function implementation
sed -n '37,80p' scripts/translation/utils/convert.ts

Repository: 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 -40

Repository: 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.ts

Repository: 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 -80

Repository: 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 -50

Repository: 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.ts

Repository: 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 -40

Repository: 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.ts

Repository: 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.

Comment thread src/lib/db/index.ts
throw new Error('Missing database connection string. Set POSTGRES_URL or DIRECT_URL in your environment.');
}

const client = postgres(connectionString);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 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:


🏁 Script executed:

cat -n src/lib/db/index.ts

Repository: 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.

Suggested change
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.

@Shashank3736 Shashank3736 merged commit c396041 into master May 1, 2026
1 of 2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant