Merge upstream/dev

This commit is contained in:
CREDO23 2026-04-27 22:44:40 +02:00
commit 2d962f6dd2
107 changed files with 15033 additions and 2277 deletions

View file

@ -0,0 +1,25 @@
import type { Metadata } from "next";
import type { ReactNode } from "react";
export const metadata: Metadata = {
title: "Announcements | SurfSense",
description: "Latest product updates, feature releases, and news from SurfSense.",
alternates: {
canonical: "https://surfsense.com/announcements",
},
openGraph: {
title: "Announcements | SurfSense",
description: "Latest product updates, feature releases, and news from SurfSense.",
url: "https://surfsense.com/announcements",
type: "website",
},
twitter: {
card: "summary_large_image",
title: "Announcements | SurfSense",
description: "Latest product updates, feature releases, and news from SurfSense.",
},
};
export default function AnnouncementsLayout({ children }: { children: ReactNode }) {
return <>{children}</>;
}

View file

@ -0,0 +1,70 @@
import type { NextRequest } from "next/server";
export const dynamic = "force-dynamic";
const HOP_BY_HOP_HEADERS = new Set([
"connection",
"keep-alive",
"proxy-authenticate",
"proxy-authorization",
"te",
"trailer",
"transfer-encoding",
"upgrade",
]);
function getBackendBaseUrl() {
const base = process.env.FASTAPI_BACKEND_INTERNAL_URL || "http://localhost:8000";
return base.endsWith("/") ? base.slice(0, -1) : base;
}
function toUpstreamHeaders(headers: Headers) {
const nextHeaders = new Headers(headers);
nextHeaders.delete("host");
nextHeaders.delete("content-length");
return nextHeaders;
}
function toClientHeaders(headers: Headers) {
const nextHeaders = new Headers(headers);
for (const header of HOP_BY_HOP_HEADERS) {
nextHeaders.delete(header);
}
return nextHeaders;
}
async function proxy(request: NextRequest, context: { params: Promise<{ path?: string[] }> }) {
const params = await context.params;
const path = params.path?.join("/") || "";
const upstreamUrl = new URL(`${getBackendBaseUrl()}/api/v1/${path}`);
upstreamUrl.search = request.nextUrl.search;
const hasBody = request.method !== "GET" && request.method !== "HEAD";
const response = await fetch(upstreamUrl, {
method: request.method,
headers: toUpstreamHeaders(request.headers),
body: hasBody ? request.body : undefined,
// `duplex: "half"` is required by the Fetch spec when streaming a
// ReadableStream as the request body. Avoids buffering uploads in heap.
// @ts-expect-error - `duplex` is not yet in lib.dom RequestInit types.
duplex: hasBody ? "half" : undefined,
redirect: "manual",
});
return new Response(response.body, {
status: response.status,
statusText: response.statusText,
headers: toClientHeaders(response.headers),
});
}
export {
proxy as GET,
proxy as POST,
proxy as PUT,
proxy as PATCH,
proxy as DELETE,
proxy as OPTIONS,
proxy as HEAD,
};

View file

@ -680,7 +680,7 @@ export default function NewChatPage() {
try {
const backendUrl = process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL || "http://localhost:8000";
const selection = await getAgentFilesystemSelection();
const selection = await getAgentFilesystemSelection(searchSpaceId);
if (
selection.filesystem_mode === "desktop_local_folder" &&
(!selection.local_filesystem_mounts || selection.local_filesystem_mounts.length === 0)
@ -1106,7 +1106,7 @@ export default function NewChatPage() {
try {
const backendUrl = process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL || "http://localhost:8000";
const selection = await getAgentFilesystemSelection();
const selection = await getAgentFilesystemSelection(searchSpaceId);
const response = await fetch(`${backendUrl}/api/v1/threads/${resumeThreadId}/resume`, {
method: "POST",
headers: {
@ -1427,9 +1427,7 @@ export default function NewChatPage() {
id: userMsgId,
role: "user",
content: isEdit
? (editExtras?.userMessageContent ?? [
{ type: "text", text: newUserQuery ?? "" },
])
? (editExtras?.userMessageContent ?? [{ type: "text", text: newUserQuery ?? "" }])
: originalUserMessageContent || [{ type: "text", text: userQueryToDisplay || "" }],
createdAt: new Date(),
metadata: isEdit ? undefined : originalUserMessageMetadata,
@ -1448,7 +1446,7 @@ export default function NewChatPage() {
]);
try {
const selection = await getAgentFilesystemSelection();
const selection = await getAgentFilesystemSelection(searchSpaceId);
const requestBody: Record<string, unknown> = {
search_space_id: searchSpaceId,
user_query: newUserQuery,
@ -1557,9 +1555,7 @@ export default function NewChatPage() {
try {
// Persist user message (for both edit and reload modes, since backend deleted it)
const userContentToPersist = isEdit
? (editExtras?.userMessageContent ?? [
{ type: "text", text: newUserQuery ?? "" },
])
? (editExtras?.userMessageContent ?? [{ type: "text", text: newUserQuery ?? "" }])
: originalUserMessageContent || [{ type: "text", text: userQueryToDisplay || "" }];
const savedUserMessage = await appendMessage(threadId, {

View file

@ -3,7 +3,7 @@
import { Check, Copy, Info } from "lucide-react";
import { useTranslations } from "next-intl";
import { useCallback, useRef, useState } from "react";
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
import { Alert, AlertDescription } from "@/components/ui/alert";
import { Button } from "@/components/ui/button";
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
import { useApiKey } from "@/hooks/use-api-key";