fix(web): use backend URL fallback for direct fetches (#1212)

Co-authored-by: Dhravya Shah <dhravyashah@gmail.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Gautam Sharma 2026-07-11 08:32:24 +05:30 committed by GitHub
parent e5e4e498f2
commit 19d7f122b2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 43 additions and 40 deletions

View file

@ -18,7 +18,7 @@ import { motion } from "motion/react"
import { dmSansClassName } from "@/lib/fonts"
import { cn } from "@lib/utils"
import { Logo } from "@ui/assets/Logo"
import { resolveAuthRedirectUrl } from "@/lib/url-helpers"
import { getBackendUrl, resolveAuthRedirectUrl } from "@/lib/url-helpers"
import { Loader2 } from "lucide-react"
function isMcpOAuthAuthorizeContext(sp: Pick<URLSearchParams, "get">): boolean {
@ -276,7 +276,7 @@ export default function LoginPage() {
const token = formData.get("token") as string
const callbackURL = getCallbackURL()
router.push(
`${process.env.NEXT_PUBLIC_BACKEND_URL}/api/auth/magic-link/verify?token=${token}&callbackURL=${encodeURIComponent(callbackURL)}`,
`${getBackendUrl()}/api/auth/magic-link/verify?token=${token}&callbackURL=${encodeURIComponent(callbackURL)}`,
)
}

View file

@ -67,6 +67,7 @@ import {
} from "@/lib/search-params"
import { getChatSpaceDisplayLabel } from "@/lib/chat-space-label"
import { getToolDocumentSpace } from "@/lib/plugin-space"
import { getBackendUrl } from "@/lib/url-helpers"
type DocumentsResponse = z.infer<typeof DocumentsWithMemoriesResponseSchema>
type DocumentWithMemories = DocumentsResponse["documents"][0]
@ -134,6 +135,7 @@ export function AppExperience() {
const { viewMode, setViewMode } = useViewMode()
useLegacyViewRedirect()
const isCompanyBrain = useHasCompanyBrain()
const backendUrl = getBackendUrl()
// Slack OAuth redirects back here with ?slack=connected — toast then clean up.
useEffect(() => {
@ -331,7 +333,7 @@ export function AppExperience() {
queryFn: async (): Promise<SpaceHighlightsResponse> => {
const spaceId = selectedProject || "sm_project_default"
const forceRefresh = highlightsForceAt > 0
const cacheKey = `${process.env.NEXT_PUBLIC_BACKEND_URL}/v3/space-highlights?spaceId=${spaceId}`
const cacheKey = `${backendUrl}/v3/space-highlights?spaceId=${spaceId}`
if (!forceRefresh) {
const cache = await caches.open(HIGHLIGHTS_CACHE_NAME)
@ -345,22 +347,19 @@ export function AppExperience() {
}
}
const response = await fetch(
`${process.env.NEXT_PUBLIC_BACKEND_URL}/v3/space-highlights`,
{
method: "POST",
headers: { "Content-Type": "application/json" },
credentials: "include",
body: JSON.stringify({
spaceId,
highlightsCount: 3,
questionsCount: 4,
includeHighlights: true,
includeQuestions: true,
forceRefresh,
}),
},
)
const response = await fetch(`${backendUrl}/v3/space-highlights`, {
method: "POST",
headers: { "Content-Type": "application/json" },
credentials: "include",
body: JSON.stringify({
spaceId,
highlightsCount: 3,
questionsCount: 4,
includeHighlights: true,
includeQuestions: true,
forceRefresh,
}),
})
if (!response.ok) {
throw new Error("Failed to fetch space highlights")
@ -404,10 +403,9 @@ export function AppExperience() {
if (stored) return JSON.parse(stored) as MemoryOfDay
} catch {}
const response = await fetch(
`${process.env.NEXT_PUBLIC_BACKEND_URL}/v3/memory-of-day`,
{ credentials: "include" },
)
const response = await fetch(`${backendUrl}/v3/memory-of-day`, {
credentials: "include",
})
if (!response.ok) return null
const data = (await response.json()) as MemoryOfDay | null
if (data) {

View file

@ -1111,7 +1111,7 @@ export function ChatSidebar({
const params = new URLSearchParams({ projectId: chatProject })
if (historyScope === "all") params.set("scope", "all")
const response = await fetch(
`${process.env.NEXT_PUBLIC_BACKEND_URL}/chat/threads?${params.toString()}`,
`${chatApiBase}/chat/threads?${params.toString()}`,
{ credentials: "include" },
)
if (response.ok) {
@ -1123,7 +1123,7 @@ export function ChatSidebar({
} finally {
setIsLoadingThreads(false)
}
}, [chatProject, historyScope])
}, [chatApiBase, chatProject, historyScope])
useEffect(() => {
if (!isHistoryOpen) return
@ -1134,10 +1134,9 @@ export function ChatSidebar({
const loadThread = useCallback(
async (id: string) => {
try {
const response = await fetch(
`${process.env.NEXT_PUBLIC_BACKEND_URL}/chat/threads/${id}`,
{ credentials: "include" },
)
const response = await fetch(`${chatApiBase}/chat/threads/${id}`, {
credentials: "include",
})
if (response.ok) {
const data = await response.json()
const uiMessages = data.messages.map(
@ -1179,7 +1178,7 @@ export function ChatSidebar({
console.error("Failed to load thread:", error)
}
},
[setThreadId],
[chatApiBase, setThreadId],
)
// Auto-restore thread from URL on mount (e.g. reload or direct link)
@ -1197,7 +1196,7 @@ export function ChatSidebar({
async (threadId: string) => {
try {
const response = await fetch(
`${process.env.NEXT_PUBLIC_BACKEND_URL}/chat/threads/${threadId}`,
`${chatApiBase}/chat/threads/${threadId}`,
{ method: "DELETE", credentials: "include" },
)
if (response.ok) {
@ -1213,7 +1212,7 @@ export function ChatSidebar({
setConfirmingDeleteId(null)
}
},
[currentChatId, handleNewChat],
[chatApiBase, currentChatId, handleNewChat],
)
const formatRelativeTime = (isoString: string): string => {

View file

@ -10,6 +10,7 @@ import { $fetch } from "@lib/api"
import { useAuth } from "@lib/auth-context"
import { analytics } from "@/lib/analytics"
import { fetchSpaceSettings, spaceSettingsKey } from "@/hooks/use-space-context"
import { getBackendUrl } from "@/lib/url-helpers"
/** Pull the human-readable message out of a $fetch error (handles `{error}`/`{message}`/string). */
function fetchErrorMessage(err: unknown, fallback: string): string {
@ -500,14 +501,11 @@ export function useDocumentMutations({
}
formData.append("metadata", JSON.stringify({ sm_source: "consumer" }))
const response = await fetch(
`${process.env.NEXT_PUBLIC_BACKEND_URL}/v3/documents/file`,
{
method: "POST",
body: formData,
credentials: "include",
},
)
const response = await fetch(`${getBackendUrl()}/v3/documents/file`, {
method: "POST",
body: formData,
credentials: "include",
})
if (!response.ok) {
let message = "Failed to upload file"

View file

@ -1,7 +1,15 @@
const PROXY_LOCAL_HOSTS = new Set(["localhost", "127.0.0.1", "::1"])
const DEFAULT_BACKEND_URL = "https://api.supermemory.ai"
const DEV_APP_ORIGIN = "https://app.dev.supermemory.ai"
const PROD_APP_ORIGIN = "https://app.supermemory.ai"
export function getBackendUrl(): string {
return (process.env.NEXT_PUBLIC_BACKEND_URL ?? DEFAULT_BACKEND_URL).replace(
/\/+$/,
"",
)
}
export function getAppOriginForCurrentEnvironment(hostname?: string): string {
const currentHostname =
hostname ?? (typeof window !== "undefined" ? window.location.hostname : "")