add Freshdesk support widget integration - #709
Conversation
Embed the official Freshdesk webchat loader as a nonce-carrying inline script (bodyClose) in nuxt.config.ts. Allow euler.freshdesk.com in the CSP (connect/style/font/frame/media-src) — the parent page loads widget.js and widget.css directly; the chat iframe is cross-origin so its internals run under Freshdesk's own CSP. object-src stays 'none'. Hide the widget (#fd_frame) on the onboarding page via a root class toggled in app.vue. Add a regression test for the connect-src entry. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Warning Review limit reached
Next review available in: 13 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Repository: euler-xyz/coderabbit/.coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughThis PR integrates a Freshdesk support widget: injects a widget loader script in Nuxt config, adds a route-based watcher in app.vue to hide the widget on the onboarding screen via a CSS class, adds corresponding CSS, and expands CSP directives to allow Freshdesk resources, with a test. ChangesFreshdesk Widget
Estimated code review effort: 2 (Simple) | ~15 minutes Sequence Diagram(s)sequenceDiagram
participant Browser
participant AppVue as app.vue
participant DOM as document.documentElement
participant Freshdesk as Freshdesk widget.js
Browser->>AppVue: route.name change
AppVue->>DOM: toggle support-widget-hidden class
Browser->>Browser: window load event
Browser->>Freshdesk: insert widget.js script tag
Freshdesk->>Freshdesk: fdWidget.init(token, host, widgetId)
Freshdesk-->>DOM: render `#fd_frame`
DOM-->>Browser: CSS hides `#fd_frame` if class present
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 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.
🧹 Nitpick comments (1)
server/plugins/csp.ts (1)
163-167: 🔒 Security & Privacy | 🔵 Trivial | 💤 Low valueRedundant entry in
connect-src.
https://euler.freshdesk.comis already covered by thehttps://*.freshdesk.comwildcard on the next line. The explicit entry can be removed for clarity, though it's harmless.♻️ Optional cleanup
// Freshdesk support widget (loaded from euler.freshdesk.com, see // freshdesk-widget script in nuxt.config.ts) - 'https://euler.freshdesk.com', 'https://*.freshdesk.com', 'wss://*.freshdesk.com',🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@server/plugins/csp.ts` around lines 163 - 167, Remove the redundant `https://euler.freshdesk.com` entry from the `connect-src` list in `server/plugins/csp.ts`, since the `freshdesk` wildcard already covers it. Keep the remaining Freshdesk sources (`https://*.freshdesk.com` and `wss://*.freshdesk.com`) unchanged, and update the surrounding comment if needed so the `connect-src` block in the CSP config remains clear and minimal.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@server/plugins/csp.ts`:
- Around line 163-167: Remove the redundant `https://euler.freshdesk.com` entry
from the `connect-src` list in `server/plugins/csp.ts`, since the `freshdesk`
wildcard already covers it. Keep the remaining Freshdesk sources
(`https://*.freshdesk.com` and `wss://*.freshdesk.com`) unchanged, and update
the surrounding comment if needed so the `connect-src` block in the CSP config
remains clear and minimal.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository: euler-xyz/coderabbit/.coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 041a2451-5aaf-4ed8-8e09-3a753143be09
📒 Files selected for processing (5)
app.vueassets/styles/main.scssnuxt.config.tsserver/plugins/csp.tstests/server/security.test.ts
LeonardEulerXYZ
left a comment
There was a problem hiding this comment.
Leonard review — PR #709
Verdict: changes requested — one privacy/UX blocker around the onboarding page.
What changed: the PR adds a global Freshdesk loader in nuxt.config.ts, widens CSP for Freshdesk widget traffic, hides #fd_frame on onboarding via a root class, and adds a CSP regression test.
Validation performed on head de1d382632942314b506a6a5f8b33b14c096ece1:
npm run test:run -- tests/server/security.test.ts— passed, 21 testsnpm run typecheck— passednpm run build— passed, with existing chunk/Rollup warnings only- Direct fetch of
https://euler.freshdesk.com/webchat/js/widget.js— HTTP 200,text/javascript - Headed/Xvfb Playwright smoke on the PR preview:
/onboarding?network=1desktop: onboarding rendered,support-widget-hiddenwas true,#fd_framedisplay wasnone/lend?network=1desktop and mobile: Lend page rendered and support widget was visible- wallet/signing smoke: not run; PR does not touch transaction construction
Screenshot evidence:
Scalability / maintainability hygiene pass: there is only one Freshdesk integration surface and the route visibility logic is centralized in app.vue plus one global stylesheet rule, so I did not find sibling route/component duplication. The reusable gap is test coverage breadth: the added test locks only connect-src, while this feature also depends on frame-src, style-src, font-src, media-src, nonce injection, and onboarding gating. I am not making that a separate blocker because the runtime smoke covered the current path, but those are the seams most likely to regress.
Bot feedback: CodeRabbit’s redundancy nit about https://euler.freshdesk.com being covered by https://*.freshdesk.com is directionally correct but low impact; it is not the material issue here.
See inline comment for the blocker.
| tagPriority: 'critical', | ||
| }, | ||
| { | ||
| id: 'freshdesk-widget', |
There was a problem hiding this comment.
This hides the Freshdesk frame on onboarding only after the global loader has already run. In the PR preview, /onboarding?network=1 had support-widget-hidden === true and #fd_frame { display: none }, but the page still loaded widget.js, created the Freshdesk iframe, and called Freshdesk visitor/ticket/token/event endpoints before the user left the connect-wallet onboarding screen.
That makes the onboarding hide visual rather than operational: a hidden third-party support widget still sends the page/referrer and creates a Freshdesk visitor session. For this surface I think we should gate initialization itself — e.g. defer injecting/loading Freshdesk until onboarding is complete or the current route is not onboarding — rather than loading globally and hiding with CSS. If the intended policy is explicitly “load Freshdesk on onboarding but hide the button,” that privacy/UX decision should be documented, but the safer invariant is no third-party widget traffic while the widget is hidden on onboarding.
|
🚅 Deployed to the euler-lite-pr-709 environment in euler-lite(dev,PR previews)
|
Browsers paint an opaque white canvas behind a cross-origin iframe when the embedding page's color-scheme (dark) mismatches the iframe document's (undeclared). Declare the widget iframe light to restore its transparency. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
LeonardEulerXYZ
left a comment
There was a problem hiding this comment.
Leonard review — PR #709
Verdict: changes requested — the Freshdesk widget is still initialized on onboarding while it is visually hidden.
Reviewed head: 2774f6b2ecaca410eb01e8d4fa362a49dccac3b9
What changed:
- Adds a global Freshdesk loader script in
nuxt.config.ts. - Widens CSP for Freshdesk script/widget traffic in
server/plugins/csp.ts. - Adds a route-driven
support-widget-hiddenroot class inapp.vueand CSS inassets/styles/main.scssto hide#fd_frameon onboarding and fix the dark-theme iframe canvas. - Adds a focused CSP regression test for Freshdesk connect-src.
Blocking finding:
- The onboarding suppression is visual only. On the PR preview,
/onboarding?network=1setssupport-widget-hidden === trueand#fd_frame { display: none }, but the global loader has already fetched Freshdesk, mounted the iframe, created a visitor session, and called ticket/token/event endpoints. That means the connect-wallet onboarding page still sends Freshdesk traffic even though the widget is hidden. The safer invariant is: if the widget is hidden on onboarding, initialization should also be deferred/gated until onboarding is complete or the current route is not onboarding. If the intended product/privacy policy is explicitly “load Freshdesk on onboarding but hide the launcher,” that should be stated deliberately; I would not let the CSS hide imply operational suppression.
Evidence:
- Desktop onboarding screenshot: https://gist.githubusercontent.com/LeonardEulerXYZ/b956cb4a4b99a86fef0ac6c82e69bf09/raw/21d818ce809e7a403155559a219a29a5b061b01c/euler-xyz-euler-lite-pr709-pr709-desktop-onboarding-hidden.png
- Mobile onboarding screenshot: https://gist.githubusercontent.com/LeonardEulerXYZ/b956cb4a4b99a86fef0ac6c82e69bf09/raw/0ef7457238b45ab09de6011a9ad23bb06cfc8d46/euler-xyz-euler-lite-pr709-pr709-mobile-onboarding-hidden.png
- Borrow route screenshot with the widget visible as expected: https://gist.githubusercontent.com/LeonardEulerXYZ/b956cb4a4b99a86fef0ac6c82e69bf09/raw/65a2999dbcc30e13b261ce1d65f7ca7acae30846/euler-xyz-euler-lite-pr709-pr709-desktop-borrow-widget-visible.png
Validation performed:
git diff --check origin/development...HEADnpm run typechecknpm run test:run -- tests/server/security.test.ts— 21 tests passednpm run build— passed; only existing bundle/chunk-size style warnings- Headed Chromium/Xvfb browser smoke against the Railway PR preview:
- desktop
/onboarding?network=1: onboarding visible, Freshdesk button hidden, but#fd_frameexists and Freshdesk network requests occur - mobile
/onboarding?network=1: same result - desktop
/borrow?network=1: app route visible and Freshdesk launcher visible
- desktop
Scalability / maintainability hygiene:
- The route-level class is centralized in
app.vue, so there is not a sibling route with a separate copy of the same hide logic. - The missing reusable boundary is the loader policy itself: the global inline script in
nuxt.config.tsowns initialization whileapp.vueowns visibility. That split is what allowed hidden UI and third-party traffic to diverge. I would prefer a small shared client-side widget controller/composable, or equivalent route-aware loader gate, with a focused regression that asserts onboarding does not create#fd_frameor call Freshdesk. - The current unit test only locks
connect-src; it does not cover the onboarding privacy/UX invariant.
Bot/reviewer feedback checked:
- CodeRabbit’s Freshdesk
connect-srcredundancy note is low-value/non-blocking. The explicithttps://euler.freshdesk.comentry is harmless beside the wildcard and is not the material risk here.
Smoke coverage: browser visual smoke + mobile smoke. Wallet/signing smoke not run; this PR changes a support widget/CSP path, not transaction construction.
Embed the official Freshdesk webchat loader as a nonce-carrying inline script (bodyClose) in nuxt.config.ts. Allow euler.freshdesk.com in the CSP (connect/style/font/frame/media-src) — the parent page loads widget.js and widget.css directly; the chat iframe is cross-origin so its internals run under Freshdesk's own CSP. object-src stays 'none'. Hide the widget (#fd_frame) on the onboarding page via a root class toggled in app.vue. Add a regression test for the connect-src entry.
Summary by CodeRabbit
New Features
Bug Fixes