Conversation
…mping the cursor to the next input field
✅ Deploy Preview for fluxdocs ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
✅ Deploy Preview for fluxsocial-dev ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
WalkthroughTwo profile view components were updated: WebLinkAdd.vue now normalizes and validates URLs, debounces and gates metadata fetches, and sends normalized URLs; WebLinkCard.vue now normalizes URLs for display, updates hostname derivation, and adds rel attributes for the anchor. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor U as User
participant WA as WebLinkAdd.vue
participant N as normalizeUrl()
participant H as looksLikeHost()
participant D as metaDebounce(500ms)
participant J as jsonlink.io
U->>WA: Type in link field
WA->>N: Normalize input
N-->>WA: normalizedUrl
WA->>H: Check host-likeness
alt Host-like and valid URL
WA->>D: Schedule getMeta(normalizedUrl)
D-->>WA: Debounced trigger
WA->>J: Fetch metadata (encodeURIComponent(url))
J-->>WA: Metadata JSON
WA->>WA: Update title/description/image
else Not host-like or invalid
WA->>WA: Skip metadata fetch
end
U->>WA: Submit create link
WA->>N: Normalize final value
WA-->>WA: Send normalized url in payload
sequenceDiagram
autonumber
participant WC as WebLinkCard.vue
participant N as normalizeUrl()
note over WC: On render/prop update
WC->>N: Normalize props.url
N-->>WC: safeUrl
alt safeUrl valid
WC-->>WC: Derive hostname from safeUrl
WC-->>User: Anchor href=safeUrl rel="noopener noreferrer"
else invalid/empty
WC-->>WC: Empty href/hostname
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
Tip 🔌 Remote MCP (Model Context Protocol) integration is now available!Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats. ✨ Finishing Touches🧪 Generate unit tests
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
app/src/views/main/profile/WebLinkCard.vue (1)
9-15: Guard empty href and harden relAvoid empty-string hrefs and add nofollow/ugc for user-supplied links.
- <a :href="safeUrl" target="_blank" rel="noopener noreferrer" class="link-card__info"> + <a :href="safeUrl || undefined" target="_blank" rel="noopener noreferrer nofollow ugc" class="link-card__info">
🧹 Nitpick comments (3)
app/src/views/main/profile/WebLinkCard.vue (2)
49-49: Deduplicate URL normalization across componentsnormalizeUrl is duplicated here and in WebLinkAdd.vue. Extract to a shared util to prevent drift and enable testing.
Example new file:
// app/src/utils/url.ts export function normalizeWebUrl(input: string): string { const value = (input || "").trim(); if (!value) return ""; const hasScheme = /^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(value); const withScheme = hasScheme ? value : `https://${value}`; try { const u = new URL(withScheme); return u.protocol === "http:" || u.protocol === "https:" ? withScheme : ""; } catch { return ""; } }Then import and use in both components.
52-55: Nit: consider including port in displayhostname omits port. If you want “example.com:8080” when present, use URL.host instead of hostname. If not desired, ignore.
app/src/views/main/profile/WebLinkAdd.vue (1)
85-94: Broaden looksLikeHost to cover IPv4/IPv6Current check rejects valid IPv6 hosts. Include IPs while keeping the early-gate intent.
function looksLikeHost(urlStr: string): boolean { try { const u = new URL(urlStr); const host = u.hostname; - // Require a dot or be localhost to reduce early validations like "https://exa" - return host === "localhost" || host.includes("."); + // Require a dotted name, localhost, or an IP (v4/v6) + const isIPv4 = /^\d{1,3}(?:\.\d{1,3}){3}$/.test(host); + const isIPv6 = /^[a-fA-F0-9:]+$/.test(host) && host.includes(":"); + return host === "localhost" || host.includes(".") || isIPv4 || isIPv6; } catch { return false; } }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (2)
app/src/views/main/profile/WebLinkAdd.vue(3 hunks)app/src/views/main/profile/WebLinkCard.vue(2 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
- GitHub Check: Redirect rules - fluxsocial-dev
- GitHub Check: Redirect rules - fluxdocs
- GitHub Check: Header rules - fluxsocial-dev
- GitHub Check: Header rules - fluxdocs
- GitHub Check: Pages changed - fluxdocs
- GitHub Check: Pages changed - fluxsocial-dev
🔇 Additional comments (2)
app/src/views/main/profile/WebLinkAdd.vue (2)
12-14: Switch to text input—LGTMPrevents native URL validation/focus jumps that were interrupting entry.
148-149: Send normalized URL on create—LGTMThis aligns creation with the new normalization flow.
| let metaDebounce: ReturnType<typeof setTimeout> | null = null; | ||
|
|
There was a problem hiding this comment.
🛠️ Refactor suggestion
Clear debounce on unmount and track requests to avoid leaks/races
Ensure pending timers don’t fire after unmount and prep a request id for stale-response protection.
-let metaDebounce: ReturnType<typeof setTimeout> | null = null;
+let metaDebounce: ReturnType<typeof setTimeout> | null = null;
+let metaReqId = 0;Add (outside this block) to imports and lifecycle:
// add to imports
import { ref, onBeforeUnmount } from "vue";
// add after declarations
onBeforeUnmount(() => {
if (metaDebounce) clearTimeout(metaDebounce);
});🤖 Prompt for AI Agents
In app/src/views/main/profile/WebLinkAdd.vue around lines 75 to 76, the debounce
timer assigned to metaDebounce can fire after the component unmounts and there’s
no request-id tracking to ignore stale async responses; import ref and
onBeforeUnmount from "vue", add an onBeforeUnmount handler that clears
metaDebounce if set, and add a simple incrementing request id (e.g., a numeric
ref or local variable updated when issuing the request) that you pass into the
async metadata fetch and check on response to ignore stale results so timers and
responses cannot cause leaks or racey state updates after unmount.
| function normalizeUrl(input: string): string { | ||
| const value = (input || "").trim(); | ||
| if (!value) return value; | ||
| // If already has a scheme, keep as is | ||
| const hasScheme = /^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(value); | ||
| return hasScheme ? value : `https://${value}`; | ||
| } | ||
|
|
There was a problem hiding this comment.
Allow-list http/https in normalizeUrl
Match WebLinkCard hardening; block non-web schemes early.
-function normalizeUrl(input: string): string {
- const value = (input || "").trim();
- if (!value) return value;
- // If already has a scheme, keep as is
- const hasScheme = /^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(value);
- return hasScheme ? value : `https://${value}`;
-}
+function normalizeUrl(input: string): string {
+ const value = (input || "").trim();
+ if (!value) return "";
+ const hasScheme = /^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(value);
+ const withScheme = hasScheme ? value : `https://${value}`;
+ try {
+ const u = new URL(withScheme);
+ return u.protocol === "http:" || u.protocol === "https:" ? withScheme : "";
+ } catch {
+ return "";
+ }
+}📝 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.
| function normalizeUrl(input: string): string { | |
| const value = (input || "").trim(); | |
| if (!value) return value; | |
| // If already has a scheme, keep as is | |
| const hasScheme = /^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(value); | |
| return hasScheme ? value : `https://${value}`; | |
| } | |
| function normalizeUrl(input: string): string { | |
| const value = (input || "").trim(); | |
| if (!value) return ""; | |
| const hasScheme = /^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(value); | |
| const withScheme = hasScheme ? value : `https://${value}`; | |
| try { | |
| const u = new URL(withScheme); | |
| return u.protocol === "http:" || u.protocol === "https:" ? withScheme : ""; | |
| } catch { | |
| return ""; | |
| } | |
| } |
🤖 Prompt for AI Agents
In app/src/views/main/profile/WebLinkAdd.vue around lines 77 to 84, normalizeUrl
currently accepts any URI scheme; change it to allow-list only http and https:
detect a leading scheme, and if one exists but is not http or https return an
empty string (block non-web schemes early), otherwise if no scheme prepend
"https://" as before; ensure case-insensitive matching and keep the function
signature returning a string.
| async function getMeta(urlForMeta: string) { | ||
| try { | ||
| loadingMeta.value = true; | ||
| const data = await fetch("https://jsonlink.io/api/extract?url=" + link.value).then((res) => res.json()); | ||
| const data = await fetch("https://jsonlink.io/api/extract?url=" + encodeURIComponent(urlForMeta)).then((res) => | ||
| res.json() | ||
| ); | ||
|
|
||
| title.value = data.title || ""; | ||
| description.value = data.description || ""; | ||
| imageUrl.value = data.images[0] || ""; |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Prevent stale meta overwrites; make images access safe; only fetch on http(s)
Concurrent fetches can race and older responses can clobber newer input; also data.images may be undefined; and we should skip meta for non-http(s).
-async function getMeta(urlForMeta: string) {
+async function getMeta(urlForMeta: string, reqId: number) {
try {
loadingMeta.value = true;
- const data = await fetch("https://jsonlink.io/api/extract?url=" + encodeURIComponent(urlForMeta)).then((res) =>
- res.json()
- );
+ const data = await fetch(
+ "https://jsonlink.io/api/extract?url=" + encodeURIComponent(urlForMeta)
+ ).then((res) => res.json());
+
+ // Drop if a newer request started after this one
+ if (reqId !== metaReqId) return;
title.value = data.title || "";
description.value = data.description || "";
- imageUrl.value = data.images[0] || "";
+ imageUrl.value = (data.images?.[0] as string | undefined) || "";
} finally {
loadingMeta.value = false;
}
}
@@
- // Validate
- // new URL will throw if not valid
+ // Validate (throws if invalid)
// We intentionally do not mutate the visible input to avoid caret jumps
// Use a debounced meta fetch to avoid interrupting typing
- new URL(normalized);
- isValidLink.value = true;
+ const u = new URL(normalized);
+ // Only accept http(s)
+ if (u.protocol !== "http:" && u.protocol !== "https:") {
+ isValidLink.value = false;
+ if (metaDebounce) clearTimeout(metaDebounce);
+ return;
+ }
+ isValidLink.value = true;
if (metaDebounce) clearTimeout(metaDebounce);
- metaDebounce = setTimeout(() => {
- getMeta(normalized);
+ metaReqId += 1;
+ const reqId = metaReqId;
+ metaDebounce = setTimeout(() => {
+ getMeta(normalized, reqId);
}, 500);Also applies to: 123-133
🤖 Prompt for AI Agents
In app/src/views/main/profile/WebLinkAdd.vue around lines 96-105 (and similarly
at 123-133), the meta fetch needs three fixes: first, only run the fetch when
the URL uses http or https (skip otherwise); second, prevent stale response
overwrites by capturing a local token (requestId or the current url value)
before awaiting and only apply results if the token still matches the latest
input; third, guard access to images (check data.images && data.images.length >
0) before reading images[0] and fall back to an empty string. Also ensure
loadingMeta is properly cleared (in a finally block) so UI state is consistent.
| function normalizeUrl(input: string): string { | ||
| const value = (input || "").trim(); | ||
| if (!value) return ""; | ||
| const hasScheme = /^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(value); | ||
| return hasScheme ? value : `https://${value}`; | ||
| } |
There was a problem hiding this comment.
Allow-list http/https to block javascript: and other unsafe schemes
Current normalizeUrl passes through any scheme, enabling clickable javascript: URLs. Restrict to http(s) and return empty for others.
-function normalizeUrl(input: string): string {
- const value = (input || "").trim();
- if (!value) return "";
- const hasScheme = /^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(value);
- return hasScheme ? value : `https://${value}`;
-}
+function normalizeUrl(input: string): string {
+ const value = (input || "").trim();
+ if (!value) return "";
+ const hasScheme = /^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(value);
+ const withScheme = hasScheme ? value : `https://${value}`;
+ try {
+ const u = new URL(withScheme);
+ return u.protocol === "http:" || u.protocol === "https:" ? withScheme : "";
+ } catch {
+ return "";
+ }
+}📝 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.
| function normalizeUrl(input: string): string { | |
| const value = (input || "").trim(); | |
| if (!value) return ""; | |
| const hasScheme = /^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(value); | |
| return hasScheme ? value : `https://${value}`; | |
| } | |
| function normalizeUrl(input: string): string { | |
| const value = (input || "").trim(); | |
| if (!value) return ""; | |
| const hasScheme = /^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(value); | |
| const withScheme = hasScheme ? value : `https://${value}`; | |
| try { | |
| const u = new URL(withScheme); | |
| return u.protocol === "http:" || u.protocol === "https:" ? withScheme : ""; | |
| } catch { | |
| return ""; | |
| } | |
| } |
🤖 Prompt for AI Agents
In app/src/views/main/profile/WebLinkCard.vue around lines 42 to 47,
normalizeUrl currently accepts any URL scheme (allowing unsafe schemes like
javascript:). Change it so that after trimming and early-return for empty, it
detects a scheme case-insensitively; if no scheme, prefix with "https://"; if a
scheme is present, only allow "http" or "https" (case-insensitive) and otherwise
return an empty string to block unsafe schemes. Ensure regex/check is updated
accordingly and tests/uses of normalizeUrl handle the empty-string case.
Summary by CodeRabbit
New Features
Bug Fixes