mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-07-10 22:32:16 +02:00
feat(rename): complete searchSpace to workspace transition across frontend and backend
- Introduced a comprehensive specification for renaming `searchSpace` to `workspace` in `surfsense_web` and `surfsense_desktop`, ensuring all TypeScript identifiers, React props, and local data structures are updated. - Implemented migration shims for persisted local state to prevent data loss during the transition. - Updated observability metrics and IPC channels to reflect the new naming convention. - Removed legacy `active-search-space` module and replaced it with `active-workspace` to maintain consistency. - Ensured no behavioral changes or data loss for users during the renaming process.
This commit is contained in:
parent
2a020629c5
commit
a8c1fb660d
259 changed files with 5480 additions and 2285 deletions
|
|
@ -27,7 +27,7 @@ export function getClientPlatform(): ClientPlatform {
|
|||
}
|
||||
|
||||
export async function getAgentFilesystemSelection(
|
||||
searchSpaceId?: number | null,
|
||||
workspaceId?: number | null,
|
||||
options?: AgentFilesystemSelectionOptions
|
||||
): Promise<AgentFilesystemSelection> {
|
||||
const platform = getClientPlatform();
|
||||
|
|
@ -39,9 +39,9 @@ export async function getAgentFilesystemSelection(
|
|||
return { ...DEFAULT_SELECTION, client_platform: platform };
|
||||
}
|
||||
try {
|
||||
const settings = await window.electronAPI.getAgentFilesystemSettings(searchSpaceId);
|
||||
const settings = await window.electronAPI.getAgentFilesystemSettings(workspaceId);
|
||||
if (settings.mode === "desktop_local_folder") {
|
||||
const mounts = await window.electronAPI.getAgentFilesystemMounts?.(searchSpaceId);
|
||||
const mounts = await window.electronAPI.getAgentFilesystemMounts?.(workspaceId);
|
||||
const localFilesystemMounts =
|
||||
mounts?.map((entry) => ({
|
||||
mount_id: entry.mount,
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ export const announcements: Announcement[] = [
|
|||
// id: "2026-02-10-podcast-improvements",
|
||||
// title: "Podcast Generation Improvements",
|
||||
// description:
|
||||
// "We've improved podcast generation with faster processing, better audio quality, and support for longer documents. Try it out in any search space.",
|
||||
// "We've improved podcast generation with faster processing, better audio quality, and support for longer documents. Try it out in any workspace.",
|
||||
// category: "update",
|
||||
// date: "2026-02-10T00:00:00Z",
|
||||
// startTime: "2026-02-10T00:00:00Z",
|
||||
|
|
@ -120,7 +120,7 @@ export const announcements: Announcement[] = [
|
|||
// id: "2026-01-28-team-collaboration",
|
||||
// title: "Enhanced Team Collaboration",
|
||||
// description:
|
||||
// "Shared search spaces now support real-time mentions, comment threads, and role-based access control. Invite your team and collaborate more effectively.",
|
||||
// "Shared workspaces now support real-time mentions, comment threads, and role-based access control. Invite your team and collaborate more effectively.",
|
||||
// category: "feature",
|
||||
// date: "2026-01-28T00:00:00Z",
|
||||
// startTime: "2026-01-28T00:00:00Z",
|
||||
|
|
|
|||
|
|
@ -42,15 +42,15 @@ const AgentPermissionRuleUpdateSchema = z.object({
|
|||
export type AgentPermissionRuleUpdate = z.infer<typeof AgentPermissionRuleUpdateSchema>;
|
||||
|
||||
class AgentPermissionsApiService {
|
||||
list = async (searchSpaceId: number): Promise<AgentPermissionRule[]> => {
|
||||
list = async (workspaceId: number): Promise<AgentPermissionRule[]> => {
|
||||
return baseApiService.get(
|
||||
`/api/v1/workspaces/${searchSpaceId}/agent/permissions/rules`,
|
||||
`/api/v1/workspaces/${workspaceId}/agent/permissions/rules`,
|
||||
AgentPermissionRuleListSchema
|
||||
);
|
||||
};
|
||||
|
||||
create = async (
|
||||
searchSpaceId: number,
|
||||
workspaceId: number,
|
||||
payload: AgentPermissionRuleCreate
|
||||
): Promise<AgentPermissionRule> => {
|
||||
const parsed = AgentPermissionRuleCreateSchema.safeParse(payload);
|
||||
|
|
@ -58,14 +58,14 @@ class AgentPermissionsApiService {
|
|||
throw new ValidationError(parsed.error.issues.map((i) => i.message).join(", "));
|
||||
}
|
||||
return baseApiService.post(
|
||||
`/api/v1/workspaces/${searchSpaceId}/agent/permissions/rules`,
|
||||
`/api/v1/workspaces/${workspaceId}/agent/permissions/rules`,
|
||||
AgentPermissionRuleSchema,
|
||||
{ body: parsed.data }
|
||||
);
|
||||
};
|
||||
|
||||
update = async (
|
||||
searchSpaceId: number,
|
||||
workspaceId: number,
|
||||
ruleId: number,
|
||||
payload: AgentPermissionRuleUpdate
|
||||
): Promise<AgentPermissionRule> => {
|
||||
|
|
@ -74,15 +74,15 @@ class AgentPermissionsApiService {
|
|||
throw new ValidationError(parsed.error.issues.map((i) => i.message).join(", "));
|
||||
}
|
||||
return baseApiService.patch(
|
||||
`/api/v1/workspaces/${searchSpaceId}/agent/permissions/rules/${ruleId}`,
|
||||
`/api/v1/workspaces/${workspaceId}/agent/permissions/rules/${ruleId}`,
|
||||
AgentPermissionRuleSchema,
|
||||
{ body: parsed.data }
|
||||
);
|
||||
};
|
||||
|
||||
remove = async (searchSpaceId: number, ruleId: number): Promise<void> => {
|
||||
remove = async (workspaceId: number, ruleId: number): Promise<void> => {
|
||||
await baseApiService.delete(
|
||||
`/api/v1/workspaces/${searchSpaceId}/agent/permissions/rules/${ruleId}`
|
||||
`/api/v1/workspaces/${workspaceId}/agent/permissions/rules/${ruleId}`
|
||||
);
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -68,8 +68,8 @@ class AutomationsApiService {
|
|||
|
||||
// Whether the workspace'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({ workspace_id: String(searchSpaceId) });
|
||||
getModelEligibility = async (workspaceId: number) => {
|
||||
const qs = new URLSearchParams({ workspace_id: String(workspaceId) });
|
||||
return baseApiService.get(`${BASE}/model-eligibility?${qs.toString()}`, modelEligibility);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ class ChatThreadsApiService {
|
|||
/**
|
||||
* List all public chat snapshots for a workspace.
|
||||
*/
|
||||
listPublicChatSnapshotsForSearchSpace = async (
|
||||
listPublicChatSnapshotsForWorkspace = async (
|
||||
request: PublicChatSnapshotsBySpaceRequest
|
||||
): Promise<PublicChatSnapshotsBySpaceResponse> => {
|
||||
const parsed = publicChatSnapshotsBySpaceRequest.safeParse(request);
|
||||
|
|
|
|||
|
|
@ -257,10 +257,7 @@ 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, String(v)])
|
||||
);
|
||||
|
||||
const queryParams = new URLSearchParams(transformedQueryParams).toString();
|
||||
|
|
@ -301,10 +298,7 @@ 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, String(v)])
|
||||
)
|
||||
: undefined;
|
||||
|
||||
|
|
@ -491,9 +485,9 @@ class DocumentsApiService {
|
|||
}) as unknown as { deleted_count: number };
|
||||
};
|
||||
|
||||
getWatchedFolders = async (searchSpaceId: number) => {
|
||||
getWatchedFolders = async (workspaceId: number) => {
|
||||
return baseApiService.get(
|
||||
`/api/v1/documents/watched-folders?workspace_id=${searchSpaceId}`,
|
||||
`/api/v1/documents/watched-folders?workspace_id=${workspaceId}`,
|
||||
folderListResponse
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -33,8 +33,8 @@ class FoldersApiService {
|
|||
});
|
||||
};
|
||||
|
||||
listFolders = async (searchSpaceId: number) => {
|
||||
return baseApiService.get(`/api/v1/folders?workspace_id=${searchSpaceId}`, folderListResponse);
|
||||
listFolders = async (workspaceId: number) => {
|
||||
return baseApiService.get(`/api/v1/folders?workspace_id=${workspaceId}`, folderListResponse);
|
||||
};
|
||||
|
||||
getFolder = async (folderId: number) => {
|
||||
|
|
|
|||
|
|
@ -7,9 +7,9 @@ import { baseApiService } from "./base-api.service";
|
|||
const BASE = "/api/v1/image-generations";
|
||||
|
||||
class ImageGenerationsApiService {
|
||||
list = async (searchSpaceId: number, limit = 100) => {
|
||||
list = async (workspaceId: number, limit = 100) => {
|
||||
const qs = new URLSearchParams({
|
||||
workspace_id: String(searchSpaceId),
|
||||
workspace_id: String(workspaceId),
|
||||
limit: String(limit),
|
||||
}).toString();
|
||||
return baseApiService.get(`${BASE}?${qs}`, imageGenerationList);
|
||||
|
|
|
|||
|
|
@ -8,9 +8,9 @@ import {
|
|||
getMembersResponse,
|
||||
getMyAccessRequest,
|
||||
getMyAccessResponse,
|
||||
type LeaveSearchSpaceRequest,
|
||||
leaveSearchSpaceRequest,
|
||||
leaveSearchSpaceResponse,
|
||||
type LeaveWorkspaceRequest,
|
||||
leaveWorkspaceRequest,
|
||||
leaveWorkspaceResponse,
|
||||
type UpdateMembershipRequest,
|
||||
updateMembershipRequest,
|
||||
updateMembershipResponse,
|
||||
|
|
@ -82,8 +82,8 @@ class MembersApiService {
|
|||
/**
|
||||
* Leave a workspace (remove self)
|
||||
*/
|
||||
leaveSearchSpace = async (request: LeaveSearchSpaceRequest) => {
|
||||
const parsedRequest = leaveSearchSpaceRequest.safeParse(request);
|
||||
leaveWorkspace = async (request: LeaveWorkspaceRequest) => {
|
||||
const parsedRequest = leaveWorkspaceRequest.safeParse(request);
|
||||
|
||||
if (!parsedRequest.success) {
|
||||
console.error("Invalid request:", parsedRequest.error);
|
||||
|
|
@ -94,7 +94,7 @@ class MembersApiService {
|
|||
|
||||
return baseApiService.delete(
|
||||
`/api/v1/workspaces/${parsedRequest.data.workspace_id}/members/me`,
|
||||
leaveSearchSpaceResponse
|
||||
leaveWorkspaceResponse
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -44,9 +44,9 @@ class ModelConnectionsApiService {
|
|||
return baseApiService.get(`/api/v1/model-providers`, modelProviderListResponse);
|
||||
};
|
||||
|
||||
getConnections = async (searchSpaceId: number): Promise<ConnectionRead[]> => {
|
||||
getConnections = async (workspaceId: number): Promise<ConnectionRead[]> => {
|
||||
return baseApiService.get(
|
||||
`/api/v1/model-connections?workspace_id=${searchSpaceId}`,
|
||||
`/api/v1/model-connections?workspace_id=${workspaceId}`,
|
||||
connectionListResponse
|
||||
);
|
||||
};
|
||||
|
|
@ -179,12 +179,12 @@ class ModelConnectionsApiService {
|
|||
return baseApiService.post(`/api/v1/models/${id}/test`, verifyConnectionResponse);
|
||||
};
|
||||
|
||||
getModelRoles = async (searchSpaceId: number): Promise<ModelRoles> => {
|
||||
return baseApiService.get(`/api/v1/workspaces/${searchSpaceId}/model-roles`, modelRoles);
|
||||
getModelRoles = async (workspaceId: number): Promise<ModelRoles> => {
|
||||
return baseApiService.get(`/api/v1/workspaces/${workspaceId}/model-roles`, modelRoles);
|
||||
};
|
||||
|
||||
updateModelRoles = async (searchSpaceId: number, roles: ModelRoles): Promise<ModelRoles> => {
|
||||
return baseApiService.put(`/api/v1/workspaces/${searchSpaceId}/model-roles`, modelRoles, {
|
||||
updateModelRoles = async (workspaceId: number, roles: ModelRoles): Promise<ModelRoles> => {
|
||||
return baseApiService.put(`/api/v1/workspaces/${workspaceId}/model-roles`, modelRoles, {
|
||||
body: roles,
|
||||
});
|
||||
};
|
||||
|
|
|
|||
|
|
@ -110,10 +110,10 @@ class NotificationsApiService {
|
|||
* Get distinct source types (connector + document types) across all
|
||||
* status notifications. Used to populate the inbox Status tab filter.
|
||||
*/
|
||||
getSourceTypes = async (searchSpaceId?: number): Promise<GetSourceTypesResponse> => {
|
||||
getSourceTypes = async (workspaceId?: number): Promise<GetSourceTypesResponse> => {
|
||||
const params = new URLSearchParams();
|
||||
if (searchSpaceId !== undefined) {
|
||||
params.append("workspace_id", String(searchSpaceId));
|
||||
if (workspaceId !== undefined) {
|
||||
params.append("workspace_id", String(workspaceId));
|
||||
}
|
||||
const queryString = params.toString();
|
||||
|
||||
|
|
@ -125,18 +125,18 @@ class NotificationsApiService {
|
|||
|
||||
/**
|
||||
* Get unread notification count with split between total and recent
|
||||
* @param searchSpaceId - Optional search space ID to filter by
|
||||
* @param workspaceId - Optional workspace ID to filter by
|
||||
* @param type - Optional notification type to filter by (type-safe enum)
|
||||
* @param category - Optional category filter ('comments' or 'status')
|
||||
*/
|
||||
getUnreadCount = async (
|
||||
searchSpaceId?: number,
|
||||
workspaceId?: number,
|
||||
type?: InboxItemTypeEnum,
|
||||
category?: NotificationCategory
|
||||
): Promise<GetUnreadCountResponse> => {
|
||||
const params = new URLSearchParams();
|
||||
if (searchSpaceId !== undefined) {
|
||||
params.append("workspace_id", String(searchSpaceId));
|
||||
if (workspaceId !== undefined) {
|
||||
params.append("workspace_id", String(workspaceId));
|
||||
}
|
||||
if (type) {
|
||||
params.append("type", type);
|
||||
|
|
@ -156,10 +156,10 @@ class NotificationsApiService {
|
|||
* Get unread counts for all categories in a single request.
|
||||
* Replaces 2 separate getUnreadCount calls (comments + status).
|
||||
*/
|
||||
getBatchUnreadCounts = async (searchSpaceId?: number): Promise<GetBatchUnreadCountResponse> => {
|
||||
getBatchUnreadCounts = async (workspaceId?: number): Promise<GetBatchUnreadCountResponse> => {
|
||||
const params = new URLSearchParams();
|
||||
if (searchSpaceId !== undefined) {
|
||||
params.append("workspace_id", String(searchSpaceId));
|
||||
if (workspaceId !== undefined) {
|
||||
params.append("workspace_id", String(workspaceId));
|
||||
}
|
||||
const queryString = params.toString();
|
||||
|
||||
|
|
|
|||
|
|
@ -15,9 +15,9 @@ const BASE = "/api/v1/podcasts";
|
|||
const voiceOptionList = z.array(voiceOption);
|
||||
|
||||
class PodcastsApiService {
|
||||
list = async (searchSpaceId: number, limit = 200) => {
|
||||
list = async (workspaceId: number, limit = 200) => {
|
||||
const qs = new URLSearchParams({
|
||||
workspace_id: String(searchSpaceId),
|
||||
workspace_id: String(workspaceId),
|
||||
limit: String(limit),
|
||||
}).toString();
|
||||
return baseApiService.get(`${BASE}?${qs}`, podcastSummaryList);
|
||||
|
|
|
|||
|
|
@ -12,10 +12,10 @@ import { ValidationError } from "@/lib/error";
|
|||
import { baseApiService } from "./base-api.service";
|
||||
|
||||
class PromptsApiService {
|
||||
list = async (searchSpaceId?: number) => {
|
||||
list = async (workspaceId?: number) => {
|
||||
const params = new URLSearchParams();
|
||||
if (searchSpaceId !== undefined) {
|
||||
params.set("workspace_id", String(searchSpaceId));
|
||||
if (workspaceId !== undefined) {
|
||||
params.set("workspace_id", String(workspaceId));
|
||||
}
|
||||
const queryString = params.toString();
|
||||
const url = queryString ? `/api/v1/prompts?${queryString}` : "/api/v1/prompts";
|
||||
|
|
|
|||
|
|
@ -4,9 +4,9 @@ import { baseApiService } from "./base-api.service";
|
|||
const BASE = "/api/v1/reports";
|
||||
|
||||
class ReportsApiService {
|
||||
list = async (searchSpaceId: number, limit = 200) => {
|
||||
list = async (workspaceId: number, limit = 200) => {
|
||||
const qs = new URLSearchParams({
|
||||
workspace_id: String(searchSpaceId),
|
||||
workspace_id: String(workspaceId),
|
||||
limit: String(limit),
|
||||
}).toString();
|
||||
return baseApiService.get(`${BASE}?${qs}`, reportList);
|
||||
|
|
|
|||
|
|
@ -4,9 +4,9 @@ import { baseApiService } from "./base-api.service";
|
|||
const BASE = "/api/v1/video-presentations";
|
||||
|
||||
class VideoPresentationsApiService {
|
||||
list = async (searchSpaceId: number, limit = 200) => {
|
||||
list = async (workspaceId: number, limit = 200) => {
|
||||
const qs = new URLSearchParams({
|
||||
workspace_id: String(searchSpaceId),
|
||||
workspace_id: String(workspaceId),
|
||||
limit: String(limit),
|
||||
}).toString();
|
||||
return baseApiService.get(`${BASE}?${qs}`, videoPresentationList);
|
||||
|
|
|
|||
|
|
@ -1,34 +1,34 @@
|
|||
import { z } from "zod";
|
||||
import {
|
||||
type CreateSearchSpaceRequest,
|
||||
createSearchSpaceRequest,
|
||||
createSearchSpaceResponse,
|
||||
type DeleteSearchSpaceRequest,
|
||||
deleteSearchSpaceRequest,
|
||||
deleteSearchSpaceResponse,
|
||||
type GetSearchSpaceRequest,
|
||||
type GetSearchSpacesRequest,
|
||||
getSearchSpaceRequest,
|
||||
getSearchSpaceResponse,
|
||||
getSearchSpacesRequest,
|
||||
getSearchSpacesResponse,
|
||||
leaveSearchSpaceResponse,
|
||||
type UpdateSearchSpaceApiAccessRequest,
|
||||
type UpdateSearchSpaceRequest,
|
||||
updateSearchSpaceApiAccessRequest,
|
||||
updateSearchSpaceApiAccessResponse,
|
||||
updateSearchSpaceRequest,
|
||||
updateSearchSpaceResponse,
|
||||
type CreateWorkspaceRequest,
|
||||
createWorkspaceRequest,
|
||||
createWorkspaceResponse,
|
||||
type DeleteWorkspaceRequest,
|
||||
deleteWorkspaceRequest,
|
||||
deleteWorkspaceResponse,
|
||||
type GetWorkspaceRequest,
|
||||
type GetWorkspacesRequest,
|
||||
getWorkspaceRequest,
|
||||
getWorkspaceResponse,
|
||||
getWorkspacesRequest,
|
||||
getWorkspacesResponse,
|
||||
leaveWorkspaceResponse,
|
||||
type UpdateWorkspaceApiAccessRequest,
|
||||
type UpdateWorkspaceRequest,
|
||||
updateWorkspaceApiAccessRequest,
|
||||
updateWorkspaceApiAccessResponse,
|
||||
updateWorkspaceRequest,
|
||||
updateWorkspaceResponse,
|
||||
} from "@/contracts/types/workspace.types";
|
||||
import { ValidationError } from "../error";
|
||||
import { baseApiService } from "./base-api.service";
|
||||
|
||||
class SearchSpacesApiService {
|
||||
class WorkspacesApiService {
|
||||
/**
|
||||
* Get a list of search spaces with optional filtering and pagination
|
||||
* Get a list of workspaces with optional filtering and pagination
|
||||
*/
|
||||
getSearchSpaces = async (request?: GetSearchSpacesRequest) => {
|
||||
const parsedRequest = getSearchSpacesRequest.safeParse(request || {});
|
||||
getWorkspaces = async (request?: GetWorkspacesRequest) => {
|
||||
const parsedRequest = getWorkspacesRequest.safeParse(request || {});
|
||||
|
||||
if (!parsedRequest.success) {
|
||||
console.error("Invalid request:", parsedRequest.error);
|
||||
|
|
@ -50,14 +50,14 @@ class SearchSpacesApiService {
|
|||
? new URLSearchParams(transformedQueryParams).toString()
|
||||
: "";
|
||||
|
||||
return baseApiService.get(`/api/v1/workspaces?${queryParams}`, getSearchSpacesResponse);
|
||||
return baseApiService.get(`/api/v1/workspaces?${queryParams}`, getWorkspacesResponse);
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a new search space
|
||||
* Create a new workspace
|
||||
*/
|
||||
createSearchSpace = async (request: CreateSearchSpaceRequest) => {
|
||||
const parsedRequest = createSearchSpaceRequest.safeParse(request);
|
||||
createWorkspace = async (request: CreateWorkspaceRequest) => {
|
||||
const parsedRequest = createWorkspaceRequest.safeParse(request);
|
||||
|
||||
if (!parsedRequest.success) {
|
||||
console.error("Invalid request:", parsedRequest.error);
|
||||
|
|
@ -66,16 +66,16 @@ class SearchSpacesApiService {
|
|||
throw new ValidationError(`Invalid request: ${errorMessage}`);
|
||||
}
|
||||
|
||||
return baseApiService.post(`/api/v1/workspaces`, createSearchSpaceResponse, {
|
||||
return baseApiService.post(`/api/v1/workspaces`, createWorkspaceResponse, {
|
||||
body: parsedRequest.data,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a single search space by ID
|
||||
* Get a single workspace by ID
|
||||
*/
|
||||
getSearchSpace = async (request: GetSearchSpaceRequest) => {
|
||||
const parsedRequest = getSearchSpaceRequest.safeParse(request);
|
||||
getWorkspace = async (request: GetWorkspaceRequest) => {
|
||||
const parsedRequest = getWorkspaceRequest.safeParse(request);
|
||||
|
||||
if (!parsedRequest.success) {
|
||||
console.error("Invalid request:", parsedRequest.error);
|
||||
|
|
@ -84,14 +84,14 @@ class SearchSpacesApiService {
|
|||
throw new ValidationError(`Invalid request: ${errorMessage}`);
|
||||
}
|
||||
|
||||
return baseApiService.get(`/api/v1/workspaces/${request.id}`, getSearchSpaceResponse);
|
||||
return baseApiService.get(`/api/v1/workspaces/${request.id}`, getWorkspaceResponse);
|
||||
};
|
||||
|
||||
/**
|
||||
* Update an existing search space
|
||||
* Update an existing workspace
|
||||
*/
|
||||
updateSearchSpace = async (request: UpdateSearchSpaceRequest) => {
|
||||
const parsedRequest = updateSearchSpaceRequest.safeParse(request);
|
||||
updateWorkspace = async (request: UpdateWorkspaceRequest) => {
|
||||
const parsedRequest = updateWorkspaceRequest.safeParse(request);
|
||||
|
||||
if (!parsedRequest.success) {
|
||||
console.error("Invalid request:", parsedRequest.error);
|
||||
|
|
@ -100,13 +100,13 @@ class SearchSpacesApiService {
|
|||
throw new ValidationError(`Invalid request: ${errorMessage}`);
|
||||
}
|
||||
|
||||
return baseApiService.put(`/api/v1/workspaces/${request.id}`, updateSearchSpaceResponse, {
|
||||
return baseApiService.put(`/api/v1/workspaces/${request.id}`, updateWorkspaceResponse, {
|
||||
body: parsedRequest.data.data,
|
||||
});
|
||||
};
|
||||
|
||||
updateSearchSpaceApiAccess = async (request: UpdateSearchSpaceApiAccessRequest) => {
|
||||
const parsedRequest = updateSearchSpaceApiAccessRequest.safeParse(request);
|
||||
updateWorkspaceApiAccess = async (request: UpdateWorkspaceApiAccessRequest) => {
|
||||
const parsedRequest = updateWorkspaceApiAccessRequest.safeParse(request);
|
||||
|
||||
if (!parsedRequest.success) {
|
||||
console.error("Invalid request:", parsedRequest.error);
|
||||
|
|
@ -116,7 +116,7 @@ class SearchSpacesApiService {
|
|||
|
||||
return baseApiService.put(
|
||||
`/api/v1/workspaces/${request.id}/api-access`,
|
||||
updateSearchSpaceApiAccessResponse,
|
||||
updateWorkspaceApiAccessResponse,
|
||||
{
|
||||
body: { api_access_enabled: parsedRequest.data.api_access_enabled },
|
||||
}
|
||||
|
|
@ -124,10 +124,10 @@ class SearchSpacesApiService {
|
|||
};
|
||||
|
||||
/**
|
||||
* Delete a search space
|
||||
* Delete a workspace
|
||||
*/
|
||||
deleteSearchSpace = async (request: DeleteSearchSpaceRequest) => {
|
||||
const parsedRequest = deleteSearchSpaceRequest.safeParse(request);
|
||||
deleteWorkspace = async (request: DeleteWorkspaceRequest) => {
|
||||
const parsedRequest = deleteWorkspaceRequest.safeParse(request);
|
||||
|
||||
if (!parsedRequest.success) {
|
||||
console.error("Invalid request:", parsedRequest.error);
|
||||
|
|
@ -136,30 +136,30 @@ class SearchSpacesApiService {
|
|||
throw new ValidationError(`Invalid request: ${errorMessage}`);
|
||||
}
|
||||
|
||||
return baseApiService.delete(`/api/v1/workspaces/${request.id}`, deleteSearchSpaceResponse);
|
||||
return baseApiService.delete(`/api/v1/workspaces/${request.id}`, deleteWorkspaceResponse);
|
||||
};
|
||||
|
||||
/**
|
||||
* Trigger AI file sorting for all documents in a search space
|
||||
* Trigger AI file sorting for all documents in a workspace
|
||||
*/
|
||||
triggerAiSort = async (searchSpaceId: number) => {
|
||||
triggerAiSort = async (workspaceId: number) => {
|
||||
return baseApiService.post(
|
||||
`/api/v1/workspaces/${searchSpaceId}/ai-sort`,
|
||||
`/api/v1/workspaces/${workspaceId}/ai-sort`,
|
||||
z.object({ message: z.string() }),
|
||||
{}
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Leave a search space (remove own membership)
|
||||
* This is used by non-owners to leave a shared search space
|
||||
* Leave a workspace (remove own membership)
|
||||
* This is used by non-owners to leave a shared workspace
|
||||
*/
|
||||
leaveSearchSpace = async (searchSpaceId: number) => {
|
||||
leaveWorkspace = async (workspaceId: number) => {
|
||||
return baseApiService.delete(
|
||||
`/api/v1/workspaces/${searchSpaceId}/members/me`,
|
||||
leaveSearchSpaceResponse
|
||||
`/api/v1/workspaces/${workspaceId}/members/me`,
|
||||
leaveWorkspaceResponse
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
export const searchSpacesApiService = new SearchSpacesApiService();
|
||||
export const workspacesApiService = new WorkspacesApiService();
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ export type BuilderExecution = z.infer<typeof builderExecutionSchema>;
|
|||
* Per-automation model selection. ``0`` means "unset" — the builder resolves it
|
||||
* to the eligible default during render, and the resolved (non-zero) ids are
|
||||
* written onto ``definition.models`` at submit so the run is insulated from
|
||||
* later chat/search-space model changes.
|
||||
* later chat/workspace model changes.
|
||||
*/
|
||||
export const builderModelsSchema = z.object({
|
||||
chatModelId: z.number().int(),
|
||||
|
|
@ -236,7 +236,7 @@ function buildDefinition(form: BuilderForm): AutomationDefinition {
|
|||
metadata: { tags: form.tags },
|
||||
// Only emit models when fully resolved (the builder seeds non-zero
|
||||
// defaults before submit). A zero/unset triple is omitted so the
|
||||
// backend falls back to the search-space snapshot.
|
||||
// backend falls back to the workspace snapshot.
|
||||
...(hasResolvedModels(form.models)
|
||||
? {
|
||||
models: {
|
||||
|
|
@ -267,11 +267,11 @@ export function buildScheduleTrigger(form: BuilderForm): TriggerCreateRequest |
|
|||
|
||||
export function buildCreatePayload(
|
||||
form: BuilderForm,
|
||||
searchSpaceId: number
|
||||
workspaceId: number
|
||||
): AutomationCreateRequest {
|
||||
const trigger = buildScheduleTrigger(form);
|
||||
return {
|
||||
workspace_id: searchSpaceId,
|
||||
workspace_id: workspaceId,
|
||||
name: form.name.trim(),
|
||||
description: form.description?.trim() ? form.description.trim() : null,
|
||||
definition: buildDefinition(form),
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ export interface RawChatErrorInput {
|
|||
error: unknown;
|
||||
flow: ChatFlow;
|
||||
context?: {
|
||||
searchSpaceId?: number;
|
||||
workspaceId?: number;
|
||||
threadId?: number | null;
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,15 +5,15 @@ import type {
|
|||
ThreadRecord,
|
||||
} from "@/lib/chat/thread-persistence";
|
||||
|
||||
type SearchSpaceKey = number | string;
|
||||
type WorkspaceKey = number | string;
|
||||
|
||||
type ThreadMetadataPatch = Partial<ThreadRecord> &
|
||||
Partial<ThreadListItem> & {
|
||||
has_comments?: boolean;
|
||||
};
|
||||
|
||||
function isSameSearchSpace(keyValue: unknown, searchSpaceId: SearchSpaceKey): boolean {
|
||||
return String(keyValue) === String(searchSpaceId);
|
||||
function isSameWorkspace(keyValue: unknown, workspaceId: WorkspaceKey): boolean {
|
||||
return String(keyValue) === String(workspaceId);
|
||||
}
|
||||
|
||||
function isThreadListResponse(value: unknown): value is ThreadListResponse {
|
||||
|
|
@ -88,30 +88,30 @@ function patchThreadRecord(
|
|||
};
|
||||
}
|
||||
|
||||
function threadListQueryFilter(searchSpaceId: SearchSpaceKey) {
|
||||
function threadListQueryFilter(workspaceId: WorkspaceKey) {
|
||||
return {
|
||||
predicate: ({ queryKey }: { queryKey: QueryKey }) =>
|
||||
Array.isArray(queryKey) &&
|
||||
queryKey[0] === "threads" &&
|
||||
isSameSearchSpace(queryKey[1], searchSpaceId),
|
||||
isSameWorkspace(queryKey[1], workspaceId),
|
||||
};
|
||||
}
|
||||
|
||||
function allThreadsQueryFilter(searchSpaceId: SearchSpaceKey) {
|
||||
function allThreadsQueryFilter(workspaceId: WorkspaceKey) {
|
||||
return {
|
||||
predicate: ({ queryKey }: { queryKey: QueryKey }) =>
|
||||
Array.isArray(queryKey) &&
|
||||
queryKey[0] === "all-threads" &&
|
||||
isSameSearchSpace(queryKey[1], searchSpaceId),
|
||||
isSameWorkspace(queryKey[1], workspaceId),
|
||||
};
|
||||
}
|
||||
|
||||
function searchThreadsQueryFilter(searchSpaceId: SearchSpaceKey) {
|
||||
function searchThreadsQueryFilter(workspaceId: WorkspaceKey) {
|
||||
return {
|
||||
predicate: ({ queryKey }: { queryKey: QueryKey }) =>
|
||||
Array.isArray(queryKey) &&
|
||||
queryKey[0] === "search-threads" &&
|
||||
isSameSearchSpace(queryKey[1], searchSpaceId),
|
||||
isSameWorkspace(queryKey[1], workspaceId),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -149,14 +149,14 @@ function updateThreadListResponse(
|
|||
|
||||
export function patchThreadEverywhere(
|
||||
queryClient: QueryClient,
|
||||
searchSpaceId: SearchSpaceKey,
|
||||
workspaceId: WorkspaceKey,
|
||||
threadId: number,
|
||||
patch: ThreadMetadataPatch
|
||||
): void {
|
||||
updateThreadListResponse(queryClient, threadListQueryFilter(searchSpaceId), threadId, patch);
|
||||
updateThreadListResponse(queryClient, allThreadsQueryFilter(searchSpaceId), threadId, patch);
|
||||
updateThreadListResponse(queryClient, threadListQueryFilter(workspaceId), threadId, patch);
|
||||
updateThreadListResponse(queryClient, allThreadsQueryFilter(workspaceId), threadId, patch);
|
||||
|
||||
queryClient.setQueriesData<ThreadListItem[]>(searchThreadsQueryFilter(searchSpaceId), (old) => {
|
||||
queryClient.setQueriesData<ThreadListItem[]>(searchThreadsQueryFilter(workspaceId), (old) => {
|
||||
if (!isThreadListItemArray(old)) return old;
|
||||
return patchThreadListItems(old, threadId, patch);
|
||||
});
|
||||
|
|
@ -169,15 +169,15 @@ export function patchThreadEverywhere(
|
|||
|
||||
export function replaceThreadEverywhere(
|
||||
queryClient: QueryClient,
|
||||
searchSpaceId: SearchSpaceKey,
|
||||
workspaceId: WorkspaceKey,
|
||||
thread: ThreadRecord
|
||||
): void {
|
||||
patchThreadEverywhere(queryClient, searchSpaceId, thread.id, thread);
|
||||
patchThreadEverywhere(queryClient, workspaceId, thread.id, thread);
|
||||
}
|
||||
|
||||
export function removeThreadEverywhere(
|
||||
queryClient: QueryClient,
|
||||
searchSpaceId: SearchSpaceKey,
|
||||
workspaceId: WorkspaceKey,
|
||||
threadId: number
|
||||
): void {
|
||||
const removeFromListResponse = (old: ThreadListResponse | undefined) => {
|
||||
|
|
@ -190,14 +190,14 @@ export function removeThreadEverywhere(
|
|||
};
|
||||
|
||||
queryClient.setQueriesData<ThreadListResponse>(
|
||||
threadListQueryFilter(searchSpaceId),
|
||||
threadListQueryFilter(workspaceId),
|
||||
removeFromListResponse
|
||||
);
|
||||
queryClient.setQueriesData<ThreadListResponse>(
|
||||
allThreadsQueryFilter(searchSpaceId),
|
||||
allThreadsQueryFilter(workspaceId),
|
||||
removeFromListResponse
|
||||
);
|
||||
queryClient.setQueriesData<ThreadListItem[]>(searchThreadsQueryFilter(searchSpaceId), (old) => {
|
||||
queryClient.setQueriesData<ThreadListItem[]>(searchThreadsQueryFilter(workspaceId), (old) => {
|
||||
if (!isThreadListItemArray(old)) return old;
|
||||
return old.filter((thread) => thread.id !== threadId);
|
||||
});
|
||||
|
|
@ -207,7 +207,7 @@ export function removeThreadEverywhere(
|
|||
|
||||
export function moveThreadArchiveState(
|
||||
queryClient: QueryClient,
|
||||
searchSpaceId: SearchSpaceKey,
|
||||
workspaceId: WorkspaceKey,
|
||||
threadId: number,
|
||||
archived: boolean
|
||||
): void {
|
||||
|
|
@ -232,14 +232,14 @@ export function moveThreadArchiveState(
|
|||
};
|
||||
|
||||
queryClient.setQueriesData<ThreadListResponse>(
|
||||
threadListQueryFilter(searchSpaceId),
|
||||
threadListQueryFilter(workspaceId),
|
||||
moveInListResponse
|
||||
);
|
||||
queryClient.setQueriesData<ThreadListResponse>(
|
||||
allThreadsQueryFilter(searchSpaceId),
|
||||
allThreadsQueryFilter(workspaceId),
|
||||
moveInListResponse
|
||||
);
|
||||
queryClient.setQueriesData<ThreadListItem[]>(searchThreadsQueryFilter(searchSpaceId), (old) => {
|
||||
queryClient.setQueriesData<ThreadListItem[]>(searchThreadsQueryFilter(workspaceId), (old) => {
|
||||
if (!isThreadListItemArray(old)) return old;
|
||||
return old.map((thread) => (thread.id === threadId ? { ...thread, archived } : thread));
|
||||
});
|
||||
|
|
|
|||
|
|
@ -89,13 +89,13 @@ export interface ThreadHistoryLoadResponse {
|
|||
// =============================================================================
|
||||
|
||||
/**
|
||||
* Fetch list of threads for a search space
|
||||
* Fetch list of threads for a workspace
|
||||
*/
|
||||
export async function fetchThreads(
|
||||
searchSpaceId: number,
|
||||
workspaceId: number,
|
||||
limit?: number
|
||||
): Promise<ThreadListResponse> {
|
||||
const params = new URLSearchParams({ workspace_id: String(searchSpaceId) });
|
||||
const params = new URLSearchParams({ workspace_id: String(workspaceId) });
|
||||
if (limit) params.append("limit", String(limit));
|
||||
return baseApiService.get<ThreadListResponse>(`/api/v1/threads?${params}`);
|
||||
}
|
||||
|
|
@ -103,12 +103,9 @@ export async function fetchThreads(
|
|||
/**
|
||||
* Search threads by title
|
||||
*/
|
||||
export async function searchThreads(
|
||||
searchSpaceId: number,
|
||||
title: string
|
||||
): Promise<ThreadListItem[]> {
|
||||
export async function searchThreads(workspaceId: number, title: string): Promise<ThreadListItem[]> {
|
||||
const params = new URLSearchParams({
|
||||
workspace_id: String(searchSpaceId),
|
||||
workspace_id: String(workspaceId),
|
||||
title,
|
||||
});
|
||||
return baseApiService.get<ThreadListItem[]>(`/api/v1/threads/search?${params}`);
|
||||
|
|
@ -117,15 +114,12 @@ export async function searchThreads(
|
|||
/**
|
||||
* Create a new thread
|
||||
*/
|
||||
export async function createThread(
|
||||
searchSpaceId: number,
|
||||
title = "New Chat"
|
||||
): Promise<ThreadRecord> {
|
||||
export async function createThread(workspaceId: number, title = "New Chat"): Promise<ThreadRecord> {
|
||||
return baseApiService.post<ThreadRecord>("/api/v1/threads", undefined, {
|
||||
body: {
|
||||
title,
|
||||
archived: false,
|
||||
workspace_id: searchSpaceId,
|
||||
workspace_id: workspaceId,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
|
@ -212,7 +206,7 @@ export async function getThreadFull(threadId: number): Promise<ThreadRecord> {
|
|||
* Regeneration request parameters
|
||||
*/
|
||||
export interface RegenerateParams {
|
||||
searchSpaceId: number;
|
||||
workspaceId: number;
|
||||
userQuery?: string | null; // New user query (for edit). Null/undefined = reload with same query
|
||||
attachments?: Array<{
|
||||
id: string;
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ export interface FolderSyncProgress {
|
|||
export interface FolderSyncParams {
|
||||
folderPath: string;
|
||||
folderName: string;
|
||||
searchSpaceId: number;
|
||||
workspaceId: number;
|
||||
excludePatterns: string[];
|
||||
fileExtensions: string[];
|
||||
processingMode?: "basic" | "premium";
|
||||
|
|
@ -59,7 +59,7 @@ async function uploadBatchesWithConcurrency(
|
|||
batches: FolderFileEntry[][],
|
||||
params: {
|
||||
folderName: string;
|
||||
searchSpaceId: number;
|
||||
workspaceId: number;
|
||||
rootFolderId: number | null;
|
||||
processingMode?: "basic" | "premium";
|
||||
signal?: AbortSignal;
|
||||
|
|
@ -95,7 +95,7 @@ async function uploadBatchesWithConcurrency(
|
|||
files,
|
||||
{
|
||||
folder_name: params.folderName,
|
||||
workspace_id: params.searchSpaceId,
|
||||
workspace_id: params.workspaceId,
|
||||
relative_paths: batch.map((e) => e.relativePath),
|
||||
root_folder_id: resolvedRootFolderId,
|
||||
processing_mode: params.processingMode,
|
||||
|
|
@ -141,7 +141,7 @@ export async function uploadFolderScan(params: FolderSyncParams): Promise<number
|
|||
const {
|
||||
folderPath,
|
||||
folderName,
|
||||
searchSpaceId,
|
||||
workspaceId,
|
||||
excludePatterns,
|
||||
fileExtensions,
|
||||
processingMode,
|
||||
|
|
@ -159,7 +159,7 @@ export async function uploadFolderScan(params: FolderSyncParams): Promise<number
|
|||
excludePatterns,
|
||||
fileExtensions,
|
||||
rootFolderId: rootFolderId ?? null,
|
||||
searchSpaceId,
|
||||
workspaceId,
|
||||
active: true,
|
||||
});
|
||||
|
||||
|
|
@ -169,7 +169,7 @@ export async function uploadFolderScan(params: FolderSyncParams): Promise<number
|
|||
|
||||
const mtimeCheckResult = await documentsApiService.folderMtimeCheck({
|
||||
folder_name: folderName,
|
||||
workspace_id: searchSpaceId,
|
||||
workspace_id: workspaceId,
|
||||
files: allFiles.map((f) => ({ relative_path: f.relativePath, mtime: f.mtimeMs / 1000 })),
|
||||
});
|
||||
|
||||
|
|
@ -187,7 +187,7 @@ export async function uploadFolderScan(params: FolderSyncParams): Promise<number
|
|||
|
||||
const uploadedRootId = await uploadBatchesWithConcurrency(batches, {
|
||||
folderName,
|
||||
searchSpaceId,
|
||||
workspaceId,
|
||||
rootFolderId: rootFolderId ?? null,
|
||||
processingMode,
|
||||
signal,
|
||||
|
|
@ -214,7 +214,7 @@ export async function uploadFolderScan(params: FolderSyncParams): Promise<number
|
|||
|
||||
await documentsApiService.folderSyncFinalize({
|
||||
folder_name: folderName,
|
||||
workspace_id: searchSpaceId,
|
||||
workspace_id: workspaceId,
|
||||
root_folder_id: rootFolderId ?? null,
|
||||
all_relative_paths: allFiles.map((f) => f.relativePath),
|
||||
});
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ export function hasEnabledChatModel(connections: ConnectionRead[]): boolean {
|
|||
export function isLlmOnboardingComplete(
|
||||
chatModelId: number | null | undefined,
|
||||
globalConnections: ConnectionRead[],
|
||||
searchSpaceConnections: ConnectionRead[]
|
||||
workspaceConnections: ConnectionRead[]
|
||||
): boolean {
|
||||
const connections = [...globalConnections, ...searchSpaceConnections];
|
||||
const connections = [...globalConnections, ...workspaceConnections];
|
||||
const resolvedChatModelId = chatModelId ?? 0;
|
||||
|
||||
if (resolvedChatModelId === 0) {
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import { getConnectorTelemetryMeta } from "@/lib/connector-telemetry";
|
|||
*
|
||||
* Categories:
|
||||
* - auth: Authentication events
|
||||
* - search_space: Search space management
|
||||
* - workspace: Search space management
|
||||
* - document: Document management
|
||||
* - chat: Chat and messaging (authenticated + anonymous)
|
||||
* - connector: External connector events (all lifecycle stages)
|
||||
|
|
@ -78,22 +78,22 @@ export function trackLogout() {
|
|||
// SEARCH SPACE EVENTS
|
||||
// ============================================
|
||||
|
||||
export function trackSearchSpaceCreated(searchSpaceId: number, name: string) {
|
||||
safeCapture("search_space_created", {
|
||||
workspace_id: searchSpaceId,
|
||||
export function trackWorkspaceCreated(workspaceId: number, name: string) {
|
||||
safeCapture("workspace_created", {
|
||||
workspace_id: workspaceId,
|
||||
name,
|
||||
});
|
||||
}
|
||||
|
||||
export function trackSearchSpaceDeleted(searchSpaceId: number) {
|
||||
safeCapture("search_space_deleted", {
|
||||
workspace_id: searchSpaceId,
|
||||
export function trackWorkspaceDeleted(workspaceId: number) {
|
||||
safeCapture("workspace_deleted", {
|
||||
workspace_id: workspaceId,
|
||||
});
|
||||
}
|
||||
|
||||
export function trackSearchSpaceViewed(searchSpaceId: number) {
|
||||
safeCapture("search_space_viewed", {
|
||||
workspace_id: searchSpaceId,
|
||||
export function trackWorkspaceViewed(workspaceId: number) {
|
||||
safeCapture("workspace_viewed", {
|
||||
workspace_id: workspaceId,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -101,15 +101,15 @@ export function trackSearchSpaceViewed(searchSpaceId: number) {
|
|||
// CHAT EVENTS
|
||||
// ============================================
|
||||
|
||||
export function trackChatCreated(searchSpaceId: number, chatId: number) {
|
||||
export function trackChatCreated(workspaceId: number, chatId: number) {
|
||||
safeCapture("chat_created", {
|
||||
workspace_id: searchSpaceId,
|
||||
workspace_id: workspaceId,
|
||||
chat_id: chatId,
|
||||
});
|
||||
}
|
||||
|
||||
export function trackChatMessageSent(
|
||||
searchSpaceId: number,
|
||||
workspaceId: number,
|
||||
chatId: number,
|
||||
options?: {
|
||||
hasAttachments?: boolean;
|
||||
|
|
@ -118,7 +118,7 @@ export function trackChatMessageSent(
|
|||
}
|
||||
) {
|
||||
safeCapture("chat_message_sent", {
|
||||
workspace_id: searchSpaceId,
|
||||
workspace_id: workspaceId,
|
||||
chat_id: chatId,
|
||||
has_attachments: options?.hasAttachments ?? false,
|
||||
has_mentioned_documents: options?.hasMentionedDocuments ?? false,
|
||||
|
|
@ -126,16 +126,16 @@ export function trackChatMessageSent(
|
|||
});
|
||||
}
|
||||
|
||||
export function trackChatResponseReceived(searchSpaceId: number, chatId: number) {
|
||||
export function trackChatResponseReceived(workspaceId: number, chatId: number) {
|
||||
safeCapture("chat_response_received", {
|
||||
workspace_id: searchSpaceId,
|
||||
workspace_id: workspaceId,
|
||||
chat_id: chatId,
|
||||
});
|
||||
}
|
||||
|
||||
export function trackChatError(searchSpaceId: number, chatId: number, error?: string) {
|
||||
export function trackChatError(workspaceId: number, chatId: number, error?: string) {
|
||||
safeCapture("chat_error", {
|
||||
workspace_id: searchSpaceId,
|
||||
workspace_id: workspaceId,
|
||||
chat_id: chatId,
|
||||
error,
|
||||
});
|
||||
|
|
@ -151,14 +151,14 @@ export interface ChatFailureTelemetry {
|
|||
}
|
||||
|
||||
export function trackChatBlocked(
|
||||
searchSpaceId: number,
|
||||
workspaceId: number,
|
||||
chatId: number | null,
|
||||
payload: ChatFailureTelemetry
|
||||
) {
|
||||
safeCapture(
|
||||
"chat_blocked",
|
||||
compact({
|
||||
workspace_id: searchSpaceId,
|
||||
workspace_id: workspaceId,
|
||||
chat_id: chatId ?? undefined,
|
||||
flow: payload.flow,
|
||||
kind: payload.kind,
|
||||
|
|
@ -171,14 +171,14 @@ export function trackChatBlocked(
|
|||
}
|
||||
|
||||
export function trackChatErrorDetailed(
|
||||
searchSpaceId: number,
|
||||
workspaceId: number,
|
||||
chatId: number | null,
|
||||
payload: ChatFailureTelemetry
|
||||
) {
|
||||
safeCapture(
|
||||
"chat_error",
|
||||
compact({
|
||||
workspace_id: searchSpaceId,
|
||||
workspace_id: workspaceId,
|
||||
chat_id: chatId ?? undefined,
|
||||
flow: payload.flow,
|
||||
kind: payload.kind,
|
||||
|
|
@ -215,48 +215,48 @@ export function trackAnonymousChatMessageSent(options: {
|
|||
// ============================================
|
||||
|
||||
export function trackDocumentUploadStarted(
|
||||
searchSpaceId: number,
|
||||
workspaceId: number,
|
||||
fileCount: number,
|
||||
totalSizeBytes: number
|
||||
) {
|
||||
safeCapture("document_upload_started", {
|
||||
workspace_id: searchSpaceId,
|
||||
workspace_id: workspaceId,
|
||||
file_count: fileCount,
|
||||
total_size_bytes: totalSizeBytes,
|
||||
});
|
||||
}
|
||||
|
||||
export function trackDocumentUploadSuccess(searchSpaceId: number, fileCount: number) {
|
||||
export function trackDocumentUploadSuccess(workspaceId: number, fileCount: number) {
|
||||
safeCapture("document_upload_success", {
|
||||
workspace_id: searchSpaceId,
|
||||
workspace_id: workspaceId,
|
||||
file_count: fileCount,
|
||||
});
|
||||
}
|
||||
|
||||
export function trackDocumentUploadFailure(searchSpaceId: number, error?: string) {
|
||||
export function trackDocumentUploadFailure(workspaceId: number, error?: string) {
|
||||
safeCapture("document_upload_failure", {
|
||||
workspace_id: searchSpaceId,
|
||||
workspace_id: workspaceId,
|
||||
error,
|
||||
});
|
||||
}
|
||||
|
||||
export function trackDocumentDeleted(searchSpaceId: number, documentId: number) {
|
||||
export function trackDocumentDeleted(workspaceId: number, documentId: number) {
|
||||
safeCapture("document_deleted", {
|
||||
workspace_id: searchSpaceId,
|
||||
workspace_id: workspaceId,
|
||||
document_id: documentId,
|
||||
});
|
||||
}
|
||||
|
||||
export function trackDocumentBulkDeleted(searchSpaceId: number, count: number) {
|
||||
export function trackDocumentBulkDeleted(workspaceId: number, count: number) {
|
||||
safeCapture("document_bulk_deleted", {
|
||||
workspace_id: searchSpaceId,
|
||||
workspace_id: workspaceId,
|
||||
count,
|
||||
});
|
||||
}
|
||||
|
||||
export function trackYouTubeImport(searchSpaceId: number, url: string) {
|
||||
export function trackYouTubeImport(workspaceId: number, url: string) {
|
||||
safeCapture("youtube_import_started", {
|
||||
workspace_id: searchSpaceId,
|
||||
workspace_id: workspaceId,
|
||||
url,
|
||||
});
|
||||
}
|
||||
|
|
@ -281,7 +281,7 @@ export type ConnectorEventStage =
|
|||
| "synced";
|
||||
|
||||
export interface ConnectorEventOptions {
|
||||
searchSpaceId?: number | null;
|
||||
workspaceId?: number | null;
|
||||
connectorId?: number | null;
|
||||
/** Source of the action (e.g. "oauth_callback", "non_oauth_form", "webcrawler_quick_add"). */
|
||||
source?: string;
|
||||
|
|
@ -303,7 +303,7 @@ export function trackConnectorEvent(
|
|||
const meta = getConnectorTelemetryMeta(connectorType);
|
||||
safeCapture(`connector_${stage}`, {
|
||||
...compact({
|
||||
workspace_id: options.searchSpaceId ?? undefined,
|
||||
workspace_id: options.workspaceId ?? undefined,
|
||||
connector_id: options.connectorId ?? undefined,
|
||||
source: options.source,
|
||||
error: options.error,
|
||||
|
|
@ -319,64 +319,64 @@ export function trackConnectorEvent(
|
|||
// ---- Convenience wrappers kept for backward compatibility ----
|
||||
|
||||
export function trackConnectorSetupStarted(
|
||||
searchSpaceId: number,
|
||||
workspaceId: number,
|
||||
connectorType: string,
|
||||
source?: string
|
||||
) {
|
||||
trackConnectorEvent("setup_started", connectorType, { searchSpaceId, source });
|
||||
trackConnectorEvent("setup_started", connectorType, { workspaceId, source });
|
||||
}
|
||||
|
||||
export function trackConnectorSetupSuccess(
|
||||
searchSpaceId: number,
|
||||
workspaceId: number,
|
||||
connectorType: string,
|
||||
connectorId: number
|
||||
) {
|
||||
trackConnectorEvent("setup_success", connectorType, { searchSpaceId, connectorId });
|
||||
trackConnectorEvent("setup_success", connectorType, { workspaceId, connectorId });
|
||||
}
|
||||
|
||||
export function trackConnectorSetupFailure(
|
||||
searchSpaceId: number | null | undefined,
|
||||
workspaceId: number | null | undefined,
|
||||
connectorType: string,
|
||||
error?: string,
|
||||
source?: string
|
||||
) {
|
||||
trackConnectorEvent("setup_failure", connectorType, {
|
||||
searchSpaceId: searchSpaceId ?? undefined,
|
||||
workspaceId: workspaceId ?? undefined,
|
||||
error,
|
||||
source,
|
||||
});
|
||||
}
|
||||
|
||||
export function trackConnectorDeleted(
|
||||
searchSpaceId: number,
|
||||
workspaceId: number,
|
||||
connectorType: string,
|
||||
connectorId: number
|
||||
) {
|
||||
trackConnectorEvent("deleted", connectorType, { searchSpaceId, connectorId });
|
||||
trackConnectorEvent("deleted", connectorType, { workspaceId, connectorId });
|
||||
}
|
||||
|
||||
export function trackConnectorSynced(
|
||||
searchSpaceId: number,
|
||||
workspaceId: number,
|
||||
connectorType: string,
|
||||
connectorId: number
|
||||
) {
|
||||
trackConnectorEvent("synced", connectorType, { searchSpaceId, connectorId });
|
||||
trackConnectorEvent("synced", connectorType, { workspaceId, connectorId });
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// SETTINGS EVENTS
|
||||
// ============================================
|
||||
|
||||
export function trackSettingsViewed(searchSpaceId: number, section: string) {
|
||||
export function trackSettingsViewed(workspaceId: number, section: string) {
|
||||
safeCapture("settings_viewed", {
|
||||
workspace_id: searchSpaceId,
|
||||
workspace_id: workspaceId,
|
||||
section,
|
||||
});
|
||||
}
|
||||
|
||||
export function trackSettingsUpdated(searchSpaceId: number, section: string, setting: string) {
|
||||
export function trackSettingsUpdated(workspaceId: number, section: string, setting: string) {
|
||||
safeCapture("settings_updated", {
|
||||
workspace_id: searchSpaceId,
|
||||
workspace_id: workspaceId,
|
||||
section,
|
||||
setting,
|
||||
});
|
||||
|
|
@ -386,16 +386,16 @@ export function trackSettingsUpdated(searchSpaceId: number, section: string, set
|
|||
// FEATURE USAGE EVENTS
|
||||
// ============================================
|
||||
|
||||
export function trackPodcastGenerated(searchSpaceId: number, chatId: number) {
|
||||
export function trackPodcastGenerated(workspaceId: number, chatId: number) {
|
||||
safeCapture("podcast_generated", {
|
||||
workspace_id: searchSpaceId,
|
||||
workspace_id: workspaceId,
|
||||
chat_id: chatId,
|
||||
});
|
||||
}
|
||||
|
||||
export function trackSourcesTabViewed(searchSpaceId: number, tab: string) {
|
||||
export function trackSourcesTabViewed(workspaceId: number, tab: string) {
|
||||
safeCapture("sources_tab_viewed", {
|
||||
workspace_id: searchSpaceId,
|
||||
workspace_id: workspaceId,
|
||||
tab,
|
||||
});
|
||||
}
|
||||
|
|
@ -414,59 +414,59 @@ export function trackDesktopDownloadClicked(options: {
|
|||
// SEARCH SPACE INVITE EVENTS
|
||||
// ============================================
|
||||
|
||||
export function trackSearchSpaceInviteSent(
|
||||
searchSpaceId: number,
|
||||
export function trackWorkspaceInviteSent(
|
||||
workspaceId: number,
|
||||
options?: {
|
||||
roleName?: string;
|
||||
hasExpiry?: boolean;
|
||||
hasMaxUses?: boolean;
|
||||
}
|
||||
) {
|
||||
safeCapture("search_space_invite_sent", {
|
||||
workspace_id: searchSpaceId,
|
||||
safeCapture("workspace_invite_sent", {
|
||||
workspace_id: workspaceId,
|
||||
role_name: options?.roleName,
|
||||
has_expiry: options?.hasExpiry ?? false,
|
||||
has_max_uses: options?.hasMaxUses ?? false,
|
||||
});
|
||||
}
|
||||
|
||||
export function trackSearchSpaceInviteAccepted(
|
||||
searchSpaceId: number,
|
||||
searchSpaceName: string,
|
||||
export function trackWorkspaceInviteAccepted(
|
||||
workspaceId: number,
|
||||
workspaceName: string,
|
||||
roleName?: string | null
|
||||
) {
|
||||
safeCapture("search_space_invite_accepted", {
|
||||
workspace_id: searchSpaceId,
|
||||
search_space_name: searchSpaceName,
|
||||
safeCapture("workspace_invite_accepted", {
|
||||
workspace_id: workspaceId,
|
||||
workspace_name: workspaceName,
|
||||
role_name: roleName,
|
||||
});
|
||||
}
|
||||
|
||||
export function trackSearchSpaceInviteDeclined(searchSpaceName?: string) {
|
||||
safeCapture("search_space_invite_declined", {
|
||||
search_space_name: searchSpaceName,
|
||||
export function trackWorkspaceInviteDeclined(workspaceName?: string) {
|
||||
safeCapture("workspace_invite_declined", {
|
||||
workspace_name: workspaceName,
|
||||
});
|
||||
}
|
||||
|
||||
export function trackSearchSpaceUserAdded(
|
||||
searchSpaceId: number,
|
||||
searchSpaceName: string,
|
||||
export function trackWorkspaceUserAdded(
|
||||
workspaceId: number,
|
||||
workspaceName: string,
|
||||
roleName?: string | null
|
||||
) {
|
||||
safeCapture("search_space_user_added", {
|
||||
workspace_id: searchSpaceId,
|
||||
search_space_name: searchSpaceName,
|
||||
safeCapture("workspace_user_added", {
|
||||
workspace_id: workspaceId,
|
||||
workspace_name: workspaceName,
|
||||
role_name: roleName,
|
||||
});
|
||||
}
|
||||
|
||||
export function trackSearchSpaceUsersViewed(
|
||||
searchSpaceId: number,
|
||||
export function trackWorkspaceUsersViewed(
|
||||
workspaceId: number,
|
||||
userCount: number,
|
||||
ownerCount: number
|
||||
) {
|
||||
safeCapture("search_space_users_viewed", {
|
||||
workspace_id: searchSpaceId,
|
||||
safeCapture("workspace_users_viewed", {
|
||||
workspace_id: workspaceId,
|
||||
user_count: userCount,
|
||||
owner_count: ownerCount,
|
||||
});
|
||||
|
|
@ -477,12 +477,12 @@ export function trackSearchSpaceUsersViewed(
|
|||
// ============================================
|
||||
|
||||
export function trackConnectorConnected(
|
||||
searchSpaceId: number,
|
||||
workspaceId: number,
|
||||
connectorType: string,
|
||||
connectorId?: number
|
||||
) {
|
||||
trackConnectorEvent("connected", connectorType, {
|
||||
searchSpaceId,
|
||||
workspaceId,
|
||||
connectorId: connectorId ?? undefined,
|
||||
});
|
||||
}
|
||||
|
|
@ -492,19 +492,19 @@ export function trackConnectorConnected(
|
|||
// ============================================
|
||||
|
||||
export function trackIndexWithDateRangeOpened(
|
||||
searchSpaceId: number,
|
||||
workspaceId: number,
|
||||
connectorType: string,
|
||||
connectorId: number
|
||||
) {
|
||||
safeCapture("index_with_date_range_opened", {
|
||||
workspace_id: searchSpaceId,
|
||||
workspace_id: workspaceId,
|
||||
connector_type: connectorType,
|
||||
connector_id: connectorId,
|
||||
});
|
||||
}
|
||||
|
||||
export function trackIndexWithDateRangeStarted(
|
||||
searchSpaceId: number,
|
||||
workspaceId: number,
|
||||
connectorType: string,
|
||||
connectorId: number,
|
||||
options?: {
|
||||
|
|
@ -513,7 +513,7 @@ export function trackIndexWithDateRangeStarted(
|
|||
}
|
||||
) {
|
||||
safeCapture("index_with_date_range_started", {
|
||||
workspace_id: searchSpaceId,
|
||||
workspace_id: workspaceId,
|
||||
connector_type: connectorType,
|
||||
connector_id: connectorId,
|
||||
has_start_date: options?.hasStartDate ?? false,
|
||||
|
|
@ -522,37 +522,37 @@ export function trackIndexWithDateRangeStarted(
|
|||
}
|
||||
|
||||
export function trackQuickIndexClicked(
|
||||
searchSpaceId: number,
|
||||
workspaceId: number,
|
||||
connectorType: string,
|
||||
connectorId: number
|
||||
) {
|
||||
safeCapture("quick_index_clicked", {
|
||||
workspace_id: searchSpaceId,
|
||||
workspace_id: workspaceId,
|
||||
connector_type: connectorType,
|
||||
connector_id: connectorId,
|
||||
});
|
||||
}
|
||||
|
||||
export function trackConfigurePeriodicIndexingOpened(
|
||||
searchSpaceId: number,
|
||||
workspaceId: number,
|
||||
connectorType: string,
|
||||
connectorId: number
|
||||
) {
|
||||
safeCapture("configure_periodic_indexing_opened", {
|
||||
workspace_id: searchSpaceId,
|
||||
workspace_id: workspaceId,
|
||||
connector_type: connectorType,
|
||||
connector_id: connectorId,
|
||||
});
|
||||
}
|
||||
|
||||
export function trackPeriodicIndexingStarted(
|
||||
searchSpaceId: number,
|
||||
workspaceId: number,
|
||||
connectorType: string,
|
||||
connectorId: number,
|
||||
frequencyMinutes: number
|
||||
) {
|
||||
safeCapture("periodic_indexing_started", {
|
||||
workspace_id: searchSpaceId,
|
||||
workspace_id: workspaceId,
|
||||
connector_type: connectorType,
|
||||
connector_id: connectorId,
|
||||
frequency_minutes: frequencyMinutes,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import type { GetConnectorsRequest } from "@/contracts/types/connector.types";
|
||||
import type { GetDocumentsRequest } from "@/contracts/types/document.types";
|
||||
import type { GetLogsRequest } from "@/contracts/types/log.types";
|
||||
import type { GetSearchSpacesRequest } from "@/contracts/types/workspace.types";
|
||||
import type { GetWorkspacesRequest } from "@/contracts/types/workspace.types";
|
||||
|
||||
/**
|
||||
* Convert an object to a stable array of [key, value] pairs sorted by key.
|
||||
|
|
@ -30,51 +30,51 @@ export const cacheKeys = {
|
|||
document: (documentId: string) => ["document", documentId] as const,
|
||||
},
|
||||
logs: {
|
||||
list: (searchSpaceId?: number | string) => ["logs", "list", searchSpaceId] as const,
|
||||
list: (workspaceId?: number | string) => ["logs", "list", workspaceId] as const,
|
||||
detail: (logId: number | string) => ["logs", "detail", logId] as const,
|
||||
summary: (searchSpaceId?: number | string) => ["logs", "summary", searchSpaceId] as const,
|
||||
summary: (workspaceId?: number | string) => ["logs", "summary", workspaceId] as const,
|
||||
withQueryParams: (queries: GetLogsRequest["queryParams"]) =>
|
||||
["logs", "with-query-params", ...stableEntries(queries)] as const,
|
||||
},
|
||||
modelConnections: {
|
||||
all: (searchSpaceId: number) => ["model-connections", searchSpaceId] as const,
|
||||
all: (workspaceId: number) => ["model-connections", workspaceId] as const,
|
||||
global: () => ["model-connections", "global"] as const,
|
||||
globalConfigStatus: () => ["model-connections", "global-config-status"] as const,
|
||||
providers: () => ["model-connections", "providers"] as const,
|
||||
roles: (searchSpaceId: number) => ["model-roles", searchSpaceId] as const,
|
||||
roles: (workspaceId: number) => ["model-roles", workspaceId] as const,
|
||||
},
|
||||
auth: {
|
||||
user: ["auth", "user"] as const,
|
||||
},
|
||||
searchSpaces: {
|
||||
all: ["search-spaces"] as const,
|
||||
withQueryParams: (queries: GetSearchSpacesRequest["queryParams"]) =>
|
||||
["search-spaces", ...stableEntries(queries)] as const,
|
||||
detail: (searchSpaceId: string) => ["search-spaces", searchSpaceId] as const,
|
||||
workspaces: {
|
||||
all: ["workspaces"] as const,
|
||||
withQueryParams: (queries: GetWorkspacesRequest["queryParams"]) =>
|
||||
["workspaces", ...stableEntries(queries)] as const,
|
||||
detail: (workspaceId: string) => ["workspaces", workspaceId] as const,
|
||||
},
|
||||
user: {
|
||||
current: () => ["user", "me"] as const,
|
||||
},
|
||||
roles: {
|
||||
all: (searchSpaceId: string) => ["roles", searchSpaceId] as const,
|
||||
byId: (searchSpaceId: string, roleId: string) => ["roles", searchSpaceId, roleId] as const,
|
||||
all: (workspaceId: string) => ["roles", workspaceId] as const,
|
||||
byId: (workspaceId: string, roleId: string) => ["roles", workspaceId, roleId] as const,
|
||||
},
|
||||
permissions: {
|
||||
all: () => ["permissions"] as const,
|
||||
},
|
||||
members: {
|
||||
all: (searchSpaceId: string) => ["members", searchSpaceId] as const,
|
||||
myAccess: (searchSpaceId: string) => ["members", "my-access", searchSpaceId] as const,
|
||||
all: (workspaceId: string) => ["members", workspaceId] as const,
|
||||
myAccess: (workspaceId: string) => ["members", "my-access", workspaceId] as const,
|
||||
},
|
||||
invites: {
|
||||
all: (searchSpaceId: string) => ["invites", searchSpaceId] as const,
|
||||
all: (workspaceId: string) => ["invites", workspaceId] as const,
|
||||
info: (inviteCode: string) => ["invites", "info", inviteCode] as const,
|
||||
},
|
||||
agentTools: {
|
||||
all: () => ["agent-tools"] as const,
|
||||
},
|
||||
connectors: {
|
||||
all: (searchSpaceId: string) => ["connectors", searchSpaceId] as const,
|
||||
all: (workspaceId: string) => ["connectors", workspaceId] as const,
|
||||
withQueryParams: (queries: GetConnectorsRequest["queryParams"]) =>
|
||||
["connectors", ...stableEntries(queries)] as const,
|
||||
byId: (connectorId: string) => ["connector", connectorId] as const,
|
||||
|
|
@ -96,31 +96,31 @@ export const cacheKeys = {
|
|||
},
|
||||
publicChatSnapshots: {
|
||||
all: ["public-chat-snapshots"] as const,
|
||||
bySearchSpace: (searchSpaceId: number) =>
|
||||
["public-chat-snapshots", "search-space", searchSpaceId] as const,
|
||||
byWorkspace: (workspaceId: number) =>
|
||||
["public-chat-snapshots", "workspace", workspaceId] as const,
|
||||
},
|
||||
prompts: {
|
||||
all: () => ["prompts"] as const,
|
||||
public: () => ["prompts", "public"] as const,
|
||||
},
|
||||
notifications: {
|
||||
search: (searchSpaceId: number | null, search: string, tab: string) =>
|
||||
["notifications", "search", searchSpaceId, search, tab] as const,
|
||||
sourceTypes: (searchSpaceId: number | null) =>
|
||||
["notifications", "source-types", searchSpaceId] as const,
|
||||
batchUnreadCounts: (searchSpaceId: number | null) =>
|
||||
["notifications", "unread-counts-batch", searchSpaceId] as const,
|
||||
search: (workspaceId: number | null, search: string, tab: string) =>
|
||||
["notifications", "search", workspaceId, search, tab] as const,
|
||||
sourceTypes: (workspaceId: number | null) =>
|
||||
["notifications", "source-types", workspaceId] as const,
|
||||
batchUnreadCounts: (workspaceId: number | null) =>
|
||||
["notifications", "unread-counts-batch", workspaceId] as const,
|
||||
},
|
||||
automations: {
|
||||
// list endpoint is keyed by pagination too so distinct pages don't collide
|
||||
list: (searchSpaceId: number, limit: number, offset: number) =>
|
||||
["automations", "list", searchSpaceId, limit, offset] as const,
|
||||
list: (workspaceId: number, limit: number, offset: number) =>
|
||||
["automations", "list", workspaceId, limit, offset] as const,
|
||||
detail: (automationId: number) => ["automations", "detail", automationId] as const,
|
||||
runs: (automationId: number, limit: number, offset: number) =>
|
||||
["automations", "runs", automationId, limit, offset] as const,
|
||||
run: (automationId: number, runId: number) =>
|
||||
["automations", "runs", automationId, runId] as const,
|
||||
modelEligibility: (searchSpaceId: number) =>
|
||||
["automations", "model-eligibility", searchSpaceId] as const,
|
||||
modelEligibility: (workspaceId: number) =>
|
||||
["automations", "model-eligibility", workspaceId] as const,
|
||||
},
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue