mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-06-18 21:15:16 +02:00
refactor(web): replace instances of BACKEND_URL with buildBackendUrl for improved URL handling
This commit is contained in:
parent
371ff866c7
commit
3f69bfd5e4
21 changed files with 98 additions and 95 deletions
|
|
@ -3,7 +3,7 @@ import { useTranslations } from "next-intl";
|
|||
import { useState } from "react";
|
||||
import { Logo } from "@/components/Logo";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { BACKEND_URL } from "@/lib/env-config";
|
||||
import { buildBackendUrl } from "@/lib/env-config";
|
||||
import { trackLoginAttempt } from "@/lib/posthog/events";
|
||||
import { AmbientBackground } from "./AmbientBackground";
|
||||
|
||||
|
|
@ -51,7 +51,7 @@ export function GoogleLoginButton() {
|
|||
// cross-origin fetch requests may not be sent on subsequent redirects.
|
||||
// The authorize-redirect endpoint does a server-side redirect to Google
|
||||
// and sets the CSRF cookie properly for same-site context.
|
||||
window.location.href = `${BACKEND_URL}/auth/google/authorize-redirect`;
|
||||
window.location.href = buildBackendUrl("/auth/google/authorize-redirect");
|
||||
};
|
||||
return (
|
||||
<div className="relative w-full overflow-hidden">
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ import { Skeleton } from "@/components/ui/skeleton";
|
|||
import type { SearchSpace } from "@/contracts/types/search-space.types";
|
||||
import { searchSpacesApiService } from "@/lib/apis/search-spaces-api.service";
|
||||
import { authenticatedFetch } from "@/lib/auth-utils";
|
||||
import { BACKEND_URL } from "@/lib/env-config";
|
||||
import { buildBackendUrl } from "@/lib/env-config";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
type GatewayConnection = {
|
||||
|
|
@ -82,13 +82,14 @@ export function MessagingChannelsContent() {
|
|||
const discordGatewayEnabled = gatewayConfig?.discord_enabled ?? false;
|
||||
|
||||
const fetchConnections = useCallback(async (platform?: GatewayPlatform) => {
|
||||
const query = platform ? `?platform=${encodeURIComponent(platform)}` : "";
|
||||
const res = await authenticatedFetch(`${BACKEND_URL}/api/v1/gateway/connections${query}`);
|
||||
const res = await authenticatedFetch(
|
||||
buildBackendUrl("/api/v1/gateway/connections", platform ? { platform } : undefined)
|
||||
);
|
||||
return (await res.json()) as GatewayConnection[];
|
||||
}, []);
|
||||
|
||||
const fetchGatewayConfig = useCallback(async () => {
|
||||
const res = await authenticatedFetch(`${BACKEND_URL}/api/v1/gateway/config`);
|
||||
const res = await authenticatedFetch(buildBackendUrl("/api/v1/gateway/config"));
|
||||
return (await res.json()) as GatewayConfig;
|
||||
}, []);
|
||||
|
||||
|
|
@ -125,7 +126,9 @@ export function MessagingChannelsContent() {
|
|||
|
||||
const refreshBaileysHealth = useCallback(async () => {
|
||||
if (whatsappMode !== "baileys") return;
|
||||
const res = await authenticatedFetch(`${BACKEND_URL}/api/v1/gateway/whatsapp/baileys/health`);
|
||||
const res = await authenticatedFetch(
|
||||
buildBackendUrl("/api/v1/gateway/whatsapp/baileys/health")
|
||||
);
|
||||
if (!res.ok) return;
|
||||
const data = (await res.json()) as BaileysHealth;
|
||||
setBaileysHealth(data);
|
||||
|
|
@ -136,7 +139,7 @@ export function MessagingChannelsContent() {
|
|||
}, [refreshBaileysHealth]);
|
||||
|
||||
async function startPairing(platform: PairingPlatform) {
|
||||
const res = await authenticatedFetch(`${BACKEND_URL}/api/v1/gateway/bindings/start`, {
|
||||
const res = await authenticatedFetch(buildBackendUrl("/api/v1/gateway/bindings/start"), {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ platform, search_space_id: searchSpaceId }),
|
||||
|
|
@ -148,7 +151,7 @@ export function MessagingChannelsContent() {
|
|||
|
||||
async function installSlackGateway() {
|
||||
const res = await authenticatedFetch(
|
||||
`${BACKEND_URL}/api/v1/gateway/slack/install?search_space_id=${searchSpaceId}`
|
||||
buildBackendUrl("/api/v1/gateway/slack/install", { search_space_id: searchSpaceId })
|
||||
);
|
||||
if (!res.ok) return;
|
||||
const data = (await res.json()) as { auth_url?: string };
|
||||
|
|
@ -159,7 +162,7 @@ export function MessagingChannelsContent() {
|
|||
|
||||
async function installDiscordGateway() {
|
||||
const res = await authenticatedFetch(
|
||||
`${BACKEND_URL}/api/v1/gateway/discord/install?search_space_id=${searchSpaceId}`
|
||||
buildBackendUrl("/api/v1/gateway/discord/install", { search_space_id: searchSpaceId })
|
||||
);
|
||||
if (!res.ok) return;
|
||||
const data = (await res.json()) as { auth_url?: string };
|
||||
|
|
@ -181,8 +184,8 @@ export function MessagingChannelsContent() {
|
|||
async function revoke(connection: GatewayConnection) {
|
||||
const url =
|
||||
connection.route_type === "account" && connection.account_id
|
||||
? `${BACKEND_URL}/api/v1/gateway/accounts/${connection.account_id}`
|
||||
: `${BACKEND_URL}/api/v1/gateway/bindings/${connection.id}`;
|
||||
? buildBackendUrl(`/api/v1/gateway/accounts/${connection.account_id}`)
|
||||
: buildBackendUrl(`/api/v1/gateway/bindings/${connection.id}`);
|
||||
await authenticatedFetch(url, {
|
||||
method: "DELETE",
|
||||
});
|
||||
|
|
@ -205,8 +208,8 @@ export function MessagingChannelsContent() {
|
|||
);
|
||||
const url =
|
||||
connection.route_type === "account" && connection.account_id
|
||||
? `${BACKEND_URL}/api/v1/gateway/accounts/${connection.account_id}/search-space`
|
||||
: `${BACKEND_URL}/api/v1/gateway/bindings/${connection.id}/search-space`;
|
||||
? buildBackendUrl(`/api/v1/gateway/accounts/${connection.account_id}/search-space`)
|
||||
: buildBackendUrl(`/api/v1/gateway/bindings/${connection.id}/search-space`);
|
||||
const res = await authenticatedFetch(url, {
|
||||
method: "PATCH",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
|
|
@ -222,9 +225,12 @@ export function MessagingChannelsContent() {
|
|||
}
|
||||
|
||||
async function resume(connection: GatewayConnection) {
|
||||
await authenticatedFetch(`${BACKEND_URL}/api/v1/gateway/bindings/${connection.id}/resume`, {
|
||||
method: "POST",
|
||||
});
|
||||
await authenticatedFetch(
|
||||
buildBackendUrl(`/api/v1/gateway/bindings/${connection.id}/resume`),
|
||||
{
|
||||
method: "POST",
|
||||
}
|
||||
);
|
||||
await refreshPlatform(connection.platform as GatewayPlatform);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ import { Spinner } from "@/components/ui/spinner";
|
|||
import { useElectronAPI } from "@/hooks/use-platform";
|
||||
import { searchSpacesApiService } from "@/lib/apis/search-spaces-api.service";
|
||||
import { setBearerToken } from "@/lib/auth-utils";
|
||||
import { BACKEND_URL } from "@/lib/env-config";
|
||||
import { buildBackendUrl } from "@/lib/env-config";
|
||||
|
||||
type ShortcutKey = "generalAssist" | "quickAsk" | "screenshotAssist";
|
||||
type ShortcutMap = typeof DEFAULT_SHORTCUTS;
|
||||
|
|
@ -240,7 +240,7 @@ export default function DesktopLoginPage() {
|
|||
const handleGoogleLogin = () => {
|
||||
if (isGoogleRedirecting) return;
|
||||
setIsGoogleRedirecting(true);
|
||||
window.location.href = `${BACKEND_URL}/auth/google/authorize-redirect`;
|
||||
window.location.href = buildBackendUrl("/auth/google/authorize-redirect");
|
||||
};
|
||||
|
||||
const autoSetSearchSpace = async () => {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
|
|||
import { Button } from "@/components/ui/button";
|
||||
import { EnumConnectorName } from "@/contracts/enums/connector";
|
||||
import { useApiKey } from "@/hooks/use-api-key";
|
||||
import { BACKEND_URL } from "@/lib/env-config";
|
||||
import { getConnectorBenefits } from "../connector-benefits";
|
||||
import type { ConnectFormProps } from "../index";
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import { Button } from "@/components/ui/button";
|
|||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { authenticatedFetch } from "@/lib/auth-utils";
|
||||
import { BACKEND_URL } from "@/lib/env-config";
|
||||
import { buildBackendUrl } from "@/lib/env-config";
|
||||
import type { ConnectorConfigProps } from "../index";
|
||||
export interface CirclebackConfigProps extends ConnectorConfigProps {
|
||||
onNameChange?: (name: string) => void;
|
||||
|
|
@ -42,12 +42,10 @@ export const CirclebackConfig: FC<CirclebackConfigProps> = ({ connector, onNameC
|
|||
const doFetch = async () => {
|
||||
if (!connector.search_space_id) return;
|
||||
|
||||
const baseUrl = BACKEND_URL;
|
||||
|
||||
setIsLoading(true);
|
||||
try {
|
||||
const response = await authenticatedFetch(
|
||||
`${baseUrl}/api/v1/webhooks/circleback/${connector.search_space_id}/info`,
|
||||
buildBackendUrl(`/api/v1/webhooks/circleback/${connector.search_space_id}/info`),
|
||||
{ signal: controller.signal }
|
||||
);
|
||||
if (controller.signal.aborted) return;
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ import type { SearchSourceConnector } from "@/contracts/types/connector.types";
|
|||
import { searchSourceConnector } from "@/contracts/types/connector.types";
|
||||
import { OAUTH_RESULT_COOKIE, parseOAuthCallbackResult } from "@/contracts/types/oauth.types";
|
||||
import { authenticatedFetch } from "@/lib/auth-utils";
|
||||
import { BACKEND_URL } from "@/lib/env-config";
|
||||
import { buildBackendUrl } from "@/lib/env-config";
|
||||
import {
|
||||
trackConnectorConnected,
|
||||
trackConnectorDeleted,
|
||||
|
|
@ -351,9 +351,7 @@ export const useConnectorDialog = () => {
|
|||
trackConnectorSetupStarted(Number(searchSpaceId), connector.connectorType, "oauth_click");
|
||||
|
||||
try {
|
||||
// Check if authEndpoint already has query parameters
|
||||
const separator = connector.authEndpoint.includes("?") ? "&" : "?";
|
||||
const url = `${BACKEND_URL}${connector.authEndpoint}${separator}space_id=${searchSpaceId}`;
|
||||
const url = buildBackendUrl(connector.authEndpoint, { space_id: searchSpaceId });
|
||||
|
||||
const response = await authenticatedFetch(url, { method: "GET" });
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import { Button } from "@/components/ui/button";
|
|||
import { Spinner } from "@/components/ui/spinner";
|
||||
import { documentsApiService } from "@/lib/apis/documents-api.service";
|
||||
import { authenticatedFetch } from "@/lib/auth-utils";
|
||||
import { BACKEND_URL } from "@/lib/env-config";
|
||||
import { buildBackendUrl } from "@/lib/env-config";
|
||||
|
||||
interface DownloadOriginalButtonProps {
|
||||
documentId: number;
|
||||
|
|
@ -41,7 +41,7 @@ export function DownloadOriginalButton({ documentId }: DownloadOriginalButtonPro
|
|||
setDownloading(true);
|
||||
try {
|
||||
const response = await authenticatedFetch(
|
||||
`${BACKEND_URL}/api/v1/documents/${documentId}/download-original`,
|
||||
buildBackendUrl(`/api/v1/documents/${documentId}/download-original`),
|
||||
{ method: "GET" }
|
||||
);
|
||||
if (!response.ok) throw new Error("Download failed");
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
"use client";
|
||||
|
||||
import { authenticatedFetch } from "@/lib/auth-utils";
|
||||
import { BACKEND_URL } from "@/lib/env-config";
|
||||
import { buildBackendUrl } from "@/lib/env-config";
|
||||
|
||||
export type MemoryScope = "user" | "team";
|
||||
|
||||
|
|
@ -30,10 +30,6 @@ function getMemoryPath(scope: MemoryScope, searchSpaceId?: number | null) {
|
|||
return `/api/v1/searchspaces/${searchSpaceId}/memory`;
|
||||
}
|
||||
|
||||
function getBackendUrl(path: string) {
|
||||
return `${BACKEND_URL}${path}`;
|
||||
}
|
||||
|
||||
export function getMemoryLimitState(length: number, limits?: MemoryLimits | null) {
|
||||
if (!limits) {
|
||||
return {
|
||||
|
|
@ -66,7 +62,7 @@ export async function fetchMemoryEditorDocument({
|
|||
title?: string | null;
|
||||
signal?: AbortSignal;
|
||||
}) {
|
||||
const response = await authenticatedFetch(getBackendUrl(getMemoryPath(scope, searchSpaceId)), {
|
||||
const response = await authenticatedFetch(buildBackendUrl(getMemoryPath(scope, searchSpaceId)), {
|
||||
method: "GET",
|
||||
signal,
|
||||
});
|
||||
|
|
@ -98,7 +94,7 @@ export async function saveMemoryMarkdown({
|
|||
searchSpaceId?: number | null;
|
||||
markdown: string;
|
||||
}) {
|
||||
const response = await authenticatedFetch(getBackendUrl(getMemoryPath(scope, searchSpaceId)), {
|
||||
const response = await authenticatedFetch(buildBackendUrl(getMemoryPath(scope, searchSpaceId)), {
|
||||
method: "PUT",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ memory_md: markdown }),
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import { Button } from "@/components/ui/button";
|
|||
import type { AnonModel, AnonQuotaResponse } from "@/contracts/types/anonymous-chat.types";
|
||||
import { anonymousChatApiService } from "@/lib/apis/anonymous-chat-api.service";
|
||||
import { readSSEStream } from "@/lib/chat/streaming-state";
|
||||
import { BACKEND_URL } from "@/lib/env-config";
|
||||
import { buildBackendUrl } from "@/lib/env-config";
|
||||
import { trackAnonymousChatMessageSent } from "@/lib/posthog/events";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { QuotaBar } from "./quota-bar";
|
||||
|
|
@ -81,7 +81,7 @@ export function AnonymousChat({ model }: AnonymousChatProps) {
|
|||
content: m.content,
|
||||
}));
|
||||
|
||||
const response = await fetch(`${BACKEND_URL}/api/v1/public/anon-chat/stream`, {
|
||||
const response = await fetch(buildBackendUrl("/api/v1/public/anon-chat/stream"), {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
credentials: "include",
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ import {
|
|||
updateThinkingSteps,
|
||||
updateToolCall,
|
||||
} from "@/lib/chat/streaming-state";
|
||||
import { BACKEND_URL } from "@/lib/env-config";
|
||||
import { buildBackendUrl } from "@/lib/env-config";
|
||||
import { trackAnonymousChatMessageSent } from "@/lib/posthog/events";
|
||||
import { FreeThread } from "./free-thread";
|
||||
import { RemoveAdsBanner } from "./remove-ads-banner";
|
||||
|
|
@ -176,7 +176,7 @@ export function FreeChatPage() {
|
|||
if (!webSearchEnabled) reqBody.disabled_tools = ["web_search"];
|
||||
if (turnstileToken) reqBody.turnstile_token = turnstileToken;
|
||||
|
||||
const response = await fetch(`${BACKEND_URL}/api/v1/public/anon-chat/stream`, {
|
||||
const response = await fetch(buildBackendUrl("/api/v1/public/anon-chat/stream"), {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
credentials: "include",
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ import { foldersApiService } from "@/lib/apis/folders-api.service";
|
|||
import { searchSpacesApiService } from "@/lib/apis/search-spaces-api.service";
|
||||
import { authenticatedFetch } from "@/lib/auth-utils";
|
||||
import { getMentionDocKey } from "@/lib/chat/mention-doc-key";
|
||||
import { BACKEND_URL } from "@/lib/env-config";
|
||||
import { buildBackendUrl } from "@/lib/env-config";
|
||||
import { uploadFolderScan } from "@/lib/folder-sync-upload";
|
||||
import { getSupportedExtensionsSet } from "@/lib/supported-extensions";
|
||||
import { queries } from "@/zero/queries/index";
|
||||
|
|
@ -751,7 +751,9 @@ function AuthenticatedDocumentsSidebarBase({
|
|||
.trim()
|
||||
.slice(0, 80) || "folder";
|
||||
await doExport(
|
||||
`${BACKEND_URL}/api/v1/search-spaces/${searchSpaceId}/export?folder_id=${ctx.folder.id}`,
|
||||
buildBackendUrl(`/api/v1/search-spaces/${searchSpaceId}/export`, {
|
||||
folder_id: ctx.folder.id,
|
||||
}),
|
||||
`${safeName}.zip`
|
||||
);
|
||||
toast.success(`Folder "${ctx.folder.name}" exported`);
|
||||
|
|
@ -803,7 +805,9 @@ function AuthenticatedDocumentsSidebarBase({
|
|||
.trim()
|
||||
.slice(0, 80) || "folder";
|
||||
await doExport(
|
||||
`${BACKEND_URL}/api/v1/search-spaces/${searchSpaceId}/export?folder_id=${folder.id}`,
|
||||
buildBackendUrl(`/api/v1/search-spaces/${searchSpaceId}/export`, {
|
||||
folder_id: folder.id,
|
||||
}),
|
||||
`${safeName}.zip`
|
||||
);
|
||||
toast.success(`Folder "${folder.name}" exported`);
|
||||
|
|
@ -823,8 +827,8 @@ function AuthenticatedDocumentsSidebarBase({
|
|||
try {
|
||||
const endpoint =
|
||||
doc.document_type === "USER_MEMORY"
|
||||
? `${BACKEND_URL}/api/v1/users/me/memory`
|
||||
: `${BACKEND_URL}/api/v1/searchspaces/${searchSpaceId}/memory`;
|
||||
? buildBackendUrl("/api/v1/users/me/memory")
|
||||
: buildBackendUrl(`/api/v1/searchspaces/${searchSpaceId}/memory`);
|
||||
const response = await authenticatedFetch(endpoint, { method: "GET" });
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({ detail: "Export failed" }));
|
||||
|
|
@ -852,7 +856,9 @@ function AuthenticatedDocumentsSidebarBase({
|
|||
|
||||
try {
|
||||
const response = await authenticatedFetch(
|
||||
`${BACKEND_URL}/api/v1/search-spaces/${searchSpaceId}/documents/${doc.id}/export?format=${format}`,
|
||||
buildBackendUrl(`/api/v1/search-spaces/${searchSpaceId}/documents/${doc.id}/export`, {
|
||||
format,
|
||||
}),
|
||||
{ method: "GET" }
|
||||
);
|
||||
|
||||
|
|
@ -1031,8 +1037,8 @@ function AuthenticatedDocumentsSidebarBase({
|
|||
}
|
||||
const endpoint =
|
||||
doc.document_type === "USER_MEMORY"
|
||||
? `${BACKEND_URL}/api/v1/users/me/memory/reset`
|
||||
: `${BACKEND_URL}/api/v1/searchspaces/${searchSpaceId}/memory/reset`;
|
||||
? buildBackendUrl("/api/v1/users/me/memory/reset")
|
||||
: buildBackendUrl(`/api/v1/searchspaces/${searchSpaceId}/memory/reset`);
|
||||
try {
|
||||
const response = await authenticatedFetch(endpoint, { method: "POST" });
|
||||
if (!response.ok) {
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ import { Spinner } from "@/components/ui/spinner";
|
|||
import { useMediaQuery } from "@/hooks/use-media-query";
|
||||
import { baseApiService } from "@/lib/apis/base-api.service";
|
||||
import { authenticatedFetch } from "@/lib/auth-utils";
|
||||
import { BACKEND_URL } from "@/lib/env-config";
|
||||
import { buildBackendUrl } from "@/lib/env-config";
|
||||
|
||||
function ReportPanelSkeleton() {
|
||||
return (
|
||||
|
|
@ -245,7 +245,7 @@ export function ReportPanelContent({
|
|||
URL.revokeObjectURL(url);
|
||||
} else {
|
||||
const response = await authenticatedFetch(
|
||||
`${BACKEND_URL}/api/v1/reports/${activeReportId}/export?format=${format}`,
|
||||
buildBackendUrl(`/api/v1/reports/${activeReportId}/export`, { format }),
|
||||
{ method: "GET" }
|
||||
);
|
||||
|
||||
|
|
@ -278,7 +278,7 @@ export function ReportPanelContent({
|
|||
setSaving(true);
|
||||
try {
|
||||
const response = await authenticatedFetch(
|
||||
`${BACKEND_URL}/api/v1/reports/${activeReportId}/content`,
|
||||
buildBackendUrl(`/api/v1/reports/${activeReportId}/content`),
|
||||
{
|
||||
method: "PUT",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
|
|
@ -506,7 +506,11 @@ export function ReportPanelContent({
|
|||
</div>
|
||||
) : reportContent.content_type === "typst" ? (
|
||||
<PdfViewer
|
||||
pdfUrl={`${BACKEND_URL}${shareToken ? `/api/v1/public/${shareToken}/reports/${activeReportId}/preview` : `/api/v1/reports/${activeReportId}/preview`}`}
|
||||
pdfUrl={buildBackendUrl(
|
||||
shareToken
|
||||
? `/api/v1/public/${shareToken}/reports/${activeReportId}/preview`
|
||||
: `/api/v1/reports/${activeReportId}/preview`
|
||||
)}
|
||||
isPublic={isPublic}
|
||||
toolbarActions={
|
||||
<>
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import { Label } from "@/components/ui/label";
|
|||
import { Skeleton } from "@/components/ui/skeleton";
|
||||
import { searchSpacesApiService } from "@/lib/apis/search-spaces-api.service";
|
||||
import { authenticatedFetch } from "@/lib/auth-utils";
|
||||
import { BACKEND_URL } from "@/lib/env-config";
|
||||
import { buildBackendUrl } from "@/lib/env-config";
|
||||
import { cacheKeys } from "@/lib/query-client/cache-keys";
|
||||
import { Spinner } from "../ui/spinner";
|
||||
|
||||
|
|
@ -49,7 +49,7 @@ export function GeneralSettingsManager({ searchSpaceId }: GeneralSettingsManager
|
|||
setIsExporting(true);
|
||||
try {
|
||||
const response = await authenticatedFetch(
|
||||
`${BACKEND_URL}/api/v1/search-spaces/${searchSpaceId}/export`,
|
||||
buildBackendUrl(`/api/v1/search-spaces/${searchSpaceId}/export`),
|
||||
{ method: "GET" }
|
||||
);
|
||||
if (!response.ok) {
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import { Button } from "@/components/ui/button";
|
|||
import { useMediaQuery } from "@/hooks/use-media-query";
|
||||
import { baseApiService } from "@/lib/apis/base-api.service";
|
||||
import { getAuthHeaders } from "@/lib/auth-utils";
|
||||
import { BACKEND_URL } from "@/lib/env-config";
|
||||
import { buildBackendUrl } from "@/lib/env-config";
|
||||
|
||||
pdfjsLib.GlobalWorkerOptions.workerSrc = new URL(
|
||||
"pdfjs-dist/build/pdf.worker.min.mjs",
|
||||
|
|
@ -223,7 +223,7 @@ function ResumeCard({
|
|||
const previewPath = shareToken
|
||||
? `/api/v1/public/${shareToken}/reports/${reportId}/preview`
|
||||
: `/api/v1/reports/${reportId}/preview`;
|
||||
setPdfUrl(`${BACKEND_URL}${previewPath}`);
|
||||
setPdfUrl(buildBackendUrl(previewPath));
|
||||
|
||||
if (autoOpen && isDesktop && !autoOpenedRef.current) {
|
||||
autoOpenedRef.current = true;
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ import {
|
|||
import { baseApiService } from "@/lib/apis/base-api.service";
|
||||
import { podcastsApiService } from "@/lib/apis/podcasts-api.service";
|
||||
import { authenticatedFetch } from "@/lib/auth-utils";
|
||||
import { BACKEND_URL } from "@/lib/env-config";
|
||||
import { buildBackendUrl } from "@/lib/env-config";
|
||||
import { speakerLabel } from "./schema";
|
||||
|
||||
// Public snapshots predate the transcript.turns shape and keep their own.
|
||||
|
|
@ -121,7 +121,7 @@ export function PodcastPlayer({
|
|||
);
|
||||
} else {
|
||||
const [audioResponse, detail] = await Promise.all([
|
||||
authenticatedFetch(`${BACKEND_URL}/api/v1/podcasts/${podcastId}/stream`, {
|
||||
authenticatedFetch(buildBackendUrl(`/api/v1/podcasts/${podcastId}/stream`), {
|
||||
method: "GET",
|
||||
signal: controller.signal,
|
||||
}),
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ import { Badge } from "@/components/ui/badge";
|
|||
import { Button } from "@/components/ui/button";
|
||||
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible";
|
||||
import { getBearerToken } from "@/lib/auth-utils";
|
||||
import { BACKEND_URL } from "@/lib/env-config";
|
||||
import { buildBackendUrl } from "@/lib/env-config";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
// ============================================================================
|
||||
|
|
@ -158,7 +158,9 @@ function truncateCommand(command: string, maxLen = 80): string {
|
|||
|
||||
async function downloadSandboxFile(threadId: string, filePath: string, fileName: string) {
|
||||
const token = getBearerToken();
|
||||
const url = `${BACKEND_URL}/api/v1/threads/${threadId}/sandbox/download?path=${encodeURIComponent(filePath)}`;
|
||||
const url = buildBackendUrl(`/api/v1/threads/${threadId}/sandbox/download`, {
|
||||
path: filePath,
|
||||
});
|
||||
const res = await fetch(url, {
|
||||
headers: { Authorization: `Bearer ${token || ""}` },
|
||||
});
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import { TextShimmerLoader } from "@/components/prompt-kit/loader";
|
|||
import { Button } from "@/components/ui/button";
|
||||
import { baseApiService } from "@/lib/apis/base-api.service";
|
||||
import { authenticatedFetch } from "@/lib/auth-utils";
|
||||
import { BACKEND_URL } from "@/lib/env-config";
|
||||
import { buildBackendUrl } from "@/lib/env-config";
|
||||
import { compileCheck, compileToComponent } from "@/lib/remotion/compile-check";
|
||||
import { FPS } from "@/lib/remotion/constants";
|
||||
import {
|
||||
|
|
@ -137,7 +137,6 @@ function VideoPresentationPlayer({
|
|||
const [isPptxExporting, setIsPptxExporting] = useState(false);
|
||||
const [pptxProgress, setPptxProgress] = useState<string | null>(null);
|
||||
|
||||
const backendUrl = BACKEND_URL ?? "";
|
||||
const audioBlobUrlsRef = useRef<string[]>([]);
|
||||
|
||||
const loadPresentation = useCallback(async () => {
|
||||
|
|
@ -177,7 +176,7 @@ function VideoPresentationPlayer({
|
|||
title: scene.title ?? slide.title,
|
||||
code: scene.code,
|
||||
durationInFrames,
|
||||
audioUrl: slide.audio_url ? `${backendUrl}${slide.audio_url}` : undefined,
|
||||
audioUrl: slide.audio_url ? buildBackendUrl(slide.audio_url) : undefined,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -222,7 +221,7 @@ function VideoPresentationPlayer({
|
|||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, [presentationId, backendUrl, shareToken]);
|
||||
}, [presentationId, shareToken]);
|
||||
|
||||
useEffect(() => {
|
||||
loadPresentation();
|
||||
|
|
|
|||
|
|
@ -13,24 +13,25 @@ const eslintConfig = [
|
|||
...compat.extends("next/core-web-vitals", "next/typescript"),
|
||||
{
|
||||
rules: {
|
||||
"no-restricted-syntax": [
|
||||
"no-restricted-imports": [
|
||||
"error",
|
||||
{
|
||||
selector:
|
||||
"NewExpression[callee.name='URL'] TemplateLiteral Identifier[name='BACKEND_URL']",
|
||||
message:
|
||||
"Use buildBackendUrl(path, params) for backend URLs. BACKEND_URL may be empty in proxy mode, and new URL('/relative') throws without a base.",
|
||||
},
|
||||
{
|
||||
selector:
|
||||
"NewExpression[callee.name='URL'] TemplateLiteral Identifier[name='backendUrl']",
|
||||
message:
|
||||
"Use buildBackendUrl(path, params) for backend URLs instead of aliasing BACKEND_URL into new URL().",
|
||||
},
|
||||
{
|
||||
selector: "VariableDeclarator[id.name='backendUrl'][init.name='BACKEND_URL']",
|
||||
message:
|
||||
"Do not alias BACKEND_URL for URL construction. Use buildBackendUrl(path, params) instead.",
|
||||
paths: [
|
||||
{
|
||||
name: "@/lib/env-config",
|
||||
importNames: ["BACKEND_URL"],
|
||||
message:
|
||||
"Use buildBackendUrl(path, params) for browser-facing backend URLs. BACKEND_URL is empty in proxy mode; importing it bypasses the single URL seam.",
|
||||
},
|
||||
],
|
||||
patterns: [
|
||||
{
|
||||
group: ["**/env-config", "**/env-config.ts"],
|
||||
importNames: ["BACKEND_URL"],
|
||||
message:
|
||||
"Use buildBackendUrl(path, params). Import BACKEND_URL only inside lib/env-config.ts.",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import {
|
|||
getAnonModelResponse,
|
||||
getAnonModelsResponse,
|
||||
} from "@/contracts/types/anonymous-chat.types";
|
||||
import { BACKEND_URL } from "../env-config";
|
||||
import { buildBackendUrl } from "../env-config";
|
||||
import { ValidationError } from "../error";
|
||||
|
||||
const BASE = "/api/v1/public/anon-chat";
|
||||
|
|
@ -17,14 +17,8 @@ export type AnonUploadResult =
|
|||
| { ok: false; reason: "quota_exceeded" };
|
||||
|
||||
class AnonymousChatApiService {
|
||||
private baseUrl: string;
|
||||
|
||||
constructor(baseUrl: string) {
|
||||
this.baseUrl = baseUrl;
|
||||
}
|
||||
|
||||
private fullUrl(path: string): string {
|
||||
return `${this.baseUrl}${BASE}${path}`;
|
||||
return buildBackendUrl(`${BASE}${path}`);
|
||||
}
|
||||
|
||||
getModels = async (): Promise<AnonModel[]> => {
|
||||
|
|
@ -102,4 +96,4 @@ class AnonymousChatApiService {
|
|||
};
|
||||
}
|
||||
|
||||
export const anonymousChatApiService = new AnonymousChatApiService(BACKEND_URL);
|
||||
export const anonymousChatApiService = new AnonymousChatApiService();
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* Authentication utilities for handling token expiration and redirects
|
||||
*/
|
||||
import { BACKEND_URL } from "@/lib/env-config";
|
||||
import { buildBackendUrl } from "@/lib/env-config";
|
||||
|
||||
const REDIRECT_PATH_KEY = "surfsense_redirect_path";
|
||||
const BEARER_TOKEN_KEY = "surfsense_bearer_token";
|
||||
|
|
@ -195,7 +195,7 @@ export async function logout(): Promise<boolean> {
|
|||
// Call backend to revoke the refresh token
|
||||
if (refreshToken) {
|
||||
try {
|
||||
const response = await fetch(`${BACKEND_URL}/auth/jwt/revoke`, {
|
||||
const response = await fetch(buildBackendUrl("/auth/jwt/revoke"), {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
|
|
@ -273,7 +273,7 @@ export async function refreshAccessToken(): Promise<string | null> {
|
|||
isRefreshing = true;
|
||||
refreshPromise = (async () => {
|
||||
try {
|
||||
const response = await fetch(`${BACKEND_URL}/auth/jwt/refresh`, {
|
||||
const response = await fetch(buildBackendUrl("/auth/jwt/refresh"), {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
*/
|
||||
|
||||
import { baseApiService } from "@/lib/apis/base-api.service";
|
||||
import { BACKEND_URL } from "@/lib/env-config";
|
||||
import { buildBackendUrl } from "@/lib/env-config";
|
||||
// =============================================================================
|
||||
// Types matching backend schemas
|
||||
// =============================================================================
|
||||
|
|
@ -227,5 +227,5 @@ export interface RegenerateParams {
|
|||
* Get the URL for the regenerate endpoint (for streaming fetch)
|
||||
*/
|
||||
export function getRegenerateUrl(threadId: number): string {
|
||||
return `${BACKEND_URL}/api/v1/threads/${threadId}/regenerate`;
|
||||
return buildBackendUrl(`/api/v1/threads/${threadId}/regenerate`);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue