diff --git a/surfsense_web/app/dashboard/[workspace_id]/onboard/page.tsx b/surfsense_web/app/dashboard/[workspace_id]/onboard/page.tsx index 861ceaa07..9da14ef4a 100644 --- a/surfsense_web/app/dashboard/[workspace_id]/onboard/page.tsx +++ b/surfsense_web/app/dashboard/[workspace_id]/onboard/page.tsx @@ -74,7 +74,7 @@ export default function OnboardPage() {

: ; diff --git a/surfsense_web/components/assistant-ui/thread.tsx b/surfsense_web/components/assistant-ui/thread.tsx index 3716c59aa..34ff4888f 100644 --- a/surfsense_web/components/assistant-ui/thread.tsx +++ b/surfsense_web/components/assistant-ui/thread.tsx @@ -108,6 +108,7 @@ import { useElectronAPI } from "@/hooks/use-platform"; import { captureDisplayToPngDataUrl } from "@/lib/chat/display-media-capture"; import { getMentionDocKey } from "@/lib/chat/mention-doc-key"; import { slideoutOpenedTickAtom } from "@/lib/layout-events"; +import { getWorkspaceIdNumber } from "@/lib/route-params"; import { cn } from "@/lib/utils"; import { DocumentMentionPicker, @@ -458,7 +459,9 @@ const Composer: FC = () => { const prevMentionedDocsRef = useRef>(new Map()); const documentPickerRef = useRef(null); const promptPickerRef = useRef(null); - const { search_space_id, chat_id } = useParams(); + const params = useParams(); + const workspaceId = getWorkspaceIdNumber(params); + const chat_id = params.chat_id; const aui = useAui(); // Desktop-only auto-focus; on mobile, programmatic focus would // summon the soft keyboard on every picker close / thread switch. @@ -824,7 +827,6 @@ const Composer: FC = () => { const handleDocumentsMention = useCallback( (mentions: MentionedDocumentInfo[]) => { - const parsedSearchSpaceId = Number(search_space_id); const editorMentionedDocs = editorRef.current?.getMentionedDocuments() ?? []; const editorDocKeys = new Set(editorMentionedDocs.map((doc) => getMentionDocKey(doc))); @@ -832,8 +834,8 @@ const Composer: FC = () => { const key = getMentionDocKey(mention); if (editorDocKeys.has(key)) continue; editorRef.current?.insertMentionChip(mention); - if (Number.isFinite(parsedSearchSpaceId)) { - promoteRecentMention(parsedSearchSpaceId, mention); + if (workspaceId) { + promoteRecentMention(workspaceId, mention); } // Track within the loop so a duplicate-in-batch can't double-insert. editorDocKeys.add(key); @@ -844,7 +846,7 @@ const Composer: FC = () => { setMentionQuery(""); setSuggestionAnchorPoint(null); }, - [search_space_id] + [workspaceId] ); useEffect(() => { @@ -893,7 +895,7 @@ const Composer: FC = () => { { diff --git a/surfsense_web/components/assistant-ui/user-message.tsx b/surfsense_web/components/assistant-ui/user-message.tsx index 9eb2f3df1..e74d2b4ab 100644 --- a/surfsense_web/components/assistant-ui/user-message.tsx +++ b/surfsense_web/components/assistant-ui/user-message.tsx @@ -26,6 +26,7 @@ import { TooltipIconButton } from "@/components/assistant-ui/tooltip-icon-button import { getConnectorIcon } from "@/contracts/enums/connectorIcons"; import { getMentionDocKey } from "@/lib/chat/mention-doc-key"; import { parseMentionSegments } from "@/lib/chat/parse-mention-segments"; +import { getWorkspaceIdNumber } from "@/lib/route-params"; interface AuthorMetadata { displayName: string | null; @@ -75,13 +76,7 @@ const UserTextPart: FC = () => { const openEditorPanel = useSetAtom(openEditorPanelAtom); const router = useRouter(); const params = useParams(); - const searchSpaceIdParam = params?.search_space_id; - const parsedSearchSpaceId = Array.isArray(searchSpaceIdParam) - ? Number(searchSpaceIdParam[0]) - : Number(searchSpaceIdParam); - const resolvedSearchSpaceId = Number.isFinite(parsedSearchSpaceId) - ? parsedSearchSpaceId - : undefined; + const resolvedSearchSpaceId = getWorkspaceIdNumber(params); const handleOpenDoc = useCallback( (docId: number, title: string) => { diff --git a/surfsense_web/components/editor-panel/memory.ts b/surfsense_web/components/editor-panel/memory.ts index 8c4dfc035..085837803 100644 --- a/surfsense_web/components/editor-panel/memory.ts +++ b/surfsense_web/components/editor-panel/memory.ts @@ -27,7 +27,7 @@ interface MemoryReadResponse { function getMemoryPath(scope: MemoryScope, searchSpaceId?: number | null) { if (scope === "user") return "/api/v1/users/me/memory"; if (!searchSpaceId) throw new Error("Missing search space context"); - return `/api/v1/searchspaces/${searchSpaceId}/memory`; + return `/api/v1/workspaces/${searchSpaceId}/memory`; } export function getMemoryLimitState(length: number, limits?: MemoryLimits | null) { diff --git a/surfsense_web/components/layout/ui/sidebar/DocumentsSidebar.tsx b/surfsense_web/components/layout/ui/sidebar/DocumentsSidebar.tsx index e70a9fec9..e3811cdd1 100644 --- a/surfsense_web/components/layout/ui/sidebar/DocumentsSidebar.tsx +++ b/surfsense_web/components/layout/ui/sidebar/DocumentsSidebar.tsx @@ -81,6 +81,7 @@ import { authenticatedFetch } from "@/lib/auth-fetch"; import { getMentionDocKey } from "@/lib/chat/mention-doc-key"; import { buildBackendUrl } from "@/lib/env-config"; import { uploadFolderScan } from "@/lib/folder-sync-upload"; +import { getWorkspaceIdNumber } from "@/lib/route-params"; import { getSupportedExtensionsSet } from "@/lib/supported-extensions"; import { queries } from "@/zero/queries/index"; import { SidebarSlideOutPanel } from "./SidebarSlideOutPanel"; @@ -228,7 +229,7 @@ function AuthenticatedDocumentsSidebarBase({ const platformElectronAPI = useElectronAPI(); const electronAPI = desktopFeaturesEnabled ? platformElectronAPI : null; const { etlService } = useRuntimeConfig(); - const searchSpaceId = Number(params.search_space_id); + const searchSpaceId = getWorkspaceIdNumber(params) ?? 0; const setConnectorDialogOpen = useSetAtom(connectorDialogOpenAtom); const openEditorPanel = useSetAtom(openEditorPanelAtom); const { data: agentFlags } = useAtomValue(agentFlagsAtom); @@ -828,7 +829,7 @@ function AuthenticatedDocumentsSidebarBase({ const endpoint = doc.document_type === "USER_MEMORY" ? buildBackendUrl("/api/v1/users/me/memory") - : buildBackendUrl(`/api/v1/searchspaces/${searchSpaceId}/memory`); + : buildBackendUrl(`/api/v1/workspaces/${searchSpaceId}/memory`); const response = await authenticatedFetch(endpoint, { method: "GET" }); if (!response.ok) { const errorData = await response.json().catch(() => ({ detail: "Export failed" })); @@ -1038,7 +1039,7 @@ function AuthenticatedDocumentsSidebarBase({ const endpoint = doc.document_type === "USER_MEMORY" ? buildBackendUrl("/api/v1/users/me/memory/reset") - : buildBackendUrl(`/api/v1/searchspaces/${searchSpaceId}/memory/reset`); + : buildBackendUrl(`/api/v1/workspaces/${searchSpaceId}/memory/reset`); try { const response = await authenticatedFetch(endpoint, { method: "POST" }); if (!response.ok) { diff --git a/surfsense_web/components/layout/ui/sidebar/InboxSidebar.tsx b/surfsense_web/components/layout/ui/sidebar/InboxSidebar.tsx index 3785dc649..6f58e2d1a 100644 --- a/surfsense_web/components/layout/ui/sidebar/InboxSidebar.tsx +++ b/surfsense_web/components/layout/ui/sidebar/InboxSidebar.tsx @@ -58,6 +58,7 @@ import { notificationsApiService } from "@/lib/apis/notifications-api.service"; import { convertRenderedToDisplay } from "@/lib/comments/utils"; import { getDocumentTypeLabel } from "@/lib/documents/document-type-labels"; import { cacheKeys } from "@/lib/query-client/cache-keys"; +import { getWorkspaceIdNumber } from "@/lib/route-params"; import { cn } from "@/lib/utils"; import { SidebarSlideOutPanel } from "./SidebarSlideOutPanel"; @@ -165,7 +166,7 @@ export function InboxSidebarContent({ const router = useRouter(); const params = useParams(); const isMobile = !useMediaQuery("(min-width: 640px)"); - const searchSpaceId = params?.search_space_id ? Number(params.search_space_id) : null; + const searchSpaceId = getWorkspaceIdNumber(params) ?? null; const [, setTargetCommentId] = useAtom(setTargetCommentIdAtom); diff --git a/surfsense_web/components/layout/ui/sidebar/Sidebar.tsx b/surfsense_web/components/layout/ui/sidebar/Sidebar.tsx index c274e1f97..6044508b0 100644 --- a/surfsense_web/components/layout/ui/sidebar/Sidebar.tsx +++ b/surfsense_web/components/layout/ui/sidebar/Sidebar.tsx @@ -10,6 +10,7 @@ import { Button } from "@/components/ui/button"; import { Progress } from "@/components/ui/progress"; import { Skeleton } from "@/components/ui/skeleton"; import { useIsAnonymous } from "@/contexts/anonymous-mode"; +import { getWorkspaceIdParam } from "@/lib/route-params"; import { cn } from "@/lib/utils"; import { SIDEBAR_MIN_WIDTH } from "../../hooks/useSidebarResize"; import type { ChatItem, NavItem, PageUsage, SearchSpace, User } from "../../types/layout.types"; @@ -377,7 +378,7 @@ function SidebarUsageFooter({ onNavigate?: () => void; }) { const params = useParams(); - const searchSpaceId = params?.search_space_id ?? ""; + const searchSpaceId = getWorkspaceIdParam(params) ?? ""; const isAnonymous = useIsAnonymous(); if (isCollapsed) return null; diff --git a/surfsense_web/components/new-chat/prompt-picker.tsx b/surfsense_web/components/new-chat/prompt-picker.tsx index 65bf0a889..d3dbb5309 100644 --- a/surfsense_web/components/new-chat/prompt-picker.tsx +++ b/surfsense_web/components/new-chat/prompt-picker.tsx @@ -24,6 +24,7 @@ import { ComposerSuggestionSeparator, ComposerSuggestionSkeleton, } from "@/components/new-chat/composer-suggestion-popup"; +import { getWorkspaceIdParam } from "@/lib/route-params"; export interface PromptPickerRef { selectHighlighted: () => void; @@ -69,9 +70,7 @@ export const PromptPicker = forwardRef(funct const createPromptIndex = filtered.length; const totalItems = filtered.length + 1; - const searchSpaceId = Array.isArray(params?.search_space_id) - ? params.search_space_id[0] - : params?.search_space_id; + const searchSpaceId = getWorkspaceIdParam(params); const handleSelect = useCallback( (index: number) => { diff --git a/surfsense_web/components/providers/I18nProvider.tsx b/surfsense_web/components/providers/I18nProvider.tsx index 679927163..be549cacf 100644 --- a/surfsense_web/components/providers/I18nProvider.tsx +++ b/surfsense_web/components/providers/I18nProvider.tsx @@ -11,7 +11,7 @@ export function I18nProvider({ children }: { children: React.ReactNode }) { const { locale, messages } = useLocaleContext(); return ( - + {children} ); diff --git a/surfsense_web/components/settings/auto-reload-settings.tsx b/surfsense_web/components/settings/auto-reload-settings.tsx index fbb7cbfb9..61fe884a0 100644 --- a/surfsense_web/components/settings/auto-reload-settings.tsx +++ b/surfsense_web/components/settings/auto-reload-settings.tsx @@ -15,6 +15,7 @@ import { Spinner } from "@/components/ui/spinner"; import { Switch } from "@/components/ui/switch"; import { stripeApiService } from "@/lib/apis/stripe-api.service"; import { AppError } from "@/lib/error"; +import { getWorkspaceIdNumber } from "@/lib/route-params"; import { queries } from "@/zero/queries"; const microsToDollars = (micros: number | null | undefined): string => { @@ -38,7 +39,7 @@ export function AutoReloadSettings() { const pathname = usePathname(); const searchParams = useSearchParams(); const queryClient = useQueryClient(); - const searchSpaceId = Number(params?.search_space_id); + const searchSpaceId = getWorkspaceIdNumber(params) ?? 0; const [enabled, setEnabled] = useState(false); const [thresholdInput, setThresholdInput] = useState(""); diff --git a/surfsense_web/components/settings/buy-credits-content.tsx b/surfsense_web/components/settings/buy-credits-content.tsx index 8cb339420..4b1f2c87a 100644 --- a/surfsense_web/components/settings/buy-credits-content.tsx +++ b/surfsense_web/components/settings/buy-credits-content.tsx @@ -10,6 +10,7 @@ import { Button } from "@/components/ui/button"; import { Spinner } from "@/components/ui/spinner"; import { stripeApiService } from "@/lib/apis/stripe-api.service"; import { AppError } from "@/lib/error"; +import { getWorkspaceIdNumber } from "@/lib/route-params"; import { cn } from "@/lib/utils"; import { queries } from "@/zero/queries"; @@ -37,7 +38,7 @@ const formatUsd = (micros: number) => { export function BuyCreditsContent() { const params = useParams(); - const searchSpaceId = Number(params?.search_space_id); + const searchSpaceId = getWorkspaceIdNumber(params) ?? 0; const [quantity, setQuantity] = useState(1); // Raw text of the amount field so the user can clear it while typing; // committed back to a clamped integer on blur. diff --git a/surfsense_web/components/settings/earn-credits-content.tsx b/surfsense_web/components/settings/earn-credits-content.tsx index 731ea7726..4637d7c72 100644 --- a/surfsense_web/components/settings/earn-credits-content.tsx +++ b/surfsense_web/components/settings/earn-credits-content.tsx @@ -20,6 +20,7 @@ import { trackIncentiveTaskClicked, trackIncentiveTaskCompleted, } from "@/lib/posthog/events"; +import { getWorkspaceIdParam } from "@/lib/route-params"; import { cn } from "@/lib/utils"; // Compact dollar label for a task's reward (e.g. "+$0.03"). @@ -32,7 +33,7 @@ const formatRewardUsd = (micros: number) => { export function EarnCreditsContent() { const params = useParams(); const queryClient = useQueryClient(); - const searchSpaceId = params?.search_space_id ?? ""; + const searchSpaceId = getWorkspaceIdParam(params) ?? ""; useEffect(() => { trackIncentivePageViewed(); diff --git a/surfsense_web/components/settings/model-connections/model-provider-connections-panel.tsx b/surfsense_web/components/settings/model-connections/model-provider-connections-panel.tsx index a703ab1c8..071f1c04d 100644 --- a/surfsense_web/components/settings/model-connections/model-provider-connections-panel.tsx +++ b/surfsense_web/components/settings/model-connections/model-provider-connections-panel.tsx @@ -126,6 +126,11 @@ export function ModelProviderConnectionsPanel({ // Each provider connect form builds its own credential payload; the backend // resolver (`to_litellm`) forwards `extra.litellm_params` straight to LiteLLM. function handleCreate(draft: ConnectionDraft) { + if (!Number.isFinite(searchSpaceId) || searchSpaceId <= 0) { + toast.error("Workspace is still loading. Please try again."); + return; + } + const models = connectionModelsForDraft(draft); const testModel = representativeTestModel(models); if (!testModel) { @@ -165,6 +170,11 @@ export function ModelProviderConnectionsPanel({ setProvider(providerId); setIsAddProviderOpen(true); if (providerId === "vertex_ai") { + if (!Number.isFinite(searchSpaceId) || searchSpaceId <= 0) { + toast.error("Workspace is still loading. Please try again."); + return; + } + previewModels.mutate( { provider: providerId, @@ -184,6 +194,11 @@ export function ModelProviderConnectionsPanel({ } function refreshConnectModels(draft: ConnectionDraft) { + if (!Number.isFinite(searchSpaceId) || searchSpaceId <= 0) { + toast.error("Workspace is still loading. Please try again."); + return; + } + previewModels.mutate( { provider, diff --git a/surfsense_web/contracts/types/document.types.ts b/surfsense_web/contracts/types/document.types.ts index da1dac537..ec146ef90 100644 --- a/surfsense_web/contracts/types/document.types.ts +++ b/surfsense_web/contracts/types/document.types.ts @@ -200,12 +200,18 @@ export const documentTitleRead = z.object({ }); export const searchDocumentTitlesRequest = z.object({ - queryParams: z.object({ - search_space_id: z.number(), - title: z.string().optional(), - page: z.number().optional(), - page_size: z.number().optional(), - }), + queryParams: z + .object({ + search_space_id: z.number().optional(), + workspace_id: z.number().optional(), + title: z.string().optional(), + page: z.number().optional(), + page_size: z.number().optional(), + }) + .refine((params) => params.search_space_id !== undefined || params.workspace_id !== undefined, { + message: "workspace_id is required", + path: ["search_space_id"], + }), }); export const searchDocumentTitlesResponse = z.object({ diff --git a/surfsense_web/contracts/types/members.types.ts b/surfsense_web/contracts/types/members.types.ts index e458807af..fee96e092 100644 --- a/surfsense_web/contracts/types/members.types.ts +++ b/surfsense_web/contracts/types/members.types.ts @@ -1,21 +1,32 @@ import { z } from "zod"; import { role } from "./roles.types"; -export const membership = z.object({ - id: z.number(), - user_id: z.string(), - search_space_id: z.number(), - role_id: z.number().nullable(), - is_owner: z.boolean(), - joined_at: z.string(), - created_at: z.string(), - role: role.nullable().optional(), - user_email: z.string().nullable().optional(), - user_display_name: z.string().nullable().optional(), - user_avatar_url: z.string().nullable().optional(), - user_last_login: z.string().nullable().optional(), - user_is_active: z.boolean().nullable().optional(), -}); +export const membership = z.preprocess( + (value) => { + if (typeof value === "object" && value !== null && !Array.isArray(value)) { + const record = value as Record; + if (record.search_space_id === undefined && record.workspace_id !== undefined) { + return { ...record, search_space_id: record.workspace_id }; + } + } + return value; + }, + z.object({ + id: z.number(), + user_id: z.string(), + search_space_id: z.number(), + role_id: z.number().nullable(), + is_owner: z.boolean(), + joined_at: z.string(), + created_at: z.string(), + role: role.nullable().optional(), + user_email: z.string().nullable().optional(), + user_display_name: z.string().nullable().optional(), + user_avatar_url: z.string().nullable().optional(), + user_last_login: z.string().nullable().optional(), + user_is_active: z.boolean().nullable().optional(), + }) +); /** * Get members @@ -69,13 +80,19 @@ export const getMyAccessRequest = z.object({ search_space_id: z.number(), }); -export const getMyAccessResponse = z.object({ - search_space_name: z.string(), - search_space_id: z.number(), - is_owner: z.boolean(), - permissions: z.array(z.string()), - role_name: z.string().nullable(), -}); +export const getMyAccessResponse = z + .object({ + workspace_name: z.string(), + workspace_id: z.number(), + is_owner: z.boolean(), + permissions: z.array(z.string()), + role_name: z.string().nullable(), + }) + .transform(({ workspace_id, workspace_name, ...rest }) => ({ + ...rest, + search_space_id: workspace_id, + search_space_name: workspace_name, + })); export type Membership = z.infer; export type GetMembersRequest = z.infer; diff --git a/surfsense_web/contracts/types/roles.types.ts b/surfsense_web/contracts/types/roles.types.ts index 9008a859a..5c939b54c 100644 --- a/surfsense_web/contracts/types/roles.types.ts +++ b/surfsense_web/contracts/types/roles.types.ts @@ -1,6 +1,6 @@ import { z } from "zod"; -export const role = z.object({ +const roleBase = z.object({ id: z.number(), name: z.string().min(1).max(100), description: z.string().max(500).nullable(), @@ -11,12 +11,22 @@ export const role = z.object({ created_at: z.string(), }); +export const role = z.preprocess((value) => { + if (typeof value === "object" && value !== null && !Array.isArray(value)) { + const record = value as Record; + if (record.search_space_id === undefined && record.workspace_id !== undefined) { + return { ...record, search_space_id: record.workspace_id }; + } + } + return value; +}, roleBase); + /** * Create role */ export const createRoleRequest = z.object({ search_space_id: z.number(), - data: role.pick({ + data: roleBase.pick({ name: true, description: true, permissions: true, @@ -51,7 +61,7 @@ export const getRoleByIdResponse = role; export const updateRoleRequest = z.object({ search_space_id: z.number(), role_id: z.number(), - data: role + data: roleBase .pick({ name: true, description: true, diff --git a/surfsense_web/contracts/types/search-space.types.ts b/surfsense_web/contracts/types/search-space.types.ts index c62b39074..acc0be07a 100644 --- a/surfsense_web/contracts/types/search-space.types.ts +++ b/surfsense_web/contracts/types/search-space.types.ts @@ -81,7 +81,7 @@ export const updateSearchSpaceApiAccessResponse = searchSpace.omit({ export const deleteSearchSpaceRequest = searchSpace.pick({ id: true }); export const deleteSearchSpaceResponse = z.object({ - message: z.literal("Search space deleted successfully"), + message: z.literal("Workspace deleted successfully"), }); /** diff --git a/surfsense_web/hooks/use-search-source-connectors.ts b/surfsense_web/hooks/use-search-source-connectors.ts index c2e9566b4..ba7bb350c 100644 --- a/surfsense_web/hooks/use-search-source-connectors.ts +++ b/surfsense_web/hooks/use-search-source-connectors.ts @@ -108,7 +108,7 @@ export const useSearchSourceConnectors = (lazy: boolean = false, searchSpaceId?: const response = await authenticatedFetch( buildBackendUrl("/api/v1/search-source-connectors", { - search_space_id: spaceId, + workspace_id: spaceId, }), { method: "GET", @@ -167,7 +167,7 @@ export const useSearchSourceConnectors = (lazy: boolean = false, searchSpaceId?: try { const response = await authenticatedFetch( buildBackendUrl("/api/v1/search-source-connectors", { - search_space_id: spaceId, + workspace_id: spaceId, }), { method: "POST", @@ -269,7 +269,7 @@ export const useSearchSourceConnectors = (lazy: boolean = false, searchSpaceId?: try { const response = await authenticatedFetch( buildBackendUrl(`/api/v1/search-source-connectors/${connectorId}/index`, { - search_space_id: searchSpaceId, + workspace_id: searchSpaceId, start_date: startDate, end_date: endDate, }), diff --git a/surfsense_web/lib/apis/agent-permissions-api.service.ts b/surfsense_web/lib/apis/agent-permissions-api.service.ts index 6927c55d0..76bde0087 100644 --- a/surfsense_web/lib/apis/agent-permissions-api.service.ts +++ b/surfsense_web/lib/apis/agent-permissions-api.service.ts @@ -44,7 +44,7 @@ export type AgentPermissionRuleUpdate = z.infer => { return baseApiService.get( - `/api/v1/searchspaces/${searchSpaceId}/agent/permissions/rules`, + `/api/v1/workspaces/${searchSpaceId}/agent/permissions/rules`, AgentPermissionRuleListSchema ); }; @@ -58,7 +58,7 @@ class AgentPermissionsApiService { throw new ValidationError(parsed.error.issues.map((i) => i.message).join(", ")); } return baseApiService.post( - `/api/v1/searchspaces/${searchSpaceId}/agent/permissions/rules`, + `/api/v1/workspaces/${searchSpaceId}/agent/permissions/rules`, AgentPermissionRuleSchema, { body: parsed.data } ); @@ -74,7 +74,7 @@ class AgentPermissionsApiService { throw new ValidationError(parsed.error.issues.map((i) => i.message).join(", ")); } return baseApiService.patch( - `/api/v1/searchspaces/${searchSpaceId}/agent/permissions/rules/${ruleId}`, + `/api/v1/workspaces/${searchSpaceId}/agent/permissions/rules/${ruleId}`, AgentPermissionRuleSchema, { body: parsed.data } ); @@ -82,7 +82,7 @@ class AgentPermissionsApiService { remove = async (searchSpaceId: number, ruleId: number): Promise => { await baseApiService.delete( - `/api/v1/searchspaces/${searchSpaceId}/agent/permissions/rules/${ruleId}` + `/api/v1/workspaces/${searchSpaceId}/agent/permissions/rules/${ruleId}` ); }; } diff --git a/surfsense_web/lib/apis/automations-api.service.ts b/surfsense_web/lib/apis/automations-api.service.ts index baaf08799..f4783d8aa 100644 --- a/surfsense_web/lib/apis/automations-api.service.ts +++ b/surfsense_web/lib/apis/automations-api.service.ts @@ -37,7 +37,7 @@ class AutomationsApiService { listAutomations = async (params: AutomationListParams) => { const qs = new URLSearchParams({ - search_space_id: String(params.search_space_id), + workspace_id: String(params.search_space_id), limit: String(params.limit), offset: String(params.offset), }); @@ -50,7 +50,10 @@ class AutomationsApiService { createAutomation = async (request: AutomationCreateRequest) => { const data = rejectIfInvalid(automationCreateRequest.safeParse(request)); - return baseApiService.post(BASE, automation, { body: data }); + const { search_space_id, ...body } = data; + return baseApiService.post(BASE, automation, { + body: { ...body, workspace_id: search_space_id }, + }); }; updateAutomation = async (automationId: number, request: AutomationUpdateRequest) => { @@ -66,7 +69,7 @@ class AutomationsApiService { // Whether the search space's models are billable for automations (premium // global or BYOK). Used to gate creation surfaces before submit. getModelEligibility = async (searchSpaceId: number) => { - const qs = new URLSearchParams({ search_space_id: String(searchSpaceId) }); + const qs = new URLSearchParams({ workspace_id: String(searchSpaceId) }); return baseApiService.get(`${BASE}/model-eligibility?${qs.toString()}`, modelEligibility); }; diff --git a/surfsense_web/lib/apis/chat-comments-api.service.ts b/surfsense_web/lib/apis/chat-comments-api.service.ts index f1ec7a5d9..8d243f394 100644 --- a/surfsense_web/lib/apis/chat-comments-api.service.ts +++ b/surfsense_web/lib/apis/chat-comments-api.service.ts @@ -140,7 +140,7 @@ class ChatCommentsApiService { const params = new URLSearchParams(); if (parsed.data.search_space_id !== undefined) { - params.set("search_space_id", String(parsed.data.search_space_id)); + params.set("workspace_id", String(parsed.data.search_space_id)); } const queryString = params.toString(); diff --git a/surfsense_web/lib/apis/chat-threads-api.service.ts b/surfsense_web/lib/apis/chat-threads-api.service.ts index 9dcd85761..3941c6a4c 100644 --- a/surfsense_web/lib/apis/chat-threads-api.service.ts +++ b/surfsense_web/lib/apis/chat-threads-api.service.ts @@ -86,7 +86,7 @@ class ChatThreadsApiService { } return baseApiService.get( - `/api/v1/searchspaces/${parsed.data.search_space_id}/snapshots`, + `/api/v1/workspaces/${parsed.data.search_space_id}/snapshots`, publicChatSnapshotsBySpaceResponse ); }; diff --git a/surfsense_web/lib/apis/connectors-api.service.ts b/surfsense_web/lib/apis/connectors-api.service.ts index 4b6d69883..92fc67a8d 100644 --- a/surfsense_web/lib/apis/connectors-api.service.ts +++ b/surfsense_web/lib/apis/connectors-api.service.ts @@ -59,7 +59,7 @@ class ConnectorsApiService { Object.entries(parsedRequest.data.queryParams) .filter(([_, v]) => v !== undefined && v !== null) .map(([k, v]) => { - return [k, String(v)]; + return [k === "search_space_id" ? "workspace_id" : k, String(v)]; }) ) : undefined; @@ -113,7 +113,7 @@ class ConnectorsApiService { Object.entries(queryParams) .filter(([_, v]) => v !== undefined && v !== null) .map(([k, v]) => { - return [k, String(v)]; + return [k === "search_space_id" ? "workspace_id" : k, String(v)]; }) ); @@ -187,7 +187,7 @@ class ConnectorsApiService { Object.entries(queryParams) .filter(([_, v]) => v !== undefined && v !== null) .map(([k, v]) => { - return [k, String(v)]; + return [k === "search_space_id" ? "workspace_id" : k, String(v)]; }) ); @@ -314,7 +314,7 @@ class ConnectorsApiService { const { search_space_id } = request.queryParams; const queryString = new URLSearchParams({ - search_space_id: String(search_space_id), + workspace_id: String(search_space_id), }).toString(); return baseApiService.get(`/api/v1/connectors/mcp?${queryString}`); @@ -334,7 +334,7 @@ class ConnectorsApiService { const { data, queryParams } = request; const queryString = new URLSearchParams({ - search_space_id: String(queryParams.search_space_id), + workspace_id: String(queryParams.search_space_id), }).toString(); return baseApiService.post( diff --git a/surfsense_web/lib/apis/documents-api.service.ts b/surfsense_web/lib/apis/documents-api.service.ts index 5b50db0c1..151b41a53 100644 --- a/surfsense_web/lib/apis/documents-api.service.ts +++ b/surfsense_web/lib/apis/documents-api.service.ts @@ -61,11 +61,12 @@ class DocumentsApiService { const transformedQueryParams = parsedRequest.data.queryParams ? Object.fromEntries( Object.entries(parsedRequest.data.queryParams).map(([k, v]) => { + const key = k === "search_space_id" ? "workspace_id" : k; // Handle array values (document_type) if (Array.isArray(v)) { - return [k, v.join(",")]; + return [key, v.join(",")]; } - return [k, String(v)]; + return [key, String(v)]; }) ) : undefined; @@ -106,8 +107,9 @@ class DocumentsApiService { throw new ValidationError(`Invalid request: ${errorMessage}`); } + const { search_space_id, ...body } = parsedRequest.data; return baseApiService.post(`/api/v1/documents`, createDocumentResponse, { - body: parsedRequest.data, + body: { ...body, workspace_id: search_space_id }, }); }; @@ -143,7 +145,7 @@ class DocumentsApiService { for (const batch of batches) { const formData = new FormData(); for (const file of batch) formData.append("files", file); - formData.append("search_space_id", String(search_space_id)); + formData.append("workspace_id", String(search_space_id)); formData.append("use_vision_llm", String(use_vision_llm)); formData.append("processing_mode", processing_mode); @@ -191,7 +193,7 @@ class DocumentsApiService { const { search_space_id, document_ids } = parsedRequest.data.queryParams; const params = new URLSearchParams({ - search_space_id: String(search_space_id), + workspace_id: String(search_space_id), document_ids: document_ids.join(","), }); @@ -218,11 +220,12 @@ class DocumentsApiService { const transformedQueryParams = parsedRequest.data.queryParams ? Object.fromEntries( Object.entries(parsedRequest.data.queryParams).map(([k, v]) => { + const key = k === "search_space_id" ? "workspace_id" : k; // Handle array values (document_type) if (Array.isArray(v)) { - return [k, v.join(",")]; + return [key, v.join(",")]; } - return [k, String(v)]; + return [key, String(v)]; }) ) : undefined; @@ -254,7 +257,10 @@ class DocumentsApiService { const transformedQueryParams = Object.fromEntries( Object.entries(parsedRequest.data.queryParams) .filter(([, v]) => v !== undefined) - .map(([k, v]) => [k, String(v)]) + .map(([k, v]) => [ + k === "search_space_id" || k === "workspace_id" ? "workspace_id" : k, + String(v), + ]) ); const queryParams = new URLSearchParams(transformedQueryParams).toString(); @@ -268,7 +274,7 @@ class DocumentsApiService { getDocumentByVirtualPath = async (request: { search_space_id: number; virtual_path: string }) => { const params = new URLSearchParams({ - search_space_id: String(request.search_space_id), + workspace_id: String(request.search_space_id), virtual_path: request.virtual_path, }); return baseApiService.get( @@ -295,7 +301,10 @@ class DocumentsApiService { // Transform query params to be string values const transformedQueryParams = parsedRequest.data.queryParams ? Object.fromEntries( - Object.entries(parsedRequest.data.queryParams).map(([k, v]) => [k, String(v)]) + Object.entries(parsedRequest.data.queryParams).map(([k, v]) => [ + k === "search_space_id" ? "workspace_id" : k, + String(v), + ]) ) : undefined; @@ -375,9 +384,10 @@ class DocumentsApiService { } const { id, data } = parsedRequest.data; + const { search_space_id, ...body } = data; return baseApiService.put(`/api/v1/documents/${id}`, updateDocumentResponse, { - body: data, + body: { ...body, workspace_id: search_space_id }, }); }; @@ -406,8 +416,9 @@ class DocumentsApiService { search_space_id: number; files: { relative_path: string; mtime: number }[]; }): Promise<{ files_to_upload: string[] }> => { + const { search_space_id, ...rest } = body; return baseApiService.post(`/api/v1/documents/folder-mtime-check`, undefined, { - body, + body: { ...rest, workspace_id: search_space_id }, }) as unknown as { files_to_upload: string[] }; }; @@ -428,7 +439,7 @@ class DocumentsApiService { formData.append("files", file); } formData.append("folder_name", metadata.folder_name); - formData.append("search_space_id", String(metadata.search_space_id)); + formData.append("workspace_id", String(metadata.search_space_id)); formData.append("relative_paths", JSON.stringify(metadata.relative_paths)); if (metadata.root_folder_id != null) { formData.append("root_folder_id", String(metadata.root_folder_id)); @@ -462,8 +473,9 @@ class DocumentsApiService { root_folder_id: number | null; relative_paths: string[]; }): Promise<{ deleted_count: number }> => { + const { search_space_id, ...rest } = body; return baseApiService.post(`/api/v1/documents/folder-unlink`, undefined, { - body, + body: { ...rest, workspace_id: search_space_id }, }) as unknown as { deleted_count: number }; }; @@ -473,14 +485,15 @@ class DocumentsApiService { root_folder_id: number | null; all_relative_paths: string[]; }): Promise<{ deleted_count: number }> => { + const { search_space_id, ...rest } = body; return baseApiService.post(`/api/v1/documents/folder-sync-finalize`, undefined, { - body, + body: { ...rest, workspace_id: search_space_id }, }) as unknown as { deleted_count: number }; }; getWatchedFolders = async (searchSpaceId: number) => { return baseApiService.get( - `/api/v1/documents/watched-folders?search_space_id=${searchSpaceId}`, + `/api/v1/documents/watched-folders?workspace_id=${searchSpaceId}`, folderListResponse ); }; diff --git a/surfsense_web/lib/apis/folders-api.service.ts b/surfsense_web/lib/apis/folders-api.service.ts index 2e535d615..54e0b41fc 100644 --- a/surfsense_web/lib/apis/folders-api.service.ts +++ b/surfsense_web/lib/apis/folders-api.service.ts @@ -27,14 +27,14 @@ class FoldersApiService { `Invalid request: ${parsed.error.issues.map((i) => i.message).join(", ")}` ); } - return baseApiService.post("/api/v1/folders", folder, { body: parsed.data }); + const { search_space_id, ...body } = parsed.data; + return baseApiService.post("/api/v1/folders", folder, { + body: { ...body, workspace_id: search_space_id }, + }); }; listFolders = async (searchSpaceId: number) => { - return baseApiService.get( - `/api/v1/folders?search_space_id=${searchSpaceId}`, - folderListResponse - ); + return baseApiService.get(`/api/v1/folders?workspace_id=${searchSpaceId}`, folderListResponse); }; getFolder = async (folderId: number) => { diff --git a/surfsense_web/lib/apis/image-generations-api.service.ts b/surfsense_web/lib/apis/image-generations-api.service.ts index 6aa17854d..6f173dafc 100644 --- a/surfsense_web/lib/apis/image-generations-api.service.ts +++ b/surfsense_web/lib/apis/image-generations-api.service.ts @@ -9,7 +9,7 @@ const BASE = "/api/v1/image-generations"; class ImageGenerationsApiService { list = async (searchSpaceId: number, limit = 100) => { const qs = new URLSearchParams({ - search_space_id: String(searchSpaceId), + workspace_id: String(searchSpaceId), limit: String(limit), }).toString(); return baseApiService.get(`${BASE}?${qs}`, imageGenerationList); diff --git a/surfsense_web/lib/apis/invites-api.service.ts b/surfsense_web/lib/apis/invites-api.service.ts index 34889e094..7d86c1639 100644 --- a/surfsense_web/lib/apis/invites-api.service.ts +++ b/surfsense_web/lib/apis/invites-api.service.ts @@ -36,7 +36,7 @@ class InvitesApiService { } return baseApiService.post( - `/api/v1/searchspaces/${parsedRequest.data.search_space_id}/invites`, + `/api/v1/workspaces/${parsedRequest.data.search_space_id}/invites`, createInviteResponse, { body: parsedRequest.data.data, @@ -58,7 +58,7 @@ class InvitesApiService { } return baseApiService.get( - `/api/v1/searchspaces/${parsedRequest.data.search_space_id}/invites`, + `/api/v1/workspaces/${parsedRequest.data.search_space_id}/invites`, getInvitesResponse ); }; @@ -77,7 +77,7 @@ class InvitesApiService { } return baseApiService.put( - `/api/v1/searchspaces/${parsedRequest.data.search_space_id}/invites/${parsedRequest.data.invite_id}`, + `/api/v1/workspaces/${parsedRequest.data.search_space_id}/invites/${parsedRequest.data.invite_id}`, updateInviteResponse, { body: parsedRequest.data.data, @@ -99,7 +99,7 @@ class InvitesApiService { } return baseApiService.delete( - `/api/v1/searchspaces/${parsedRequest.data.search_space_id}/invites/${parsedRequest.data.invite_id}`, + `/api/v1/workspaces/${parsedRequest.data.search_space_id}/invites/${parsedRequest.data.invite_id}`, deleteInviteResponse ); }; diff --git a/surfsense_web/lib/apis/logs-api.service.ts b/surfsense_web/lib/apis/logs-api.service.ts index 700089354..17355100f 100644 --- a/surfsense_web/lib/apis/logs-api.service.ts +++ b/surfsense_web/lib/apis/logs-api.service.ts @@ -36,11 +36,12 @@ class LogsApiService { const transformedQueryParams = parsedRequest.data.queryParams ? Object.fromEntries( Object.entries(parsedRequest.data.queryParams).map(([k, v]) => { + const key = k === "search_space_id" ? "workspace_id" : k; // Handle array values (document_type) if (Array.isArray(v)) { - return [k, v.join(",")]; + return [key, v.join(",")]; } - return [k, String(v)]; + return [key, String(v)]; }) ) : undefined; @@ -74,8 +75,9 @@ class LogsApiService { const errorMessage = parsedRequest.error.issues.map((issue) => issue.message).join(", "); throw new ValidationError(`Invalid request: ${errorMessage}`); } + const { search_space_id, ...body } = parsedRequest.data; return baseApiService.post(`/api/v1/logs`, createLogResponse, { - body: parsedRequest.data, + body: { ...body, workspace_id: search_space_id }, }); }; @@ -89,8 +91,9 @@ class LogsApiService { const errorMessage = parsedRequest.error.issues.map((issue) => issue.message).join(", "); throw new ValidationError(`Invalid request: ${errorMessage}`); } + const { search_space_id, ...body } = parsedRequest.data; return baseApiService.put(`/api/v1/logs/${logId}`, updateLogResponse, { - body: parsedRequest.data, + body: search_space_id === undefined ? body : { ...body, workspace_id: search_space_id }, }); }; @@ -118,7 +121,7 @@ class LogsApiService { throw new ValidationError(`Invalid request: ${errorMessage}`); } const { search_space_id, hours } = parsedRequest.data; - const url = `/api/v1/logs/search-space/${search_space_id}/summary${hours ? `?hours=${hours}` : ""}`; + const url = `/api/v1/logs/workspaces/${search_space_id}/summary${hours ? `?hours=${hours}` : ""}`; return baseApiService.get(url, getLogSummaryResponse); }; } diff --git a/surfsense_web/lib/apis/members-api.service.ts b/surfsense_web/lib/apis/members-api.service.ts index 8d0d19663..9b4017149 100644 --- a/surfsense_web/lib/apis/members-api.service.ts +++ b/surfsense_web/lib/apis/members-api.service.ts @@ -33,7 +33,7 @@ class MembersApiService { } return baseApiService.get( - `/api/v1/searchspaces/${parsedRequest.data.search_space_id}/members`, + `/api/v1/workspaces/${parsedRequest.data.search_space_id}/members`, getMembersResponse ); }; @@ -52,7 +52,7 @@ class MembersApiService { } return baseApiService.put( - `/api/v1/searchspaces/${parsedRequest.data.search_space_id}/members/${parsedRequest.data.membership_id}`, + `/api/v1/workspaces/${parsedRequest.data.search_space_id}/members/${parsedRequest.data.membership_id}`, updateMembershipResponse, { body: parsedRequest.data.data, @@ -74,7 +74,7 @@ class MembersApiService { } return baseApiService.delete( - `/api/v1/searchspaces/${parsedRequest.data.search_space_id}/members/${parsedRequest.data.membership_id}`, + `/api/v1/workspaces/${parsedRequest.data.search_space_id}/members/${parsedRequest.data.membership_id}`, deleteMembershipResponse ); }; @@ -93,7 +93,7 @@ class MembersApiService { } return baseApiService.delete( - `/api/v1/searchspaces/${parsedRequest.data.search_space_id}/members/me`, + `/api/v1/workspaces/${parsedRequest.data.search_space_id}/members/me`, leaveSearchSpaceResponse ); }; @@ -112,7 +112,7 @@ class MembersApiService { } return baseApiService.get( - `/api/v1/searchspaces/${parsedRequest.data.search_space_id}/my-access`, + `/api/v1/workspaces/${parsedRequest.data.search_space_id}/my-access`, getMyAccessResponse ); }; diff --git a/surfsense_web/lib/apis/model-connections-api.service.ts b/surfsense_web/lib/apis/model-connections-api.service.ts index c69bcbef2..d8c6099ff 100644 --- a/surfsense_web/lib/apis/model-connections-api.service.ts +++ b/surfsense_web/lib/apis/model-connections-api.service.ts @@ -46,7 +46,7 @@ class ModelConnectionsApiService { getConnections = async (searchSpaceId: number): Promise => { return baseApiService.get( - `/api/v1/model-connections?search_space_id=${searchSpaceId}`, + `/api/v1/model-connections?workspace_id=${searchSpaceId}`, connectionListResponse ); }; @@ -56,8 +56,15 @@ class ModelConnectionsApiService { if (!parsed.success) { throw new ValidationError(parsed.error.issues.map((issue) => issue.message).join(", ")); } + const { search_space_id, ...body } = parsed.data; + if ( + body.scope === "SEARCH_SPACE" && + (!Number.isFinite(search_space_id) || (search_space_id ?? 0) <= 0) + ) { + throw new ValidationError("workspace_id is required"); + } return baseApiService.post(`/api/v1/model-connections`, connectionRead, { - body: parsed.data, + body: { ...body, workspace_id: search_space_id }, }); }; @@ -91,11 +98,18 @@ class ModelConnectionsApiService { if (!parsed.success) { throw new ValidationError(parsed.error.issues.map((issue) => issue.message).join(", ")); } + const { search_space_id, ...body } = parsed.data; + if ( + body.scope === "SEARCH_SPACE" && + (!Number.isFinite(search_space_id) || (search_space_id ?? 0) <= 0) + ) { + throw new ValidationError("workspace_id is required"); + } return baseApiService.post( `/api/v1/model-connections/discover-preview`, modelPreviewListResponse, { - body: parsed.data, + body: { ...body, workspace_id: search_space_id }, } ); }; @@ -107,8 +121,15 @@ class ModelConnectionsApiService { if (!parsed.success) { throw new ValidationError(parsed.error.issues.map((issue) => issue.message).join(", ")); } + const { search_space_id, ...body } = parsed.data; + if ( + body.scope === "SEARCH_SPACE" && + (!Number.isFinite(search_space_id) || (search_space_id ?? 0) <= 0) + ) { + throw new ValidationError("workspace_id is required"); + } return baseApiService.post(`/api/v1/model-connections/test-preview`, verifyConnectionResponse, { - body: parsed.data, + body: { ...body, workspace_id: search_space_id }, }); }; @@ -159,11 +180,11 @@ class ModelConnectionsApiService { }; getModelRoles = async (searchSpaceId: number): Promise => { - return baseApiService.get(`/api/v1/search-spaces/${searchSpaceId}/model-roles`, modelRoles); + return baseApiService.get(`/api/v1/workspaces/${searchSpaceId}/model-roles`, modelRoles); }; updateModelRoles = async (searchSpaceId: number, roles: ModelRoles): Promise => { - return baseApiService.put(`/api/v1/search-spaces/${searchSpaceId}/model-roles`, modelRoles, { + return baseApiService.put(`/api/v1/workspaces/${searchSpaceId}/model-roles`, modelRoles, { body: roles, }); }; diff --git a/surfsense_web/lib/apis/notifications-api.service.ts b/surfsense_web/lib/apis/notifications-api.service.ts index bec28df29..6cb9c2147 100644 --- a/surfsense_web/lib/apis/notifications-api.service.ts +++ b/surfsense_web/lib/apis/notifications-api.service.ts @@ -42,7 +42,7 @@ class NotificationsApiService { const params = new URLSearchParams(); if (queryParams.search_space_id !== undefined) { - params.append("search_space_id", String(queryParams.search_space_id)); + params.append("workspace_id", String(queryParams.search_space_id)); } if (queryParams.type) { params.append("type", queryParams.type); @@ -113,7 +113,7 @@ class NotificationsApiService { getSourceTypes = async (searchSpaceId?: number): Promise => { const params = new URLSearchParams(); if (searchSpaceId !== undefined) { - params.append("search_space_id", String(searchSpaceId)); + params.append("workspace_id", String(searchSpaceId)); } const queryString = params.toString(); @@ -136,7 +136,7 @@ class NotificationsApiService { ): Promise => { const params = new URLSearchParams(); if (searchSpaceId !== undefined) { - params.append("search_space_id", String(searchSpaceId)); + params.append("workspace_id", String(searchSpaceId)); } if (type) { params.append("type", type); @@ -159,7 +159,7 @@ class NotificationsApiService { getBatchUnreadCounts = async (searchSpaceId?: number): Promise => { const params = new URLSearchParams(); if (searchSpaceId !== undefined) { - params.append("search_space_id", String(searchSpaceId)); + params.append("workspace_id", String(searchSpaceId)); } const queryString = params.toString(); diff --git a/surfsense_web/lib/apis/podcasts-api.service.ts b/surfsense_web/lib/apis/podcasts-api.service.ts index 3a18c7951..1108c8ca3 100644 --- a/surfsense_web/lib/apis/podcasts-api.service.ts +++ b/surfsense_web/lib/apis/podcasts-api.service.ts @@ -17,7 +17,7 @@ const voiceOptionList = z.array(voiceOption); class PodcastsApiService { list = async (searchSpaceId: number, limit = 200) => { const qs = new URLSearchParams({ - search_space_id: String(searchSpaceId), + workspace_id: String(searchSpaceId), limit: String(limit), }).toString(); return baseApiService.get(`${BASE}?${qs}`, podcastSummaryList); diff --git a/surfsense_web/lib/apis/prompts-api.service.ts b/surfsense_web/lib/apis/prompts-api.service.ts index 38c2ffb4e..359e920b8 100644 --- a/surfsense_web/lib/apis/prompts-api.service.ts +++ b/surfsense_web/lib/apis/prompts-api.service.ts @@ -15,7 +15,7 @@ class PromptsApiService { list = async (searchSpaceId?: number) => { const params = new URLSearchParams(); if (searchSpaceId !== undefined) { - params.set("search_space_id", String(searchSpaceId)); + params.set("workspace_id", String(searchSpaceId)); } const queryString = params.toString(); const url = queryString ? `/api/v1/prompts?${queryString}` : "/api/v1/prompts"; @@ -30,8 +30,9 @@ class PromptsApiService { throw new ValidationError(`Invalid request: ${errorMessage}`); } + const { search_space_id, ...body } = parsed.data; return baseApiService.post("/api/v1/prompts", promptRead, { - body: parsed.data, + body: { ...body, workspace_id: search_space_id }, }); }; diff --git a/surfsense_web/lib/apis/reports-api.service.ts b/surfsense_web/lib/apis/reports-api.service.ts index bc4483f37..e795e7f66 100644 --- a/surfsense_web/lib/apis/reports-api.service.ts +++ b/surfsense_web/lib/apis/reports-api.service.ts @@ -6,7 +6,7 @@ const BASE = "/api/v1/reports"; class ReportsApiService { list = async (searchSpaceId: number, limit = 200) => { const qs = new URLSearchParams({ - search_space_id: String(searchSpaceId), + workspace_id: String(searchSpaceId), limit: String(limit), }).toString(); return baseApiService.get(`${BASE}?${qs}`, reportList); diff --git a/surfsense_web/lib/apis/roles-api.service.ts b/surfsense_web/lib/apis/roles-api.service.ts index 10023799b..6d08f7dff 100644 --- a/surfsense_web/lib/apis/roles-api.service.ts +++ b/surfsense_web/lib/apis/roles-api.service.ts @@ -30,7 +30,7 @@ class RolesApiService { } return baseApiService.post( - `/api/v1/searchspaces/${parsedRequest.data.search_space_id}/roles`, + `/api/v1/workspaces/${parsedRequest.data.search_space_id}/roles`, createRoleResponse, { body: parsedRequest.data.data, @@ -49,7 +49,7 @@ class RolesApiService { } return baseApiService.get( - `/api/v1/searchspaces/${parsedRequest.data.search_space_id}/roles`, + `/api/v1/workspaces/${parsedRequest.data.search_space_id}/roles`, getRolesResponse ); }; @@ -65,7 +65,7 @@ class RolesApiService { } return baseApiService.get( - `/api/v1/searchspaces/${parsedRequest.data.search_space_id}/roles/${parsedRequest.data.role_id}`, + `/api/v1/workspaces/${parsedRequest.data.search_space_id}/roles/${parsedRequest.data.role_id}`, getRoleByIdResponse ); }; @@ -81,7 +81,7 @@ class RolesApiService { } return baseApiService.put( - `/api/v1/searchspaces/${parsedRequest.data.search_space_id}/roles/${parsedRequest.data.role_id}`, + `/api/v1/workspaces/${parsedRequest.data.search_space_id}/roles/${parsedRequest.data.role_id}`, updateRoleResponse, { body: parsedRequest.data.data, @@ -100,7 +100,7 @@ class RolesApiService { } return baseApiService.delete( - `/api/v1/searchspaces/${parsedRequest.data.search_space_id}/roles/${parsedRequest.data.role_id}`, + `/api/v1/workspaces/${parsedRequest.data.search_space_id}/roles/${parsedRequest.data.role_id}`, deleteRoleResponse ); }; diff --git a/surfsense_web/lib/apis/search-spaces-api.service.ts b/surfsense_web/lib/apis/search-spaces-api.service.ts index 7f98399bd..3477a70f4 100644 --- a/surfsense_web/lib/apis/search-spaces-api.service.ts +++ b/surfsense_web/lib/apis/search-spaces-api.service.ts @@ -50,7 +50,7 @@ class SearchSpacesApiService { ? new URLSearchParams(transformedQueryParams).toString() : ""; - return baseApiService.get(`/api/v1/searchspaces?${queryParams}`, getSearchSpacesResponse); + return baseApiService.get(`/api/v1/workspaces?${queryParams}`, getSearchSpacesResponse); }; /** @@ -66,7 +66,7 @@ class SearchSpacesApiService { throw new ValidationError(`Invalid request: ${errorMessage}`); } - return baseApiService.post(`/api/v1/searchspaces`, createSearchSpaceResponse, { + return baseApiService.post(`/api/v1/workspaces`, createSearchSpaceResponse, { body: parsedRequest.data, }); }; @@ -84,7 +84,7 @@ class SearchSpacesApiService { throw new ValidationError(`Invalid request: ${errorMessage}`); } - return baseApiService.get(`/api/v1/searchspaces/${request.id}`, getSearchSpaceResponse); + return baseApiService.get(`/api/v1/workspaces/${request.id}`, getSearchSpaceResponse); }; /** @@ -100,7 +100,7 @@ class SearchSpacesApiService { throw new ValidationError(`Invalid request: ${errorMessage}`); } - return baseApiService.put(`/api/v1/searchspaces/${request.id}`, updateSearchSpaceResponse, { + return baseApiService.put(`/api/v1/workspaces/${request.id}`, updateSearchSpaceResponse, { body: parsedRequest.data.data, }); }; @@ -115,7 +115,7 @@ class SearchSpacesApiService { } return baseApiService.put( - `/api/v1/searchspaces/${request.id}/api-access`, + `/api/v1/workspaces/${request.id}/api-access`, updateSearchSpaceApiAccessResponse, { body: { api_access_enabled: parsedRequest.data.api_access_enabled }, @@ -136,7 +136,7 @@ class SearchSpacesApiService { throw new ValidationError(`Invalid request: ${errorMessage}`); } - return baseApiService.delete(`/api/v1/searchspaces/${request.id}`, deleteSearchSpaceResponse); + return baseApiService.delete(`/api/v1/workspaces/${request.id}`, deleteSearchSpaceResponse); }; /** @@ -144,7 +144,7 @@ class SearchSpacesApiService { */ triggerAiSort = async (searchSpaceId: number) => { return baseApiService.post( - `/api/v1/searchspaces/${searchSpaceId}/ai-sort`, + `/api/v1/workspaces/${searchSpaceId}/ai-sort`, z.object({ message: z.string() }), {} ); @@ -156,7 +156,7 @@ class SearchSpacesApiService { */ leaveSearchSpace = async (searchSpaceId: number) => { return baseApiService.delete( - `/api/v1/searchspaces/${searchSpaceId}/members/me`, + `/api/v1/workspaces/${searchSpaceId}/members/me`, leaveSearchSpaceResponse ); }; diff --git a/surfsense_web/lib/apis/stripe-api.service.ts b/surfsense_web/lib/apis/stripe-api.service.ts index b2f5698fb..cc91f6c38 100644 --- a/surfsense_web/lib/apis/stripe-api.service.ts +++ b/surfsense_web/lib/apis/stripe-api.service.ts @@ -23,10 +23,11 @@ class StripeApiService { createCreditCheckoutSession = async ( request: CreateCreditCheckoutSessionRequest ): Promise => { + const { search_space_id, ...body } = request; return baseApiService.post( "/api/v1/stripe/create-credit-checkout-session", createCreditCheckoutSessionResponse, - { body: request } + { body: { ...body, workspace_id: search_space_id } } ); }; @@ -74,10 +75,11 @@ class StripeApiService { createAutoReloadSetupSession = async ( request: CreateAutoReloadSetupSessionRequest ): Promise => { + const { search_space_id } = request; return baseApiService.post( "/api/v1/stripe/auto-reload/setup", createAutoReloadSetupSessionResponse, - { body: request } + { body: { workspace_id: search_space_id } } ); }; } diff --git a/surfsense_web/lib/apis/video-presentations-api.service.ts b/surfsense_web/lib/apis/video-presentations-api.service.ts index ef3ac21ed..4c8da510d 100644 --- a/surfsense_web/lib/apis/video-presentations-api.service.ts +++ b/surfsense_web/lib/apis/video-presentations-api.service.ts @@ -6,7 +6,7 @@ const BASE = "/api/v1/video-presentations"; class VideoPresentationsApiService { list = async (searchSpaceId: number, limit = 200) => { const qs = new URLSearchParams({ - search_space_id: String(searchSpaceId), + workspace_id: String(searchSpaceId), limit: String(limit), }).toString(); return baseApiService.get(`${BASE}?${qs}`, videoPresentationList); diff --git a/surfsense_web/lib/chat/thread-persistence.ts b/surfsense_web/lib/chat/thread-persistence.ts index dc5846f23..f3e808803 100644 --- a/surfsense_web/lib/chat/thread-persistence.ts +++ b/surfsense_web/lib/chat/thread-persistence.ts @@ -95,7 +95,7 @@ export async function fetchThreads( searchSpaceId: number, limit?: number ): Promise { - const params = new URLSearchParams({ search_space_id: String(searchSpaceId) }); + const params = new URLSearchParams({ workspace_id: String(searchSpaceId) }); if (limit) params.append("limit", String(limit)); return baseApiService.get(`/api/v1/threads?${params}`); } @@ -108,7 +108,7 @@ export async function searchThreads( title: string ): Promise { const params = new URLSearchParams({ - search_space_id: String(searchSpaceId), + workspace_id: String(searchSpaceId), title, }); return baseApiService.get(`/api/v1/threads/search?${params}`); @@ -125,7 +125,7 @@ export async function createThread( body: { title, archived: false, - search_space_id: searchSpaceId, + workspace_id: searchSpaceId, }, }); } diff --git a/surfsense_web/lib/route-params.ts b/surfsense_web/lib/route-params.ts new file mode 100644 index 000000000..02e5a8194 --- /dev/null +++ b/surfsense_web/lib/route-params.ts @@ -0,0 +1,11 @@ +type RouteParams = Record; + +export function getWorkspaceIdParam(params: RouteParams | null | undefined): string | undefined { + const value = params?.workspace_id ?? params?.search_space_id; + return Array.isArray(value) ? value[0] : value; +} + +export function getWorkspaceIdNumber(params: RouteParams | null | undefined): number | undefined { + const parsed = Number(getWorkspaceIdParam(params)); + return Number.isFinite(parsed) && parsed > 0 ? parsed : undefined; +} diff --git a/surfsense_web/tests/fixtures/chat-thread.fixture.ts b/surfsense_web/tests/fixtures/chat-thread.fixture.ts index 81cf3b5c3..52eebf4a3 100644 --- a/surfsense_web/tests/fixtures/chat-thread.fixture.ts +++ b/surfsense_web/tests/fixtures/chat-thread.fixture.ts @@ -29,7 +29,7 @@ export const chatThreadFixtures = { headers: authHeaders(apiToken), data: { title: "e2e-drive-journey", - search_space_id: searchSpace.id, + workspace_id: searchSpace.id, visibility: "PRIVATE", }, }); diff --git a/surfsense_web/tests/helpers/api/chat.ts b/surfsense_web/tests/helpers/api/chat.ts index f5e1f92bb..ba01e2928 100644 --- a/surfsense_web/tests/helpers/api/chat.ts +++ b/surfsense_web/tests/helpers/api/chat.ts @@ -24,7 +24,7 @@ export async function streamChatToCompletion( headers: authHeaders(token), data: { chat_id: args.threadId, - search_space_id: args.searchSpaceId, + workspace_id: args.searchSpaceId, user_query: args.query, }, }); diff --git a/surfsense_web/tests/helpers/api/connectors.ts b/surfsense_web/tests/helpers/api/connectors.ts index 3d9b2ee59..1730fee67 100644 --- a/surfsense_web/tests/helpers/api/connectors.ts +++ b/surfsense_web/tests/helpers/api/connectors.ts @@ -16,7 +16,7 @@ export async function listConnectors( searchSpaceId: number ): Promise { const response = await request.get( - `${BACKEND_URL}/api/v1/search-source-connectors?search_space_id=${searchSpaceId}`, + `${BACKEND_URL}/api/v1/search-source-connectors?workspace_id=${searchSpaceId}`, { headers: authHeaders(token) } ); if (!response.ok()) { @@ -115,7 +115,7 @@ export async function triggerIndex( body: IndexBody ): Promise<{ ok: true }> { const response = await request.post( - `${BACKEND_URL}/api/v1/search-source-connectors/${connectorId}/index?search_space_id=${searchSpaceId}`, + `${BACKEND_URL}/api/v1/search-source-connectors/${connectorId}/index?workspace_id=${searchSpaceId}`, { headers: authHeaders(token), data: body } ); if (!response.ok()) { @@ -134,7 +134,7 @@ export async function triggerIndexExpectDisabled( body: IndexBody = {} ): Promise<{ indexing_started: false; message?: string }> { const response = await request.post( - `${BACKEND_URL}/api/v1/search-source-connectors/${connectorId}/index?search_space_id=${searchSpaceId}`, + `${BACKEND_URL}/api/v1/search-source-connectors/${connectorId}/index?workspace_id=${searchSpaceId}`, { headers: authHeaders(token), data: body } ); if (!response.ok()) { diff --git a/surfsense_web/tests/helpers/api/documents.ts b/surfsense_web/tests/helpers/api/documents.ts index 9658feead..48b8c297e 100644 --- a/surfsense_web/tests/helpers/api/documents.ts +++ b/surfsense_web/tests/helpers/api/documents.ts @@ -21,7 +21,7 @@ export async function listDocuments( limit = 100 ): Promise { const response = await request.get( - `${BACKEND_URL}/api/v1/documents?search_space_id=${searchSpaceId}&limit=${limit}`, + `${BACKEND_URL}/api/v1/documents?workspace_id=${searchSpaceId}&limit=${limit}`, { headers: authHeaders(token) } ); if (!response.ok()) { diff --git a/surfsense_web/tests/helpers/api/search-spaces.ts b/surfsense_web/tests/helpers/api/search-spaces.ts index 06274790e..a6f251c69 100644 --- a/surfsense_web/tests/helpers/api/search-spaces.ts +++ b/surfsense_web/tests/helpers/api/search-spaces.ts @@ -13,7 +13,7 @@ export async function createSearchSpace( name: string, description = "E2E test search space" ): Promise { - const response = await request.post(`${BACKEND_URL}/api/v1/searchspaces`, { + const response = await request.post(`${BACKEND_URL}/api/v1/workspaces`, { headers: authHeaders(token), data: { name, description }, }); @@ -28,7 +28,7 @@ export async function deleteSearchSpace( token: string, id: number ): Promise { - const response = await request.delete(`${BACKEND_URL}/api/v1/searchspaces/${id}`, { + const response = await request.delete(`${BACKEND_URL}/api/v1/workspaces/${id}`, { headers: authHeaders(token), }); if (!response.ok() && response.status() !== 404) { diff --git a/surfsense_web/tests/smoke/chat-stream.spec.ts b/surfsense_web/tests/smoke/chat-stream.spec.ts index 20fe67f43..8d69163a9 100644 --- a/surfsense_web/tests/smoke/chat-stream.spec.ts +++ b/surfsense_web/tests/smoke/chat-stream.spec.ts @@ -12,7 +12,7 @@ test.describe("Smoke", () => { headers: authHeaders(apiToken), data: { title: "e2e-chat-stream-smoke", - search_space_id: searchSpace.id, + workspace_id: searchSpace.id, visibility: "PRIVATE", }, }); diff --git a/surfsense_web/zero/schema/automations.ts b/surfsense_web/zero/schema/automations.ts index f9b89c533..d161731d9 100644 --- a/surfsense_web/zero/schema/automations.ts +++ b/surfsense_web/zero/schema/automations.ts @@ -3,7 +3,7 @@ import { json, number, string, table } from "@rocicorp/zero"; export const automationTable = table("automations") .columns({ id: number(), - searchSpaceId: number().from("search_space_id"), + searchSpaceId: number().from("workspace_id"), }) .primaryKey("id"); diff --git a/surfsense_web/zero/schema/chat.ts b/surfsense_web/zero/schema/chat.ts index 07229ac94..60d8e5c45 100644 --- a/surfsense_web/zero/schema/chat.ts +++ b/surfsense_web/zero/schema/chat.ts @@ -23,7 +23,7 @@ export const newChatMessageTable = table("new_chat_messages") export const newChatThreadTable = table("new_chat_threads") .columns({ id: number(), - searchSpaceId: number().from("search_space_id"), + searchSpaceId: number().from("workspace_id"), }) .primaryKey("id"); diff --git a/surfsense_web/zero/schema/documents.ts b/surfsense_web/zero/schema/documents.ts index 988056297..1065cd2c3 100644 --- a/surfsense_web/zero/schema/documents.ts +++ b/surfsense_web/zero/schema/documents.ts @@ -5,7 +5,7 @@ export const documentTable = table("documents") id: number(), title: string(), documentType: string().from("document_type"), - searchSpaceId: number().from("search_space_id"), + searchSpaceId: number().from("workspace_id"), folderId: number().optional().from("folder_id"), createdById: string().optional().from("created_by_id"), status: json(), @@ -24,7 +24,7 @@ export const searchSourceConnectorTable = table("search_source_connectors") periodicIndexingEnabled: boolean().from("periodic_indexing_enabled"), indexingFrequencyMinutes: number().optional().from("indexing_frequency_minutes"), nextScheduledAt: number().optional().from("next_scheduled_at"), - searchSpaceId: number().from("search_space_id"), + searchSpaceId: number().from("workspace_id"), userId: string().from("user_id"), createdAt: number().from("created_at"), }) diff --git a/surfsense_web/zero/schema/folders.ts b/surfsense_web/zero/schema/folders.ts index c5b192942..a2428da7a 100644 --- a/surfsense_web/zero/schema/folders.ts +++ b/surfsense_web/zero/schema/folders.ts @@ -6,7 +6,7 @@ export const folderTable = table("folders") name: string(), position: string(), parentId: number().optional().from("parent_id"), - searchSpaceId: number().from("search_space_id"), + searchSpaceId: number().from("workspace_id"), createdById: string().optional().from("created_by_id"), createdAt: number().from("created_at"), updatedAt: number().from("updated_at"), diff --git a/surfsense_web/zero/schema/inbox.ts b/surfsense_web/zero/schema/inbox.ts index 946180ba4..15991ed20 100644 --- a/surfsense_web/zero/schema/inbox.ts +++ b/surfsense_web/zero/schema/inbox.ts @@ -4,7 +4,7 @@ export const notificationTable = table("notifications") .columns({ id: number(), userId: string().from("user_id"), - searchSpaceId: number().optional().from("search_space_id"), + searchSpaceId: number().optional().from("workspace_id"), type: string(), title: string(), message: string(), diff --git a/surfsense_web/zero/schema/podcasts.ts b/surfsense_web/zero/schema/podcasts.ts index d473d776f..1b29d3161 100644 --- a/surfsense_web/zero/schema/podcasts.ts +++ b/surfsense_web/zero/schema/podcasts.ts @@ -12,7 +12,7 @@ export const podcastTable = table("podcasts") specVersion: number().from("spec_version"), durationSeconds: number().optional().from("duration_seconds"), error: string().optional(), - searchSpaceId: number().from("search_space_id"), + searchSpaceId: number().from("workspace_id"), threadId: number().optional().from("thread_id"), createdAt: number().from("created_at"), })