diff --git a/front/components/assistant_builder/spaces/SpaceSelector.tsx b/front/components/assistant_builder/spaces/SpaceSelector.tsx index 3b2cc614c229..fee0ba7e8e0e 100644 --- a/front/components/assistant_builder/spaces/SpaceSelector.tsx +++ b/front/components/assistant_builder/spaces/SpaceSelector.tsx @@ -8,7 +8,11 @@ import { import type { SpaceType } from "@dust-tt/types"; import React, { useState } from "react"; -import { getSpaceIcon, getSpaceName, groupSpaces } from "@app/lib/spaces"; +import { + getSpaceIcon, + getSpaceName, + groupSpacesForDisplay, +} from "@app/lib/spaces"; import { classNames } from "@app/lib/utils"; interface SpaceSelectorProps { @@ -39,7 +43,7 @@ export function SpaceSelector({ } // Group by kind and sort. - const sortedSpaces = groupSpaces(spaces) + const sortedSpaces = groupSpacesForDisplay(spaces) .filter((i) => i.section !== "system") .map((i) => i.spaces.sort((a, b) => { diff --git a/front/components/spaces/SpaceSideBarMenu.tsx b/front/components/spaces/SpaceSideBarMenu.tsx index 86346943be9f..d8847fd806f3 100644 --- a/front/components/spaces/SpaceSideBarMenu.tsx +++ b/front/components/spaces/SpaceSideBarMenu.tsx @@ -28,7 +28,11 @@ import { getConnectorProviderLogoWithFallback } from "@app/lib/connector_provide import { getVisualForContentNode } from "@app/lib/content_nodes"; import { getDataSourceNameFromView } from "@app/lib/data_sources"; import type { SpaceSectionGroupType } from "@app/lib/spaces"; -import { getSpaceIcon, getSpaceName, groupSpaces } from "@app/lib/spaces"; +import { + getSpaceIcon, + getSpaceName, + groupSpacesForDisplay, +} from "@app/lib/spaces"; import { useApps } from "@app/lib/swr/apps"; import { useDataSourceViewContentNodes } from "@app/lib/swr/data_source_views"; import { @@ -88,7 +92,7 @@ export default function SpaceSideBarMenu({ return <>; } - const sortedGroupedSpaces = groupSpaces(spaces).filter( + const sortedGroupedSpaces = groupSpacesForDisplay(spaces).filter( ({ section, spaces }) => section !== "system" || spaces.length !== 0 ); diff --git a/front/lib/resources/space_resource.ts b/front/lib/resources/space_resource.ts index 091671c29e75..28a5962ba4bf 100644 --- a/front/lib/resources/space_resource.ts +++ b/front/lib/resources/space_resource.ts @@ -112,9 +112,21 @@ export class SpaceResource extends BaseResource { [globalGroup] )); + const conversationsSpace = + existingSpaces.find((s) => s.kind === "conversations") || + (await SpaceResource.makeNew( + { + name: "Conversations", + kind: "conversations", + workspaceId: auth.getNonNullableWorkspace().id, + }, + [globalGroup] + )); + return { systemSpace, globalSpace, + conversationsSpace, }; } @@ -170,25 +182,39 @@ export class SpaceResource extends BaseResource { } static async listWorkspaceSpaces( - auth: Authenticator + auth: Authenticator, + options?: { includeConversationsSpace?: boolean } ): Promise { const spaces = await this.baseFetch(auth); - return spaces.filter((s) => s.canList(auth)); + if (!options?.includeConversationsSpace) { + return spaces.filter((s) => !s.isConversations()); + } + return spaces; } static async listWorkspaceSpacesAsMember(auth: Authenticator) { const spaces = await this.baseFetch(auth); // using canRead() as we know that only members can read spaces (but admins can list them) - return spaces.filter((s) => s.canList(auth) && s.canRead(auth)); + // also, conversations space is not meant for members + return spaces.filter( + (s) => s.canList(auth) && s.canRead(auth) && !s.isConversations() + ); } - static async listWorkspaceDefaultSpaces(auth: Authenticator) { + static async listWorkspaceDefaultSpaces( + auth: Authenticator, + options?: { includeConversationsSpace?: boolean } + ) { return this.baseFetch(auth, { where: { kind: { - [Op.in]: ["system", "global"], + [Op.in]: [ + "system", + "global", + ...(options?.includeConversationsSpace ? ["conversations"] : []), + ], }, }, }); @@ -470,8 +496,8 @@ export class SpaceResource extends BaseResource { ]; } - // Default Workspace space. - if (this.isGlobal()) { + // Default Workspace space and Conversations space. + if (this.isGlobal() || this.isConversations()) { return [ { workspaceId: this.workspaceId, @@ -567,6 +593,10 @@ export class SpaceResource extends BaseResource { return this.kind === "system"; } + isConversations() { + return this.kind === "conversations"; + } + isRegular() { return this.kind === "regular"; } diff --git a/front/lib/resources/storage/models/spaces.ts b/front/lib/resources/storage/models/spaces.ts index 6d2985f08049..33a8d26f428d 100644 --- a/front/lib/resources/storage/models/spaces.ts +++ b/front/lib/resources/storage/models/spaces.ts @@ -1,5 +1,11 @@ import type { SpaceKind } from "@dust-tt/types"; -import type { CreationOptional, ForeignKey, NonAttribute } from "sequelize"; +import { isUniqueSpaceKind } from "@dust-tt/types"; +import type { + CreationOptional, + ForeignKey, + NonAttribute, + Transaction, +} from "sequelize"; import { DataTypes } from "sequelize"; import { Workspace } from "@app/lib/models/workspace"; @@ -66,3 +72,25 @@ SpaceModel.belongsTo(Workspace, { foreignKey: { allowNull: false }, onDelete: "RESTRICT", }); + +SpaceModel.addHook( + "beforeCreate", + "enforce_one_special_space_per_workspace", + async (space: SpaceModel, options: { transaction: Transaction }) => { + if (isUniqueSpaceKind(space.kind)) { + const existingSpace = await SpaceModel.findOne({ + where: { + workspaceId: space.workspaceId, + kind: space.kind, + }, + transaction: options.transaction, + }); + + if (existingSpace) { + throw new Error(`A ${space.kind} space exists for this workspace.`, { + cause: `enforce_one_${space.kind}_space_per_workspace`, + }); + } + } + } +); diff --git a/front/lib/spaces.ts b/front/lib/spaces.ts index 244278b52bef..704c9eb541f3 100644 --- a/front/lib/spaces.ts +++ b/front/lib/spaces.ts @@ -1,5 +1,6 @@ import { LockIcon, PlanetIcon, ServerIcon } from "@dust-tt/sparkle"; import type { PlanType, SpaceType, WorkspaceType } from "@dust-tt/types"; +import { assertNever } from "@dust-tt/types"; import { groupBy } from "lodash"; import type React from "react"; @@ -37,19 +38,34 @@ export const dustAppsListUrl = ( return `/w/${owner.sId}/spaces/${space.sId}/categories/apps`; }; -export const groupSpaces = (spaces: SpaceType[]) => { +export const groupSpacesForDisplay = (spaces: SpaceType[]) => { + // Conversations space should never be displayed + const spacesWithoutConversations = spaces.filter( + (space) => space.kind !== "conversations" + ); // Group by kind and sort. - const groupedSpaces = groupBy(spaces, (space): SpaceSectionGroupType => { - switch (space.kind) { - case "public": - case "system": - return space.kind; + const groupedSpaces = groupBy( + spacesWithoutConversations, + (space): SpaceSectionGroupType => { + // please ts + if (space.kind === "conversations") { + throw new Error("Conversations space should never be displayed"); + } - case "global": - case "regular": - return space.isRestricted ? "restricted" : "shared"; + switch (space.kind) { + case "public": + case "system": + return space.kind; + + case "global": + case "regular": + return space.isRestricted ? "restricted" : "shared"; + + default: + assertNever(space.kind); + } } - }); + ); return SPACE_SECTION_GROUP_ORDER.map((section) => ({ section, diff --git a/front/migrations/20241114_conversations_spaces_backfill.ts b/front/migrations/20241114_conversations_spaces_backfill.ts new file mode 100644 index 000000000000..2346e9822f87 --- /dev/null +++ b/front/migrations/20241114_conversations_spaces_backfill.ts @@ -0,0 +1,60 @@ +import _ from "lodash"; + +import { Workspace } from "@app/lib/models/workspace"; +import { GroupResource } from "@app/lib/resources/group_resource"; +import { SpaceResource } from "@app/lib/resources/space_resource"; +import { makeScript } from "@app/scripts/helpers"; + +async function backfillWorkspacesGroup(execute: boolean) { + const workspaces = await Workspace.findAll(); + + const chunks = _.chunk(workspaces, 16); + for (const [i, c] of chunks.entries()) { + console.log( + `[execute=${execute}] Processing chunk of ${c.length} workspaces... (${ + i + 1 + }/${chunks.length})` + ); + if (execute) { + await Promise.all( + c.map((w) => + (async () => { + try { + const workspaceGroup = + await GroupResource.internalFetchWorkspaceGlobalGroup(w.id); + if (!workspaceGroup) { + throw new Error("Workspace group not found"); + } + await SpaceResource.makeNew( + { + name: "Conversations", + kind: "conversations", + workspaceId: w.id, + }, + [workspaceGroup] + ); + } catch (error) { + if ( + error instanceof Error && + error.cause && + error.cause === "enforce_one_conversations_space_per_workspace" + ) { + console.log( + `Conversation already exists for workspace ${w.id}` + ); + } else { + throw error; + } + } + })() + ) + ); + } + } + + console.log(`Done.`); +} + +makeScript({}, async ({ execute }) => { + await backfillWorkspacesGroup(execute); +}); diff --git a/front/pages/api/v1/w/[wId]/spaces/[spaceId]/apps/[aId]/runs/[runId]/index.ts b/front/pages/api/v1/w/[wId]/spaces/[spaceId]/apps/[aId]/runs/[runId]/index.ts index 53723aeac987..062663d49b72 100644 --- a/front/pages/api/v1/w/[wId]/spaces/[spaceId]/apps/[aId]/runs/[runId]/index.ts +++ b/front/pages/api/v1/w/[wId]/spaces/[spaceId]/apps/[aId]/runs/[runId]/index.ts @@ -95,6 +95,16 @@ async function handler( }); } + if (app.space.kind === "conversations") { + return apiError(req, res, { + status_code: 404, + api_error: { + type: "space_not_found", + message: "The space you're trying to access was not found", + }, + }); + } + switch (req.method) { case "GET": const runId = req.query.runId as string; diff --git a/front/pages/api/v1/w/[wId]/spaces/[spaceId]/apps/[aId]/runs/index.ts b/front/pages/api/v1/w/[wId]/spaces/[spaceId]/apps/[aId]/runs/index.ts index da2ea7b23835..3d251e9f3f92 100644 --- a/front/pages/api/v1/w/[wId]/spaces/[spaceId]/apps/[aId]/runs/index.ts +++ b/front/pages/api/v1/w/[wId]/spaces/[spaceId]/apps/[aId]/runs/index.ts @@ -207,6 +207,16 @@ async function handler( }); } + if (app.space.kind === "conversations") { + return apiError(req, res, { + status_code: 404, + api_error: { + type: "space_not_found", + message: "The space you're trying to access was not found", + }, + }); + } + if (!app.canRead(keyAuth)) { return apiError(req, res, { status_code: 403, diff --git a/front/pages/api/v1/w/[wId]/spaces/[spaceId]/apps/index.ts b/front/pages/api/v1/w/[wId]/spaces/[spaceId]/apps/index.ts index a3b126367b93..65150222dbb4 100644 --- a/front/pages/api/v1/w/[wId]/spaces/[spaceId]/apps/index.ts +++ b/front/pages/api/v1/w/[wId]/spaces/[spaceId]/apps/index.ts @@ -104,6 +104,16 @@ async function handler( }); } + if (space.kind === "conversations") { + return apiError(req, res, { + status_code: 404, + api_error: { + type: "space_not_found", + message: "The space you're trying to access was not found", + }, + }); + } + switch (req.method) { case "GET": const apps = await AppResource.listBySpace(auth, space); diff --git a/front/pages/api/v1/w/[wId]/spaces/[spaceId]/data_source_views/[dsvId]/index.ts b/front/pages/api/v1/w/[wId]/spaces/[spaceId]/data_source_views/[dsvId]/index.ts index b27fec77bd77..600a7ad38f31 100644 --- a/front/pages/api/v1/w/[wId]/spaces/[spaceId]/data_source_views/[dsvId]/index.ts +++ b/front/pages/api/v1/w/[wId]/spaces/[spaceId]/data_source_views/[dsvId]/index.ts @@ -186,6 +186,16 @@ async function handler( }); } + if (dataSourceView.space.kind === "conversations") { + return apiError(req, res, { + status_code: 404, + api_error: { + type: "space_not_found", + message: "The space you're trying to access was not found", + }, + }); + } + switch (req.method) { case "GET": return res.status(200).json({ diff --git a/front/pages/api/v1/w/[wId]/spaces/[spaceId]/data_source_views/[dsvId]/search.ts b/front/pages/api/v1/w/[wId]/spaces/[spaceId]/data_source_views/[dsvId]/search.ts index d1aa52f47a25..3425895535ea 100644 --- a/front/pages/api/v1/w/[wId]/spaces/[spaceId]/data_source_views/[dsvId]/search.ts +++ b/front/pages/api/v1/w/[wId]/spaces/[spaceId]/data_source_views/[dsvId]/search.ts @@ -172,6 +172,16 @@ async function handler( }); } + if (dataSourceView.space.kind === "conversations") { + return apiError(req, res, { + status_code: 404, + api_error: { + type: "space_not_found", + message: "The space you're trying to access was not found", + }, + }); + } + switch (req.method) { case "GET": { // I could not find a way to make the query params be an array if there is only one tag. diff --git a/front/pages/api/v1/w/[wId]/spaces/[spaceId]/data_source_views/index.ts b/front/pages/api/v1/w/[wId]/spaces/[spaceId]/data_source_views/index.ts index 71c3b58ece2b..fbb77a088c06 100644 --- a/front/pages/api/v1/w/[wId]/spaces/[spaceId]/data_source_views/index.ts +++ b/front/pages/api/v1/w/[wId]/spaces/[spaceId]/data_source_views/index.ts @@ -78,6 +78,16 @@ async function handler( }); } + if (space.kind === "conversations") { + return apiError(req, res, { + status_code: 404, + api_error: { + type: "space_not_found", + message: "The space you're trying to access was not found", + }, + }); + } + switch (req.method) { case "GET": const dataSourceViews = await DataSourceViewResource.listBySpace( diff --git a/front/pages/api/v1/w/[wId]/spaces/[spaceId]/data_sources/[dsId]/documents/[documentId]/index.ts b/front/pages/api/v1/w/[wId]/spaces/[spaceId]/data_sources/[dsId]/documents/[documentId]/index.ts index dc6252c00909..19065bf5ac09 100644 --- a/front/pages/api/v1/w/[wId]/spaces/[spaceId]/data_sources/[dsId]/documents/[documentId]/index.ts +++ b/front/pages/api/v1/w/[wId]/spaces/[spaceId]/data_sources/[dsId]/documents/[documentId]/index.ts @@ -295,6 +295,16 @@ async function handler( }); } + if (dataSource.space.kind === "conversations") { + return apiError(req, res, { + status_code: 404, + api_error: { + type: "space_not_found", + message: "The space you're trying to access was not found", + }, + }); + } + const owner = auth.getNonNullableWorkspace(); const plan = auth.getNonNullablePlan(); diff --git a/front/pages/api/v1/w/[wId]/spaces/[spaceId]/data_sources/[dsId]/documents/[documentId]/parents.ts b/front/pages/api/v1/w/[wId]/spaces/[spaceId]/data_sources/[dsId]/documents/[documentId]/parents.ts index 6539c2b031e1..03c1de6804df 100644 --- a/front/pages/api/v1/w/[wId]/spaces/[spaceId]/data_sources/[dsId]/documents/[documentId]/parents.ts +++ b/front/pages/api/v1/w/[wId]/spaces/[spaceId]/data_sources/[dsId]/documents/[documentId]/parents.ts @@ -121,6 +121,16 @@ async function handler( }); } + if (dataSource.space.kind === "conversations") { + return apiError(req, res, { + status_code: 404, + api_error: { + type: "space_not_found", + message: "The space you're trying to access was not found", + }, + }); + } + switch (req.method) { case "POST": if ( diff --git a/front/pages/api/v1/w/[wId]/spaces/[spaceId]/data_sources/[dsId]/documents/index.ts b/front/pages/api/v1/w/[wId]/spaces/[spaceId]/data_sources/[dsId]/documents/index.ts index 4d689cf43d10..0e6c20295e4f 100644 --- a/front/pages/api/v1/w/[wId]/spaces/[spaceId]/data_sources/[dsId]/documents/index.ts +++ b/front/pages/api/v1/w/[wId]/spaces/[spaceId]/data_sources/[dsId]/documents/index.ts @@ -117,6 +117,16 @@ async function handler( }); } + if (dataSource.space.kind === "conversations") { + return apiError(req, res, { + status_code: 404, + api_error: { + type: "space_not_found", + message: "The space you're trying to access was not found", + }, + }); + } + const coreAPI = new CoreAPI(config.getCoreAPIConfig(), logger); switch (req.method) { case "GET": diff --git a/front/pages/api/v1/w/[wId]/spaces/[spaceId]/data_sources/[dsId]/search.ts b/front/pages/api/v1/w/[wId]/spaces/[spaceId]/data_sources/[dsId]/search.ts index ee427f5652d4..61754490a55a 100644 --- a/front/pages/api/v1/w/[wId]/spaces/[spaceId]/data_sources/[dsId]/search.ts +++ b/front/pages/api/v1/w/[wId]/spaces/[spaceId]/data_sources/[dsId]/search.ts @@ -194,6 +194,16 @@ async function handler( }); } + if (dataSource.space.kind === "conversations") { + return apiError(req, res, { + status_code: 404, + api_error: { + type: "space_not_found", + message: "The space you're trying to access was not found", + }, + }); + } + switch (req.method) { case "GET": { // I could not find a way to make the query params be an array if there is only one tag. diff --git a/front/pages/api/v1/w/[wId]/spaces/[spaceId]/data_sources/[dsId]/tables/[tId]/index.ts b/front/pages/api/v1/w/[wId]/spaces/[spaceId]/data_sources/[dsId]/tables/[tId]/index.ts index 9493127930c1..fa1fd45cebbf 100644 --- a/front/pages/api/v1/w/[wId]/spaces/[spaceId]/data_sources/[dsId]/tables/[tId]/index.ts +++ b/front/pages/api/v1/w/[wId]/spaces/[spaceId]/data_sources/[dsId]/tables/[tId]/index.ts @@ -149,6 +149,16 @@ async function handler( }); } + if (dataSource.space.kind === "conversations") { + return apiError(req, res, { + status_code: 404, + api_error: { + type: "space_not_found", + message: "The space you're trying to access was not found", + }, + }); + } + switch (req.method) { case "GET": const coreAPI = new CoreAPI(config.getCoreAPIConfig(), logger); diff --git a/front/pages/api/v1/w/[wId]/spaces/[spaceId]/data_sources/[dsId]/tables/[tId]/parents.ts b/front/pages/api/v1/w/[wId]/spaces/[spaceId]/data_sources/[dsId]/tables/[tId]/parents.ts index 5a454443c050..045f479286c0 100644 --- a/front/pages/api/v1/w/[wId]/spaces/[spaceId]/data_sources/[dsId]/tables/[tId]/parents.ts +++ b/front/pages/api/v1/w/[wId]/spaces/[spaceId]/data_sources/[dsId]/tables/[tId]/parents.ts @@ -75,6 +75,16 @@ async function handler( }); } + if (dataSource.space.kind === "conversations") { + return apiError(req, res, { + status_code: 404, + api_error: { + type: "space_not_found", + message: "The space you're trying to access was not found", + }, + }); + } + switch (req.method) { case "POST": const r = await PostTableParentsRequestSchema.safeParse(req.body); diff --git a/front/pages/api/v1/w/[wId]/spaces/[spaceId]/data_sources/[dsId]/tables/[tId]/rows/[rId].ts b/front/pages/api/v1/w/[wId]/spaces/[spaceId]/data_sources/[dsId]/tables/[tId]/rows/[rId].ts index f2d93c3e93fb..be9118182d80 100644 --- a/front/pages/api/v1/w/[wId]/spaces/[spaceId]/data_sources/[dsId]/tables/[tId]/rows/[rId].ts +++ b/front/pages/api/v1/w/[wId]/spaces/[spaceId]/data_sources/[dsId]/tables/[tId]/rows/[rId].ts @@ -166,6 +166,16 @@ async function handler( }); } + if (dataSource.space.kind === "conversations") { + return apiError(req, res, { + status_code: 404, + api_error: { + type: "space_not_found", + message: "The space you're trying to access was not found", + }, + }); + } + const coreAPI = new CoreAPI(config.getCoreAPIConfig(), logger); switch (req.method) { diff --git a/front/pages/api/v1/w/[wId]/spaces/[spaceId]/data_sources/[dsId]/tables/[tId]/rows/index.ts b/front/pages/api/v1/w/[wId]/spaces/[spaceId]/data_sources/[dsId]/tables/[tId]/rows/index.ts index 0d3ff89fe73c..45df8cb94cf8 100644 --- a/front/pages/api/v1/w/[wId]/spaces/[spaceId]/data_sources/[dsId]/tables/[tId]/rows/index.ts +++ b/front/pages/api/v1/w/[wId]/spaces/[spaceId]/data_sources/[dsId]/tables/[tId]/rows/index.ts @@ -210,6 +210,16 @@ async function handler( }); } + if (dataSource.space.kind === "conversations") { + return apiError(req, res, { + status_code: 404, + api_error: { + type: "space_not_found", + message: "The space you're trying to access was not found", + }, + }); + } + const coreAPI = new CoreAPI(config.getCoreAPIConfig(), logger); switch (req.method) { case "GET": diff --git a/front/pages/api/v1/w/[wId]/spaces/[spaceId]/data_sources/[dsId]/tables/csv.ts b/front/pages/api/v1/w/[wId]/spaces/[spaceId]/data_sources/[dsId]/tables/csv.ts index 7bb68a5c3bfd..cb0790b1f7a0 100644 --- a/front/pages/api/v1/w/[wId]/spaces/[spaceId]/data_sources/[dsId]/tables/csv.ts +++ b/front/pages/api/v1/w/[wId]/spaces/[spaceId]/data_sources/[dsId]/tables/csv.ts @@ -89,6 +89,16 @@ async function handler( }); } + if (dataSource.space.kind === "conversations") { + return apiError(req, res, { + status_code: 404, + api_error: { + type: "space_not_found", + message: "The space you're trying to access was not found", + }, + }); + } + switch (req.method) { case "POST": { const r = UpsertTableFromCsvRequestSchema.safeParse(req.body); diff --git a/front/pages/api/v1/w/[wId]/spaces/[spaceId]/data_sources/[dsId]/tables/index.ts b/front/pages/api/v1/w/[wId]/spaces/[spaceId]/data_sources/[dsId]/tables/index.ts index 574b51c07ed1..3727c358fe28 100644 --- a/front/pages/api/v1/w/[wId]/spaces/[spaceId]/data_sources/[dsId]/tables/index.ts +++ b/front/pages/api/v1/w/[wId]/spaces/[spaceId]/data_sources/[dsId]/tables/index.ts @@ -176,6 +176,16 @@ async function handler( }); } + if (dataSource.space.kind === "conversations") { + return apiError(req, res, { + status_code: 404, + api_error: { + type: "space_not_found", + message: "The space you're trying to access was not found", + }, + }); + } + const coreAPI = new CoreAPI(config.getCoreAPIConfig(), logger); switch (req.method) { diff --git a/front/pages/api/v1/w/[wId]/spaces/[spaceId]/data_sources/[dsId]/tokenize.ts b/front/pages/api/v1/w/[wId]/spaces/[spaceId]/data_sources/[dsId]/tokenize.ts index b22decfb1227..f4f570988a0a 100644 --- a/front/pages/api/v1/w/[wId]/spaces/[spaceId]/data_sources/[dsId]/tokenize.ts +++ b/front/pages/api/v1/w/[wId]/spaces/[spaceId]/data_sources/[dsId]/tokenize.ts @@ -79,6 +79,16 @@ async function handler( }); } + if (dataSource.space.kind === "conversations") { + return apiError(req, res, { + status_code: 404, + api_error: { + type: "space_not_found", + message: "The space you're trying to access was not found", + }, + }); + } + switch (req.method) { case "POST": { const bodyValidation = PostDatasourceTokenizeBodySchema.decode(req.body); diff --git a/front/pages/api/v1/w/[wId]/spaces/[spaceId]/data_sources/index.ts b/front/pages/api/v1/w/[wId]/spaces/[spaceId]/data_sources/index.ts index a76fcaecc882..7b0f71e89e72 100644 --- a/front/pages/api/v1/w/[wId]/spaces/[spaceId]/data_sources/index.ts +++ b/front/pages/api/v1/w/[wId]/spaces/[spaceId]/data_sources/index.ts @@ -73,6 +73,16 @@ async function handler( }); } + if (space.kind === "conversations") { + return apiError(req, res, { + status_code: 404, + api_error: { + type: "space_not_found", + message: "The space you're trying to access was not found", + }, + }); + } + const dataSources = await DataSourceResource.listBySpace(auth, space); switch (req.method) { diff --git a/front/pages/api/v1/w/[wId]/spaces/index.ts b/front/pages/api/v1/w/[wId]/spaces/index.ts index 54007afb6dd5..a8b018655406 100644 --- a/front/pages/api/v1/w/[wId]/spaces/index.ts +++ b/front/pages/api/v1/w/[wId]/spaces/index.ts @@ -65,7 +65,12 @@ async function handler( ): Promise { switch (req.method) { case "GET": - const spaces = await SpaceResource.listWorkspaceSpaces(auth); + const allSpaces = await SpaceResource.listWorkspaceSpaces(auth); + + // conversations space should not be shown + const spaces = allSpaces.filter( + (space) => space.kind !== "conversations" + ); const isLegacyRequest = req.url?.includes("/vaults"); if (isLegacyRequest) { diff --git a/front/pages/api/w/[wId]/spaces/[spaceId]/apps/[aId]/datasets/[name]/index.ts b/front/pages/api/w/[wId]/spaces/[spaceId]/apps/[aId]/datasets/[name]/index.ts index cc6b0d8c421e..c78b05b15eb9 100644 --- a/front/pages/api/w/[wId]/spaces/[spaceId]/apps/[aId]/datasets/[name]/index.ts +++ b/front/pages/api/w/[wId]/spaces/[spaceId]/apps/[aId]/datasets/[name]/index.ts @@ -56,6 +56,16 @@ async function handler( }); } + if (app.space.isConversations()) { + return apiError(req, res, { + status_code: 404, + api_error: { + type: "space_not_found", + message: "The space you're trying to access was not found", + }, + }); + } + if (!app.canRead(auth)) { return apiError(req, res, { status_code: 403, diff --git a/front/pages/api/w/[wId]/spaces/[spaceId]/apps/[aId]/datasets/index.ts b/front/pages/api/w/[wId]/spaces/[spaceId]/apps/[aId]/datasets/index.ts index 669fb2177a15..6dcbf9803f17 100644 --- a/front/pages/api/w/[wId]/spaces/[spaceId]/apps/[aId]/datasets/index.ts +++ b/front/pages/api/w/[wId]/spaces/[spaceId]/apps/[aId]/datasets/index.ts @@ -74,6 +74,16 @@ async function handler( }); } + if (app.space.isConversations()) { + return apiError(req, res, { + status_code: 404, + api_error: { + type: "space_not_found", + message: "The space you're trying to access was not found", + }, + }); + } + if (!app.canWrite(auth)) { return apiError(req, res, { status_code: 403, diff --git a/front/pages/api/w/[wId]/spaces/[spaceId]/apps/[aId]/index.ts b/front/pages/api/w/[wId]/spaces/[spaceId]/apps/[aId]/index.ts index ef5749915eae..e81ef1fb76b2 100644 --- a/front/pages/api/w/[wId]/spaces/[spaceId]/apps/[aId]/index.ts +++ b/front/pages/api/w/[wId]/spaces/[spaceId]/apps/[aId]/index.ts @@ -47,6 +47,16 @@ async function handler( }); } + if (app.space.isConversations()) { + return apiError(req, res, { + status_code: 404, + api_error: { + type: "space_not_found", + message: "The space you're trying to access was not found", + }, + }); + } + switch (req.method) { case "GET": res.status(200).json({ diff --git a/front/pages/api/w/[wId]/spaces/[spaceId]/apps/[aId]/runs/[runId]/blocks/[type]/[name]/index.ts b/front/pages/api/w/[wId]/spaces/[spaceId]/apps/[aId]/runs/[runId]/blocks/[type]/[name]/index.ts index f504f67d127a..d83f73669d3a 100644 --- a/front/pages/api/w/[wId]/spaces/[spaceId]/apps/[aId]/runs/[runId]/blocks/[type]/[name]/index.ts +++ b/front/pages/api/w/[wId]/spaces/[spaceId]/apps/[aId]/runs/[runId]/blocks/[type]/[name]/index.ts @@ -46,6 +46,16 @@ async function handler( }); } + if (app.space.isConversations()) { + return apiError(req, res, { + status_code: 404, + api_error: { + type: "space_not_found", + message: "The space you're trying to access was not found", + }, + }); + } + if (!app.canWrite(auth)) { return apiError(req, res, { status_code: 403, diff --git a/front/pages/api/w/[wId]/spaces/[spaceId]/apps/[aId]/runs/[runId]/status.ts b/front/pages/api/w/[wId]/spaces/[spaceId]/apps/[aId]/runs/[runId]/status.ts index 98eebcee72c0..a3636521286e 100644 --- a/front/pages/api/w/[wId]/spaces/[spaceId]/apps/[aId]/runs/[runId]/status.ts +++ b/front/pages/api/w/[wId]/spaces/[spaceId]/apps/[aId]/runs/[runId]/status.ts @@ -50,6 +50,16 @@ async function handler( }); } + if (app.space.isConversations()) { + return apiError(req, res, { + status_code: 404, + api_error: { + type: "space_not_found", + message: "The space you're trying to access was not found", + }, + }); + } + let runId: string | null = typeof req.query.runId === "string" ? req.query.runId : null; diff --git a/front/pages/api/w/[wId]/spaces/[spaceId]/apps/[aId]/runs/index.ts b/front/pages/api/w/[wId]/spaces/[spaceId]/apps/[aId]/runs/index.ts index 9b9db2d37043..36fb953d2070 100644 --- a/front/pages/api/w/[wId]/spaces/[spaceId]/apps/[aId]/runs/index.ts +++ b/front/pages/api/w/[wId]/spaces/[spaceId]/apps/[aId]/runs/index.ts @@ -57,6 +57,16 @@ async function handler( }); } + if (app.space.isConversations()) { + return apiError(req, res, { + status_code: 404, + api_error: { + type: "space_not_found", + message: "The space you're trying to access was not found", + }, + }); + } + if (!app.canWrite(auth)) { return apiError(req, res, { status_code: 403, diff --git a/front/pages/api/w/[wId]/spaces/[spaceId]/apps/[aId]/state.ts b/front/pages/api/w/[wId]/spaces/[spaceId]/apps/[aId]/state.ts index 20d68fe876d7..6a079c7fb779 100644 --- a/front/pages/api/w/[wId]/spaces/[spaceId]/apps/[aId]/state.ts +++ b/front/pages/api/w/[wId]/spaces/[spaceId]/apps/[aId]/state.ts @@ -37,6 +37,16 @@ async function handler( }); } + if (app.space.isConversations()) { + return apiError(req, res, { + status_code: 404, + api_error: { + type: "space_not_found", + message: "The space you're trying to access was not found", + }, + }); + } + if (!app.canWrite(auth)) { return apiError(req, res, { status_code: 403, diff --git a/front/pages/api/w/[wId]/spaces/[spaceId]/apps/index.ts b/front/pages/api/w/[wId]/spaces/[spaceId]/apps/index.ts index 9e2a44fd41bf..f5f77c759b62 100644 --- a/front/pages/api/w/[wId]/spaces/[spaceId]/apps/index.ts +++ b/front/pages/api/w/[wId]/spaces/[spaceId]/apps/index.ts @@ -37,7 +37,7 @@ async function handler( }); } const space = await SpaceResource.fetchById(auth, spaceId); - if (!space || !space.canList(auth)) { + if (!space || !space.canList(auth) || space.isConversations()) { return apiError(req, res, { status_code: 404, api_error: { diff --git a/front/pages/api/w/[wId]/spaces/[spaceId]/data_source_views/[dsvId]/content-nodes.ts b/front/pages/api/w/[wId]/spaces/[spaceId]/data_source_views/[dsvId]/content-nodes.ts index 9d680965e9f4..8d4847ea7b87 100644 --- a/front/pages/api/w/[wId]/spaces/[spaceId]/data_source_views/[dsvId]/content-nodes.ts +++ b/front/pages/api/w/[wId]/spaces/[spaceId]/data_source_views/[dsvId]/content-nodes.ts @@ -62,6 +62,16 @@ async function handler( }); } + if (dataSourceView.space.isConversations()) { + return apiError(req, res, { + status_code: 404, + api_error: { + type: "space_not_found", + message: "The space you're trying to access was not found", + }, + }); + } + if (req.method !== "POST") { return apiError(req, res, { status_code: 405, diff --git a/front/pages/api/w/[wId]/spaces/[spaceId]/data_source_views/[dsvId]/documents/[documentId]/index.ts b/front/pages/api/w/[wId]/spaces/[spaceId]/data_source_views/[dsvId]/documents/[documentId]/index.ts index 0a4f77845b42..bf60c4f17c65 100644 --- a/front/pages/api/w/[wId]/spaces/[spaceId]/data_source_views/[dsvId]/documents/[documentId]/index.ts +++ b/front/pages/api/w/[wId]/spaces/[spaceId]/data_source_views/[dsvId]/documents/[documentId]/index.ts @@ -49,6 +49,17 @@ async function handler( }, }); } + + if (dataSourceView.space.isConversations()) { + return apiError(req, res, { + status_code: 404, + api_error: { + type: "space_not_found", + message: "The space you're trying to access was not found", + }, + }); + } + const coreAPI = new CoreAPI(apiConfig.getCoreAPIConfig(), logger); switch (req.method) { diff --git a/front/pages/api/w/[wId]/spaces/[spaceId]/data_source_views/[dsvId]/index.ts b/front/pages/api/w/[wId]/spaces/[spaceId]/data_source_views/[dsvId]/index.ts index 8e4612532f8c..25822e2987f7 100644 --- a/front/pages/api/w/[wId]/spaces/[spaceId]/data_source_views/[dsvId]/index.ts +++ b/front/pages/api/w/[wId]/spaces/[spaceId]/data_source_views/[dsvId]/index.ts @@ -49,6 +49,17 @@ async function handler( }, }); } + + if (dataSourceView.space.isConversations()) { + return apiError(req, res, { + status_code: 404, + api_error: { + type: "space_not_found", + message: "The space you're trying to access was not found", + }, + }); + } + switch (req.method) { case "GET": { return res.status(200).json({ diff --git a/front/pages/api/w/[wId]/spaces/[spaceId]/data_source_views/[dsvId]/tables/[tableId]/index.ts b/front/pages/api/w/[wId]/spaces/[spaceId]/data_source_views/[dsvId]/tables/[tableId]/index.ts index e53b6a5dc52b..f48e9f6b8856 100644 --- a/front/pages/api/w/[wId]/spaces/[spaceId]/data_source_views/[dsvId]/tables/[tableId]/index.ts +++ b/front/pages/api/w/[wId]/spaces/[spaceId]/data_source_views/[dsvId]/tables/[tableId]/index.ts @@ -51,6 +51,16 @@ async function handler( }); } + if (dataSourceView.space.isConversations()) { + return apiError(req, res, { + status_code: 404, + api_error: { + type: "space_not_found", + message: "The space you're trying to access was not found", + }, + }); + } + switch (req.method) { case "GET": const coreAPI = new CoreAPI(apiConfig.getCoreAPIConfig(), logger); diff --git a/front/pages/api/w/[wId]/spaces/[spaceId]/data_source_views/[dsvId]/tables/index.ts b/front/pages/api/w/[wId]/spaces/[spaceId]/data_source_views/[dsvId]/tables/index.ts index 14753674f963..25d3017326b8 100644 --- a/front/pages/api/w/[wId]/spaces/[spaceId]/data_source_views/[dsvId]/tables/index.ts +++ b/front/pages/api/w/[wId]/spaces/[spaceId]/data_source_views/[dsvId]/tables/index.ts @@ -47,6 +47,16 @@ async function handler( }); } + if (dataSourceView.space.isConversations()) { + return apiError(req, res, { + status_code: 404, + api_error: { + type: "space_not_found", + message: "The space you're trying to access was not found", + }, + }); + } + switch (req.method) { case "GET": const paginationRes = getOffsetPaginationParams(req); diff --git a/front/pages/api/w/[wId]/spaces/[spaceId]/data_source_views/index.ts b/front/pages/api/w/[wId]/spaces/[spaceId]/data_source_views/index.ts index 8ab47b4bf0dd..d6fb7fee4d3f 100644 --- a/front/pages/api/w/[wId]/spaces/[spaceId]/data_source_views/index.ts +++ b/front/pages/api/w/[wId]/spaces/[spaceId]/data_source_views/index.ts @@ -68,6 +68,16 @@ async function handler( }); } + if (space.isConversations()) { + return apiError(req, res, { + status_code: 404, + api_error: { + type: "space_not_found", + message: "The space you're trying to access was not found", + }, + }); + } + switch (req.method) { case "GET": { const category = diff --git a/front/pages/api/w/[wId]/spaces/[spaceId]/data_sources/[dsId]/configuration.ts b/front/pages/api/w/[wId]/spaces/[spaceId]/data_sources/[dsId]/configuration.ts index 5b2476565fcc..48ccb8638165 100644 --- a/front/pages/api/w/[wId]/spaces/[spaceId]/data_sources/[dsId]/configuration.ts +++ b/front/pages/api/w/[wId]/spaces/[spaceId]/data_sources/[dsId]/configuration.ts @@ -67,6 +67,16 @@ async function handler( }); } + if (dataSource.space.isConversations()) { + return apiError(req, res, { + status_code: 404, + api_error: { + type: "space_not_found", + message: "The space you're trying to access was not found", + }, + }); + } + // Only Slack & Webcrawler connectors have configurations. // SlackConfiguration.botEnabled can only be updated from a Poke route. // So these routes are currently only for Webcrawler connectors. diff --git a/front/pages/api/w/[wId]/spaces/[spaceId]/data_sources/[dsId]/documents/[documentId]/index.ts b/front/pages/api/w/[wId]/spaces/[spaceId]/data_sources/[dsId]/documents/[documentId]/index.ts index f3d7e59be610..0cdf33e71ff8 100644 --- a/front/pages/api/w/[wId]/spaces/[spaceId]/data_sources/[dsId]/documents/[documentId]/index.ts +++ b/front/pages/api/w/[wId]/spaces/[spaceId]/data_sources/[dsId]/documents/[documentId]/index.ts @@ -68,6 +68,16 @@ async function handler( }); } + if (dataSource.space.isConversations()) { + return apiError(req, res, { + status_code: 404, + api_error: { + type: "space_not_found", + message: "The space you're trying to access was not found", + }, + }); + } + switch (req.method) { case "PATCH": if (!dataSource.canWrite(auth)) { diff --git a/front/pages/api/w/[wId]/spaces/[spaceId]/data_sources/[dsId]/documents/index.ts b/front/pages/api/w/[wId]/spaces/[spaceId]/data_sources/[dsId]/documents/index.ts index 4eb71cadfe1d..b833e54c611e 100644 --- a/front/pages/api/w/[wId]/spaces/[spaceId]/data_sources/[dsId]/documents/index.ts +++ b/front/pages/api/w/[wId]/spaces/[spaceId]/data_sources/[dsId]/documents/index.ts @@ -57,6 +57,16 @@ async function handler( }); } + if (dataSource.space.isConversations()) { + return apiError(req, res, { + status_code: 404, + api_error: { + type: "space_not_found", + message: "The space you're trying to access was not found", + }, + }); + } + switch (req.method) { case "POST": if (!dataSource.canWrite(auth)) { diff --git a/front/pages/api/w/[wId]/spaces/[spaceId]/data_sources/[dsId]/index.ts b/front/pages/api/w/[wId]/spaces/[spaceId]/data_sources/[dsId]/index.ts index 6e6b24fccd04..81155a2e1a81 100644 --- a/front/pages/api/w/[wId]/spaces/[spaceId]/data_sources/[dsId]/index.ts +++ b/front/pages/api/w/[wId]/spaces/[spaceId]/data_sources/[dsId]/index.ts @@ -39,7 +39,7 @@ async function handler( } const space = await SpaceResource.fetchById(auth, spaceId); - if (!space) { + if (!space || space.isConversations()) { return apiError(req, res, { status_code: 404, api_error: { diff --git a/front/pages/api/w/[wId]/spaces/[spaceId]/data_sources/[dsId]/tables/[tableId]/index.ts b/front/pages/api/w/[wId]/spaces/[spaceId]/data_sources/[dsId]/tables/[tableId]/index.ts index 29a7f9aa9db8..3fbf012b9472 100644 --- a/front/pages/api/w/[wId]/spaces/[spaceId]/data_sources/[dsId]/tables/[tableId]/index.ts +++ b/front/pages/api/w/[wId]/spaces/[spaceId]/data_sources/[dsId]/tables/[tableId]/index.ts @@ -63,6 +63,16 @@ async function handler( }); } + if (dataSource.space.isConversations()) { + return apiError(req, res, { + status_code: 404, + api_error: { + type: "space_not_found", + message: "The space you're trying to access was not found", + }, + }); + } + switch (req.method) { case "PATCH": if (!dataSource.canWrite(auth)) { diff --git a/front/pages/api/w/[wId]/spaces/[spaceId]/data_sources/[dsId]/tables/index.ts b/front/pages/api/w/[wId]/spaces/[spaceId]/data_sources/[dsId]/tables/index.ts index 6fecee5d5cfc..0431c1f0aee1 100644 --- a/front/pages/api/w/[wId]/spaces/[spaceId]/data_sources/[dsId]/tables/index.ts +++ b/front/pages/api/w/[wId]/spaces/[spaceId]/data_sources/[dsId]/tables/index.ts @@ -53,6 +53,16 @@ async function handler( }); } + if (dataSource.space.isConversations()) { + return apiError(req, res, { + status_code: 404, + api_error: { + type: "space_not_found", + message: "The space you're trying to access was not found", + }, + }); + } + switch (req.method) { case "POST": if (!dataSource.canWrite(auth)) { diff --git a/front/pages/api/w/[wId]/spaces/[spaceId]/data_sources/index.ts b/front/pages/api/w/[wId]/spaces/[spaceId]/data_sources/index.ts index 4e642fa9befe..45f6ec88d56c 100644 --- a/front/pages/api/w/[wId]/spaces/[spaceId]/data_sources/index.ts +++ b/front/pages/api/w/[wId]/spaces/[spaceId]/data_sources/index.ts @@ -103,7 +103,7 @@ async function handler( } const space = await SpaceResource.fetchById(auth, spaceId); - if (!space) { + if (!space || space.isConversations()) { return apiError(req, res, { status_code: 404, api_error: { diff --git a/front/pages/api/w/[wId]/spaces/[spaceId]/index.ts b/front/pages/api/w/[wId]/spaces/[spaceId]/index.ts index 885d6fb8a58a..4eebad6f0a19 100644 --- a/front/pages/api/w/[wId]/spaces/[spaceId]/index.ts +++ b/front/pages/api/w/[wId]/spaces/[spaceId]/index.ts @@ -58,7 +58,7 @@ async function handler( } const space = await SpaceResource.fetchById(auth, spaceId); - if (!space || !space.canList(auth)) { + if (!space || space.isConversations()) { return apiError(req, res, { status_code: 404, api_error: { diff --git a/front/pages/api/w/[wId]/spaces/[spaceId]/members.ts b/front/pages/api/w/[wId]/spaces/[spaceId]/members.ts index 4910d360f638..73ec4a21e615 100644 --- a/front/pages/api/w/[wId]/spaces/[spaceId]/members.ts +++ b/front/pages/api/w/[wId]/spaces/[spaceId]/members.ts @@ -31,7 +31,7 @@ async function handler( } const space = await SpaceResource.fetchById(auth, spaceId); - if (!space) { + if (!space || space.isConversations()) { return apiError(req, res, { status_code: 404, api_error: { diff --git a/front/pages/api/w/[wId]/spaces/index.ts b/front/pages/api/w/[wId]/spaces/index.ts index f791ac48a603..f92dd60bcb71 100644 --- a/front/pages/api/w/[wId]/spaces/index.ts +++ b/front/pages/api/w/[wId]/spaces/index.ts @@ -68,6 +68,9 @@ async function handler( spaces = await SpaceResource.listWorkspaceSpacesAsMember(auth); } + // Filter out conversation spaces. + spaces = spaces.filter((s) => s.kind !== "conversations"); + return res.status(200).json({ spaces: spaces.map((s) => s.toJSON()), }); diff --git a/front/temporal/scrub_workspace/activities.ts b/front/temporal/scrub_workspace/activities.ts index 7666d51e0b86..b6d69dd9ee8c 100644 --- a/front/temporal/scrub_workspace/activities.ts +++ b/front/temporal/scrub_workspace/activities.ts @@ -152,8 +152,10 @@ async function archiveAssistants(auth: Authenticator) { } async function deleteDatasources(auth: Authenticator) { - const globalAndSystemSpaces = - await SpaceResource.listWorkspaceDefaultSpaces(auth); + const globalAndSystemSpaces = await SpaceResource.listWorkspaceDefaultSpaces( + auth, + { includeConversationsSpace: true } + ); // Retrieve and delete all data sources associated with the system and global spaces. // Others will be deleted when deleting the spaces. diff --git a/sdks/js/src/types.ts b/sdks/js/src/types.ts index 12347c932618..2d974328c4db 100644 --- a/sdks/js/src/types.ts +++ b/sdks/js/src/types.ts @@ -1386,6 +1386,7 @@ const SpaceKindSchema = FlexibleEnumSchema([ "global", "system", "public", + "conversations", ]); const SpaceTypeSchema = z.object({ diff --git a/types/src/front/space.ts b/types/src/front/space.ts index 40fec52e32cb..0a7deee6515c 100644 --- a/types/src/front/space.ts +++ b/types/src/front/space.ts @@ -1,6 +1,14 @@ -const SPACE_KINDS = ["regular", "global", "system", "public"] as const; +export const UNIQUE_SPACE_KINDS = [ + "global", + "system", + "conversations", +] as const; + +const SPACE_KINDS = [...UNIQUE_SPACE_KINDS, "public", "regular"] as const; + export type SpaceKind = (typeof SPACE_KINDS)[number]; +export type UniqueSpaceKind = (typeof UNIQUE_SPACE_KINDS)[number]; export type SpaceType = { createdAt: number; groupIds: string[]; @@ -10,3 +18,7 @@ export type SpaceType = { sId: string; updatedAt: number; }; + +export function isUniqueSpaceKind(kind: SpaceKind): kind is UniqueSpaceKind { + return UNIQUE_SPACE_KINDS.includes(kind as UniqueSpaceKind); +}