mirror of
https://github.com/rowboatlabs/rowboat.git
synced 2026-05-31 19:15:17 +02:00
ddd refactor - projects (#208)
This commit is contained in:
parent
4b095d16cc
commit
580ecc7f98
72 changed files with 2460 additions and 624 deletions
|
|
@ -1,37 +1,32 @@
|
|||
"use server";
|
||||
import { z } from "zod";
|
||||
import {
|
||||
listToolkits as libListToolkits,
|
||||
listTools as libListTools,
|
||||
getConnectedAccount as libGetConnectedAccount,
|
||||
listAuthConfigs as libListAuthConfigs,
|
||||
createAuthConfig as libCreateAuthConfig,
|
||||
getToolkit as libGetToolkit,
|
||||
createConnectedAccount as libCreateConnectedAccount,
|
||||
ZToolkit,
|
||||
ZGetToolkitResponse,
|
||||
ZTool,
|
||||
ZListResponse,
|
||||
ZCreateConnectedAccountResponse,
|
||||
ZAuthScheme,
|
||||
ZCredentials,
|
||||
} from "@/app/lib/composio/composio";
|
||||
import { ComposioConnectedAccount } from "@/app/lib/types/project_types";
|
||||
import { getProjectConfig, projectAuthCheck } from "./project.actions";
|
||||
import { projectsCollection } from "../lib/mongodb";
|
||||
import { ZToolkit, ZGetToolkitResponse, ZTool, ZListResponse, ZCreateConnectedAccountResponse, ZAuthScheme, ZCredentials } from "@/app/lib/composio/composio";
|
||||
import { ComposioConnectedAccount } from "@/src/entities/models/project";
|
||||
import { container } from "@/di/container";
|
||||
import { ICreateComposioTriggerDeploymentController } from "@/src/interface-adapters/controllers/composio-trigger-deployments/create-composio-trigger-deployment.controller";
|
||||
import { IListComposioTriggerDeploymentsController } from "@/src/interface-adapters/controllers/composio-trigger-deployments/list-composio-trigger-deployments.controller";
|
||||
import { IDeleteComposioTriggerDeploymentController } from "@/src/interface-adapters/controllers/composio-trigger-deployments/delete-composio-trigger-deployment.controller";
|
||||
import { IListComposioTriggerTypesController } from "@/src/interface-adapters/controllers/composio-trigger-deployments/list-composio-trigger-types.controller";
|
||||
import { IDeleteComposioConnectedAccountController } from "@/src/interface-adapters/controllers/composio/delete-composio-connected-account.controller";
|
||||
import { IDeleteComposioConnectedAccountController } from "@/src/interface-adapters/controllers/projects/delete-composio-connected-account.controller";
|
||||
import { authCheck } from "./auth.actions";
|
||||
import { ICreateComposioManagedConnectedAccountController } from "@/src/interface-adapters/controllers/projects/create-composio-managed-connected-account.controller";
|
||||
import { ICreateCustomConnectedAccountController } from "@/src/interface-adapters/controllers/projects/create-custom-connected-account.controller";
|
||||
import { ISyncConnectedAccountController } from "@/src/interface-adapters/controllers/projects/sync-connected-account.controller";
|
||||
import { IListComposioToolkitsController } from "@/src/interface-adapters/controllers/projects/list-composio-toolkits.controller";
|
||||
import { IGetComposioToolkitController } from "@/src/interface-adapters/controllers/projects/get-composio-toolkit.controller";
|
||||
import { IListComposioToolsController } from "@/src/interface-adapters/controllers/projects/list-composio-tools.controller";
|
||||
|
||||
const createComposioTriggerDeploymentController = container.resolve<ICreateComposioTriggerDeploymentController>("createComposioTriggerDeploymentController");
|
||||
const listComposioTriggerDeploymentsController = container.resolve<IListComposioTriggerDeploymentsController>("listComposioTriggerDeploymentsController");
|
||||
const deleteComposioTriggerDeploymentController = container.resolve<IDeleteComposioTriggerDeploymentController>("deleteComposioTriggerDeploymentController");
|
||||
const listComposioTriggerTypesController = container.resolve<IListComposioTriggerTypesController>("listComposioTriggerTypesController");
|
||||
const deleteComposioConnectedAccountController = container.resolve<IDeleteComposioConnectedAccountController>("deleteComposioConnectedAccountController");
|
||||
const createComposioManagedConnectedAccountController = container.resolve<ICreateComposioManagedConnectedAccountController>("createComposioManagedConnectedAccountController");
|
||||
const createCustomConnectedAccountController = container.resolve<ICreateCustomConnectedAccountController>("createCustomConnectedAccountController");
|
||||
const syncConnectedAccountController = container.resolve<ISyncConnectedAccountController>("syncConnectedAccountController");
|
||||
const listComposioToolkitsController = container.resolve<IListComposioToolkitsController>("listComposioToolkitsController");
|
||||
const getComposioToolkitController = container.resolve<IGetComposioToolkitController>("getComposioToolkitController");
|
||||
const listComposioToolsController = container.resolve<IListComposioToolsController>("listComposioToolsController");
|
||||
|
||||
const ZCreateCustomConnectedAccountRequest = z.object({
|
||||
toolkitSlug: z.string(),
|
||||
|
|
@ -43,164 +38,72 @@ const ZCreateCustomConnectedAccountRequest = z.object({
|
|||
});
|
||||
|
||||
export async function listToolkits(projectId: string, cursor: string | null = null): Promise<z.infer<ReturnType<typeof ZListResponse<typeof ZToolkit>>>> {
|
||||
await projectAuthCheck(projectId);
|
||||
return await libListToolkits(cursor);
|
||||
const user = await authCheck();
|
||||
return await listComposioToolkitsController.execute({
|
||||
caller: 'user',
|
||||
userId: user._id,
|
||||
projectId,
|
||||
cursor,
|
||||
});
|
||||
}
|
||||
|
||||
export async function getToolkit(projectId: string, toolkitSlug: string): Promise<z.infer<typeof ZGetToolkitResponse>> {
|
||||
await projectAuthCheck(projectId);
|
||||
return await libGetToolkit(toolkitSlug);
|
||||
const user = await authCheck();
|
||||
return await getComposioToolkitController.execute({
|
||||
caller: 'user',
|
||||
userId: user._id,
|
||||
projectId,
|
||||
toolkitSlug,
|
||||
});
|
||||
}
|
||||
|
||||
export async function listTools(projectId: string, toolkitSlug: string, searchQuery: string | null, cursor: string | null = null): Promise<z.infer<ReturnType<typeof ZListResponse<typeof ZTool>>>> {
|
||||
await projectAuthCheck(projectId);
|
||||
return await libListTools(toolkitSlug, searchQuery, cursor);
|
||||
const user = await authCheck();
|
||||
return await listComposioToolsController.execute({
|
||||
caller: 'user',
|
||||
userId: user._id,
|
||||
projectId,
|
||||
toolkitSlug,
|
||||
searchQuery,
|
||||
cursor,
|
||||
});
|
||||
}
|
||||
|
||||
export async function createComposioManagedOauth2ConnectedAccount(projectId: string, toolkitSlug: string, callbackUrl: string): Promise<z.infer<typeof ZCreateConnectedAccountResponse>> {
|
||||
await projectAuthCheck(projectId);
|
||||
|
||||
// fetch managed auth configs
|
||||
const configs = await libListAuthConfigs(toolkitSlug, null, true);
|
||||
|
||||
// check if managed oauth2 config exists
|
||||
let authConfigId: string | undefined = undefined;
|
||||
const authConfig = configs.items.find(config => config.auth_scheme === 'OAUTH2' && config.is_composio_managed);
|
||||
authConfigId = authConfig?.id;
|
||||
if (!authConfig) {
|
||||
// create a new managed oauth2 auth config
|
||||
const newAuthConfig = await libCreateAuthConfig({
|
||||
toolkit: {
|
||||
slug: toolkitSlug,
|
||||
},
|
||||
auth_config: {
|
||||
type: 'use_composio_managed_auth',
|
||||
name: 'composio-managed-oauth2',
|
||||
},
|
||||
});
|
||||
authConfigId = newAuthConfig.auth_config.id;
|
||||
}
|
||||
if (!authConfigId) {
|
||||
throw new Error(`No managed oauth2 auth config found for toolkit ${toolkitSlug}`);
|
||||
}
|
||||
|
||||
// create new connected account
|
||||
const response = await libCreateConnectedAccount({
|
||||
auth_config: {
|
||||
id: authConfigId,
|
||||
},
|
||||
connection: {
|
||||
user_id: projectId,
|
||||
callback_url: callbackUrl,
|
||||
},
|
||||
const user = await authCheck();
|
||||
return await createComposioManagedConnectedAccountController.execute({
|
||||
caller: 'user',
|
||||
userId: user._id,
|
||||
projectId,
|
||||
toolkitSlug,
|
||||
callbackUrl,
|
||||
});
|
||||
|
||||
// update project with new connected account
|
||||
const key = `composioConnectedAccounts.${toolkitSlug}`;
|
||||
const data: z.infer<typeof ComposioConnectedAccount> = {
|
||||
id: response.id,
|
||||
authConfigId: authConfigId,
|
||||
status: 'INITIATED',
|
||||
createdAt: new Date().toISOString(),
|
||||
lastUpdatedAt: new Date().toISOString(),
|
||||
}
|
||||
await projectsCollection.updateOne({ _id: projectId }, { $set: { [key]: data } });
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
export async function createCustomConnectedAccount(projectId: string, request: z.infer<typeof ZCreateCustomConnectedAccountRequest>): Promise<z.infer<typeof ZCreateConnectedAccountResponse>> {
|
||||
await projectAuthCheck(projectId);
|
||||
|
||||
// first, create the auth config
|
||||
const authConfig = await libCreateAuthConfig({
|
||||
toolkit: {
|
||||
slug: request.toolkitSlug,
|
||||
},
|
||||
auth_config: {
|
||||
type: 'use_custom_auth',
|
||||
authScheme: request.authConfig.authScheme,
|
||||
credentials: request.authConfig.credentials,
|
||||
name: `pid-${projectId}-${Date.now()}`,
|
||||
},
|
||||
const user = await authCheck();
|
||||
return await createCustomConnectedAccountController.execute({
|
||||
caller: 'user',
|
||||
userId: user._id,
|
||||
projectId,
|
||||
toolkitSlug: request.toolkitSlug,
|
||||
authConfig: request.authConfig,
|
||||
callbackUrl: request.callbackUrl,
|
||||
});
|
||||
|
||||
// then, create the connected account
|
||||
let state = undefined;
|
||||
if (request.authConfig.authScheme !== 'OAUTH2') {
|
||||
state = {
|
||||
authScheme: request.authConfig.authScheme,
|
||||
val: {
|
||||
status: 'ACTIVE' as const,
|
||||
...request.authConfig.credentials,
|
||||
},
|
||||
};
|
||||
}
|
||||
const response = await libCreateConnectedAccount({
|
||||
auth_config: {
|
||||
id: authConfig.auth_config.id,
|
||||
},
|
||||
connection: {
|
||||
state,
|
||||
user_id: projectId,
|
||||
callback_url: request.callbackUrl,
|
||||
},
|
||||
});
|
||||
|
||||
// update project with new connected account
|
||||
const key = `composioConnectedAccounts.${request.toolkitSlug}`;
|
||||
const data: z.infer<typeof ComposioConnectedAccount> = {
|
||||
id: response.id,
|
||||
authConfigId: authConfig.auth_config.id,
|
||||
status: 'INITIATED',
|
||||
createdAt: new Date().toISOString(),
|
||||
lastUpdatedAt: new Date().toISOString(),
|
||||
}
|
||||
await projectsCollection.updateOne({ _id: projectId }, { $set: { [key]: data } });
|
||||
|
||||
// return the connected account
|
||||
return response;
|
||||
}
|
||||
|
||||
export async function syncConnectedAccount(projectId: string, toolkitSlug: string, connectedAccountId: string): Promise<z.infer<typeof ComposioConnectedAccount>> {
|
||||
await projectAuthCheck(projectId);
|
||||
|
||||
// ensure that the connected account belongs to this project
|
||||
const project = await getProjectConfig(projectId);
|
||||
const account = project.composioConnectedAccounts?.[toolkitSlug];
|
||||
if (!account || account.id !== connectedAccountId) {
|
||||
throw new Error(`Connected account ${connectedAccountId} not found in project ${projectId}`);
|
||||
}
|
||||
|
||||
// if account is already active, nothing to sync
|
||||
if (account.status === 'ACTIVE') {
|
||||
return account;
|
||||
}
|
||||
|
||||
// get the connected account
|
||||
const response = await libGetConnectedAccount(connectedAccountId);
|
||||
|
||||
// update project with new connected account
|
||||
const key = `composioConnectedAccounts.${response.toolkit.slug}`;
|
||||
switch (response.status) {
|
||||
case 'INITIALIZING':
|
||||
case 'INITIATED':
|
||||
account.status = 'INITIATED';
|
||||
break;
|
||||
case 'ACTIVE':
|
||||
account.status = 'ACTIVE';
|
||||
break;
|
||||
default:
|
||||
account.status = 'FAILED';
|
||||
break;
|
||||
}
|
||||
account.lastUpdatedAt = new Date().toISOString();
|
||||
await projectsCollection.updateOne({ _id: projectId }, { $set: { [key]: account } });
|
||||
|
||||
return account;
|
||||
const user = await authCheck();
|
||||
return await syncConnectedAccountController.execute({
|
||||
caller: 'user',
|
||||
userId: user._id,
|
||||
projectId,
|
||||
toolkitSlug,
|
||||
connectedAccountId,
|
||||
});
|
||||
}
|
||||
|
||||
export async function deleteConnectedAccount(projectId: string, toolkitSlug: string, connectedAccountId: string): Promise<boolean> {
|
||||
export async function deleteConnectedAccount(projectId: string, toolkitSlug: string): Promise<boolean> {
|
||||
const user = await authCheck();
|
||||
|
||||
await deleteComposioConnectedAccountController.execute({
|
||||
|
|
@ -208,7 +111,6 @@ export async function deleteConnectedAccount(projectId: string, toolkitSlug: str
|
|||
userId: user._id,
|
||||
projectId,
|
||||
toolkitSlug,
|
||||
connectedAccountId,
|
||||
});
|
||||
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -1,12 +1,13 @@
|
|||
'use server';
|
||||
|
||||
import { projectsCollection } from '../lib/mongodb';
|
||||
import { z } from 'zod';
|
||||
import { projectAuthCheck } from './project.actions';
|
||||
import { CustomMcpServer } from '../lib/types/project_types';
|
||||
import { CustomMcpServer } from "@/src/entities/models/project";
|
||||
import { getMcpClient } from '../lib/mcp';
|
||||
import { WorkflowTool } from '../lib/types/workflow_types';
|
||||
import { authCheck } from './auth.actions';
|
||||
import { container } from '@/di/container';
|
||||
import { IAddCustomMcpServerController } from '@/src/interface-adapters/controllers/projects/add-custom-mcp-server.controller';
|
||||
import { IRemoveCustomMcpServerController } from '@/src/interface-adapters/controllers/projects/remove-custom-mcp-server.controller';
|
||||
|
||||
type McpServerType = z.infer<typeof CustomMcpServer>;
|
||||
|
||||
|
|
@ -22,26 +23,30 @@ function validateUrl(url: string): string {
|
|||
}
|
||||
}
|
||||
|
||||
const addCustomMcpServerController = container.resolve<IAddCustomMcpServerController>('addCustomMcpServerController');
|
||||
const removeCustomMcpServerController = container.resolve<IRemoveCustomMcpServerController>('removeCustomMcpServerController');
|
||||
|
||||
export async function addServer(projectId: string, name: string, server: McpServerType): Promise<void> {
|
||||
await projectAuthCheck(projectId);
|
||||
|
||||
// Validate the server URL
|
||||
const user = await authCheck();
|
||||
// validate early for UX; use-case will validate again
|
||||
validateUrl(server.serverUrl);
|
||||
|
||||
// Update the customMcpServers record with the server
|
||||
await projectsCollection.updateOne(
|
||||
{ _id: projectId },
|
||||
{ $set: { [`customMcpServers.${name}`]: server } }
|
||||
);
|
||||
await addCustomMcpServerController.execute({
|
||||
caller: 'user',
|
||||
userId: user._id,
|
||||
projectId,
|
||||
name,
|
||||
server,
|
||||
});
|
||||
}
|
||||
|
||||
export async function removeServer(projectId: string, name: string): Promise<void> {
|
||||
await projectAuthCheck(projectId);
|
||||
|
||||
await projectsCollection.updateOne(
|
||||
{ _id: projectId },
|
||||
{ $unset: { [`customMcpServers.${name}`]: "" } }
|
||||
);
|
||||
const user = await authCheck();
|
||||
await removeCustomMcpServerController.execute({
|
||||
caller: 'user',
|
||||
userId: user._id,
|
||||
projectId,
|
||||
name,
|
||||
});
|
||||
}
|
||||
|
||||
export async function fetchTools(serverUrl: string, serverName: string): Promise<z.infer<typeof WorkflowTool>[]> {
|
||||
|
|
|
|||
|
|
@ -1,36 +1,43 @@
|
|||
'use server';
|
||||
import { redirect } from "next/navigation";
|
||||
import { db, projectsCollection } from "../lib/mongodb";
|
||||
import { z } from 'zod';
|
||||
import crypto from 'crypto';
|
||||
import { revalidatePath } from "next/cache";
|
||||
import { container } from "@/di/container";
|
||||
import { redirect } from "next/navigation";
|
||||
import { templates } from "../lib/project_templates";
|
||||
import { authCheck } from "./auth.actions";
|
||||
import { User, WithStringId } from "../lib/types/types";
|
||||
import { ApiKey } from "@/src/entities/models/api-key";
|
||||
import { Project } from "../lib/types/project_types";
|
||||
import { Project } from "@/src/entities/models/project";
|
||||
import { USE_AUTH } from "../lib/feature_flags";
|
||||
import { authorizeUserAction } from "./billing.actions";
|
||||
import { Workflow } from "../lib/types/workflow_types";
|
||||
import { IProjectActionAuthorizationPolicy } from "@/src/application/policies/project-action-authorization.policy";
|
||||
import { ICreateApiKeyController } from "@/src/interface-adapters/controllers/api-keys/create-api-key.controller";
|
||||
import { IListApiKeysController } from "@/src/interface-adapters/controllers/api-keys/list-api-keys.controller";
|
||||
import { IDeleteApiKeyController } from "@/src/interface-adapters/controllers/api-keys/delete-api-key.controller";
|
||||
import { IApiKeysRepository } from "@/src/application/repositories/api-keys.repository.interface";
|
||||
import { IProjectMembersRepository } from "@/src/application/repositories/project-members.repository.interface";
|
||||
import { IDataSourcesRepository } from "@/src/application/repositories/data-sources.repository.interface";
|
||||
import { IDataSourceDocsRepository } from "@/src/application/repositories/data-source-docs.repository.interface";
|
||||
import { container } from "@/di/container";
|
||||
import { qdrantClient } from "../lib/qdrant";
|
||||
import { ICreateProjectController } from "@/src/interface-adapters/controllers/projects/create-project.controller";
|
||||
import { BillingError } from "@/src/entities/errors/common";
|
||||
import { IFetchProjectController } from "@/src/interface-adapters/controllers/projects/fetch-project.controller";
|
||||
import { IListProjectsController } from "@/src/interface-adapters/controllers/projects/list-projects.controller";
|
||||
import { IRotateSecretController } from "@/src/interface-adapters/controllers/projects/rotate-secret.controller";
|
||||
import { IUpdateWebhookUrlController } from "@/src/interface-adapters/controllers/projects/update-webhook-url.controller";
|
||||
import { IUpdateProjectNameController } from "@/src/interface-adapters/controllers/projects/update-project-name.controller";
|
||||
import { IDeleteProjectController } from "@/src/interface-adapters/controllers/projects/delete-project.controller";
|
||||
import { IUpdateDraftWorkflowController } from "@/src/interface-adapters/controllers/projects/update-draft-workflow.controller";
|
||||
import { IUpdateLiveWorkflowController } from "@/src/interface-adapters/controllers/projects/update-live-workflow.controller";
|
||||
import { IRevertToLiveWorkflowController } from "@/src/interface-adapters/controllers/projects/revert-to-live-workflow.controller";
|
||||
|
||||
const projectActionAuthorizationPolicy = container.resolve<IProjectActionAuthorizationPolicy>('projectActionAuthorizationPolicy');
|
||||
const createApiKeyController = container.resolve<ICreateApiKeyController>('createApiKeyController');
|
||||
const listApiKeysController = container.resolve<IListApiKeysController>('listApiKeysController');
|
||||
const deleteApiKeyController = container.resolve<IDeleteApiKeyController>('deleteApiKeyController');
|
||||
const apiKeysRepository = container.resolve<IApiKeysRepository>('apiKeysRepository');
|
||||
const projectMembersRepository = container.resolve<IProjectMembersRepository>('projectMembersRepository');
|
||||
const dataSourcesRepository = container.resolve<IDataSourcesRepository>('dataSourcesRepository');
|
||||
const dataSourceDocsRepository = container.resolve<IDataSourceDocsRepository>('dataSourceDocsRepository');
|
||||
const createProjectController = container.resolve<ICreateProjectController>('createProjectController');
|
||||
const fetchProjectController = container.resolve<IFetchProjectController>('fetchProjectController');
|
||||
const listProjectsController = container.resolve<IListProjectsController>('listProjectsController');
|
||||
const rotateSecretController = container.resolve<IRotateSecretController>('rotateSecretController');
|
||||
const updateWebhookUrlController = container.resolve<IUpdateWebhookUrlController>('updateWebhookUrlController');
|
||||
const updateProjectNameController = container.resolve<IUpdateProjectNameController>('updateProjectNameController');
|
||||
const deleteProjectController = container.resolve<IDeleteProjectController>('deleteProjectController');
|
||||
const updateDraftWorkflowController = container.resolve<IUpdateDraftWorkflowController>('updateDraftWorkflowController');
|
||||
const updateLiveWorkflowController = container.resolve<IUpdateLiveWorkflowController>('updateLiveWorkflowController');
|
||||
const revertToLiveWorkflowController = container.resolve<IRevertToLiveWorkflowController>('revertToLiveWorkflowController');
|
||||
|
||||
export async function listTemplates() {
|
||||
const templatesArray = Object.entries(templates)
|
||||
|
|
@ -55,143 +62,105 @@ export async function projectAuthCheck(projectId: string) {
|
|||
});
|
||||
}
|
||||
|
||||
async function createBaseProject(
|
||||
name: string,
|
||||
user: WithStringId<z.infer<typeof User>>,
|
||||
workflow?: z.infer<typeof Workflow>
|
||||
): Promise<{ id: string } | { billingError: string }> {
|
||||
// fetch project count for this user
|
||||
const projectCount = await projectsCollection.countDocuments({
|
||||
createdByUserId: user._id,
|
||||
});
|
||||
// billing limit check
|
||||
const authResponse = await authorizeUserAction({
|
||||
type: 'create_project',
|
||||
data: {
|
||||
existingProjectCount: projectCount,
|
||||
},
|
||||
});
|
||||
if (!authResponse.success) {
|
||||
return { billingError: authResponse.error || 'Billing error' };
|
||||
}
|
||||
|
||||
// choose a fallback name
|
||||
if (!name) {
|
||||
name = `Assistant ${projectCount + 1}`;
|
||||
}
|
||||
|
||||
const projectId = crypto.randomUUID();
|
||||
const chatClientId = crypto.randomBytes(16).toString('base64url');
|
||||
const secret = crypto.randomBytes(32).toString('hex');
|
||||
|
||||
// Create project
|
||||
await projectsCollection.insertOne({
|
||||
_id: projectId,
|
||||
name,
|
||||
createdAt: (new Date()).toISOString(),
|
||||
lastUpdatedAt: (new Date()).toISOString(),
|
||||
createdByUserId: user._id,
|
||||
draftWorkflow: workflow,
|
||||
liveWorkflow: workflow,
|
||||
chatClientId,
|
||||
secret,
|
||||
testRunCounter: 0,
|
||||
});
|
||||
|
||||
// Add user to project
|
||||
await projectMembersRepository.create({
|
||||
userId: user._id,
|
||||
projectId,
|
||||
});
|
||||
|
||||
// Add first api key
|
||||
await createApiKey(projectId);
|
||||
|
||||
return { id: projectId };
|
||||
}
|
||||
|
||||
export async function createProject(formData: FormData): Promise<{ id: string } | { billingError: string }> {
|
||||
const user = await authCheck();
|
||||
const name = formData.get('name') as string | null;
|
||||
const templateKey = formData.get('template') as string | null;
|
||||
|
||||
const { agents, prompts, tools, startAgent } = templates[templateKey || 'default'];
|
||||
const response = await createBaseProject(name || '', user, {
|
||||
agents,
|
||||
prompts,
|
||||
tools,
|
||||
startAgent,
|
||||
lastUpdatedAt: (new Date()).toISOString(),
|
||||
});
|
||||
if ('billingError' in response) {
|
||||
return response;
|
||||
}
|
||||
try {
|
||||
const project = await createProjectController.execute({
|
||||
userId: user._id,
|
||||
data: {
|
||||
name: name || '',
|
||||
mode: {
|
||||
template: templateKey || 'default',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const projectId = response.id;
|
||||
return { id: projectId };
|
||||
return { id: project.id };
|
||||
} catch (error) {
|
||||
if (error instanceof BillingError) {
|
||||
return { billingError: error.message };
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
export async function createProjectFromWorkflowJson(formData: FormData): Promise<{ id: string } | { billingError: string }> {
|
||||
const user = await authCheck();
|
||||
const name = formData.get('name') as string | null;
|
||||
|
||||
const workflowJson = formData.get('workflowJson') as string;
|
||||
const workflow = Workflow.parse(JSON.parse(workflowJson));
|
||||
const response = await createBaseProject(name || 'Imported project', user, {
|
||||
...workflow,
|
||||
lastUpdatedAt: (new Date()).toISOString(),
|
||||
});
|
||||
if ('billingError' in response) {
|
||||
return response;
|
||||
}
|
||||
|
||||
const projectId = response.id;
|
||||
return { id: projectId };
|
||||
try {
|
||||
const project = await createProjectController.execute({
|
||||
userId: user._id,
|
||||
data: {
|
||||
name: name || '',
|
||||
mode: {
|
||||
workflowJson,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
return { id: project.id };
|
||||
} catch (error) {
|
||||
if (error instanceof BillingError) {
|
||||
return { billingError: error.message };
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
export async function getProjectConfig(projectId: string): Promise<WithStringId<z.infer<typeof Project>>> {
|
||||
await projectAuthCheck(projectId);
|
||||
const project = await projectsCollection.findOne({
|
||||
_id: projectId,
|
||||
export async function fetchProject(projectId: string): Promise<z.infer<typeof Project>> {
|
||||
const user = await authCheck();
|
||||
const project = await fetchProjectController.execute({
|
||||
caller: 'user',
|
||||
userId: user._id,
|
||||
projectId,
|
||||
});
|
||||
|
||||
if (!project) {
|
||||
throw new Error('Project config not found');
|
||||
throw new Error('Project not found');
|
||||
}
|
||||
|
||||
return project;
|
||||
}
|
||||
|
||||
export async function listProjects(): Promise<z.infer<typeof Project>[]> {
|
||||
const user = await authCheck();
|
||||
const memberships = [];
|
||||
|
||||
const projects = [];
|
||||
let cursor = undefined;
|
||||
do {
|
||||
const result = await projectMembersRepository.findByUserId(user._id, cursor);
|
||||
memberships.push(...result.items);
|
||||
const result = await listProjectsController.execute({
|
||||
userId: user._id,
|
||||
cursor,
|
||||
});
|
||||
projects.push(...result.items);
|
||||
cursor = result.nextCursor;
|
||||
} while (cursor);
|
||||
const projectIds = memberships.map((m) => m.projectId);
|
||||
const projects = await projectsCollection.find({
|
||||
_id: { $in: projectIds },
|
||||
}).toArray();
|
||||
|
||||
return projects;
|
||||
}
|
||||
|
||||
export async function rotateSecret(projectId: string): Promise<string> {
|
||||
await projectAuthCheck(projectId);
|
||||
const secret = crypto.randomBytes(32).toString('hex');
|
||||
await projectsCollection.updateOne(
|
||||
{ _id: projectId },
|
||||
{ $set: { secret } }
|
||||
);
|
||||
return secret;
|
||||
const user = await authCheck();
|
||||
return await rotateSecretController.execute({
|
||||
caller: 'user',
|
||||
userId: user._id,
|
||||
projectId,
|
||||
});
|
||||
}
|
||||
|
||||
export async function updateWebhookUrl(projectId: string, url: string) {
|
||||
await projectAuthCheck(projectId);
|
||||
await projectsCollection.updateOne(
|
||||
{ _id: projectId },
|
||||
{ $set: { webhookUrl: url } }
|
||||
);
|
||||
const user = await authCheck();
|
||||
await updateWebhookUrlController.execute({
|
||||
caller: 'user',
|
||||
userId: user._id,
|
||||
projectId,
|
||||
url,
|
||||
});
|
||||
}
|
||||
|
||||
export async function createApiKey(projectId: string): Promise<z.infer<typeof ApiKey>> {
|
||||
|
|
@ -223,98 +192,51 @@ export async function listApiKeys(projectId: string): Promise<z.infer<typeof Api
|
|||
}
|
||||
|
||||
export async function updateProjectName(projectId: string, name: string) {
|
||||
await projectAuthCheck(projectId);
|
||||
await projectsCollection.updateOne({ _id: projectId }, { $set: { name } });
|
||||
revalidatePath(`/projects/${projectId}`, 'layout');
|
||||
}
|
||||
|
||||
interface McpServerDeletionError {
|
||||
serverName: string;
|
||||
error: string;
|
||||
const user = await authCheck();
|
||||
await updateProjectNameController.execute({
|
||||
caller: 'user',
|
||||
userId: user._id,
|
||||
projectId,
|
||||
name,
|
||||
});
|
||||
}
|
||||
|
||||
export async function deleteProject(projectId: string) {
|
||||
await projectAuthCheck(projectId);
|
||||
|
||||
// delete api keys
|
||||
await apiKeysRepository.deleteAll(projectId);
|
||||
|
||||
// delete data sources data
|
||||
await dataSourceDocsRepository.deleteByProjectId(projectId);
|
||||
await dataSourcesRepository.deleteByProjectId(projectId);
|
||||
await qdrantClient.delete("embeddings", {
|
||||
filter: {
|
||||
must: [
|
||||
{ key: "projectId", match: { value: projectId } },
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
// delete project members
|
||||
await projectMembersRepository.deleteByProjectId(projectId);
|
||||
|
||||
// delete workflow versions
|
||||
await db.collection('agent_workflows').deleteMany({
|
||||
const user = await authCheck();
|
||||
await deleteProjectController.execute({
|
||||
caller: 'user',
|
||||
userId: user._id,
|
||||
projectId,
|
||||
});
|
||||
|
||||
// delete scenarios
|
||||
await db.collection('test_scenarios').deleteMany({
|
||||
projectId,
|
||||
});
|
||||
|
||||
// delete project
|
||||
await projectsCollection.deleteOne({
|
||||
_id: projectId,
|
||||
});
|
||||
|
||||
redirect('/projects');
|
||||
}
|
||||
|
||||
export async function saveWorkflow(projectId: string, workflow: z.infer<typeof Workflow>) {
|
||||
await projectAuthCheck(projectId);
|
||||
|
||||
// update the project's draft workflow
|
||||
workflow.lastUpdatedAt = new Date().toISOString();
|
||||
await projectsCollection.updateOne({
|
||||
_id: projectId,
|
||||
}, {
|
||||
$set: {
|
||||
draftWorkflow: workflow,
|
||||
},
|
||||
const user = await authCheck();
|
||||
await updateDraftWorkflowController.execute({
|
||||
caller: 'user',
|
||||
userId: user._id,
|
||||
projectId,
|
||||
workflow,
|
||||
});
|
||||
}
|
||||
|
||||
export async function publishWorkflow(projectId: string, workflow: z.infer<typeof Workflow>) {
|
||||
await projectAuthCheck(projectId);
|
||||
|
||||
// update the project's draft workflow
|
||||
workflow.lastUpdatedAt = new Date().toISOString();
|
||||
await projectsCollection.updateOne({
|
||||
_id: projectId,
|
||||
}, {
|
||||
$set: {
|
||||
liveWorkflow: workflow,
|
||||
},
|
||||
const user = await authCheck();
|
||||
await updateLiveWorkflowController.execute({
|
||||
caller: 'user',
|
||||
userId: user._id,
|
||||
projectId,
|
||||
workflow,
|
||||
});
|
||||
}
|
||||
|
||||
export async function revertToLiveWorkflow(projectId: string) {
|
||||
await projectAuthCheck(projectId);
|
||||
|
||||
const project = await getProjectConfig(projectId);
|
||||
const workflow = project.liveWorkflow;
|
||||
|
||||
if (!workflow) {
|
||||
throw new Error('No live workflow found');
|
||||
}
|
||||
|
||||
workflow.lastUpdatedAt = new Date().toISOString();
|
||||
await projectsCollection.updateOne({
|
||||
_id: projectId,
|
||||
}, {
|
||||
$set: {
|
||||
draftWorkflow: workflow,
|
||||
},
|
||||
const user = await authCheck();
|
||||
await revertToLiveWorkflowController.execute({
|
||||
caller: 'user',
|
||||
userId: user._id,
|
||||
projectId,
|
||||
});
|
||||
}
|
||||
|
|
@ -1,16 +1,11 @@
|
|||
import { getResponse } from "@/app/lib/agents";
|
||||
import { projectsCollection, twilioConfigsCollection, twilioInboundCallsCollection } from "@/app/lib/mongodb";
|
||||
import { twilioConfigsCollection, twilioInboundCallsCollection } from "@/app/lib/mongodb";
|
||||
import { PrefixLogger } from "@/app/lib/utils";
|
||||
import VoiceResponse from "twilio/lib/twiml/VoiceResponse";
|
||||
import { z } from "zod";
|
||||
import { TwilioInboundCall } from "@/app/lib/types/voice_types";
|
||||
import { hangup, reject, XmlResponse, ZStandardRequestParams } from "../utils";
|
||||
|
||||
export async function POST(request: Request) {
|
||||
let logger = new PrefixLogger("twilioInboundCall");
|
||||
logger.log("Received inbound call request");
|
||||
const recvdAt = new Date();
|
||||
|
||||
/*
|
||||
form data example
|
||||
...
|
||||
|
|
@ -44,6 +39,13 @@ export async function POST(request: Request) {
|
|||
FromState: 'PXXXXXXX'
|
||||
}
|
||||
*/
|
||||
export async function POST(request: Request) {
|
||||
return new Response('Not implemented', { status: 501 });
|
||||
/*
|
||||
let logger = new PrefixLogger("twilioInboundCall");
|
||||
logger.log("Received inbound call request");
|
||||
const recvdAt = new Date();
|
||||
|
||||
// parse and validate form data
|
||||
const formData = await request.formData();
|
||||
logger.log('request body:', JSON.stringify(Object.fromEntries(formData)));
|
||||
|
|
@ -67,6 +69,7 @@ export async function POST(request: Request) {
|
|||
const project = await projectsCollection.findOne({
|
||||
_id: projectId,
|
||||
});
|
||||
const project = null;
|
||||
if (!project) {
|
||||
logger.log(`Project ${projectId} not found`);
|
||||
return reject('rejected');
|
||||
|
|
@ -114,4 +117,5 @@ export async function POST(request: Request) {
|
|||
action: `/api/twilio/turn/${data.CallSid}`,
|
||||
});
|
||||
return XmlResponse(response);
|
||||
*/
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import { getResponse } from "@/app/lib/agents";
|
||||
import { projectsCollection, twilioInboundCallsCollection } from "@/app/lib/mongodb";
|
||||
import { twilioInboundCallsCollection } from "@/app/lib/mongodb";
|
||||
import { PrefixLogger } from "@/app/lib/utils";
|
||||
import VoiceResponse from "twilio/lib/twiml/VoiceResponse";
|
||||
import { z } from "zod";
|
||||
|
|
@ -15,6 +15,8 @@ export async function POST(
|
|||
request: Request,
|
||||
{ params }: { params: Promise<{ callSid: string }> }
|
||||
) {
|
||||
return new Response('Not implemented', { status: 501 });
|
||||
/*
|
||||
const { callSid } = await params;
|
||||
let logger = new PrefixLogger(`turn:${callSid}`);
|
||||
logger.log("Received turn");
|
||||
|
|
@ -93,4 +95,5 @@ export async function POST(
|
|||
action: `/api/twilio/turn/${callSid}`,
|
||||
});
|
||||
return XmlResponse(response);
|
||||
*/
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import { NextRequest } from "next/server";
|
||||
import { apiV1 } from "rowboat-shared";
|
||||
import { projectsCollection, chatsCollection, chatMessagesCollection } from "../../../../../../lib/mongodb";
|
||||
import { chatsCollection, chatMessagesCollection } from "../../../../../../lib/mongodb";
|
||||
import { z } from "zod";
|
||||
import { ObjectId, WithId } from "mongodb";
|
||||
import { authCheck } from "../../../utils";
|
||||
|
|
@ -112,6 +112,8 @@ export async function POST(
|
|||
req: NextRequest,
|
||||
{ params }: { params: Promise<{ chatId: string }> }
|
||||
): Promise<Response> {
|
||||
return new Response('Not implemented', { status: 501 });
|
||||
/*
|
||||
return await authCheck(req, async (session) => {
|
||||
const { chatId } = await params;
|
||||
const logger = new PrefixLogger(`widget-chat:${chatId}`);
|
||||
|
|
@ -234,4 +236,5 @@ export async function POST(
|
|||
_id: undefined,
|
||||
});
|
||||
});
|
||||
*/
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,9 +4,10 @@ import { SignJWT, jwtVerify } from "jose";
|
|||
import { z } from "zod";
|
||||
import { Session } from "../../utils";
|
||||
import { apiV1 } from "rowboat-shared";
|
||||
import { projectsCollection } from "../../../../../lib/mongodb";
|
||||
|
||||
export async function POST(req: NextRequest): Promise<Response> {
|
||||
return new Response('Not implemented', { status: 501 });
|
||||
/*
|
||||
return await clientIdCheck(req, async (projectId) => {
|
||||
// decode and validate JWT
|
||||
const json = await req.json();
|
||||
|
|
@ -52,4 +53,5 @@ export async function POST(req: NextRequest): Promise<Response> {
|
|||
|
||||
return Response.json(response);
|
||||
});
|
||||
*/
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import { NextRequest } from "next/server";
|
||||
import { z } from "zod";
|
||||
import { jwtVerify } from "jose";
|
||||
import { projectsCollection } from "../../../lib/mongodb";
|
||||
|
||||
export const Session = z.object({
|
||||
userId: z.string(),
|
||||
|
|
@ -18,6 +17,8 @@ export const Session = z.object({
|
|||
in the request headers and calls the provided handler function.
|
||||
*/
|
||||
export async function clientIdCheck(req: NextRequest, handler: (projectId: string) => Promise<Response>): Promise<Response> {
|
||||
return new Response('Not implemented', { status: 501 });
|
||||
/*
|
||||
const clientId = req.headers.get('x-client-id')?.trim();
|
||||
if (!clientId) {
|
||||
return Response.json({ error: "Missing client ID in request" }, { status: 400 });
|
||||
|
|
@ -31,6 +32,7 @@ export async function clientIdCheck(req: NextRequest, handler: (projectId: strin
|
|||
// set the project id in the request headers
|
||||
req.headers.set('x-project-id', project._id);
|
||||
return await handler(project._id);
|
||||
*/
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -42,6 +44,8 @@ export async function clientIdCheck(req: NextRequest, handler: (projectId: strin
|
|||
provided handler function.
|
||||
*/
|
||||
export async function authCheck(req: NextRequest, handler: (session: z.infer<typeof Session>) => Promise<Response>): Promise<Response> {
|
||||
return new Response('Not implemented', { status: 501 });
|
||||
/*
|
||||
const authHeader = req.headers.get('Authorization');
|
||||
if (!authHeader?.startsWith('Bearer ')) {
|
||||
return Response.json({ error: "Authorization header must be a Bearer token" }, { status: 400 });
|
||||
|
|
@ -59,4 +63,5 @@ export async function authCheck(req: NextRequest, handler: (session: z.infer<typ
|
|||
}
|
||||
|
||||
return await handler(session.payload as z.infer<typeof Session>);
|
||||
*/
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ import crypto from "crypto";
|
|||
// Internal dependencies
|
||||
import { embeddingModel } from '../lib/embedding';
|
||||
import { getMcpClient } from "./mcp";
|
||||
import { projectsCollection } from "./mongodb";
|
||||
import { qdrantClient } from '../lib/qdrant';
|
||||
import { EmbeddingRecord } from "./types/datasource_types";
|
||||
import { WorkflowAgent, WorkflowTool } from "./types/workflow_types";
|
||||
|
|
@ -20,6 +19,7 @@ import { DataSource } from "@/src/entities/models/data-source";
|
|||
import { IDataSourcesRepository } from "@/src/application/repositories/data-sources.repository.interface";
|
||||
import { IDataSourceDocsRepository } from "@/src/application/repositories/data-source-docs.repository.interface";
|
||||
import { container } from "@/di/container";
|
||||
import { IProjectsRepository } from "@/src/application/repositories/projects.repository.interface";
|
||||
|
||||
// Provider configuration
|
||||
const PROVIDER_API_KEY = process.env.PROVIDER_API_KEY || process.env.OPENAI_API_KEY || '';
|
||||
|
|
@ -195,9 +195,9 @@ export async function invokeWebhookTool(
|
|||
logger.log(`name: ${name}`);
|
||||
logger.log(`input: ${JSON.stringify(input)}`);
|
||||
|
||||
const project = await projectsCollection.findOne({
|
||||
"_id": projectId,
|
||||
});
|
||||
const projectsRepository = container.resolve<IProjectsRepository>('projectsRepository');
|
||||
|
||||
const project = await projectsRepository.fetch(projectId);
|
||||
if (!project) {
|
||||
throw new Error('Project not found');
|
||||
}
|
||||
|
|
@ -278,7 +278,8 @@ export async function invokeMcpTool(
|
|||
logger.log(`mcpServerName: ${mcpServerName}`);
|
||||
|
||||
// Get project configuration
|
||||
const project = await projectsCollection.findOne({ _id: projectId });
|
||||
const projectsRepository = container.resolve<IProjectsRepository>('projectsRepository');
|
||||
const project = await projectsRepository.fetch(projectId);
|
||||
if (!project) {
|
||||
throw new Error(`project ${projectId} not found`);
|
||||
}
|
||||
|
|
@ -317,7 +318,8 @@ export async function invokeComposioTool(
|
|||
|
||||
let connectedAccountId: string | undefined = undefined;
|
||||
if (!noAuth) {
|
||||
const project = await projectsCollection.findOne({ _id: projectId });
|
||||
const projectsRepository = container.resolve<IProjectsRepository>('projectsRepository');
|
||||
const project = await projectsRepository.fetch(projectId);
|
||||
if (!project) {
|
||||
throw new Error(`project ${projectId} not found`);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,10 +2,12 @@ import { WithStringId } from './types/types';
|
|||
import { z } from 'zod';
|
||||
import { Customer, AuthorizeRequest, AuthorizeResponse, LogUsageRequest, UsageResponse, CustomerPortalSessionResponse, PricesResponse, UpdateSubscriptionPlanRequest, UpdateSubscriptionPlanResponse, ModelsResponse, UsageItem } from './types/billing_types';
|
||||
import { ObjectId } from 'mongodb';
|
||||
import { projectsCollection, usersCollection } from './mongodb';
|
||||
import { usersCollection } from './mongodb';
|
||||
import { redirect } from 'next/navigation';
|
||||
import { getUserFromSessionId, requireAuth } from './auth';
|
||||
import { USE_BILLING } from './feature_flags';
|
||||
import { container } from '@/di/container';
|
||||
import { IProjectsRepository } from '@/src/application/repositories/projects.repository.interface';
|
||||
|
||||
const BILLING_API_URL = process.env.BILLING_API_URL || 'http://billing';
|
||||
const BILLING_API_KEY = process.env.BILLING_API_KEY || 'test';
|
||||
|
|
@ -37,19 +39,28 @@ export class UsageTracker{
|
|||
}
|
||||
}
|
||||
|
||||
export async function getCustomerIdForProject(projectId: string): Promise<string> {
|
||||
const project = await projectsCollection.findOne({ _id: projectId });
|
||||
if (!project) {
|
||||
throw new Error("Project not found");
|
||||
}
|
||||
const user = await usersCollection.findOne({ _id: new ObjectId(project.createdByUserId) });
|
||||
export async function getCustomerForUserId(userId: string): Promise<WithStringId<z.infer<typeof Customer>> | null> {
|
||||
const user = await usersCollection.findOne({ _id: new ObjectId(userId) });
|
||||
if (!user) {
|
||||
throw new Error("User not found");
|
||||
}
|
||||
if (!user.billingCustomerId) {
|
||||
return null;
|
||||
}
|
||||
return await getBillingCustomer(user.billingCustomerId);
|
||||
}
|
||||
|
||||
export async function getCustomerIdForProject(projectId: string): Promise<string> {
|
||||
const projectsRepository = container.resolve<IProjectsRepository>('projectsRepository');
|
||||
const project = await projectsRepository.fetch(projectId);
|
||||
if (!project) {
|
||||
throw new Error("Project not found");
|
||||
}
|
||||
const customer = await getCustomerForUserId(project.createdByUserId);
|
||||
if (!customer) {
|
||||
throw new Error("User has no billing customer id");
|
||||
}
|
||||
return user.billingCustomerId;
|
||||
return customer._id;
|
||||
}
|
||||
|
||||
export async function getBillingCustomer(id: string): Promise<WithStringId<z.infer<typeof Customer>> | null> {
|
||||
|
|
|
|||
|
|
@ -1,84 +0,0 @@
|
|||
import { agentWorkflowsCollection, projectsCollection } from "./mongodb";
|
||||
import { ObjectId } from "mongodb";
|
||||
import { Workflow } from "./types/workflow_types";
|
||||
import { z } from "zod";
|
||||
|
||||
export async function migrate_versioned_workflows(projectId: string) {
|
||||
// fetch project data
|
||||
const project = await projectsCollection.findOne({ _id: projectId });
|
||||
if (!project) {
|
||||
throw new Error(`Project ${projectId} not found`);
|
||||
}
|
||||
|
||||
// Skip if project already has workflows migrated
|
||||
if (project.draftWorkflow && project.liveWorkflow) {
|
||||
console.log(`Project ${projectId} already has migrated workflows, skipping...`);
|
||||
return;
|
||||
}
|
||||
|
||||
const updateFields: { draftWorkflow?: z.infer<typeof Workflow>; liveWorkflow?: z.infer<typeof Workflow> } = {};
|
||||
|
||||
// 1. Migrate published workflow to liveWorkflow
|
||||
if (project.publishedWorkflowId) {
|
||||
const publishedWorkflow = await agentWorkflowsCollection.findOne({
|
||||
_id: new ObjectId(project.publishedWorkflowId)
|
||||
});
|
||||
|
||||
if (publishedWorkflow) {
|
||||
// @ts-ignore - Workflow type mismatch
|
||||
const { _id, name, createdAt, projectId, ...rest } = publishedWorkflow;
|
||||
updateFields.liveWorkflow = rest;
|
||||
console.log(`Found published workflow ${project.publishedWorkflowId} for project ${projectId}`);
|
||||
} else {
|
||||
console.warn(`Published workflow ${project.publishedWorkflowId} not found for project ${projectId}`);
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Find the latest workflow for draft (that isn't the published one)
|
||||
const workflows = await agentWorkflowsCollection.find({
|
||||
projectId,
|
||||
}).sort({ lastUpdatedAt: -1 }).toArray();
|
||||
|
||||
let latestWorkflow;
|
||||
for (const workflow of workflows) {
|
||||
// Skip if this is the published workflow
|
||||
if (project.publishedWorkflowId && workflow._id.toString() === project.publishedWorkflowId) {
|
||||
continue;
|
||||
}
|
||||
|
||||
latestWorkflow = workflow;
|
||||
break;
|
||||
}
|
||||
|
||||
// Handle cases where no published workflow exists
|
||||
if (!updateFields.liveWorkflow && latestWorkflow) {
|
||||
// No published workflow found, use latest as live workflow
|
||||
// @ts-ignore - Workflow type mismatch
|
||||
const { _id, name, createdAt, projectId, ...rest } = latestWorkflow;
|
||||
updateFields.liveWorkflow = rest;
|
||||
console.log(`No published workflow found, using latest workflow as live for project ${projectId}`);
|
||||
}
|
||||
|
||||
// Set draft workflow
|
||||
if (latestWorkflow) {
|
||||
// @ts-ignore - Workflow type mismatch
|
||||
const { _id, name, createdAt, projectId, ...rest } = latestWorkflow;
|
||||
updateFields.draftWorkflow = rest;
|
||||
console.log(`Found draft workflow for project ${projectId}`);
|
||||
} else if (updateFields.liveWorkflow) {
|
||||
// No separate draft found, use the published workflow as draft too
|
||||
updateFields.draftWorkflow = updateFields.liveWorkflow;
|
||||
console.log(`No separate draft found, using live workflow as draft for project ${projectId}`);
|
||||
}
|
||||
|
||||
// 3. Update the project with the migrated workflows
|
||||
if (Object.keys(updateFields).length > 0) {
|
||||
await projectsCollection.updateOne(
|
||||
{ _id: projectId },
|
||||
{ $set: updateFields }
|
||||
);
|
||||
console.log(`Successfully migrated ${Object.keys(updateFields).length} workflow(s) for project ${projectId}`);
|
||||
} else {
|
||||
console.log(`No workflows found to migrate for project ${projectId}`);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
import { MongoClient } from "mongodb";
|
||||
import { User } from "./types/types";
|
||||
import { Workflow } from "./types/workflow_types";
|
||||
import { Project } from "./types/project_types";
|
||||
import { TwilioConfig, TwilioInboundCall } from "./types/voice_types";
|
||||
import { z } from 'zod';
|
||||
import { apiV1 } from "rowboat-shared";
|
||||
|
|
@ -9,7 +8,6 @@ import { apiV1 } from "rowboat-shared";
|
|||
const client = new MongoClient(process.env["MONGODB_CONNECTION_STRING"] || "mongodb://localhost:27017");
|
||||
|
||||
export const db = client.db("rowboat");
|
||||
export const projectsCollection = db.collection<z.infer<typeof Project>>("projects");
|
||||
export const agentWorkflowsCollection = db.collection<z.infer<typeof Workflow>>("agent_workflows");
|
||||
export const chatsCollection = db.collection<z.infer<typeof apiV1.Chat>>("chats");
|
||||
export const chatMessagesCollection = db.collection<z.infer<typeof apiV1.ChatMessage>>("chat_messages");
|
||||
|
|
|
|||
|
|
@ -1,37 +0,0 @@
|
|||
import { z } from "zod";
|
||||
import { MCPServer } from "./types";
|
||||
import { Workflow } from "./workflow_types";
|
||||
|
||||
export const ComposioConnectedAccount = z.object({
|
||||
id: z.string(),
|
||||
authConfigId: z.string(),
|
||||
status: z.enum([
|
||||
'INITIATED',
|
||||
'ACTIVE',
|
||||
'FAILED',
|
||||
]),
|
||||
createdAt: z.string().datetime(),
|
||||
lastUpdatedAt: z.string().datetime(),
|
||||
});
|
||||
|
||||
export const CustomMcpServer = z.object({
|
||||
serverUrl: z.string(),
|
||||
});
|
||||
|
||||
export const Project = z.object({
|
||||
_id: z.string().uuid(),
|
||||
name: z.string(),
|
||||
createdAt: z.string().datetime(),
|
||||
lastUpdatedAt: z.string().datetime(),
|
||||
createdByUserId: z.string(),
|
||||
secret: z.string(),
|
||||
chatClientId: z.string(),
|
||||
draftWorkflow: Workflow.optional(),
|
||||
liveWorkflow: Workflow.optional(),
|
||||
webhookUrl: z.string().optional(),
|
||||
publishedWorkflowId: z.string().optional(),
|
||||
testRunCounter: z.number().default(0),
|
||||
mcpServers: z.array(MCPServer).optional(),
|
||||
composioConnectedAccounts: z.record(z.string(), ComposioConnectedAccount).optional(),
|
||||
customMcpServers: z.record(z.string(), CustomMcpServer).optional(),
|
||||
});
|
||||
|
|
@ -4,7 +4,7 @@ import { Metadata } from "next";
|
|||
import { Spinner, Dropdown, DropdownMenu, DropdownItem, DropdownTrigger, Modal, ModalContent, ModalHeader, ModalBody, ModalFooter, Input, useDisclosure, Divider, Textarea } from "@heroui/react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { ReactNode, useEffect, useState } from "react";
|
||||
import { getProjectConfig, updateProjectName, updateWebhookUrl, deleteProject, rotateSecret } from "../../../actions/project.actions";
|
||||
import { fetchProject, updateProjectName, updateWebhookUrl, deleteProject, rotateSecret } from "../../../actions/project.actions";
|
||||
import { CopyButton } from "../../../../components/common/copy-button";
|
||||
import { InputField } from "../../../lib/components/input-field";
|
||||
import { EyeIcon, EyeOffIcon, Settings, Plus, MoreVertical } from "lucide-react";
|
||||
|
|
@ -64,7 +64,7 @@ export function BasicSettingsSection({
|
|||
|
||||
useEffect(() => {
|
||||
setLoading(true);
|
||||
getProjectConfig(projectId).then((project) => {
|
||||
fetchProject(projectId).then((project) => {
|
||||
setProjectName(project?.name);
|
||||
setLoading(false);
|
||||
});
|
||||
|
|
@ -117,7 +117,7 @@ export function SecretSection({
|
|||
|
||||
useEffect(() => {
|
||||
setLoading(true);
|
||||
getProjectConfig(projectId).then((project) => {
|
||||
fetchProject(projectId).then((project) => {
|
||||
setSecret(project.secret);
|
||||
setLoading(false);
|
||||
});
|
||||
|
|
@ -191,7 +191,7 @@ export function WebhookUrlSection({
|
|||
|
||||
useEffect(() => {
|
||||
setLoading(true);
|
||||
getProjectConfig(projectId).then((project) => {
|
||||
fetchProject(projectId).then((project) => {
|
||||
setWebhookUrl(project.webhookUrl || null);
|
||||
setLoading(false);
|
||||
});
|
||||
|
|
@ -230,6 +230,7 @@ export function WebhookUrlSection({
|
|||
</Section>;
|
||||
}
|
||||
|
||||
/*
|
||||
export function ChatWidgetSection({
|
||||
projectId,
|
||||
chatWidgetHost,
|
||||
|
|
@ -242,7 +243,7 @@ export function ChatWidgetSection({
|
|||
|
||||
useEffect(() => {
|
||||
setLoading(true);
|
||||
getProjectConfig(projectId).then((project) => {
|
||||
fetchProject(projectId).then((project) => {
|
||||
setChatClientId(project.chatClientId);
|
||||
setLoading(false);
|
||||
});
|
||||
|
|
@ -282,6 +283,7 @@ export function ChatWidgetSection({
|
|||
/>}
|
||||
</Section>;
|
||||
}
|
||||
*/
|
||||
|
||||
export function DeleteProjectSection({
|
||||
projectId,
|
||||
|
|
@ -298,7 +300,7 @@ export function DeleteProjectSection({
|
|||
|
||||
useEffect(() => {
|
||||
setLoading(true);
|
||||
getProjectConfig(projectId).then((project) => {
|
||||
fetchProject(projectId).then((project) => {
|
||||
setProjectName(project.name);
|
||||
setLoading(false);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -3,10 +3,9 @@
|
|||
import { ReactNode, useEffect, useState, useCallback } from "react";
|
||||
import { Spinner, Dropdown, Modal, ModalContent, ModalHeader, ModalBody, ModalFooter, Input, useDisclosure } from "@heroui/react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { getProjectConfig, createApiKey, deleteApiKey, listApiKeys, deleteProject, rotateSecret, updateProjectName, saveWorkflow } from "../../../../actions/project.actions";
|
||||
import { fetchProject, createApiKey, deleteApiKey, listApiKeys, deleteProject, rotateSecret, updateProjectName, saveWorkflow } from "../../../../actions/project.actions";
|
||||
import { CopyButton } from "../../../../../components/common/copy-button";
|
||||
import { EyeIcon, EyeOffIcon, PlusIcon, Trash2Icon } from "lucide-react";
|
||||
import { WithStringId } from "../../../../lib/types/types";
|
||||
import { ApiKey } from "@/src/entities/models/api-key";
|
||||
import { z } from "zod";
|
||||
import { RelativeTime } from "@primer/react";
|
||||
|
|
@ -14,7 +13,7 @@ import { Label } from "../../../../lib/components/label";
|
|||
import { sectionHeaderStyles, sectionDescriptionStyles } from './shared-styles';
|
||||
import { clsx } from "clsx";
|
||||
import { InputField } from "../../../../lib/components/input-field";
|
||||
import { Project, ComposioConnectedAccount } from "../../../../lib/types/project_types";
|
||||
import { ComposioConnectedAccount } from "@/src/entities/models/project";
|
||||
import { getToolkit, listComposioTriggerDeployments, deleteComposioTriggerDeployment } from "../../../../actions/composio.actions";
|
||||
import { deleteConnectedAccount } from "../../../../actions/composio.actions";
|
||||
import { PictureImg } from "@/components/ui/picture-img";
|
||||
|
|
@ -80,7 +79,7 @@ function ProjectNameSection({
|
|||
|
||||
useEffect(() => {
|
||||
setLoading(true);
|
||||
getProjectConfig(projectId).then((project) => {
|
||||
fetchProject(projectId).then((project) => {
|
||||
setProjectName(project?.name);
|
||||
setLoading(false);
|
||||
});
|
||||
|
|
@ -140,7 +139,7 @@ function SecretSection({ projectId }: { projectId: string }) {
|
|||
|
||||
useEffect(() => {
|
||||
setLoading(true);
|
||||
getProjectConfig(projectId).then((project) => {
|
||||
fetchProject(projectId).then((project) => {
|
||||
setSecret(project.secret);
|
||||
setLoading(false);
|
||||
});
|
||||
|
|
@ -361,13 +360,14 @@ function ApiKeysSection({ projectId }: { projectId: string }) {
|
|||
</Section>;
|
||||
}
|
||||
|
||||
/*
|
||||
export function ChatWidgetSection({ projectId, chatWidgetHost }: { projectId: string, chatWidgetHost: string }) {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [chatClientId, setChatClientId] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
setLoading(true);
|
||||
getProjectConfig(projectId).then((project) => {
|
||||
fetchProject(projectId).then((project) => {
|
||||
setChatClientId(project.chatClientId);
|
||||
setLoading(false);
|
||||
});
|
||||
|
|
@ -414,6 +414,7 @@ export function ChatWidgetSection({ projectId, chatWidgetHost }: { projectId: st
|
|||
</Section>
|
||||
);
|
||||
}
|
||||
*/
|
||||
|
||||
interface ConnectedToolkit {
|
||||
slug: string;
|
||||
|
|
@ -435,7 +436,7 @@ function DisconnectToolkitsSection({ projectId, onProjectConfigUpdated }: {
|
|||
const loadConnectedToolkits = useCallback(async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const project = await getProjectConfig(projectId);
|
||||
const project = await fetchProject(projectId);
|
||||
const connectedAccounts = project.composioConnectedAccounts || {};
|
||||
const workflow = project.draftWorkflow;
|
||||
|
||||
|
|
@ -502,7 +503,7 @@ function DisconnectToolkitsSection({ projectId, onProjectConfigUpdated }: {
|
|||
setDisconnectingToolkit(selectedToolkit.slug);
|
||||
try {
|
||||
// Step 1: Get current project and workflow
|
||||
const project = await getProjectConfig(projectId);
|
||||
const project = await fetchProject(projectId);
|
||||
const currentWorkflow = project.draftWorkflow;
|
||||
|
||||
if (currentWorkflow) {
|
||||
|
|
@ -541,7 +542,6 @@ function DisconnectToolkitsSection({ projectId, onProjectConfigUpdated }: {
|
|||
await deleteConnectedAccount(
|
||||
projectId,
|
||||
selectedToolkit.slug,
|
||||
selectedToolkit.connectedAccount.id
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -695,7 +695,7 @@ function DeleteProjectSection({ projectId }: { projectId: string }) {
|
|||
|
||||
useEffect(() => {
|
||||
setLoadingInitial(true);
|
||||
getProjectConfig(projectId).then((project) => {
|
||||
fetchProject(projectId).then((project) => {
|
||||
setProjectName(project.name);
|
||||
setLoadingInitial(false);
|
||||
});
|
||||
|
|
@ -803,7 +803,7 @@ export function ProjectSection({
|
|||
<div className="p-6 space-y-6">
|
||||
<ProjectIdSection projectId={projectId} />
|
||||
<ApiKeysSection projectId={projectId} />
|
||||
{useChatWidget && <ChatWidgetSection projectId={projectId} chatWidgetHost={chatWidgetHost} />}
|
||||
{/*{useChatWidget && <ChatWidgetSection projectId={projectId} chatWidgetHost={chatWidgetHost} />}*/}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import { Input } from '@/components/ui/input';
|
|||
import { Info, Plus, Trash2 } from 'lucide-react';
|
||||
import { z } from 'zod';
|
||||
import { Workflow, WorkflowTool } from '@/app/lib/types/workflow_types';
|
||||
import { getProjectConfig } from '@/app/actions/project.actions';
|
||||
import { fetchProject } from '@/app/actions/project.actions';
|
||||
import { addServer, removeServer } from '@/app/actions/custom-mcp-server.actions';
|
||||
import { fetchTools } from "@/app/actions/custom-mcp-server.actions";
|
||||
import { ServerCard } from './ServerCard';
|
||||
|
|
@ -50,7 +50,7 @@ export function CustomMcpServers({ tools: workflowTools, onAddTool }: CustomMcpS
|
|||
setLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
const project = await getProjectConfig(projectId);
|
||||
const project = await fetchProject(projectId);
|
||||
setServers(project.customMcpServers || {});
|
||||
} catch (err: any) {
|
||||
setError(err?.message || 'Failed to load servers');
|
||||
|
|
|
|||
|
|
@ -6,10 +6,10 @@ import { Button } from '@/components/ui/button';
|
|||
import { RefreshCw, Search } from 'lucide-react';
|
||||
import clsx from 'clsx';
|
||||
import { listToolkits } from '@/app/actions/composio.actions';
|
||||
import { getProjectConfig } from '@/app/actions/project.actions';
|
||||
import { fetchProject } from '@/app/actions/project.actions';
|
||||
import { z } from 'zod';
|
||||
import { ZToolkit, ZListResponse, ZTool } from '@/app/lib/composio/composio';
|
||||
import { Project } from '@/app/lib/types/project_types';
|
||||
import { Project } from "@/src/entities/models/project";
|
||||
import { ToolkitCard } from './ToolkitCard';
|
||||
import { Workflow } from '@/app/lib/types/workflow_types';
|
||||
|
||||
|
|
@ -42,7 +42,7 @@ export function SelectComposioToolkit({
|
|||
|
||||
const loadProjectConfig = useCallback(async () => {
|
||||
try {
|
||||
const config = await getProjectConfig(projectId);
|
||||
const config = await fetchProject(projectId);
|
||||
setProjectConfig(config);
|
||||
} catch (err: any) {
|
||||
console.error('Error fetching project config:', err);
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
import { useState, useEffect } from "react";
|
||||
import { Spinner, Button, Input } from "@heroui/react";
|
||||
import { getProjectConfig, updateWebhookUrl } from "@/app/actions/project.actions";
|
||||
import { fetchProject, updateWebhookUrl } from "@/app/actions/project.actions";
|
||||
import { clsx } from "clsx";
|
||||
import { ProjectWideChangeConfirmationModal } from '@/components/common/project-wide-change-confirmation-modal';
|
||||
|
||||
|
|
@ -21,7 +21,7 @@ export function WebhookConfig({ projectId }: { projectId: string }) {
|
|||
|
||||
async function loadConfig() {
|
||||
try {
|
||||
const project = await getProjectConfig(projectId);
|
||||
const project = await fetchProject(projectId);
|
||||
if (mounted) {
|
||||
setWebhookUrl(project.webhookUrl || null);
|
||||
setEditValue(project.webhookUrl || '');
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
"use client";
|
||||
import { MCPServer, WithStringId } from "../../../lib/types/types";
|
||||
import { DataSource } from "@/src/entities/models/data-source";
|
||||
import { Project } from "../../../lib/types/project_types";
|
||||
import { Project } from "@/src/entities/models/project";
|
||||
import { z } from "zod";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { WorkflowEditor } from "./workflow_editor";
|
||||
import { Spinner } from "@heroui/react";
|
||||
import { listDataSources } from "../../../actions/data-source.actions";
|
||||
import { revertToLiveWorkflow } from "@/app/actions/project.actions";
|
||||
import { getProjectConfig } from "@/app/actions/project.actions";
|
||||
import { fetchProject } from "@/app/actions/project.actions";
|
||||
import { Workflow, WorkflowTool } from "@/app/lib/types/workflow_types";
|
||||
import { getEligibleModels } from "@/app/actions/billing.actions";
|
||||
import { ModelsResponse } from "@/app/lib/types/billing_types";
|
||||
|
|
@ -31,12 +31,11 @@ export function App({
|
|||
chatWidgetHost: string;
|
||||
}) {
|
||||
const [mode, setMode] = useState<'draft' | 'live'>('draft');
|
||||
const [project, setProject] = useState<WithStringId<z.infer<typeof Project>> | null>(null);
|
||||
const [project, setProject] = useState<z.infer<typeof Project> | null>(null);
|
||||
const [dataSources, setDataSources] = useState<z.infer<typeof DataSource>[] | null>(null);
|
||||
const [projectConfig, setProjectConfig] = useState<z.infer<typeof Project> | null>(null);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [eligibleModels, setEligibleModels] = useState<z.infer<typeof ModelsResponse> | "*">("*");
|
||||
const [projectMcpServers, setProjectMcpServers] = useState<Array<z.infer<typeof MCPServer>>>([]);
|
||||
|
||||
console.log('workflow app.tsx render');
|
||||
|
||||
|
|
@ -53,7 +52,7 @@ export function App({
|
|||
dataSources,
|
||||
eligibleModels,
|
||||
] = await Promise.all([
|
||||
getProjectConfig(projectId),
|
||||
fetchProject(projectId),
|
||||
listDataSources(projectId),
|
||||
getEligibleModels(),
|
||||
]);
|
||||
|
|
@ -61,23 +60,15 @@ export function App({
|
|||
setProject(project);
|
||||
setDataSources(dataSources);
|
||||
setEligibleModels(eligibleModels);
|
||||
if (project.mcpServers) {
|
||||
setProjectMcpServers(project.mcpServers);
|
||||
}
|
||||
setLoading(false);
|
||||
}, [projectId]);
|
||||
|
||||
const handleProjectToolsUpdate = useCallback(async () => {
|
||||
// Lightweight refresh for tool-only updates
|
||||
const projectConfig = await getProjectConfig(projectId);
|
||||
const projectConfig = await fetchProject(projectId);
|
||||
|
||||
setProject(projectConfig);
|
||||
setProjectConfig(projectConfig);
|
||||
|
||||
// Update MCP servers if they changed
|
||||
if (projectConfig.mcpServers) {
|
||||
setProjectMcpServers(projectConfig.mcpServers);
|
||||
}
|
||||
}, [projectId]);
|
||||
|
||||
const handleDataSourcesUpdate = useCallback(async () => {
|
||||
|
|
@ -88,7 +79,7 @@ export function App({
|
|||
|
||||
const handleProjectConfigUpdate = useCallback(async () => {
|
||||
// Refresh project config when project name or other settings change
|
||||
const updatedProjectConfig = await getProjectConfig(projectId);
|
||||
const updatedProjectConfig = await fetchProject(projectId);
|
||||
setProject(updatedProjectConfig);
|
||||
setProjectConfig(updatedProjectConfig);
|
||||
}, [projectId]);
|
||||
|
|
@ -146,7 +137,6 @@ export function App({
|
|||
useRagUploads={useRagUploads}
|
||||
useRagS3Uploads={useRagS3Uploads}
|
||||
useRagScraping={useRagScraping}
|
||||
mcpServerUrls={projectMcpServers}
|
||||
defaultModel={defaultModel}
|
||||
eligibleModels={eligibleModels}
|
||||
onChangeMode={handleSetMode}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import { ComposioTriggerTypesPanel } from './ComposioTriggerTypesPanel';
|
|||
import { TriggerConfigForm } from './TriggerConfigForm';
|
||||
import { ToolkitAuthModal } from '../../tools/components/ToolkitAuthModal';
|
||||
import { ZToolkit } from '@/app/lib/composio/composio';
|
||||
import { Project } from '@/app/lib/types/project_types';
|
||||
import { Project } from "@/src/entities/models/project";
|
||||
|
||||
interface TriggersModalProps {
|
||||
isOpen: boolean;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import React, { forwardRef, useImperativeHandle } from "react";
|
||||
import { z } from "zod";
|
||||
import { WorkflowPrompt, WorkflowAgent, WorkflowTool, WorkflowPipeline, Workflow } from "../../../lib/types/workflow_types";
|
||||
import { Project } from "../../../lib/types/project_types";
|
||||
import { Project } from "@/src/entities/models/project";
|
||||
import { DataSource } from "@/src/entities/models/data-source";
|
||||
import { WithStringId } from "../../../lib/types/types";
|
||||
import { Dropdown, DropdownItem, DropdownTrigger, DropdownMenu } from "@heroui/react";
|
||||
|
|
@ -1353,7 +1353,7 @@ const ComposioCard = ({
|
|||
const connectedAccountId = projectConfig?.composioConnectedAccounts?.[card.slug]?.id;
|
||||
try {
|
||||
if (connectedAccountId) {
|
||||
await deleteConnectedAccount(projectId, card.slug, connectedAccountId);
|
||||
await deleteConnectedAccount(projectId, card.slug);
|
||||
}
|
||||
} catch (err) {
|
||||
// ignore error, continue to remove tools
|
||||
|
|
@ -1375,7 +1375,7 @@ const ComposioCard = ({
|
|||
setIsProcessingAuth(true);
|
||||
try {
|
||||
if (connectedAccountId) {
|
||||
await deleteConnectedAccount(projectId, card.slug, connectedAccountId);
|
||||
await deleteConnectedAccount(projectId, card.slug);
|
||||
onProjectToolsUpdated?.();
|
||||
}
|
||||
} catch (err: any) {
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
import { Metadata } from "next";
|
||||
import { App } from "./app";
|
||||
import { USE_RAG, USE_RAG_UPLOADS, USE_RAG_S3_UPLOADS, USE_RAG_SCRAPING } from "@/app/lib/feature_flags";
|
||||
import { projectsCollection } from "@/app/lib/mongodb";
|
||||
import { notFound } from "next/navigation";
|
||||
import { requireActiveBillingSubscription } from '@/app/lib/billing';
|
||||
import { migrate_versioned_workflows } from "@/app/lib/migrate_versioned_workflows";
|
||||
import { container } from "@/di/container";
|
||||
import { IProjectsRepository } from "@/src/application/repositories/projects.repository.interface";
|
||||
|
||||
const projectsRepository = container.resolve<IProjectsRepository>('projectsRepository');
|
||||
|
||||
const DEFAULT_MODEL = process.env.PROVIDER_DEFAULT_MODEL || "gpt-4.1";
|
||||
|
||||
|
|
@ -20,18 +22,11 @@ export default async function Page(
|
|||
const params = await props.params;
|
||||
await requireActiveBillingSubscription();
|
||||
console.log('->>> workflow page being rendered');
|
||||
const project = await projectsCollection.findOne({
|
||||
_id: params.projectId,
|
||||
});
|
||||
const project = await projectsRepository.fetch(params.projectId);
|
||||
if (!project) {
|
||||
notFound();
|
||||
}
|
||||
|
||||
// migrate versioned workflows for this project
|
||||
if (!project.draftWorkflow) {
|
||||
await migrate_versioned_workflows(params.projectId);
|
||||
}
|
||||
|
||||
console.log('/workflow page.tsx serve');
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import React, { useReducer, Reducer, useState, useCallback, useEffect, useRef, c
|
|||
import { MCPServer, Message, WithStringId } from "../../../lib/types/types";
|
||||
import { Workflow, WorkflowTool, WorkflowPrompt, WorkflowAgent, WorkflowPipeline } from "../../../lib/types/workflow_types";
|
||||
import { DataSource } from "@/src/entities/models/data-source";
|
||||
import { Project } from "../../../lib/types/project_types";
|
||||
import { Project } from "@/src/entities/models/project";
|
||||
import { produce, applyPatches, enablePatches, produceWithPatches, Patch } from 'immer';
|
||||
import { AgentConfig } from "../entities/agent_config";
|
||||
import { PipelineConfig } from "../entities/pipeline_config";
|
||||
|
|
@ -36,7 +36,6 @@ import { Button as CustomButton } from "@/components/ui/button";
|
|||
import { ConfigApp } from "../config/app";
|
||||
import { InputField } from "@/app/lib/components/input-field";
|
||||
import { VoiceSection } from "../config/components/voice";
|
||||
import { ChatWidgetSection } from "../config/components/project";
|
||||
import { TriggersModal } from "./components/TriggersModal";
|
||||
import { TopBar } from "./components/TopBar";
|
||||
|
||||
|
|
@ -808,7 +807,6 @@ export function WorkflowEditor({
|
|||
useRagUploads,
|
||||
useRagS3Uploads,
|
||||
useRagScraping,
|
||||
mcpServerUrls,
|
||||
defaultModel,
|
||||
projectConfig,
|
||||
eligibleModels,
|
||||
|
|
@ -827,7 +825,6 @@ export function WorkflowEditor({
|
|||
useRagUploads: boolean;
|
||||
useRagS3Uploads: boolean;
|
||||
useRagScraping: boolean;
|
||||
mcpServerUrls: Array<z.infer<typeof MCPServer>>;
|
||||
defaultModel: string;
|
||||
projectConfig: z.infer<typeof Project>;
|
||||
eligibleModels: z.infer<typeof ModelsResponse> | "*";
|
||||
|
|
@ -1499,6 +1496,7 @@ export function WorkflowEditor({
|
|||
</Modal>
|
||||
|
||||
{/* Chat Widget Modal */}
|
||||
{/*
|
||||
<Modal
|
||||
isOpen={isChatWidgetModalOpen}
|
||||
onClose={onChatWidgetModalClose}
|
||||
|
|
@ -1519,6 +1517,7 @@ export function WorkflowEditor({
|
|||
</ModalBody>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
*/}
|
||||
|
||||
{/* Triggers Management Modal */}
|
||||
<TriggersModal
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
'use client';
|
||||
|
||||
import { Project } from "../lib/types/project_types";
|
||||
import { Project } from "@/src/entities/models/project";
|
||||
import { useEffect, useState } from "react";
|
||||
import { z } from "zod";
|
||||
import { listProjects } from "../actions/project.actions";
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import { TextareaWithSend } from "@/app/components/ui/textarea-with-send";
|
|||
import { Workflow } from '../../lib/types/workflow_types';
|
||||
import { PictureImg } from '@/components/ui/picture-img';
|
||||
import { Tabs, Tab } from "@/components/ui/tabs";
|
||||
import { Project } from "@/app/lib/types/project_types";
|
||||
import { Project } from "@/src/entities/models/project";
|
||||
import { z } from "zod";
|
||||
import Link from 'next/link';
|
||||
|
||||
|
|
@ -313,8 +313,8 @@ export function BuildAssistantSection({ defaultName }: BuildAssistantSectionProp
|
|||
<div className="space-y-2">
|
||||
{currentProjects.map((project) => (
|
||||
<Link
|
||||
key={project._id}
|
||||
href={`/projects/${project._id}/workflow`}
|
||||
key={project.id}
|
||||
href={`/projects/${project.id}/workflow`}
|
||||
className="flex items-center justify-between p-4 border border-gray-200 dark:border-gray-700 rounded-lg hover:border-blue-300 dark:hover:border-blue-600 hover:bg-gray-50 dark:hover:bg-gray-700/50 transition-all group hover:shadow-sm"
|
||||
>
|
||||
<div className="flex-1 min-w-0">
|
||||
|
|
@ -325,7 +325,8 @@ export function BuildAssistantSection({ defaultName }: BuildAssistantSectionProp
|
|||
{project.name}
|
||||
</div>
|
||||
<div className="text-xs text-gray-500 dark:text-gray-400 mt-1">
|
||||
Created {new Date(project.createdAt).toLocaleDateString()} • Last updated {new Date(project.lastUpdatedAt).toLocaleDateString()}
|
||||
Created {new Date(project.createdAt).toLocaleDateString()}
|
||||
{project.lastUpdatedAt && `• Last updated ${new Date(project.lastUpdatedAt).toLocaleDateString()}`}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
'use client';
|
||||
import { Project } from "@/app/lib/types/project_types";
|
||||
import { Project } from "@/src/entities/models/project";
|
||||
import { z } from "zod";
|
||||
import { useState } from "react";
|
||||
import clsx from 'clsx';
|
||||
|
|
@ -48,8 +48,8 @@ export function ProjectList({ projects, isLoading, searchQuery }: ProjectListPro
|
|||
<div className="flex-1 overflow-y-auto" style={{ maxHeight: 'calc(100vh - 400px)' }}>
|
||||
{currentProjects.map((project) => (
|
||||
<Link
|
||||
key={project._id}
|
||||
href={`/projects/${project._id}`}
|
||||
key={project.id}
|
||||
href={`/projects/${project.id}`}
|
||||
className={clsx(
|
||||
"block px-4 py-3",
|
||||
tokens.transitions.default,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { Project } from "@/app/lib/types/project_types";
|
||||
import { Project } from "@/src/entities/models/project";
|
||||
import { z } from "zod";
|
||||
import { ProjectList } from "./project-list";
|
||||
import { HorizontalDivider } from "@/components/ui/horizontal-divider";
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ import {
|
|||
LogsIcon,
|
||||
Clock
|
||||
} from "lucide-react";
|
||||
import { getProjectConfig } from "@/app/actions/project.actions";
|
||||
import { fetchProject } from "@/app/actions/project.actions";
|
||||
import { createProjectWithOptions } from "../../lib/project-creation-utils";
|
||||
import { useTheme } from "@/app/providers/theme-provider";
|
||||
import { USE_PRODUCT_TOUR } from '@/app/lib/feature_flags';
|
||||
|
|
@ -56,7 +56,7 @@ export default function Sidebar({ projectId, useAuth, collapsed = false, onToggl
|
|||
async function fetchProjectName() {
|
||||
if (!isProjectsRoute && projectId) {
|
||||
try {
|
||||
const project = await getProjectConfig(projectId);
|
||||
const project = await fetchProject(projectId);
|
||||
setProjectName(project.name);
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch project name:', error);
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import Link from "next/link";
|
|||
import { useEffect, useState } from "react";
|
||||
import clsx from "clsx";
|
||||
import Menu from "./menu";
|
||||
import { getProjectConfig } from "@/app/actions/project.actions";
|
||||
import { fetchProject } from "@/app/actions/project.actions";
|
||||
import { FolderOpenIcon, PanelLeftCloseIcon, PanelLeftOpenIcon } from "lucide-react";
|
||||
|
||||
export function Nav({
|
||||
|
|
@ -17,7 +17,7 @@ export function Nav({
|
|||
|
||||
useEffect(() => {
|
||||
async function getProject() {
|
||||
const project = await getProjectConfig(projectId);
|
||||
const project = await fetchProject(projectId);
|
||||
setProjectName(project.name);
|
||||
}
|
||||
getProject();
|
||||
|
|
|
|||
|
|
@ -25,14 +25,12 @@ import { CreateComposioTriggerDeploymentUseCase } from "@/src/application/use-ca
|
|||
import { ListComposioTriggerDeploymentsUseCase } from "@/src/application/use-cases/composio-trigger-deployments/list-composio-trigger-deployments.use-case";
|
||||
import { DeleteComposioTriggerDeploymentUseCase } from "@/src/application/use-cases/composio-trigger-deployments/delete-composio-trigger-deployment.use-case";
|
||||
import { ListComposioTriggerTypesUseCase } from "@/src/application/use-cases/composio-trigger-deployments/list-composio-trigger-types.use-case";
|
||||
import { DeleteComposioConnectedAccountUseCase } from "@/src/application/use-cases/composio/delete-composio-connected-account.use-case";
|
||||
import { HandleCompsioWebhookRequestUseCase } from "@/src/application/use-cases/composio/webhook/handle-composio-webhook-request.use-case";
|
||||
import { MongoDBJobsRepository } from "@/src/infrastructure/repositories/mongodb.jobs.repository";
|
||||
import { CreateComposioTriggerDeploymentController } from "@/src/interface-adapters/controllers/composio-trigger-deployments/create-composio-trigger-deployment.controller";
|
||||
import { DeleteComposioTriggerDeploymentController } from "@/src/interface-adapters/controllers/composio-trigger-deployments/delete-composio-trigger-deployment.controller";
|
||||
import { ListComposioTriggerDeploymentsController } from "@/src/interface-adapters/controllers/composio-trigger-deployments/list-composio-trigger-deployments.controller";
|
||||
import { ListComposioTriggerTypesController } from "@/src/interface-adapters/controllers/composio-trigger-deployments/list-composio-trigger-types.controller";
|
||||
import { DeleteComposioConnectedAccountController } from "@/src/interface-adapters/controllers/composio/delete-composio-connected-account.controller";
|
||||
import { HandleComposioWebhookRequestController } from "@/src/interface-adapters/controllers/composio/webhook/handle-composio-webhook-request.controller";
|
||||
import { JobsWorker } from "@/src/application/workers/jobs.worker";
|
||||
import { JobRulesWorker } from "@/src/application/workers/job-rules.worker";
|
||||
|
|
@ -45,6 +43,28 @@ import { FetchJobController } from "@/src/interface-adapters/controllers/jobs/fe
|
|||
import { FetchConversationUseCase } from "@/src/application/use-cases/conversations/fetch-conversation.use-case";
|
||||
import { FetchConversationController } from "@/src/interface-adapters/controllers/conversations/fetch-conversation.controller";
|
||||
|
||||
// Projects
|
||||
import { CreateProjectUseCase } from "@/src/application/use-cases/projects/create-project.use-case";
|
||||
import { CreateProjectController } from "@/src/interface-adapters/controllers/projects/create-project.controller";
|
||||
import { DeleteComposioConnectedAccountUseCase } from "@/src/application/use-cases/projects/delete-composio-connected-account.use-case";
|
||||
import { DeleteComposioConnectedAccountController } from "@/src/interface-adapters/controllers/projects/delete-composio-connected-account.controller";
|
||||
import { CreateComposioManagedConnectedAccountUseCase } from "@/src/application/use-cases/projects/create-composio-managed-connected-account.use-case";
|
||||
import { CreateCustomConnectedAccountUseCase } from "@/src/application/use-cases/projects/create-custom-connected-account.use-case";
|
||||
import { SyncConnectedAccountUseCase } from "@/src/application/use-cases/projects/sync-connected-account.use-case";
|
||||
import { ListComposioToolkitsUseCase } from "@/src/application/use-cases/projects/list-composio-toolkits.use-case";
|
||||
import { GetComposioToolkitUseCase } from "@/src/application/use-cases/projects/get-composio-toolkit.use-case";
|
||||
import { ListComposioToolsUseCase } from "@/src/application/use-cases/projects/list-composio-tools.use-case";
|
||||
import { AddCustomMcpServerUseCase } from "@/src/application/use-cases/projects/add-custom-mcp-server.use-case";
|
||||
import { RemoveCustomMcpServerUseCase } from "@/src/application/use-cases/projects/remove-custom-mcp-server.use-case";
|
||||
import { CreateComposioManagedConnectedAccountController } from "@/src/interface-adapters/controllers/projects/create-composio-managed-connected-account.controller";
|
||||
import { CreateCustomConnectedAccountController } from "@/src/interface-adapters/controllers/projects/create-custom-connected-account.controller";
|
||||
import { SyncConnectedAccountController } from "@/src/interface-adapters/controllers/projects/sync-connected-account.controller";
|
||||
import { ListComposioToolkitsController } from "@/src/interface-adapters/controllers/projects/list-composio-toolkits.controller";
|
||||
import { GetComposioToolkitController } from "@/src/interface-adapters/controllers/projects/get-composio-toolkit.controller";
|
||||
import { ListComposioToolsController } from "@/src/interface-adapters/controllers/projects/list-composio-tools.controller";
|
||||
import { AddCustomMcpServerController } from "@/src/interface-adapters/controllers/projects/add-custom-mcp-server.controller";
|
||||
import { RemoveCustomMcpServerController } from "@/src/interface-adapters/controllers/projects/remove-custom-mcp-server.controller";
|
||||
|
||||
// Scheduled Job Rules
|
||||
import { MongoDBScheduledJobRulesRepository } from "@/src/infrastructure/repositories/mongodb.scheduled-job-rules.repository";
|
||||
import { CreateScheduledJobRuleUseCase } from "@/src/application/use-cases/scheduled-job-rules/create-scheduled-job-rule.use-case";
|
||||
|
|
@ -104,6 +124,24 @@ import { DeleteDocFromDataSourceController } from "@/src/interface-adapters/cont
|
|||
import { RecrawlWebDataSourceController } from "@/src/interface-adapters/controllers/data-sources/recrawl-web-data-source.controller";
|
||||
import { GetUploadUrlsForFilesController } from "@/src/interface-adapters/controllers/data-sources/get-upload-urls-for-files.controller";
|
||||
import { GetDownloadUrlForFileController } from "@/src/interface-adapters/controllers/data-sources/get-download-url-for-file.controller";
|
||||
import { DeleteProjectController } from "@/src/interface-adapters/controllers/projects/delete-project.controller";
|
||||
import { DeleteProjectUseCase } from "@/src/application/use-cases/projects/delete-project.use-case";
|
||||
import { ListProjectsUseCase } from "@/src/application/use-cases/projects/list-projects.use-case";
|
||||
import { ListProjectsController } from "@/src/interface-adapters/controllers/projects/list-projects.controller";
|
||||
import { FetchProjectUseCase } from "@/src/application/use-cases/projects/fetch-project.use-case";
|
||||
import { FetchProjectController } from "@/src/interface-adapters/controllers/projects/fetch-project.controller";
|
||||
import { RotateSecretUseCase } from "@/src/application/use-cases/projects/rotate-secret.use-case";
|
||||
import { RotateSecretController } from "@/src/interface-adapters/controllers/projects/rotate-secret.controller";
|
||||
import { UpdateWebhookUrlUseCase } from "@/src/application/use-cases/projects/update-webhook-url.use-case";
|
||||
import { UpdateWebhookUrlController } from "@/src/interface-adapters/controllers/projects/update-webhook-url.controller";
|
||||
import { UpdateProjectNameUseCase } from "@/src/application/use-cases/projects/update-project-name.use-case";
|
||||
import { UpdateProjectNameController } from "@/src/interface-adapters/controllers/projects/update-project-name.controller";
|
||||
import { UpdateDraftWorkflowUseCase } from "@/src/application/use-cases/projects/update-draft-workflow.use-case";
|
||||
import { UpdateDraftWorkflowController } from "@/src/interface-adapters/controllers/projects/update-draft-workflow.controller";
|
||||
import { UpdateLiveWorkflowUseCase } from "@/src/application/use-cases/projects/update-live-workflow.use-case";
|
||||
import { UpdateLiveWorkflowController } from "@/src/interface-adapters/controllers/projects/update-live-workflow.controller";
|
||||
import { RevertToLiveWorkflowUseCase } from "@/src/application/use-cases/projects/revert-to-live-workflow.use-case";
|
||||
import { RevertToLiveWorkflowController } from "@/src/interface-adapters/controllers/projects/revert-to-live-workflow.controller";
|
||||
|
||||
export const container = createContainer({
|
||||
injectionMode: InjectionMode.PROXY,
|
||||
|
|
@ -209,11 +247,50 @@ container.register({
|
|||
toggleRecurringJobRuleController: asClass(ToggleRecurringJobRuleController).singleton(),
|
||||
deleteRecurringJobRuleController: asClass(DeleteRecurringJobRuleController).singleton(),
|
||||
|
||||
// projects
|
||||
// ---
|
||||
createProjectUseCase: asClass(CreateProjectUseCase).singleton(),
|
||||
createProjectController: asClass(CreateProjectController).singleton(),
|
||||
fetchProjectUseCase: asClass(FetchProjectUseCase).singleton(),
|
||||
fetchProjectController: asClass(FetchProjectController).singleton(),
|
||||
listProjectsUseCase: asClass(ListProjectsUseCase).singleton(),
|
||||
listProjectsController: asClass(ListProjectsController).singleton(),
|
||||
rotateSecretUseCase: asClass(RotateSecretUseCase).singleton(),
|
||||
rotateSecretController: asClass(RotateSecretController).singleton(),
|
||||
updateWebhookUrlUseCase: asClass(UpdateWebhookUrlUseCase).singleton(),
|
||||
updateWebhookUrlController: asClass(UpdateWebhookUrlController).singleton(),
|
||||
updateProjectNameUseCase: asClass(UpdateProjectNameUseCase).singleton(),
|
||||
updateProjectNameController: asClass(UpdateProjectNameController).singleton(),
|
||||
updateDraftWorkflowUseCase: asClass(UpdateDraftWorkflowUseCase).singleton(),
|
||||
updateDraftWorkflowController: asClass(UpdateDraftWorkflowController).singleton(),
|
||||
updateLiveWorkflowUseCase: asClass(UpdateLiveWorkflowUseCase).singleton(),
|
||||
updateLiveWorkflowController: asClass(UpdateLiveWorkflowController).singleton(),
|
||||
revertToLiveWorkflowUseCase: asClass(RevertToLiveWorkflowUseCase).singleton(),
|
||||
revertToLiveWorkflowController: asClass(RevertToLiveWorkflowController).singleton(),
|
||||
deleteProjectUseCase: asClass(DeleteProjectUseCase).singleton(),
|
||||
deleteProjectController: asClass(DeleteProjectController).singleton(),
|
||||
deleteComposioConnectedAccountController: asClass(DeleteComposioConnectedAccountController).singleton(),
|
||||
deleteComposioConnectedAccountUseCase: asClass(DeleteComposioConnectedAccountUseCase).singleton(),
|
||||
createComposioManagedConnectedAccountUseCase: asClass(CreateComposioManagedConnectedAccountUseCase).singleton(),
|
||||
createComposioManagedConnectedAccountController: asClass(CreateComposioManagedConnectedAccountController).singleton(),
|
||||
createCustomConnectedAccountUseCase: asClass(CreateCustomConnectedAccountUseCase).singleton(),
|
||||
createCustomConnectedAccountController: asClass(CreateCustomConnectedAccountController).singleton(),
|
||||
syncConnectedAccountUseCase: asClass(SyncConnectedAccountUseCase).singleton(),
|
||||
syncConnectedAccountController: asClass(SyncConnectedAccountController).singleton(),
|
||||
listComposioToolkitsUseCase: asClass(ListComposioToolkitsUseCase).singleton(),
|
||||
listComposioToolkitsController: asClass(ListComposioToolkitsController).singleton(),
|
||||
getComposioToolkitUseCase: asClass(GetComposioToolkitUseCase).singleton(),
|
||||
getComposioToolkitController: asClass(GetComposioToolkitController).singleton(),
|
||||
listComposioToolsUseCase: asClass(ListComposioToolsUseCase).singleton(),
|
||||
listComposioToolsController: asClass(ListComposioToolsController).singleton(),
|
||||
addCustomMcpServerUseCase: asClass(AddCustomMcpServerUseCase).singleton(),
|
||||
addCustomMcpServerController: asClass(AddCustomMcpServerController).singleton(),
|
||||
removeCustomMcpServerUseCase: asClass(RemoveCustomMcpServerUseCase).singleton(),
|
||||
removeCustomMcpServerController: asClass(RemoveCustomMcpServerController).singleton(),
|
||||
|
||||
// composio
|
||||
// ---
|
||||
deleteComposioConnectedAccountUseCase: asClass(DeleteComposioConnectedAccountUseCase).singleton(),
|
||||
handleCompsioWebhookRequestUseCase: asClass(HandleCompsioWebhookRequestUseCase).singleton(),
|
||||
deleteComposioConnectedAccountController: asClass(DeleteComposioConnectedAccountController).singleton(),
|
||||
handleComposioWebhookRequestController: asClass(HandleComposioWebhookRequestController).singleton(),
|
||||
|
||||
// composio trigger deployments
|
||||
|
|
|
|||
|
|
@ -1,8 +1,147 @@
|
|||
import { z } from "zod";
|
||||
import { Project } from "@/src/entities/models/project";
|
||||
import { ComposioConnectedAccount, CustomMcpServer, Project } from "@/src/entities/models/project";
|
||||
import { Workflow } from "@/app/lib/types/workflow_types";
|
||||
import { PaginatedList } from "@/src/entities/common/paginated-list";
|
||||
|
||||
/**
|
||||
* Schema for creating a new project. Includes name, creator, and optional workflows and secret.
|
||||
*/
|
||||
export const CreateSchema = Project
|
||||
.pick({
|
||||
name: true,
|
||||
createdByUserId: true,
|
||||
secret: true,
|
||||
})
|
||||
.extend({
|
||||
workflow: Workflow.omit({ lastUpdatedAt: true }),
|
||||
});
|
||||
|
||||
/**
|
||||
* Schema for adding a Composio connected account to a project.
|
||||
* Contains the toolkit slug and account data.
|
||||
*/
|
||||
export const AddComposioConnectedAccountSchema = z.object({
|
||||
toolkitSlug: z.string(),
|
||||
data: ComposioConnectedAccount,
|
||||
});
|
||||
|
||||
/**
|
||||
* Schema for adding a custom MCP server to a project.
|
||||
* Contains the server name and server data.
|
||||
*/
|
||||
export const AddCustomMcpServerSchema = z.object({
|
||||
name: z.string(),
|
||||
data: CustomMcpServer,
|
||||
});
|
||||
|
||||
/**
|
||||
* Repository interface for managing projects and their integrations.
|
||||
*/
|
||||
export interface IProjectsRepository {
|
||||
/**
|
||||
* Creates a new project.
|
||||
* @param data - The project creation data matching CreateSchema.
|
||||
* @returns The created Project object.
|
||||
*/
|
||||
create(data: z.infer<typeof CreateSchema>): Promise<z.infer<typeof Project>>;
|
||||
|
||||
/**
|
||||
* Fetches a project by its ID.
|
||||
* @param id - The project ID.
|
||||
* @returns The Project object if found, otherwise null.
|
||||
*/
|
||||
fetch(id: string): Promise<z.infer<typeof Project> | null>;
|
||||
|
||||
/**
|
||||
* Count projects created by user
|
||||
* @param createdByUserId - The creator user ID.
|
||||
* @returns The number of projects created by the user.
|
||||
*/
|
||||
countCreatedProjects(createdByUserId: string): Promise<number>;
|
||||
|
||||
/**
|
||||
* Lists projects for a user.
|
||||
* @param userId - The user ID.
|
||||
* @returns The list of projects.
|
||||
*/
|
||||
listProjects(userId: string, cursor?: string, limit?: number): Promise<z.infer<ReturnType<typeof PaginatedList<typeof Project>>>>;
|
||||
|
||||
/**
|
||||
* Adds a Composio connected account to a project.
|
||||
* @param projectId - The project ID.
|
||||
* @param data - The connected account data.
|
||||
* @returns The updated Project object.
|
||||
*/
|
||||
addComposioConnectedAccount(projectId: string, data: z.infer<typeof AddComposioConnectedAccountSchema>): Promise<z.infer<typeof Project>>;
|
||||
|
||||
/**
|
||||
* Deletes a Composio connected account from a project.
|
||||
* @param projectId - The project ID.
|
||||
* @param toolkitSlug - The toolkit slug to remove.
|
||||
* @returns True if the account was deleted, false otherwise.
|
||||
*/
|
||||
deleteComposioConnectedAccount(projectId: string, toolkitSlug: string): Promise<boolean>;
|
||||
|
||||
/**
|
||||
* Adds a custom MCP server to a project.
|
||||
* @param projectId - The project ID.
|
||||
* @param data - The custom MCP server data.
|
||||
* @returns The updated Project object.
|
||||
*/
|
||||
addCustomMcpServer(projectId: string, data: z.infer<typeof AddCustomMcpServerSchema>): Promise<z.infer<typeof Project>>;
|
||||
|
||||
/**
|
||||
* Deletes a custom MCP server from a project.
|
||||
* @param projectId - The project ID.
|
||||
* @param name - The name of the custom MCP server to remove.
|
||||
* @returns True if the server was deleted, false otherwise.
|
||||
*/
|
||||
deleteCustomMcpServer(projectId: string, name: string): Promise<boolean>;
|
||||
|
||||
/**
|
||||
* Updates the secret for a project.
|
||||
* @param projectId - The project ID.
|
||||
* @param secret - The new secret value.
|
||||
* @returns The updated Project object.
|
||||
*/
|
||||
updateSecret(projectId: string, secret: string): Promise<z.infer<typeof Project>>;
|
||||
|
||||
/**
|
||||
* Updates the webhook URL for a project.
|
||||
* @param projectId - The project ID.
|
||||
* @param url - The new webhook URL.
|
||||
* @returns The updated Project object.
|
||||
*/
|
||||
updateWebhookUrl(projectId: string, url: string): Promise<z.infer<typeof Project>>;
|
||||
|
||||
/**
|
||||
* Updates the name of a project.
|
||||
* @param projectId - The project ID.
|
||||
* @param name - The new project name.
|
||||
* @returns The updated Project object.
|
||||
*/
|
||||
updateName(projectId: string, name: string): Promise<z.infer<typeof Project>>;
|
||||
|
||||
/**
|
||||
* Updates the draft workflow for a project.
|
||||
* @param projectId - The project ID.
|
||||
* @param workflow - The new draft workflow.
|
||||
* @returns The updated Project object.
|
||||
*/
|
||||
updateDraftWorkflow(projectId: string, workflow: z.infer<typeof Workflow>): Promise<z.infer<typeof Project>>;
|
||||
|
||||
/**
|
||||
* Updates the live workflow for a project.
|
||||
* @param projectId - The project ID.
|
||||
* @param workflow - The new live workflow.
|
||||
* @returns The updated Project object.
|
||||
*/
|
||||
updateLiveWorkflow(projectId: string, workflow: z.infer<typeof Workflow>): Promise<z.infer<typeof Project>>;
|
||||
|
||||
/**
|
||||
* Deletes a project by its ID.
|
||||
* @param projectId - The project ID.
|
||||
* @returns True if the project was deleted, false otherwise.
|
||||
*/
|
||||
delete(projectId: string): Promise<boolean>;
|
||||
}
|
||||
|
|
@ -1,5 +1,4 @@
|
|||
import { BadRequestError, NotFoundError } from '@/src/entities/errors/common';
|
||||
import { projectsCollection } from "@/app/lib/mongodb";
|
||||
import { IConversationsRepository } from "@/src/application/repositories/conversations.repository.interface";
|
||||
import { z } from "zod";
|
||||
import { Conversation } from "@/src/entities/models/conversation";
|
||||
|
|
@ -7,6 +6,7 @@ import { Workflow } from "@/app/lib/types/workflow_types";
|
|||
import { IUsageQuotaPolicy } from '../../policies/usage-quota.policy.interface';
|
||||
import { IProjectActionAuthorizationPolicy } from '../../policies/project-action-authorization.policy';
|
||||
import { Reason } from '@/src/entities/models/turn';
|
||||
import { IProjectsRepository } from '../../repositories/projects.repository.interface';
|
||||
|
||||
const inputSchema = z.object({
|
||||
caller: z.enum(["user", "api", "job_worker"]),
|
||||
|
|
@ -26,19 +26,23 @@ export class CreateConversationUseCase implements ICreateConversationUseCase {
|
|||
private readonly conversationsRepository: IConversationsRepository;
|
||||
private readonly usageQuotaPolicy: IUsageQuotaPolicy;
|
||||
private readonly projectActionAuthorizationPolicy: IProjectActionAuthorizationPolicy;
|
||||
private readonly projectsRepository: IProjectsRepository;
|
||||
|
||||
constructor({
|
||||
conversationsRepository,
|
||||
usageQuotaPolicy,
|
||||
projectActionAuthorizationPolicy,
|
||||
projectsRepository,
|
||||
}: {
|
||||
conversationsRepository: IConversationsRepository,
|
||||
usageQuotaPolicy: IUsageQuotaPolicy,
|
||||
projectActionAuthorizationPolicy: IProjectActionAuthorizationPolicy,
|
||||
projectsRepository: IProjectsRepository,
|
||||
}) {
|
||||
this.conversationsRepository = conversationsRepository;
|
||||
this.usageQuotaPolicy = usageQuotaPolicy;
|
||||
this.projectActionAuthorizationPolicy = projectActionAuthorizationPolicy;
|
||||
this.projectsRepository = projectsRepository;
|
||||
}
|
||||
|
||||
async execute(data: z.infer<typeof inputSchema>): Promise<z.infer<typeof Conversation>> {
|
||||
|
|
@ -61,9 +65,7 @@ export class CreateConversationUseCase implements ICreateConversationUseCase {
|
|||
|
||||
// if workflow is not provided, fetch workflow
|
||||
if (!workflow) {
|
||||
const project = await projectsCollection.findOne({
|
||||
_id: projectId,
|
||||
});
|
||||
const project = await this.projectsRepository.fetch(projectId);
|
||||
if (!project) {
|
||||
throw new NotFoundError('Project not found');
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,63 @@
|
|||
import { z } from "zod";
|
||||
import { IProjectsRepository } from "../../repositories/projects.repository.interface";
|
||||
import { IProjectActionAuthorizationPolicy } from "../../policies/project-action-authorization.policy";
|
||||
import { IUsageQuotaPolicy } from "../../policies/usage-quota.policy.interface";
|
||||
import { CustomMcpServer } from "@/src/entities/models/project";
|
||||
|
||||
export const InputSchema = z.object({
|
||||
caller: z.enum(["user", "api"]),
|
||||
userId: z.string().optional(),
|
||||
apiKey: z.string().optional(),
|
||||
projectId: z.string(),
|
||||
name: z.string(),
|
||||
server: CustomMcpServer,
|
||||
});
|
||||
|
||||
export interface IAddCustomMcpServerUseCase {
|
||||
execute(request: z.infer<typeof InputSchema>): Promise<void>;
|
||||
}
|
||||
|
||||
function validateHttpHttpsUrl(url: string): string {
|
||||
const parsedUrl = new URL(url);
|
||||
if (parsedUrl.protocol !== 'http:' && parsedUrl.protocol !== 'https:') {
|
||||
throw new Error('Invalid protocol');
|
||||
}
|
||||
return parsedUrl.toString();
|
||||
}
|
||||
|
||||
export class AddCustomMcpServerUseCase implements IAddCustomMcpServerUseCase {
|
||||
private readonly projectsRepository: IProjectsRepository;
|
||||
private readonly projectActionAuthorizationPolicy: IProjectActionAuthorizationPolicy;
|
||||
private readonly usageQuotaPolicy: IUsageQuotaPolicy;
|
||||
|
||||
constructor({
|
||||
projectsRepository,
|
||||
projectActionAuthorizationPolicy,
|
||||
usageQuotaPolicy,
|
||||
}: {
|
||||
projectsRepository: IProjectsRepository,
|
||||
projectActionAuthorizationPolicy: IProjectActionAuthorizationPolicy,
|
||||
usageQuotaPolicy: IUsageQuotaPolicy,
|
||||
}) {
|
||||
this.projectsRepository = projectsRepository;
|
||||
this.projectActionAuthorizationPolicy = projectActionAuthorizationPolicy;
|
||||
this.usageQuotaPolicy = usageQuotaPolicy;
|
||||
}
|
||||
|
||||
async execute(request: z.infer<typeof InputSchema>): Promise<void> {
|
||||
const { caller, userId, apiKey, projectId, name } = request;
|
||||
|
||||
await this.projectActionAuthorizationPolicy.authorize({ caller, userId, apiKey, projectId });
|
||||
await this.usageQuotaPolicy.assertAndConsume(projectId);
|
||||
|
||||
// Validate server URL
|
||||
const serverUrl = validateHttpHttpsUrl(request.server.serverUrl);
|
||||
|
||||
await this.projectsRepository.addCustomMcpServer(projectId, {
|
||||
name,
|
||||
data: { serverUrl },
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
import { z } from "zod";
|
||||
import { IProjectsRepository } from "../../repositories/projects.repository.interface";
|
||||
import { IProjectActionAuthorizationPolicy } from "../../policies/project-action-authorization.policy";
|
||||
import { IUsageQuotaPolicy } from "../../policies/usage-quota.policy.interface";
|
||||
import { ComposioConnectedAccount } from "@/src/entities/models/project";
|
||||
import { ZAuthScheme, ZCreateAuthConfigResponse, ZCreateConnectedAccountResponse, listAuthConfigs, createAuthConfig, createConnectedAccount } from "@/app/lib/composio/composio";
|
||||
|
||||
export const InputSchema = z.object({
|
||||
caller: z.enum(["user", "api"]),
|
||||
userId: z.string().optional(),
|
||||
apiKey: z.string().optional(),
|
||||
projectId: z.string(),
|
||||
toolkitSlug: z.string(),
|
||||
callbackUrl: z.string(),
|
||||
});
|
||||
|
||||
export interface ICreateComposioManagedConnectedAccountUseCase {
|
||||
execute(request: z.infer<typeof InputSchema>): Promise<z.infer<typeof ZCreateConnectedAccountResponse>>;
|
||||
}
|
||||
|
||||
export class CreateComposioManagedConnectedAccountUseCase implements ICreateComposioManagedConnectedAccountUseCase {
|
||||
private readonly projectsRepository: IProjectsRepository;
|
||||
private readonly projectActionAuthorizationPolicy: IProjectActionAuthorizationPolicy;
|
||||
private readonly usageQuotaPolicy: IUsageQuotaPolicy;
|
||||
|
||||
constructor({
|
||||
projectsRepository,
|
||||
projectActionAuthorizationPolicy,
|
||||
usageQuotaPolicy,
|
||||
}: {
|
||||
projectsRepository: IProjectsRepository,
|
||||
projectActionAuthorizationPolicy: IProjectActionAuthorizationPolicy,
|
||||
usageQuotaPolicy: IUsageQuotaPolicy,
|
||||
}) {
|
||||
this.projectsRepository = projectsRepository;
|
||||
this.projectActionAuthorizationPolicy = projectActionAuthorizationPolicy;
|
||||
this.usageQuotaPolicy = usageQuotaPolicy;
|
||||
}
|
||||
|
||||
async execute(request: z.infer<typeof InputSchema>): Promise<z.infer<typeof ZCreateConnectedAccountResponse>> {
|
||||
const { caller, userId, apiKey, projectId, toolkitSlug, callbackUrl } = request;
|
||||
|
||||
await this.projectActionAuthorizationPolicy.authorize({ caller, userId, apiKey, projectId });
|
||||
await this.usageQuotaPolicy.assertAndConsume(projectId);
|
||||
|
||||
// fetch managed auth configs
|
||||
const configs = await listAuthConfigs(toolkitSlug, null, true);
|
||||
|
||||
// check if managed oauth2 config exists or create one
|
||||
let authConfigId: string | undefined = undefined;
|
||||
const managedOauth2 = configs.items.find(cfg => cfg.auth_scheme === 'OAUTH2' && cfg.is_composio_managed);
|
||||
if (managedOauth2) {
|
||||
authConfigId = managedOauth2.id;
|
||||
} else {
|
||||
const created: z.infer<typeof ZCreateAuthConfigResponse> = await createAuthConfig({
|
||||
toolkit: { slug: toolkitSlug },
|
||||
auth_config: {
|
||||
type: 'use_composio_managed_auth',
|
||||
name: 'composio-managed-oauth2',
|
||||
},
|
||||
});
|
||||
authConfigId = created.auth_config.id;
|
||||
}
|
||||
|
||||
if (!authConfigId) {
|
||||
throw new Error(`No managed oauth2 auth config found for toolkit ${toolkitSlug}`);
|
||||
}
|
||||
|
||||
// create connected account
|
||||
const response = await createConnectedAccount({
|
||||
auth_config: { id: authConfigId },
|
||||
connection: { user_id: projectId, callback_url: callbackUrl },
|
||||
});
|
||||
|
||||
// persist to project
|
||||
const now = new Date().toISOString();
|
||||
const account: z.infer<typeof ComposioConnectedAccount> = {
|
||||
id: response.id,
|
||||
authConfigId,
|
||||
status: 'INITIATED',
|
||||
createdAt: now,
|
||||
lastUpdatedAt: now,
|
||||
};
|
||||
|
||||
await this.projectsRepository.addComposioConnectedAccount(projectId, {
|
||||
toolkitSlug,
|
||||
data: account,
|
||||
});
|
||||
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,98 @@
|
|||
import { z } from "zod";
|
||||
import { IProjectsRepository } from "../../repositories/projects.repository.interface";
|
||||
import { IProjectActionAuthorizationPolicy } from "../../policies/project-action-authorization.policy";
|
||||
import { IUsageQuotaPolicy } from "../../policies/usage-quota.policy.interface";
|
||||
import { ComposioConnectedAccount } from "@/src/entities/models/project";
|
||||
import { ZAuthScheme, ZCredentials, ZCreateAuthConfigResponse, ZCreateConnectedAccountResponse, ZCreateConnectedAccountRequest, createAuthConfig, createConnectedAccount } from "@/app/lib/composio/composio";
|
||||
|
||||
export const InputSchema = z.object({
|
||||
caller: z.enum(["user", "api"]),
|
||||
userId: z.string().optional(),
|
||||
apiKey: z.string().optional(),
|
||||
projectId: z.string(),
|
||||
toolkitSlug: z.string(),
|
||||
authConfig: z.object({
|
||||
authScheme: ZAuthScheme,
|
||||
credentials: ZCredentials,
|
||||
}),
|
||||
callbackUrl: z.string(),
|
||||
});
|
||||
|
||||
export interface ICreateCustomConnectedAccountUseCase {
|
||||
execute(request: z.infer<typeof InputSchema>): Promise<z.infer<typeof ZCreateConnectedAccountResponse>>;
|
||||
}
|
||||
|
||||
export class CreateCustomConnectedAccountUseCase implements ICreateCustomConnectedAccountUseCase {
|
||||
private readonly projectsRepository: IProjectsRepository;
|
||||
private readonly projectActionAuthorizationPolicy: IProjectActionAuthorizationPolicy;
|
||||
private readonly usageQuotaPolicy: IUsageQuotaPolicy;
|
||||
|
||||
constructor({
|
||||
projectsRepository,
|
||||
projectActionAuthorizationPolicy,
|
||||
usageQuotaPolicy,
|
||||
}: {
|
||||
projectsRepository: IProjectsRepository,
|
||||
projectActionAuthorizationPolicy: IProjectActionAuthorizationPolicy,
|
||||
usageQuotaPolicy: IUsageQuotaPolicy,
|
||||
}) {
|
||||
this.projectsRepository = projectsRepository;
|
||||
this.projectActionAuthorizationPolicy = projectActionAuthorizationPolicy;
|
||||
this.usageQuotaPolicy = usageQuotaPolicy;
|
||||
}
|
||||
|
||||
async execute(request: z.infer<typeof InputSchema>): Promise<z.infer<typeof ZCreateConnectedAccountResponse>> {
|
||||
const { caller, userId, apiKey, projectId, toolkitSlug, authConfig, callbackUrl } = request;
|
||||
|
||||
await this.projectActionAuthorizationPolicy.authorize({ caller, userId, apiKey, projectId });
|
||||
await this.usageQuotaPolicy.assertAndConsume(projectId);
|
||||
|
||||
// create custom auth config
|
||||
const created: z.infer<typeof ZCreateAuthConfigResponse> = await createAuthConfig({
|
||||
toolkit: { slug: toolkitSlug },
|
||||
auth_config: {
|
||||
type: 'use_custom_auth',
|
||||
authScheme: authConfig.authScheme,
|
||||
credentials: authConfig.credentials,
|
||||
name: `pid-${projectId}-${Date.now()}`,
|
||||
},
|
||||
});
|
||||
|
||||
// initiate connected account
|
||||
let state: z.infer<typeof ZCreateConnectedAccountRequest>["connection"]["state"] = undefined;
|
||||
if (authConfig.authScheme !== 'OAUTH2') {
|
||||
state = {
|
||||
authScheme: authConfig.authScheme,
|
||||
val: { status: 'ACTIVE', ...authConfig.credentials },
|
||||
} as any;
|
||||
}
|
||||
|
||||
const response = await createConnectedAccount({
|
||||
auth_config: { id: created.auth_config.id },
|
||||
connection: {
|
||||
state,
|
||||
user_id: projectId,
|
||||
callback_url: callbackUrl,
|
||||
},
|
||||
});
|
||||
|
||||
// persist to project
|
||||
const now = new Date().toISOString();
|
||||
const account: z.infer<typeof ComposioConnectedAccount> = {
|
||||
id: response.id,
|
||||
authConfigId: created.auth_config.id,
|
||||
status: 'INITIATED',
|
||||
createdAt: now,
|
||||
lastUpdatedAt: now,
|
||||
};
|
||||
|
||||
await this.projectsRepository.addComposioConnectedAccount(projectId, {
|
||||
toolkitSlug,
|
||||
data: account,
|
||||
});
|
||||
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,120 @@
|
|||
import { z } from "zod";
|
||||
import crypto from 'crypto';
|
||||
import { IProjectsRepository } from "../../repositories/projects.repository.interface";
|
||||
import { IUsageQuotaPolicy } from "../../policies/usage-quota.policy.interface";
|
||||
import { BadRequestError, BillingError } from "@/src/entities/errors/common";
|
||||
import { IProjectMembersRepository } from "../../repositories/project-members.repository.interface";
|
||||
import { authorize, getCustomerForUserId } from "@/app/lib/billing";
|
||||
import { USE_BILLING } from "@/app/lib/feature_flags";
|
||||
import { Project } from "@/src/entities/models/project";
|
||||
import { Workflow } from "@/app/lib/types/workflow_types";
|
||||
import { templates } from "@/app/lib/project_templates";
|
||||
|
||||
export const Mode = z.union([
|
||||
z.object({
|
||||
template: z.string(),
|
||||
}),
|
||||
z.object({
|
||||
workflowJson: z.string(),
|
||||
}),
|
||||
])
|
||||
|
||||
export const InputSchema = z.object({
|
||||
userId: z.string(),
|
||||
data: z.object({
|
||||
name: z.string().optional(),
|
||||
mode: Mode,
|
||||
}),
|
||||
});
|
||||
|
||||
const workflowSchema = Workflow.omit({ lastUpdatedAt: true });
|
||||
|
||||
export interface ICreateProjectUseCase {
|
||||
execute(request: z.infer<typeof InputSchema>): Promise<z.infer<typeof Project>>;
|
||||
}
|
||||
|
||||
export class CreateProjectUseCase implements ICreateProjectUseCase {
|
||||
private readonly projectsRepository: IProjectsRepository;
|
||||
private readonly projectMembersRepository: IProjectMembersRepository;
|
||||
private readonly usageQuotaPolicy: IUsageQuotaPolicy;
|
||||
|
||||
constructor({
|
||||
projectsRepository,
|
||||
projectMembersRepository,
|
||||
usageQuotaPolicy,
|
||||
}: {
|
||||
projectsRepository: IProjectsRepository,
|
||||
projectMembersRepository: IProjectMembersRepository,
|
||||
usageQuotaPolicy: IUsageQuotaPolicy,
|
||||
}) {
|
||||
this.projectsRepository = projectsRepository;
|
||||
this.projectMembersRepository = projectMembersRepository;
|
||||
this.usageQuotaPolicy = usageQuotaPolicy;
|
||||
}
|
||||
|
||||
async execute(request: z.infer<typeof InputSchema>): Promise<z.infer<typeof Project>> {
|
||||
// fetch current project count for this user
|
||||
const count = await this.projectsRepository.countCreatedProjects(request.userId);
|
||||
|
||||
// Check billing auth
|
||||
if (USE_BILLING) {
|
||||
// get billing customer id for project
|
||||
const customer = await getCustomerForUserId(request.userId);
|
||||
if (!customer) {
|
||||
throw new BillingError("User has no billing customer id");
|
||||
}
|
||||
|
||||
// validate enough credits
|
||||
const result = await authorize(customer._id, {
|
||||
type: "create_project",
|
||||
data: {
|
||||
existingProjectCount: count,
|
||||
},
|
||||
});
|
||||
if (!result.success) {
|
||||
throw new BillingError(result.error || 'Billing error');
|
||||
}
|
||||
}
|
||||
|
||||
// generate workflow based on input
|
||||
let workflow: z.infer<typeof workflowSchema>;
|
||||
if ('template' in request.data.mode) {
|
||||
const template = templates[request.data.mode.template] || templates.default;
|
||||
workflow = {
|
||||
agents: template.agents,
|
||||
prompts: template.prompts,
|
||||
tools: template.tools,
|
||||
startAgent: template.startAgent,
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
workflow = workflowSchema.parse(JSON.parse(request.data.mode.workflowJson));
|
||||
} catch (error) {
|
||||
throw new BadRequestError('Invalid workflow JSON');
|
||||
}
|
||||
}
|
||||
|
||||
// create project secret
|
||||
const secret = crypto.randomBytes(32).toString('hex');
|
||||
|
||||
// create project
|
||||
const project = await this.projectsRepository.create({
|
||||
...request.data,
|
||||
workflow,
|
||||
createdByUserId: request.userId,
|
||||
name: request.data.name || `Assistant ${count + 1}`,
|
||||
secret,
|
||||
});
|
||||
|
||||
// create membership
|
||||
await this.projectMembersRepository.create({
|
||||
projectId: project.id,
|
||||
userId: request.userId,
|
||||
});
|
||||
|
||||
// assert and consume quota
|
||||
await this.usageQuotaPolicy.assertAndConsume(project.id);
|
||||
|
||||
return project;
|
||||
}
|
||||
}
|
||||
|
|
@ -14,7 +14,6 @@ const inputSchema = z.object({
|
|||
apiKey: z.string().optional(),
|
||||
projectId: z.string(),
|
||||
toolkitSlug: z.string(),
|
||||
connectedAccountId: z.string(),
|
||||
});
|
||||
|
||||
export interface IDeleteComposioConnectedAccountUseCase {
|
||||
|
|
@ -67,19 +66,19 @@ export class DeleteComposioConnectedAccountUseCase implements IDeleteComposioCon
|
|||
|
||||
// ensure connected account exists
|
||||
const account = project.composioConnectedAccounts?.[request.toolkitSlug];
|
||||
if (!account || account.id !== request.connectedAccountId) {
|
||||
if (!account) {
|
||||
throw new BadRequestError('Invalid connected account');
|
||||
}
|
||||
|
||||
// delete the connected account from composio
|
||||
// this will also delete any trigger instances associated with the connected account
|
||||
const result = await deleteConnectedAccount(request.connectedAccountId);
|
||||
const result = await deleteConnectedAccount(account.id);
|
||||
if (!result.success) {
|
||||
throw new Error(`Failed to delete connected account ${request.connectedAccountId}`);
|
||||
throw new Error(`Failed to delete connected account ${account.id}`);
|
||||
}
|
||||
|
||||
// delete trigger deployments data from db
|
||||
await this.composioTriggerDeploymentsRepository.deleteByConnectedAccountId(request.connectedAccountId);
|
||||
await this.composioTriggerDeploymentsRepository.deleteByConnectedAccountId(account.id);
|
||||
|
||||
// get auth config data
|
||||
const authConfig = await getAuthConfig(account.authConfigId);
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
import { z } from "zod";
|
||||
import { IProjectsRepository } from "../../repositories/projects.repository.interface";
|
||||
import { IProjectMembersRepository } from "../../repositories/project-members.repository.interface";
|
||||
import { IProjectActionAuthorizationPolicy } from "../../policies/project-action-authorization.policy";
|
||||
import { IApiKeysRepository } from "../../repositories/api-keys.repository.interface";
|
||||
import { IDataSourceDocsRepository } from "../../repositories/data-source-docs.repository.interface";
|
||||
import { IDataSourcesRepository } from "../../repositories/data-sources.repository.interface";
|
||||
import { qdrantClient } from "@/app/lib/qdrant";
|
||||
|
||||
export const InputSchema = z.object({
|
||||
projectId: z.string(),
|
||||
userId: z.string(),
|
||||
caller: z.enum(["user", "api"]),
|
||||
apiKey: z.string().optional(),
|
||||
});
|
||||
|
||||
export interface IDeleteProjectUseCase {
|
||||
execute(request: z.infer<typeof InputSchema>): Promise<void>;
|
||||
}
|
||||
|
||||
export class DeleteProjectUseCase implements IDeleteProjectUseCase {
|
||||
private readonly projectsRepository: IProjectsRepository;
|
||||
private readonly projectMembersRepository: IProjectMembersRepository;
|
||||
private readonly projectActionAuthorizationPolicy: IProjectActionAuthorizationPolicy;
|
||||
private readonly apiKeysRepository: IApiKeysRepository;
|
||||
private readonly dataSourceDocsRepository: IDataSourceDocsRepository;
|
||||
private readonly dataSourcesRepository: IDataSourcesRepository;
|
||||
|
||||
constructor({ projectsRepository, projectMembersRepository, projectActionAuthorizationPolicy, apiKeysRepository, dataSourceDocsRepository, dataSourcesRepository}: {
|
||||
projectsRepository: IProjectsRepository,
|
||||
projectMembersRepository: IProjectMembersRepository,
|
||||
projectActionAuthorizationPolicy: IProjectActionAuthorizationPolicy,
|
||||
apiKeysRepository: IApiKeysRepository,
|
||||
dataSourceDocsRepository: IDataSourceDocsRepository,
|
||||
dataSourcesRepository: IDataSourcesRepository,
|
||||
}) {
|
||||
this.projectsRepository = projectsRepository;
|
||||
this.projectMembersRepository = projectMembersRepository;
|
||||
this.projectActionAuthorizationPolicy = projectActionAuthorizationPolicy;
|
||||
this.apiKeysRepository = apiKeysRepository;
|
||||
this.dataSourceDocsRepository = dataSourceDocsRepository;
|
||||
this.dataSourcesRepository = dataSourcesRepository;
|
||||
}
|
||||
|
||||
async execute(request: z.infer<typeof InputSchema>): Promise<void> {
|
||||
const { projectId, userId, caller, apiKey } = request;
|
||||
await this.projectActionAuthorizationPolicy.authorize({
|
||||
caller,
|
||||
userId,
|
||||
apiKey,
|
||||
projectId,
|
||||
});
|
||||
|
||||
// delete memberships
|
||||
await this.projectMembersRepository.deleteByProjectId(projectId);
|
||||
|
||||
// delete api keys
|
||||
await this.apiKeysRepository.deleteAll(projectId);
|
||||
|
||||
// delete data sources data
|
||||
await this.dataSourceDocsRepository.deleteByProjectId(projectId);
|
||||
await this.dataSourcesRepository.deleteByProjectId(projectId);
|
||||
await qdrantClient.delete("embeddings", {
|
||||
filter: {
|
||||
must: [
|
||||
{ key: "projectId", match: { value: projectId } },
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
// delete project members
|
||||
await this.projectMembersRepository.deleteByProjectId(projectId);
|
||||
|
||||
// delete project
|
||||
await this.projectsRepository.delete(projectId);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
import z from "zod";
|
||||
import { Project } from "@/src/entities/models/project";
|
||||
import { IProjectsRepository } from "../../repositories/projects.repository.interface";
|
||||
import { IProjectMembersRepository } from "../../repositories/project-members.repository.interface";
|
||||
import { IProjectActionAuthorizationPolicy } from "../../policies/project-action-authorization.policy";
|
||||
import { IUsageQuotaPolicy } from "../../policies/usage-quota.policy.interface";
|
||||
|
||||
const inputSchema = z.object({
|
||||
caller: z.enum(["user", "api"]),
|
||||
userId: z.string().optional(),
|
||||
apiKey: z.string().optional(),
|
||||
projectId: z.string(),
|
||||
});
|
||||
|
||||
export interface IFetchProjectUseCase {
|
||||
execute(request: z.infer<typeof inputSchema>): Promise<z.infer<typeof Project> | null>;
|
||||
}
|
||||
|
||||
export class FetchProjectUseCase implements IFetchProjectUseCase {
|
||||
private readonly projectsRepository: IProjectsRepository;
|
||||
private readonly projectMembersRepository: IProjectMembersRepository;
|
||||
private readonly projectActionAuthorizationPolicy: IProjectActionAuthorizationPolicy;
|
||||
private readonly usageQuotaPolicy: IUsageQuotaPolicy;
|
||||
|
||||
constructor({
|
||||
projectsRepository,
|
||||
projectMembersRepository,
|
||||
projectActionAuthorizationPolicy,
|
||||
usageQuotaPolicy,
|
||||
}: {
|
||||
projectsRepository: IProjectsRepository,
|
||||
projectMembersRepository: IProjectMembersRepository,
|
||||
projectActionAuthorizationPolicy: IProjectActionAuthorizationPolicy,
|
||||
usageQuotaPolicy: IUsageQuotaPolicy,
|
||||
}) {
|
||||
this.projectsRepository = projectsRepository;
|
||||
this.projectMembersRepository = projectMembersRepository;
|
||||
this.projectActionAuthorizationPolicy = projectActionAuthorizationPolicy;
|
||||
this.usageQuotaPolicy = usageQuotaPolicy;
|
||||
}
|
||||
|
||||
async execute(request: z.infer<typeof inputSchema>): Promise<z.infer<typeof Project> | null> {
|
||||
// extract projectid from conversation
|
||||
const { projectId } = request;
|
||||
|
||||
// authz check
|
||||
await this.projectActionAuthorizationPolicy.authorize({
|
||||
caller: request.caller,
|
||||
userId: request.userId,
|
||||
apiKey: request.apiKey,
|
||||
projectId,
|
||||
});
|
||||
|
||||
// assert and consume quota
|
||||
await this.usageQuotaPolicy.assertAndConsume(projectId);
|
||||
|
||||
return await this.projectsRepository.fetch(projectId);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
import { z } from "zod";
|
||||
import { IProjectActionAuthorizationPolicy } from "../../policies/project-action-authorization.policy";
|
||||
import { IUsageQuotaPolicy } from "../../policies/usage-quota.policy.interface";
|
||||
import { ZGetToolkitResponse, getToolkit } from "@/app/lib/composio/composio";
|
||||
|
||||
export const InputSchema = z.object({
|
||||
caller: z.enum(["user", "api"]),
|
||||
userId: z.string().optional(),
|
||||
apiKey: z.string().optional(),
|
||||
projectId: z.string(),
|
||||
toolkitSlug: z.string(),
|
||||
});
|
||||
|
||||
export interface IGetComposioToolkitUseCase {
|
||||
execute(request: z.infer<typeof InputSchema>): Promise<z.infer<typeof ZGetToolkitResponse>>;
|
||||
}
|
||||
|
||||
export class GetComposioToolkitUseCase implements IGetComposioToolkitUseCase {
|
||||
private readonly projectActionAuthorizationPolicy: IProjectActionAuthorizationPolicy;
|
||||
private readonly usageQuotaPolicy: IUsageQuotaPolicy;
|
||||
|
||||
constructor({ projectActionAuthorizationPolicy, usageQuotaPolicy }: { projectActionAuthorizationPolicy: IProjectActionAuthorizationPolicy, usageQuotaPolicy: IUsageQuotaPolicy }) {
|
||||
this.projectActionAuthorizationPolicy = projectActionAuthorizationPolicy;
|
||||
this.usageQuotaPolicy = usageQuotaPolicy;
|
||||
}
|
||||
|
||||
async execute(request: z.infer<typeof InputSchema>): Promise<z.infer<typeof ZGetToolkitResponse>> {
|
||||
const { caller, userId, apiKey, projectId, toolkitSlug } = request;
|
||||
await this.projectActionAuthorizationPolicy.authorize({ caller, userId, apiKey, projectId });
|
||||
await this.usageQuotaPolicy.assertAndConsume(projectId);
|
||||
return await getToolkit(toolkitSlug);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
import { z } from "zod";
|
||||
import { IProjectActionAuthorizationPolicy } from "../../policies/project-action-authorization.policy";
|
||||
import { IUsageQuotaPolicy } from "../../policies/usage-quota.policy.interface";
|
||||
import { ZToolkit, ZListResponse, listToolkits } from "@/app/lib/composio/composio";
|
||||
|
||||
export const InputSchema = z.object({
|
||||
caller: z.enum(["user", "api"]),
|
||||
userId: z.string().optional(),
|
||||
apiKey: z.string().optional(),
|
||||
projectId: z.string(),
|
||||
cursor: z.string().nullable().optional(),
|
||||
});
|
||||
|
||||
export interface IListComposioToolkitsUseCase {
|
||||
execute(request: z.infer<typeof InputSchema>): Promise<z.infer<ReturnType<typeof ZListResponse<typeof ZToolkit>>>>;
|
||||
}
|
||||
|
||||
export class ListComposioToolkitsUseCase implements IListComposioToolkitsUseCase {
|
||||
private readonly projectActionAuthorizationPolicy: IProjectActionAuthorizationPolicy;
|
||||
private readonly usageQuotaPolicy: IUsageQuotaPolicy;
|
||||
|
||||
constructor({ projectActionAuthorizationPolicy, usageQuotaPolicy }: { projectActionAuthorizationPolicy: IProjectActionAuthorizationPolicy, usageQuotaPolicy: IUsageQuotaPolicy }) {
|
||||
this.projectActionAuthorizationPolicy = projectActionAuthorizationPolicy;
|
||||
this.usageQuotaPolicy = usageQuotaPolicy;
|
||||
}
|
||||
|
||||
async execute(request: z.infer<typeof InputSchema>): Promise<z.infer<ReturnType<typeof ZListResponse<typeof ZToolkit>>>> {
|
||||
const { caller, userId, apiKey, projectId, cursor } = request;
|
||||
await this.projectActionAuthorizationPolicy.authorize({ caller, userId, apiKey, projectId });
|
||||
await this.usageQuotaPolicy.assertAndConsume(projectId);
|
||||
return await listToolkits(cursor ?? null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
import { z } from "zod";
|
||||
import { IProjectActionAuthorizationPolicy } from "../../policies/project-action-authorization.policy";
|
||||
import { IUsageQuotaPolicy } from "../../policies/usage-quota.policy.interface";
|
||||
import { ZTool, ZListResponse, listTools } from "@/app/lib/composio/composio";
|
||||
|
||||
export const InputSchema = z.object({
|
||||
caller: z.enum(["user", "api"]),
|
||||
userId: z.string().optional(),
|
||||
apiKey: z.string().optional(),
|
||||
projectId: z.string(),
|
||||
toolkitSlug: z.string(),
|
||||
searchQuery: z.string().nullable().optional(),
|
||||
cursor: z.string().nullable().optional(),
|
||||
});
|
||||
|
||||
export interface IListComposioToolsUseCase {
|
||||
execute(request: z.infer<typeof InputSchema>): Promise<z.infer<ReturnType<typeof ZListResponse<typeof ZTool>>>>;
|
||||
}
|
||||
|
||||
export class ListComposioToolsUseCase implements IListComposioToolsUseCase {
|
||||
private readonly projectActionAuthorizationPolicy: IProjectActionAuthorizationPolicy;
|
||||
private readonly usageQuotaPolicy: IUsageQuotaPolicy;
|
||||
|
||||
constructor({ projectActionAuthorizationPolicy, usageQuotaPolicy }: { projectActionAuthorizationPolicy: IProjectActionAuthorizationPolicy, usageQuotaPolicy: IUsageQuotaPolicy }) {
|
||||
this.projectActionAuthorizationPolicy = projectActionAuthorizationPolicy;
|
||||
this.usageQuotaPolicy = usageQuotaPolicy;
|
||||
}
|
||||
|
||||
async execute(request: z.infer<typeof InputSchema>): Promise<z.infer<ReturnType<typeof ZListResponse<typeof ZTool>>>> {
|
||||
const { caller, userId, apiKey, projectId, toolkitSlug, searchQuery, cursor } = request;
|
||||
await this.projectActionAuthorizationPolicy.authorize({ caller, userId, apiKey, projectId });
|
||||
await this.usageQuotaPolicy.assertAndConsume(projectId);
|
||||
return await listTools(toolkitSlug, searchQuery ?? null, cursor ?? null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
import { z } from "zod";
|
||||
import { IProjectsRepository } from "../../repositories/projects.repository.interface";
|
||||
import { Project } from "@/src/entities/models/project";
|
||||
import { PaginatedList } from "@/src/entities/common/paginated-list";
|
||||
|
||||
export const InputSchema = z.object({
|
||||
userId: z.string(),
|
||||
cursor: z.string().optional(),
|
||||
limit: z.number().optional(),
|
||||
});
|
||||
|
||||
export interface IListProjectsUseCase {
|
||||
execute(request: z.infer<typeof InputSchema>): Promise<z.infer<ReturnType<typeof PaginatedList<typeof Project>>>>;
|
||||
}
|
||||
|
||||
export class ListProjectsUseCase implements IListProjectsUseCase {
|
||||
private readonly projectsRepository: IProjectsRepository;
|
||||
|
||||
constructor({
|
||||
projectsRepository,
|
||||
}: {
|
||||
projectsRepository: IProjectsRepository,
|
||||
}) {
|
||||
this.projectsRepository = projectsRepository;
|
||||
}
|
||||
|
||||
async execute(request: z.infer<typeof InputSchema>): Promise<z.infer<ReturnType<typeof PaginatedList<typeof Project>>>> {
|
||||
const { userId, cursor, limit } = request;
|
||||
|
||||
// fetch projects for user
|
||||
return await this.projectsRepository.listProjects(userId, cursor, limit);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
import { z } from "zod";
|
||||
import { IProjectsRepository } from "../../repositories/projects.repository.interface";
|
||||
import { IProjectActionAuthorizationPolicy } from "../../policies/project-action-authorization.policy";
|
||||
import { IUsageQuotaPolicy } from "../../policies/usage-quota.policy.interface";
|
||||
|
||||
export const InputSchema = z.object({
|
||||
caller: z.enum(["user", "api"]),
|
||||
userId: z.string().optional(),
|
||||
apiKey: z.string().optional(),
|
||||
projectId: z.string(),
|
||||
name: z.string(),
|
||||
});
|
||||
|
||||
export interface IRemoveCustomMcpServerUseCase {
|
||||
execute(request: z.infer<typeof InputSchema>): Promise<void>;
|
||||
}
|
||||
|
||||
export class RemoveCustomMcpServerUseCase implements IRemoveCustomMcpServerUseCase {
|
||||
private readonly projectsRepository: IProjectsRepository;
|
||||
private readonly projectActionAuthorizationPolicy: IProjectActionAuthorizationPolicy;
|
||||
private readonly usageQuotaPolicy: IUsageQuotaPolicy;
|
||||
|
||||
constructor({
|
||||
projectsRepository,
|
||||
projectActionAuthorizationPolicy,
|
||||
usageQuotaPolicy,
|
||||
}: {
|
||||
projectsRepository: IProjectsRepository,
|
||||
projectActionAuthorizationPolicy: IProjectActionAuthorizationPolicy,
|
||||
usageQuotaPolicy: IUsageQuotaPolicy,
|
||||
}) {
|
||||
this.projectsRepository = projectsRepository;
|
||||
this.projectActionAuthorizationPolicy = projectActionAuthorizationPolicy;
|
||||
this.usageQuotaPolicy = usageQuotaPolicy;
|
||||
}
|
||||
|
||||
async execute(request: z.infer<typeof InputSchema>): Promise<void> {
|
||||
const { caller, userId, apiKey, projectId, name } = request;
|
||||
await this.projectActionAuthorizationPolicy.authorize({ caller, userId, apiKey, projectId });
|
||||
await this.usageQuotaPolicy.assertAndConsume(projectId);
|
||||
await this.projectsRepository.deleteCustomMcpServer(projectId, name);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
import { z } from "zod";
|
||||
import { IProjectsRepository } from "../../repositories/projects.repository.interface";
|
||||
import { IProjectActionAuthorizationPolicy } from "../../policies/project-action-authorization.policy";
|
||||
import { IUsageQuotaPolicy } from "../../policies/usage-quota.policy.interface";
|
||||
import { NotFoundError, BadRequestError } from "@/src/entities/errors/common";
|
||||
|
||||
export const InputSchema = z.object({
|
||||
caller: z.enum(["user", "api"]),
|
||||
userId: z.string().optional(),
|
||||
apiKey: z.string().optional(),
|
||||
projectId: z.string(),
|
||||
});
|
||||
|
||||
export interface IRevertToLiveWorkflowUseCase {
|
||||
execute(request: z.infer<typeof InputSchema>): Promise<void>;
|
||||
}
|
||||
|
||||
export class RevertToLiveWorkflowUseCase implements IRevertToLiveWorkflowUseCase {
|
||||
private readonly projectsRepository: IProjectsRepository;
|
||||
private readonly projectActionAuthorizationPolicy: IProjectActionAuthorizationPolicy;
|
||||
private readonly usageQuotaPolicy: IUsageQuotaPolicy;
|
||||
|
||||
constructor({
|
||||
projectsRepository,
|
||||
projectActionAuthorizationPolicy,
|
||||
usageQuotaPolicy,
|
||||
}: {
|
||||
projectsRepository: IProjectsRepository,
|
||||
projectActionAuthorizationPolicy: IProjectActionAuthorizationPolicy,
|
||||
usageQuotaPolicy: IUsageQuotaPolicy,
|
||||
}) {
|
||||
this.projectsRepository = projectsRepository;
|
||||
this.projectActionAuthorizationPolicy = projectActionAuthorizationPolicy;
|
||||
this.usageQuotaPolicy = usageQuotaPolicy;
|
||||
}
|
||||
|
||||
async execute(request: z.infer<typeof InputSchema>): Promise<void> {
|
||||
const { projectId } = request;
|
||||
await this.projectActionAuthorizationPolicy.authorize({
|
||||
caller: request.caller,
|
||||
userId: request.userId,
|
||||
apiKey: request.apiKey,
|
||||
projectId,
|
||||
});
|
||||
await this.usageQuotaPolicy.assertAndConsume(projectId);
|
||||
|
||||
const project = await this.projectsRepository.fetch(projectId);
|
||||
if (!project) {
|
||||
throw new NotFoundError("Project not found");
|
||||
}
|
||||
const live = project.liveWorkflow;
|
||||
if (!live) {
|
||||
throw new BadRequestError("No live workflow found");
|
||||
}
|
||||
const draft = { ...live, lastUpdatedAt: new Date().toISOString() };
|
||||
await this.projectsRepository.updateDraftWorkflow(projectId, draft);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
import { z } from "zod";
|
||||
import crypto from "crypto";
|
||||
import { IProjectsRepository } from "../../repositories/projects.repository.interface";
|
||||
import { IProjectActionAuthorizationPolicy } from "../../policies/project-action-authorization.policy";
|
||||
import { IUsageQuotaPolicy } from "../../policies/usage-quota.policy.interface";
|
||||
|
||||
export const InputSchema = z.object({
|
||||
projectId: z.string(),
|
||||
userId: z.string(),
|
||||
caller: z.enum(["user", "api"]),
|
||||
apiKey: z.string().optional(),
|
||||
});
|
||||
|
||||
export interface IRotateSecretUseCase {
|
||||
execute(request: z.infer<typeof InputSchema>): Promise<string>;
|
||||
}
|
||||
|
||||
export class RotateSecretUseCase implements IRotateSecretUseCase {
|
||||
private readonly projectsRepository: IProjectsRepository;
|
||||
private readonly projectActionAuthorizationPolicy: IProjectActionAuthorizationPolicy;
|
||||
private readonly usageQuotaPolicy: IUsageQuotaPolicy;
|
||||
|
||||
constructor({ projectsRepository, projectActionAuthorizationPolicy, usageQuotaPolicy }: { projectsRepository: IProjectsRepository, projectActionAuthorizationPolicy: IProjectActionAuthorizationPolicy, usageQuotaPolicy: IUsageQuotaPolicy }) {
|
||||
this.projectsRepository = projectsRepository;
|
||||
this.projectActionAuthorizationPolicy = projectActionAuthorizationPolicy;
|
||||
this.usageQuotaPolicy = usageQuotaPolicy;
|
||||
}
|
||||
|
||||
async execute(request: z.infer<typeof InputSchema>): Promise<string> {
|
||||
const { projectId, userId, caller, apiKey } = request;
|
||||
// project-level authz check
|
||||
await this.projectActionAuthorizationPolicy.authorize({
|
||||
caller,
|
||||
userId,
|
||||
apiKey,
|
||||
projectId,
|
||||
});
|
||||
|
||||
// assert and consume quota
|
||||
await this.usageQuotaPolicy.assertAndConsume(projectId);
|
||||
|
||||
const secret = crypto.randomBytes(32).toString("hex");
|
||||
await this.projectsRepository.updateSecret(projectId, secret);
|
||||
return secret;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
import { z } from "zod";
|
||||
import { IProjectsRepository } from "../../repositories/projects.repository.interface";
|
||||
import { IProjectActionAuthorizationPolicy } from "../../policies/project-action-authorization.policy";
|
||||
import { IUsageQuotaPolicy } from "../../policies/usage-quota.policy.interface";
|
||||
import { ComposioConnectedAccount } from "@/src/entities/models/project";
|
||||
import { ZConnectedAccount, getConnectedAccount } from "@/app/lib/composio/composio";
|
||||
|
||||
export const InputSchema = z.object({
|
||||
caller: z.enum(["user", "api"]),
|
||||
userId: z.string().optional(),
|
||||
apiKey: z.string().optional(),
|
||||
projectId: z.string(),
|
||||
toolkitSlug: z.string(),
|
||||
connectedAccountId: z.string(),
|
||||
});
|
||||
|
||||
export interface ISyncConnectedAccountUseCase {
|
||||
execute(request: z.infer<typeof InputSchema>): Promise<z.infer<typeof ComposioConnectedAccount>>;
|
||||
}
|
||||
|
||||
export class SyncConnectedAccountUseCase implements ISyncConnectedAccountUseCase {
|
||||
private readonly projectsRepository: IProjectsRepository;
|
||||
private readonly projectActionAuthorizationPolicy: IProjectActionAuthorizationPolicy;
|
||||
private readonly usageQuotaPolicy: IUsageQuotaPolicy;
|
||||
|
||||
constructor({
|
||||
projectsRepository,
|
||||
projectActionAuthorizationPolicy,
|
||||
usageQuotaPolicy,
|
||||
}: {
|
||||
projectsRepository: IProjectsRepository,
|
||||
projectActionAuthorizationPolicy: IProjectActionAuthorizationPolicy,
|
||||
usageQuotaPolicy: IUsageQuotaPolicy,
|
||||
}) {
|
||||
this.projectsRepository = projectsRepository;
|
||||
this.projectActionAuthorizationPolicy = projectActionAuthorizationPolicy;
|
||||
this.usageQuotaPolicy = usageQuotaPolicy;
|
||||
}
|
||||
|
||||
async execute(request: z.infer<typeof InputSchema>): Promise<z.infer<typeof ComposioConnectedAccount>> {
|
||||
const { caller, userId, apiKey, projectId, toolkitSlug, connectedAccountId } = request;
|
||||
|
||||
await this.projectActionAuthorizationPolicy.authorize({ caller, userId, apiKey, projectId });
|
||||
await this.usageQuotaPolicy.assertAndConsume(projectId);
|
||||
|
||||
// fetch project & account to verify
|
||||
const project = await this.projectsRepository.fetch(projectId);
|
||||
if (!project) {
|
||||
throw new Error('Project not found');
|
||||
}
|
||||
const account = project.composioConnectedAccounts?.[toolkitSlug];
|
||||
if (!account || account.id !== connectedAccountId) {
|
||||
throw new Error(`Connected account ${connectedAccountId} not found in project ${projectId}`);
|
||||
}
|
||||
|
||||
if (account.status === 'ACTIVE') {
|
||||
return account;
|
||||
}
|
||||
|
||||
// get latest status from Composio
|
||||
const response = await getConnectedAccount(connectedAccountId);
|
||||
|
||||
const updated: z.infer<typeof ComposioConnectedAccount> = {
|
||||
...account,
|
||||
status: (() => {
|
||||
switch (response.status) {
|
||||
case 'INITIALIZING':
|
||||
case 'INITIATED':
|
||||
return 'INITIATED' as const;
|
||||
case 'ACTIVE':
|
||||
return 'ACTIVE' as const;
|
||||
default:
|
||||
return 'FAILED' as const;
|
||||
}
|
||||
})(),
|
||||
lastUpdatedAt: new Date().toISOString(),
|
||||
};
|
||||
|
||||
await this.projectsRepository.addComposioConnectedAccount(projectId, {
|
||||
toolkitSlug,
|
||||
data: updated,
|
||||
});
|
||||
|
||||
return updated;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
import { z } from "zod";
|
||||
import { IProjectsRepository } from "../../repositories/projects.repository.interface";
|
||||
import { IProjectActionAuthorizationPolicy } from "../../policies/project-action-authorization.policy";
|
||||
import { IUsageQuotaPolicy } from "../../policies/usage-quota.policy.interface";
|
||||
import { Workflow } from "@/app/lib/types/workflow_types";
|
||||
|
||||
export const InputSchema = z.object({
|
||||
caller: z.enum(["user", "api"]),
|
||||
userId: z.string().optional(),
|
||||
apiKey: z.string().optional(),
|
||||
projectId: z.string(),
|
||||
workflow: Workflow,
|
||||
});
|
||||
|
||||
export interface IUpdateDraftWorkflowUseCase {
|
||||
execute(request: z.infer<typeof InputSchema>): Promise<void>;
|
||||
}
|
||||
|
||||
export class UpdateDraftWorkflowUseCase implements IUpdateDraftWorkflowUseCase {
|
||||
private readonly projectsRepository: IProjectsRepository;
|
||||
private readonly projectActionAuthorizationPolicy: IProjectActionAuthorizationPolicy;
|
||||
private readonly usageQuotaPolicy: IUsageQuotaPolicy;
|
||||
|
||||
constructor({
|
||||
projectsRepository,
|
||||
projectActionAuthorizationPolicy,
|
||||
usageQuotaPolicy,
|
||||
}: {
|
||||
projectsRepository: IProjectsRepository,
|
||||
projectActionAuthorizationPolicy: IProjectActionAuthorizationPolicy,
|
||||
usageQuotaPolicy: IUsageQuotaPolicy,
|
||||
}) {
|
||||
this.projectsRepository = projectsRepository;
|
||||
this.projectActionAuthorizationPolicy = projectActionAuthorizationPolicy;
|
||||
this.usageQuotaPolicy = usageQuotaPolicy;
|
||||
}
|
||||
|
||||
async execute(request: z.infer<typeof InputSchema>): Promise<void> {
|
||||
const { projectId } = request;
|
||||
await this.projectActionAuthorizationPolicy.authorize({
|
||||
caller: request.caller,
|
||||
userId: request.userId,
|
||||
apiKey: request.apiKey,
|
||||
projectId,
|
||||
});
|
||||
await this.usageQuotaPolicy.assertAndConsume(projectId);
|
||||
|
||||
const workflow = { ...request.workflow, lastUpdatedAt: new Date().toISOString() } as z.infer<typeof Workflow>;
|
||||
await this.projectsRepository.updateDraftWorkflow(projectId, workflow);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
import { z } from "zod";
|
||||
import { IProjectsRepository } from "../../repositories/projects.repository.interface";
|
||||
import { IProjectActionAuthorizationPolicy } from "../../policies/project-action-authorization.policy";
|
||||
import { IUsageQuotaPolicy } from "../../policies/usage-quota.policy.interface";
|
||||
import { Workflow } from "@/app/lib/types/workflow_types";
|
||||
|
||||
export const InputSchema = z.object({
|
||||
caller: z.enum(["user", "api"]),
|
||||
userId: z.string().optional(),
|
||||
apiKey: z.string().optional(),
|
||||
projectId: z.string(),
|
||||
workflow: Workflow,
|
||||
});
|
||||
|
||||
export interface IUpdateLiveWorkflowUseCase {
|
||||
execute(request: z.infer<typeof InputSchema>): Promise<void>;
|
||||
}
|
||||
|
||||
export class UpdateLiveWorkflowUseCase implements IUpdateLiveWorkflowUseCase {
|
||||
private readonly projectsRepository: IProjectsRepository;
|
||||
private readonly projectActionAuthorizationPolicy: IProjectActionAuthorizationPolicy;
|
||||
private readonly usageQuotaPolicy: IUsageQuotaPolicy;
|
||||
|
||||
constructor({
|
||||
projectsRepository,
|
||||
projectActionAuthorizationPolicy,
|
||||
usageQuotaPolicy,
|
||||
}: {
|
||||
projectsRepository: IProjectsRepository,
|
||||
projectActionAuthorizationPolicy: IProjectActionAuthorizationPolicy,
|
||||
usageQuotaPolicy: IUsageQuotaPolicy,
|
||||
}) {
|
||||
this.projectsRepository = projectsRepository;
|
||||
this.projectActionAuthorizationPolicy = projectActionAuthorizationPolicy;
|
||||
this.usageQuotaPolicy = usageQuotaPolicy;
|
||||
}
|
||||
|
||||
async execute(request: z.infer<typeof InputSchema>): Promise<void> {
|
||||
const { projectId } = request;
|
||||
await this.projectActionAuthorizationPolicy.authorize({
|
||||
caller: request.caller,
|
||||
userId: request.userId,
|
||||
apiKey: request.apiKey,
|
||||
projectId,
|
||||
});
|
||||
await this.usageQuotaPolicy.assertAndConsume(projectId);
|
||||
|
||||
const workflow = { ...request.workflow, lastUpdatedAt: new Date().toISOString() } as z.infer<typeof Workflow>;
|
||||
await this.projectsRepository.updateLiveWorkflow(projectId, workflow);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
import { z } from "zod";
|
||||
import { IProjectsRepository } from "../../repositories/projects.repository.interface";
|
||||
import { IProjectActionAuthorizationPolicy } from "../../policies/project-action-authorization.policy";
|
||||
import { IUsageQuotaPolicy } from "../../policies/usage-quota.policy.interface";
|
||||
|
||||
export const InputSchema = z.object({
|
||||
projectId: z.string(),
|
||||
userId: z.string(),
|
||||
caller: z.enum(["user", "api"]),
|
||||
apiKey: z.string().optional(),
|
||||
name: z.string(),
|
||||
});
|
||||
|
||||
export interface IUpdateProjectNameUseCase {
|
||||
execute(request: z.infer<typeof InputSchema>): Promise<void>;
|
||||
}
|
||||
|
||||
export class UpdateProjectNameUseCase implements IUpdateProjectNameUseCase {
|
||||
private readonly projectsRepository: IProjectsRepository;
|
||||
private readonly projectActionAuthorizationPolicy: IProjectActionAuthorizationPolicy;
|
||||
private readonly usageQuotaPolicy: IUsageQuotaPolicy;
|
||||
|
||||
constructor({ projectsRepository, projectActionAuthorizationPolicy, usageQuotaPolicy }: { projectsRepository: IProjectsRepository, projectActionAuthorizationPolicy: IProjectActionAuthorizationPolicy, usageQuotaPolicy: IUsageQuotaPolicy }) {
|
||||
this.projectsRepository = projectsRepository;
|
||||
this.projectActionAuthorizationPolicy = projectActionAuthorizationPolicy;
|
||||
this.usageQuotaPolicy = usageQuotaPolicy;
|
||||
}
|
||||
|
||||
async execute(request: z.infer<typeof InputSchema>): Promise<void> {
|
||||
const { projectId, userId, caller, apiKey, name } = request;
|
||||
await this.projectActionAuthorizationPolicy.authorize({
|
||||
caller,
|
||||
userId,
|
||||
apiKey,
|
||||
projectId,
|
||||
});
|
||||
|
||||
// assert and consume quota
|
||||
await this.usageQuotaPolicy.assertAndConsume(projectId);
|
||||
|
||||
await this.projectsRepository.updateName(projectId, name);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
import { z } from "zod";
|
||||
import { IProjectsRepository } from "../../repositories/projects.repository.interface";
|
||||
import { IProjectActionAuthorizationPolicy } from "../../policies/project-action-authorization.policy";
|
||||
import { IUsageQuotaPolicy } from "../../policies/usage-quota.policy.interface";
|
||||
|
||||
export const InputSchema = z.object({
|
||||
projectId: z.string(),
|
||||
userId: z.string(),
|
||||
caller: z.enum(["user", "api"]),
|
||||
apiKey: z.string().optional(),
|
||||
url: z.string(),
|
||||
});
|
||||
|
||||
export interface IUpdateWebhookUrlUseCase {
|
||||
execute(request: z.infer<typeof InputSchema>): Promise<void>;
|
||||
}
|
||||
|
||||
export class UpdateWebhookUrlUseCase implements IUpdateWebhookUrlUseCase {
|
||||
private readonly projectsRepository: IProjectsRepository;
|
||||
private readonly projectActionAuthorizationPolicy: IProjectActionAuthorizationPolicy;
|
||||
private readonly usageQuotaPolicy: IUsageQuotaPolicy;
|
||||
|
||||
constructor({ projectsRepository, projectActionAuthorizationPolicy, usageQuotaPolicy }: { projectsRepository: IProjectsRepository, projectActionAuthorizationPolicy: IProjectActionAuthorizationPolicy, usageQuotaPolicy: IUsageQuotaPolicy }) {
|
||||
this.projectsRepository = projectsRepository;
|
||||
this.projectActionAuthorizationPolicy = projectActionAuthorizationPolicy;
|
||||
this.usageQuotaPolicy = usageQuotaPolicy;
|
||||
}
|
||||
|
||||
async execute(request: z.infer<typeof InputSchema>): Promise<void> {
|
||||
const { projectId, userId, caller, apiKey, url } = request;
|
||||
await this.projectActionAuthorizationPolicy.authorize({
|
||||
caller,
|
||||
userId,
|
||||
apiKey,
|
||||
projectId,
|
||||
});
|
||||
|
||||
// assert and consume quota
|
||||
await this.usageQuotaPolicy.assertAndConsume(projectId);
|
||||
|
||||
await this.projectsRepository.updateWebhookUrl(projectId, url);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,10 +1,32 @@
|
|||
import { Workflow } from "@/app/lib/types/workflow_types";
|
||||
import { z } from "zod";
|
||||
import { Project as ExistingProjectSchema } from "@/app/lib/types/project_types";
|
||||
|
||||
export const Project = ExistingProjectSchema
|
||||
.omit({
|
||||
_id: true,
|
||||
})
|
||||
.extend({
|
||||
id: z.string().uuid(),
|
||||
});
|
||||
export const ComposioConnectedAccount = z.object({
|
||||
id: z.string(),
|
||||
authConfigId: z.string(),
|
||||
status: z.enum([
|
||||
'INITIATED',
|
||||
'ACTIVE',
|
||||
'FAILED',
|
||||
]),
|
||||
createdAt: z.string().datetime(),
|
||||
lastUpdatedAt: z.string().datetime(),
|
||||
});
|
||||
|
||||
export const CustomMcpServer = z.object({
|
||||
serverUrl: z.string(),
|
||||
});
|
||||
|
||||
export const Project = z.object({
|
||||
id: z.string().uuid(),
|
||||
name: z.string(),
|
||||
createdAt: z.string().datetime(),
|
||||
lastUpdatedAt: z.string().datetime().optional(),
|
||||
createdByUserId: z.string(),
|
||||
secret: z.string(),
|
||||
draftWorkflow: Workflow,
|
||||
liveWorkflow: Workflow,
|
||||
webhookUrl: z.string().optional(),
|
||||
composioConnectedAccounts: z.record(z.string(), ComposioConnectedAccount).optional(),
|
||||
customMcpServers: z.record(z.string(), CustomMcpServer).optional(),
|
||||
});
|
||||
|
|
@ -1,31 +1,240 @@
|
|||
import { IProjectsRepository } from "@/src/application/repositories/projects.repository.interface";
|
||||
import { db } from "@/app/lib/mongodb";
|
||||
import { CreateSchema, IProjectsRepository, AddComposioConnectedAccountSchema, AddCustomMcpServerSchema } from "@/src/application/repositories/projects.repository.interface";
|
||||
import { NotFoundError } from "@/src/entities/errors/common";
|
||||
import { Project } from "@/src/entities/models/project";
|
||||
import { projectsCollection } from "@/app/lib/mongodb";
|
||||
import { z } from "zod";
|
||||
import { IProjectMembersRepository } from "@/src/application/repositories/project-members.repository.interface";
|
||||
import { PaginatedList } from "@/src/entities/common/paginated-list";
|
||||
|
||||
const docSchema = Project
|
||||
.omit({
|
||||
id: true,
|
||||
})
|
||||
.extend({
|
||||
id: z.string().uuid(),
|
||||
_id: z.string().uuid(),
|
||||
});
|
||||
|
||||
export class MongodbProjectsRepository implements IProjectsRepository {
|
||||
async fetch(id: string): Promise<z.infer<typeof docSchema> | null> {
|
||||
const doc = await projectsCollection.findOne({ _id: id });
|
||||
private readonly projectMembersRepository: IProjectMembersRepository;
|
||||
private collection = db.collection<z.infer<typeof docSchema>>('projects');
|
||||
|
||||
constructor({
|
||||
projectMembersRepository,
|
||||
}: {
|
||||
projectMembersRepository: IProjectMembersRepository,
|
||||
}) {
|
||||
this.projectMembersRepository = projectMembersRepository;
|
||||
}
|
||||
|
||||
async create(data: z.infer<typeof CreateSchema>): Promise<z.infer<typeof Project>> {
|
||||
const now = new Date();
|
||||
|
||||
const wflow = {
|
||||
...data.workflow,
|
||||
lastUpdatedAt: now.toISOString(),
|
||||
};
|
||||
|
||||
const id = crypto.randomUUID();
|
||||
|
||||
const doc = {
|
||||
...data,
|
||||
liveWorkflow: wflow,
|
||||
draftWorkflow: wflow,
|
||||
createdAt: now.toISOString(),
|
||||
};
|
||||
await this.collection.insertOne({
|
||||
...doc,
|
||||
_id: id,
|
||||
});
|
||||
return {
|
||||
...doc,
|
||||
id,
|
||||
};
|
||||
}
|
||||
|
||||
async fetch(id: string): Promise<z.infer<typeof Project> | null> {
|
||||
const doc = await this.collection.findOne({ _id: id });
|
||||
if (!doc) {
|
||||
return null;
|
||||
}
|
||||
const { _id, ...rest } = doc;
|
||||
return {
|
||||
...rest,
|
||||
id: _id.toString(),
|
||||
id,
|
||||
};
|
||||
}
|
||||
|
||||
async countCreatedProjects(createdByUserId: string): Promise<number> {
|
||||
return await this.collection.countDocuments({ createdByUserId });
|
||||
}
|
||||
|
||||
async listProjects(userId: string, cursor?: string, limit?: number): Promise<z.infer<ReturnType<typeof PaginatedList<typeof Project>>>> {
|
||||
const memberships = await this.projectMembersRepository.findByUserId(userId, cursor, limit);
|
||||
const projectIds = memberships.items.map((m) => m.projectId);
|
||||
const projects = await this.collection.find({
|
||||
_id: { $in: projectIds },
|
||||
}).toArray();
|
||||
return {
|
||||
items: projects.map((p) => ({
|
||||
...p,
|
||||
id: p._id,
|
||||
})),
|
||||
nextCursor: memberships.nextCursor,
|
||||
};
|
||||
}
|
||||
|
||||
async addComposioConnectedAccount(projectId: string, data: z.infer<typeof AddComposioConnectedAccountSchema>): Promise<z.infer<typeof Project>> {
|
||||
const key = `composioConnectedAccounts.${data.toolkitSlug}`;
|
||||
const result = await this.collection.findOneAndUpdate(
|
||||
{ _id: projectId },
|
||||
{
|
||||
$set: {
|
||||
[key]: data.data,
|
||||
lastUpdatedAt: new Date().toISOString(),
|
||||
}
|
||||
},
|
||||
{ returnDocument: 'after' }
|
||||
);
|
||||
if (!result) {
|
||||
throw new NotFoundError('Project not found');
|
||||
}
|
||||
const { _id, ...rest } = result;
|
||||
return { ...rest, id: _id };
|
||||
}
|
||||
|
||||
async deleteComposioConnectedAccount(projectId: string, toolkitSlug: string): Promise<boolean> {
|
||||
const result = await projectsCollection.updateOne({ _id: projectId }, { $unset: { [`composioConnectedAccounts.${toolkitSlug}`]: "" } });
|
||||
const result = await this.collection.updateOne({
|
||||
_id: projectId,
|
||||
}, {
|
||||
$unset: {
|
||||
[`composioConnectedAccounts.${toolkitSlug}`]: "",
|
||||
}
|
||||
});
|
||||
return result.modifiedCount > 0;
|
||||
}
|
||||
|
||||
async addCustomMcpServer(projectId: string, data: z.infer<typeof AddCustomMcpServerSchema>): Promise<z.infer<typeof Project>> {
|
||||
const key = `customMcpServers.${data.name}`;
|
||||
const result = await this.collection.findOneAndUpdate(
|
||||
{ _id: projectId },
|
||||
{
|
||||
$set: {
|
||||
[key]: data.data,
|
||||
lastUpdatedAt: new Date().toISOString(),
|
||||
}
|
||||
},
|
||||
{ returnDocument: 'after' }
|
||||
);
|
||||
if (!result) {
|
||||
throw new NotFoundError('Project not found');
|
||||
}
|
||||
const { _id, ...rest } = result;
|
||||
return { ...rest, id: _id };
|
||||
}
|
||||
|
||||
async deleteCustomMcpServer(projectId: string, name: string): Promise<boolean> {
|
||||
const result = await this.collection.updateOne({
|
||||
_id: projectId,
|
||||
}, {
|
||||
$unset: {
|
||||
[`customMcpServers.${name}`]: "",
|
||||
}
|
||||
});
|
||||
return result.modifiedCount > 0;
|
||||
}
|
||||
|
||||
async updateSecret(projectId: string, secret: string): Promise<z.infer<typeof Project>> {
|
||||
const result = await this.collection.findOneAndUpdate(
|
||||
{ _id: projectId },
|
||||
{
|
||||
$set: {
|
||||
secret,
|
||||
lastUpdatedAt: new Date().toISOString(),
|
||||
}
|
||||
},
|
||||
{ returnDocument: 'after' }
|
||||
);
|
||||
if (!result) {
|
||||
throw new NotFoundError('Project not found');
|
||||
}
|
||||
const { _id, ...rest } = result;
|
||||
return { ...rest, id: _id };
|
||||
}
|
||||
|
||||
async updateWebhookUrl(projectId: string, url: string): Promise<z.infer<typeof Project>> {
|
||||
const result = await this.collection.findOneAndUpdate(
|
||||
{ _id: projectId },
|
||||
{
|
||||
$set: {
|
||||
webhookUrl: url,
|
||||
lastUpdatedAt: new Date().toISOString(),
|
||||
}
|
||||
},
|
||||
{ returnDocument: 'after' }
|
||||
);
|
||||
if (!result) {
|
||||
throw new NotFoundError('Project not found');
|
||||
}
|
||||
const { _id, ...rest } = result;
|
||||
return { ...rest, id: _id };
|
||||
}
|
||||
|
||||
async updateName(projectId: string, name: string): Promise<z.infer<typeof Project>> {
|
||||
const result = await this.collection.findOneAndUpdate(
|
||||
{ _id: projectId },
|
||||
{
|
||||
$set: {
|
||||
name,
|
||||
lastUpdatedAt: new Date().toISOString(),
|
||||
}
|
||||
},
|
||||
{ returnDocument: 'after' }
|
||||
);
|
||||
if (!result) {
|
||||
throw new NotFoundError('Project not found');
|
||||
}
|
||||
const { _id, ...rest } = result;
|
||||
return { ...rest, id: _id };
|
||||
}
|
||||
|
||||
async updateDraftWorkflow(projectId: string, workflow: z.infer<typeof import("@/app/lib/types/workflow_types").Workflow>): Promise<z.infer<typeof Project>> {
|
||||
const result = await this.collection.findOneAndUpdate(
|
||||
{ _id: projectId },
|
||||
{
|
||||
$set: {
|
||||
draftWorkflow: workflow,
|
||||
lastUpdatedAt: new Date().toISOString(),
|
||||
}
|
||||
},
|
||||
{ returnDocument: 'after' }
|
||||
);
|
||||
if (!result) {
|
||||
throw new NotFoundError('Project not found');
|
||||
}
|
||||
const { _id, ...rest } = result;
|
||||
return { ...rest, id: _id };
|
||||
}
|
||||
|
||||
async updateLiveWorkflow(projectId: string, workflow: z.infer<typeof import("@/app/lib/types/workflow_types").Workflow>): Promise<z.infer<typeof Project>> {
|
||||
const result = await this.collection.findOneAndUpdate(
|
||||
{ _id: projectId },
|
||||
{
|
||||
$set: {
|
||||
liveWorkflow: workflow,
|
||||
lastUpdatedAt: new Date().toISOString(),
|
||||
}
|
||||
},
|
||||
{ returnDocument: 'after' }
|
||||
);
|
||||
if (!result) {
|
||||
throw new NotFoundError('Project not found');
|
||||
}
|
||||
const { _id, ...rest } = result;
|
||||
return { ...rest, id: _id };
|
||||
}
|
||||
|
||||
async delete(projectId: string): Promise<boolean> {
|
||||
const result = await this.collection.deleteOne({ _id: projectId });
|
||||
return result.deletedCount > 0;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
import { BadRequestError } from "@/src/entities/errors/common";
|
||||
import z from "zod";
|
||||
import { IAddCustomMcpServerUseCase } from "@/src/application/use-cases/projects/add-custom-mcp-server.use-case";
|
||||
import { CustomMcpServer } from "@/src/entities/models/project";
|
||||
|
||||
const inputSchema = z.object({
|
||||
caller: z.enum(["user", "api"]),
|
||||
userId: z.string().optional(),
|
||||
apiKey: z.string().optional(),
|
||||
projectId: z.string(),
|
||||
name: z.string(),
|
||||
server: CustomMcpServer,
|
||||
});
|
||||
|
||||
export interface IAddCustomMcpServerController {
|
||||
execute(request: z.infer<typeof inputSchema>): Promise<void>;
|
||||
}
|
||||
|
||||
export class AddCustomMcpServerController implements IAddCustomMcpServerController {
|
||||
private readonly addCustomMcpServerUseCase: IAddCustomMcpServerUseCase;
|
||||
|
||||
constructor({ addCustomMcpServerUseCase }: { addCustomMcpServerUseCase: IAddCustomMcpServerUseCase }) {
|
||||
this.addCustomMcpServerUseCase = addCustomMcpServerUseCase;
|
||||
}
|
||||
|
||||
async execute(request: z.infer<typeof inputSchema>): Promise<void> {
|
||||
const result = inputSchema.safeParse(request);
|
||||
if (!result.success) {
|
||||
throw new BadRequestError(`Invalid request: ${JSON.stringify(result.error)}`);
|
||||
}
|
||||
return await this.addCustomMcpServerUseCase.execute(result.data);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
import { BadRequestError } from "@/src/entities/errors/common";
|
||||
import z from "zod";
|
||||
import { ICreateComposioManagedConnectedAccountUseCase } from "@/src/application/use-cases/projects/create-composio-managed-connected-account.use-case";
|
||||
import { ZCreateConnectedAccountResponse } from "@/app/lib/composio/composio";
|
||||
|
||||
const inputSchema = z.object({
|
||||
caller: z.enum(["user", "api"]),
|
||||
userId: z.string().optional(),
|
||||
apiKey: z.string().optional(),
|
||||
projectId: z.string(),
|
||||
toolkitSlug: z.string(),
|
||||
callbackUrl: z.string(),
|
||||
});
|
||||
|
||||
export interface ICreateComposioManagedConnectedAccountController {
|
||||
execute(request: z.infer<typeof inputSchema>): Promise<z.infer<typeof ZCreateConnectedAccountResponse>>;
|
||||
}
|
||||
|
||||
export class CreateComposioManagedConnectedAccountController implements ICreateComposioManagedConnectedAccountController {
|
||||
private readonly createComposioManagedConnectedAccountUseCase: ICreateComposioManagedConnectedAccountUseCase;
|
||||
|
||||
constructor({ createComposioManagedConnectedAccountUseCase }: { createComposioManagedConnectedAccountUseCase: ICreateComposioManagedConnectedAccountUseCase }) {
|
||||
this.createComposioManagedConnectedAccountUseCase = createComposioManagedConnectedAccountUseCase;
|
||||
}
|
||||
|
||||
async execute(request: z.infer<typeof inputSchema>): Promise<z.infer<typeof ZCreateConnectedAccountResponse>> {
|
||||
const result = inputSchema.safeParse(request);
|
||||
if (!result.success) {
|
||||
throw new BadRequestError(`Invalid request: ${JSON.stringify(result.error)}`);
|
||||
}
|
||||
return await this.createComposioManagedConnectedAccountUseCase.execute(result.data);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
import { BadRequestError } from "@/src/entities/errors/common";
|
||||
import z from "zod";
|
||||
import { ICreateCustomConnectedAccountUseCase } from "@/src/application/use-cases/projects/create-custom-connected-account.use-case";
|
||||
import { ZAuthScheme, ZCredentials, ZCreateConnectedAccountResponse } from "@/app/lib/composio/composio";
|
||||
|
||||
const inputSchema = z.object({
|
||||
caller: z.enum(["user", "api"]),
|
||||
userId: z.string().optional(),
|
||||
apiKey: z.string().optional(),
|
||||
projectId: z.string(),
|
||||
toolkitSlug: z.string(),
|
||||
authConfig: z.object({
|
||||
authScheme: ZAuthScheme,
|
||||
credentials: ZCredentials,
|
||||
}),
|
||||
callbackUrl: z.string(),
|
||||
});
|
||||
|
||||
export interface ICreateCustomConnectedAccountController {
|
||||
execute(request: z.infer<typeof inputSchema>): Promise<z.infer<typeof ZCreateConnectedAccountResponse>>;
|
||||
}
|
||||
|
||||
export class CreateCustomConnectedAccountController implements ICreateCustomConnectedAccountController {
|
||||
private readonly createCustomConnectedAccountUseCase: ICreateCustomConnectedAccountUseCase;
|
||||
|
||||
constructor({ createCustomConnectedAccountUseCase }: { createCustomConnectedAccountUseCase: ICreateCustomConnectedAccountUseCase }) {
|
||||
this.createCustomConnectedAccountUseCase = createCustomConnectedAccountUseCase;
|
||||
}
|
||||
|
||||
async execute(request: z.infer<typeof inputSchema>): Promise<z.infer<typeof ZCreateConnectedAccountResponse>> {
|
||||
const result = inputSchema.safeParse(request);
|
||||
if (!result.success) {
|
||||
throw new BadRequestError(`Invalid request: ${JSON.stringify(result.error)}`);
|
||||
}
|
||||
return await this.createCustomConnectedAccountUseCase.execute(result.data);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
import { BadRequestError } from "@/src/entities/errors/common";
|
||||
import z from "zod";
|
||||
import { ICreateProjectUseCase, InputSchema } from "@/src/application/use-cases/projects/create-project.use-case";
|
||||
import { Project } from "@/src/entities/models/project";
|
||||
|
||||
export interface ICreateProjectController {
|
||||
execute(request: z.infer<typeof InputSchema>): Promise<z.infer<typeof Project>>;
|
||||
}
|
||||
|
||||
export class CreateProjectController implements ICreateProjectController {
|
||||
private readonly createProjectUseCase: ICreateProjectUseCase;
|
||||
|
||||
constructor({
|
||||
createProjectUseCase,
|
||||
}: {
|
||||
createProjectUseCase: ICreateProjectUseCase,
|
||||
}) {
|
||||
this.createProjectUseCase = createProjectUseCase;
|
||||
}
|
||||
|
||||
async execute(request: z.infer<typeof InputSchema>): Promise<z.infer<typeof Project>> {
|
||||
// parse input
|
||||
const result = InputSchema.safeParse(request);
|
||||
if (!result.success) {
|
||||
throw new BadRequestError(`Invalid request: ${JSON.stringify(result.error)}`);
|
||||
}
|
||||
const { userId, data } = result.data;
|
||||
|
||||
// execute use case
|
||||
return await this.createProjectUseCase.execute({
|
||||
userId,
|
||||
data,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import { BadRequestError } from "@/src/entities/errors/common";
|
||||
import z from "zod";
|
||||
import { IDeleteComposioConnectedAccountUseCase } from "@/src/application/use-cases/composio/delete-composio-connected-account.use-case";
|
||||
import { IDeleteComposioConnectedAccountUseCase } from "@/src/application/use-cases/projects/delete-composio-connected-account.use-case";
|
||||
|
||||
const inputSchema = z.object({
|
||||
caller: z.enum(["user", "api"]),
|
||||
|
|
@ -8,7 +8,6 @@ const inputSchema = z.object({
|
|||
apiKey: z.string().optional(),
|
||||
projectId: z.string(),
|
||||
toolkitSlug: z.string(),
|
||||
connectedAccountId: z.string(),
|
||||
});
|
||||
|
||||
export interface IDeleteComposioConnectedAccountController {
|
||||
|
|
@ -32,7 +31,7 @@ export class DeleteComposioConnectedAccountController implements IDeleteComposio
|
|||
if (!result.success) {
|
||||
throw new BadRequestError(`Invalid request: ${JSON.stringify(result.error)}`);
|
||||
}
|
||||
const { caller, userId, apiKey, projectId, toolkitSlug, connectedAccountId } = result.data;
|
||||
const { caller, userId, apiKey, projectId, toolkitSlug } = result.data;
|
||||
|
||||
// execute use case
|
||||
return await this.deleteComposioConnectedAccountUseCase.execute({
|
||||
|
|
@ -41,7 +40,6 @@ export class DeleteComposioConnectedAccountController implements IDeleteComposio
|
|||
apiKey,
|
||||
projectId,
|
||||
toolkitSlug,
|
||||
connectedAccountId,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
import { BadRequestError } from "@/src/entities/errors/common";
|
||||
import z from "zod";
|
||||
import { IDeleteProjectUseCase } from "@/src/application/use-cases/projects/delete-project.use-case";
|
||||
import { InputSchema } from "@/src/application/use-cases/projects/delete-project.use-case";
|
||||
|
||||
export interface IDeleteProjectController {
|
||||
execute(request: z.infer<typeof InputSchema>): Promise<void>;
|
||||
}
|
||||
|
||||
export class DeleteProjectController implements IDeleteProjectController {
|
||||
private readonly deleteProjectUseCase: IDeleteProjectUseCase;
|
||||
|
||||
constructor({
|
||||
deleteProjectUseCase,
|
||||
}: {
|
||||
deleteProjectUseCase: IDeleteProjectUseCase,
|
||||
}) {
|
||||
this.deleteProjectUseCase = deleteProjectUseCase;
|
||||
}
|
||||
|
||||
async execute(request: z.infer<typeof InputSchema>): Promise<void> {
|
||||
// parse input
|
||||
const result = InputSchema.safeParse(request);
|
||||
if (!result.success) {
|
||||
throw new BadRequestError(`Invalid request: ${JSON.stringify(result.error)}`);
|
||||
}
|
||||
// execute use case
|
||||
return await this.deleteProjectUseCase.execute(result.data);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
import { BadRequestError } from "@/src/entities/errors/common";
|
||||
import z from "zod";
|
||||
import { IFetchProjectUseCase } from "@/src/application/use-cases/projects/fetch-project.use-case";
|
||||
import { Project } from "@/src/entities/models/project";
|
||||
|
||||
const inputSchema = z.object({
|
||||
caller: z.enum(["user", "api"]),
|
||||
userId: z.string().optional(),
|
||||
apiKey: z.string().optional(),
|
||||
projectId: z.string(),
|
||||
});
|
||||
|
||||
export interface IFetchProjectController {
|
||||
execute(request: z.infer<typeof inputSchema>): Promise<z.infer<typeof Project> | null>;
|
||||
}
|
||||
|
||||
export class FetchProjectController implements IFetchProjectController {
|
||||
private readonly fetchProjectUseCase: IFetchProjectUseCase;
|
||||
|
||||
constructor({
|
||||
fetchProjectUseCase,
|
||||
}: {
|
||||
fetchProjectUseCase: IFetchProjectUseCase,
|
||||
}) {
|
||||
this.fetchProjectUseCase = fetchProjectUseCase;
|
||||
}
|
||||
|
||||
async execute(request: z.infer<typeof inputSchema>): Promise<z.infer<typeof Project> | null> {
|
||||
// parse input
|
||||
const result = inputSchema.safeParse(request);
|
||||
if (!result.success) {
|
||||
throw new BadRequestError(`Invalid request: ${JSON.stringify(result.error)}`);
|
||||
}
|
||||
const { caller, userId, apiKey, projectId } = result.data;
|
||||
|
||||
// execute use case
|
||||
return await this.fetchProjectUseCase.execute({
|
||||
caller,
|
||||
userId,
|
||||
apiKey,
|
||||
projectId,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
import { BadRequestError } from "@/src/entities/errors/common";
|
||||
import z from "zod";
|
||||
import { IGetComposioToolkitUseCase } from "@/src/application/use-cases/projects/get-composio-toolkit.use-case";
|
||||
import { ZGetToolkitResponse } from "@/app/lib/composio/composio";
|
||||
|
||||
const inputSchema = z.object({
|
||||
caller: z.enum(["user", "api"]),
|
||||
userId: z.string().optional(),
|
||||
apiKey: z.string().optional(),
|
||||
projectId: z.string(),
|
||||
toolkitSlug: z.string(),
|
||||
});
|
||||
|
||||
export interface IGetComposioToolkitController {
|
||||
execute(request: z.infer<typeof inputSchema>): Promise<z.infer<typeof ZGetToolkitResponse>>;
|
||||
}
|
||||
|
||||
export class GetComposioToolkitController implements IGetComposioToolkitController {
|
||||
private readonly getComposioToolkitUseCase: IGetComposioToolkitUseCase;
|
||||
|
||||
constructor({ getComposioToolkitUseCase }: { getComposioToolkitUseCase: IGetComposioToolkitUseCase }) {
|
||||
this.getComposioToolkitUseCase = getComposioToolkitUseCase;
|
||||
}
|
||||
|
||||
async execute(request: z.infer<typeof inputSchema>): Promise<z.infer<typeof ZGetToolkitResponse>> {
|
||||
const result = inputSchema.safeParse(request);
|
||||
if (!result.success) {
|
||||
throw new BadRequestError(`Invalid request: ${JSON.stringify(result.error)}`);
|
||||
}
|
||||
return await this.getComposioToolkitUseCase.execute(result.data);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
import { BadRequestError } from "@/src/entities/errors/common";
|
||||
import z from "zod";
|
||||
import { IListComposioToolkitsUseCase } from "@/src/application/use-cases/projects/list-composio-toolkits.use-case";
|
||||
import { ZToolkit, ZListResponse } from "@/app/lib/composio/composio";
|
||||
|
||||
const inputSchema = z.object({
|
||||
caller: z.enum(["user", "api"]),
|
||||
userId: z.string().optional(),
|
||||
apiKey: z.string().optional(),
|
||||
projectId: z.string(),
|
||||
cursor: z.string().nullable().optional(),
|
||||
});
|
||||
|
||||
export interface IListComposioToolkitsController {
|
||||
execute(request: z.infer<typeof inputSchema>): Promise<z.infer<ReturnType<typeof ZListResponse<typeof ZToolkit>>>>;
|
||||
}
|
||||
|
||||
export class ListComposioToolkitsController implements IListComposioToolkitsController {
|
||||
private readonly listComposioToolkitsUseCase: IListComposioToolkitsUseCase;
|
||||
|
||||
constructor({ listComposioToolkitsUseCase }: { listComposioToolkitsUseCase: IListComposioToolkitsUseCase }) {
|
||||
this.listComposioToolkitsUseCase = listComposioToolkitsUseCase;
|
||||
}
|
||||
|
||||
async execute(request: z.infer<typeof inputSchema>): Promise<z.infer<ReturnType<typeof ZListResponse<typeof ZToolkit>>>> {
|
||||
const result = inputSchema.safeParse(request);
|
||||
if (!result.success) {
|
||||
throw new BadRequestError(`Invalid request: ${JSON.stringify(result.error)}`);
|
||||
}
|
||||
return await this.listComposioToolkitsUseCase.execute(result.data);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
import { BadRequestError } from "@/src/entities/errors/common";
|
||||
import z from "zod";
|
||||
import { IListComposioToolsUseCase } from "@/src/application/use-cases/projects/list-composio-tools.use-case";
|
||||
import { ZTool, ZListResponse } from "@/app/lib/composio/composio";
|
||||
|
||||
const inputSchema = z.object({
|
||||
caller: z.enum(["user", "api"]),
|
||||
userId: z.string().optional(),
|
||||
apiKey: z.string().optional(),
|
||||
projectId: z.string(),
|
||||
toolkitSlug: z.string(),
|
||||
searchQuery: z.string().nullable().optional(),
|
||||
cursor: z.string().nullable().optional(),
|
||||
});
|
||||
|
||||
export interface IListComposioToolsController {
|
||||
execute(request: z.infer<typeof inputSchema>): Promise<z.infer<ReturnType<typeof ZListResponse<typeof ZTool>>>>;
|
||||
}
|
||||
|
||||
export class ListComposioToolsController implements IListComposioToolsController {
|
||||
private readonly listComposioToolsUseCase: IListComposioToolsUseCase;
|
||||
|
||||
constructor({ listComposioToolsUseCase }: { listComposioToolsUseCase: IListComposioToolsUseCase }) {
|
||||
this.listComposioToolsUseCase = listComposioToolsUseCase;
|
||||
}
|
||||
|
||||
async execute(request: z.infer<typeof inputSchema>): Promise<z.infer<ReturnType<typeof ZListResponse<typeof ZTool>>>> {
|
||||
const result = inputSchema.safeParse(request);
|
||||
if (!result.success) {
|
||||
throw new BadRequestError(`Invalid request: ${JSON.stringify(result.error)}`);
|
||||
}
|
||||
return await this.listComposioToolsUseCase.execute(result.data);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
import { BadRequestError } from "@/src/entities/errors/common";
|
||||
import z from "zod";
|
||||
import { IListProjectsUseCase } from "@/src/application/use-cases/projects/list-projects.use-case";
|
||||
import { Project } from "@/src/entities/models/project";
|
||||
import { PaginatedList } from "@/src/entities/common/paginated-list";
|
||||
import { InputSchema } from "@/src/application/use-cases/projects/list-projects.use-case";
|
||||
|
||||
export interface IListProjectsController {
|
||||
execute(request: z.infer<typeof InputSchema>): Promise<z.infer<ReturnType<typeof PaginatedList<typeof Project>>> >;
|
||||
}
|
||||
|
||||
export class ListProjectsController implements IListProjectsController {
|
||||
private readonly listProjectsUseCase: IListProjectsUseCase;
|
||||
|
||||
constructor({
|
||||
listProjectsUseCase,
|
||||
}: {
|
||||
listProjectsUseCase: IListProjectsUseCase,
|
||||
}) {
|
||||
this.listProjectsUseCase = listProjectsUseCase;
|
||||
}
|
||||
|
||||
async execute(request: z.infer<typeof InputSchema>): Promise<z.infer<ReturnType<typeof PaginatedList<typeof Project>>>> {
|
||||
// parse input
|
||||
const result = InputSchema.safeParse(request);
|
||||
if (!result.success) {
|
||||
throw new BadRequestError(`Invalid request: ${JSON.stringify(result.error)}`);
|
||||
}
|
||||
// execute use case
|
||||
return await this.listProjectsUseCase.execute(result.data);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
import { BadRequestError } from "@/src/entities/errors/common";
|
||||
import z from "zod";
|
||||
import { IRemoveCustomMcpServerUseCase } from "@/src/application/use-cases/projects/remove-custom-mcp-server.use-case";
|
||||
|
||||
const inputSchema = z.object({
|
||||
caller: z.enum(["user", "api"]),
|
||||
userId: z.string().optional(),
|
||||
apiKey: z.string().optional(),
|
||||
projectId: z.string(),
|
||||
name: z.string(),
|
||||
});
|
||||
|
||||
export interface IRemoveCustomMcpServerController {
|
||||
execute(request: z.infer<typeof inputSchema>): Promise<void>;
|
||||
}
|
||||
|
||||
export class RemoveCustomMcpServerController implements IRemoveCustomMcpServerController {
|
||||
private readonly removeCustomMcpServerUseCase: IRemoveCustomMcpServerUseCase;
|
||||
|
||||
constructor({ removeCustomMcpServerUseCase }: { removeCustomMcpServerUseCase: IRemoveCustomMcpServerUseCase }) {
|
||||
this.removeCustomMcpServerUseCase = removeCustomMcpServerUseCase;
|
||||
}
|
||||
|
||||
async execute(request: z.infer<typeof inputSchema>): Promise<void> {
|
||||
const result = inputSchema.safeParse(request);
|
||||
if (!result.success) {
|
||||
throw new BadRequestError(`Invalid request: ${JSON.stringify(result.error)}`);
|
||||
}
|
||||
return await this.removeCustomMcpServerUseCase.execute(result.data);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
import { BadRequestError } from "@/src/entities/errors/common";
|
||||
import z from "zod";
|
||||
import { IRevertToLiveWorkflowUseCase } from "@/src/application/use-cases/projects/revert-to-live-workflow.use-case";
|
||||
import { InputSchema } from "@/src/application/use-cases/projects/revert-to-live-workflow.use-case";
|
||||
|
||||
export interface IRevertToLiveWorkflowController {
|
||||
execute(request: z.infer<typeof InputSchema>): Promise<void>;
|
||||
}
|
||||
|
||||
export class RevertToLiveWorkflowController implements IRevertToLiveWorkflowController {
|
||||
private readonly revertToLiveWorkflowUseCase: IRevertToLiveWorkflowUseCase;
|
||||
|
||||
constructor({ revertToLiveWorkflowUseCase }: { revertToLiveWorkflowUseCase: IRevertToLiveWorkflowUseCase }) {
|
||||
this.revertToLiveWorkflowUseCase = revertToLiveWorkflowUseCase;
|
||||
}
|
||||
|
||||
async execute(request: z.infer<typeof InputSchema>): Promise<void> {
|
||||
const result = InputSchema.safeParse(request);
|
||||
if (!result.success) {
|
||||
throw new BadRequestError(`Invalid request: ${JSON.stringify(result.error)}`);
|
||||
}
|
||||
return await this.revertToLiveWorkflowUseCase.execute(result.data);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
import { BadRequestError } from "@/src/entities/errors/common";
|
||||
import z from "zod";
|
||||
import { IRotateSecretUseCase } from "@/src/application/use-cases/projects/rotate-secret.use-case";
|
||||
|
||||
const inputSchema = z.object({
|
||||
caller: z.enum(["user", "api"]),
|
||||
userId: z.string(),
|
||||
apiKey: z.string().optional(),
|
||||
projectId: z.string(),
|
||||
});
|
||||
|
||||
export interface IRotateSecretController {
|
||||
execute(request: z.infer<typeof inputSchema>): Promise<string>;
|
||||
}
|
||||
|
||||
export class RotateSecretController implements IRotateSecretController {
|
||||
private readonly rotateSecretUseCase: IRotateSecretUseCase;
|
||||
|
||||
constructor({
|
||||
rotateSecretUseCase,
|
||||
}: {
|
||||
rotateSecretUseCase: IRotateSecretUseCase,
|
||||
}) {
|
||||
this.rotateSecretUseCase = rotateSecretUseCase;
|
||||
}
|
||||
|
||||
async execute(request: z.infer<typeof inputSchema>): Promise<string> {
|
||||
// parse input
|
||||
const result = inputSchema.safeParse(request);
|
||||
if (!result.success) {
|
||||
throw new BadRequestError(`Invalid request: ${JSON.stringify(result.error)}`);
|
||||
}
|
||||
|
||||
// execute use case
|
||||
return await this.rotateSecretUseCase.execute(request);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
import { BadRequestError } from "@/src/entities/errors/common";
|
||||
import z from "zod";
|
||||
import { ISyncConnectedAccountUseCase } from "@/src/application/use-cases/projects/sync-connected-account.use-case";
|
||||
import { ComposioConnectedAccount } from "@/src/entities/models/project";
|
||||
|
||||
const inputSchema = z.object({
|
||||
caller: z.enum(["user", "api"]),
|
||||
userId: z.string().optional(),
|
||||
apiKey: z.string().optional(),
|
||||
projectId: z.string(),
|
||||
toolkitSlug: z.string(),
|
||||
connectedAccountId: z.string(),
|
||||
});
|
||||
|
||||
export interface ISyncConnectedAccountController {
|
||||
execute(request: z.infer<typeof inputSchema>): Promise<z.infer<typeof ComposioConnectedAccount>>;
|
||||
}
|
||||
|
||||
export class SyncConnectedAccountController implements ISyncConnectedAccountController {
|
||||
private readonly syncConnectedAccountUseCase: ISyncConnectedAccountUseCase;
|
||||
|
||||
constructor({ syncConnectedAccountUseCase }: { syncConnectedAccountUseCase: ISyncConnectedAccountUseCase }) {
|
||||
this.syncConnectedAccountUseCase = syncConnectedAccountUseCase;
|
||||
}
|
||||
|
||||
async execute(request: z.infer<typeof inputSchema>): Promise<z.infer<typeof ComposioConnectedAccount>> {
|
||||
const result = inputSchema.safeParse(request);
|
||||
if (!result.success) {
|
||||
throw new BadRequestError(`Invalid request: ${JSON.stringify(result.error)}`);
|
||||
}
|
||||
return await this.syncConnectedAccountUseCase.execute(result.data);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
import { BadRequestError } from "@/src/entities/errors/common";
|
||||
import z from "zod";
|
||||
import { IUpdateDraftWorkflowUseCase } from "@/src/application/use-cases/projects/update-draft-workflow.use-case";
|
||||
import { InputSchema } from "@/src/application/use-cases/projects/update-draft-workflow.use-case";
|
||||
|
||||
export interface IUpdateDraftWorkflowController {
|
||||
execute(request: z.infer<typeof InputSchema>): Promise<void>;
|
||||
}
|
||||
|
||||
export class UpdateDraftWorkflowController implements IUpdateDraftWorkflowController {
|
||||
private readonly updateDraftWorkflowUseCase: IUpdateDraftWorkflowUseCase;
|
||||
|
||||
constructor({ updateDraftWorkflowUseCase }: { updateDraftWorkflowUseCase: IUpdateDraftWorkflowUseCase }) {
|
||||
this.updateDraftWorkflowUseCase = updateDraftWorkflowUseCase;
|
||||
}
|
||||
|
||||
async execute(request: z.infer<typeof InputSchema>): Promise<void> {
|
||||
const result = InputSchema.safeParse(request);
|
||||
if (!result.success) {
|
||||
throw new BadRequestError(`Invalid request: ${JSON.stringify(result.error)}`);
|
||||
}
|
||||
return await this.updateDraftWorkflowUseCase.execute(result.data);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
import { BadRequestError } from "@/src/entities/errors/common";
|
||||
import z from "zod";
|
||||
import { IUpdateLiveWorkflowUseCase } from "@/src/application/use-cases/projects/update-live-workflow.use-case";
|
||||
import { InputSchema } from "@/src/application/use-cases/projects/update-live-workflow.use-case";
|
||||
|
||||
export interface IUpdateLiveWorkflowController {
|
||||
execute(request: z.infer<typeof InputSchema>): Promise<void>;
|
||||
}
|
||||
|
||||
export class UpdateLiveWorkflowController implements IUpdateLiveWorkflowController {
|
||||
private readonly updateLiveWorkflowUseCase: IUpdateLiveWorkflowUseCase;
|
||||
|
||||
constructor({ updateLiveWorkflowUseCase }: { updateLiveWorkflowUseCase: IUpdateLiveWorkflowUseCase }) {
|
||||
this.updateLiveWorkflowUseCase = updateLiveWorkflowUseCase;
|
||||
}
|
||||
|
||||
async execute(request: z.infer<typeof InputSchema>): Promise<void> {
|
||||
const result = InputSchema.safeParse(request);
|
||||
if (!result.success) {
|
||||
throw new BadRequestError(`Invalid request: ${JSON.stringify(result.error)}`);
|
||||
}
|
||||
return await this.updateLiveWorkflowUseCase.execute(result.data);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
import { BadRequestError } from "@/src/entities/errors/common";
|
||||
import z from "zod";
|
||||
import { IUpdateProjectNameUseCase } from "@/src/application/use-cases/projects/update-project-name.use-case";
|
||||
import { InputSchema } from "@/src/application/use-cases/projects/update-project-name.use-case";
|
||||
|
||||
export interface IUpdateProjectNameController {
|
||||
execute(request: z.infer<typeof InputSchema>): Promise<void>;
|
||||
}
|
||||
|
||||
export class UpdateProjectNameController implements IUpdateProjectNameController {
|
||||
private readonly updateProjectNameUseCase: IUpdateProjectNameUseCase;
|
||||
|
||||
constructor({
|
||||
updateProjectNameUseCase,
|
||||
}: {
|
||||
updateProjectNameUseCase: IUpdateProjectNameUseCase,
|
||||
}) {
|
||||
this.updateProjectNameUseCase = updateProjectNameUseCase;
|
||||
}
|
||||
|
||||
async execute(request: z.infer<typeof InputSchema>): Promise<void> {
|
||||
// parse input
|
||||
const result = InputSchema.safeParse(request);
|
||||
if (!result.success) {
|
||||
throw new BadRequestError(`Invalid request: ${JSON.stringify(result.error)}`);
|
||||
}
|
||||
// execute use case
|
||||
return await this.updateProjectNameUseCase.execute(result.data);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
import { BadRequestError } from "@/src/entities/errors/common";
|
||||
import z from "zod";
|
||||
import { IUpdateWebhookUrlUseCase } from "@/src/application/use-cases/projects/update-webhook-url.use-case";
|
||||
import { InputSchema } from "@/src/application/use-cases/projects/update-webhook-url.use-case";
|
||||
|
||||
export interface IUpdateWebhookUrlController {
|
||||
execute(request: z.infer<typeof InputSchema>): Promise<void>;
|
||||
}
|
||||
|
||||
export class UpdateWebhookUrlController implements IUpdateWebhookUrlController {
|
||||
private readonly updateWebhookUrlUseCase: IUpdateWebhookUrlUseCase;
|
||||
|
||||
constructor({
|
||||
updateWebhookUrlUseCase,
|
||||
}: {
|
||||
updateWebhookUrlUseCase: IUpdateWebhookUrlUseCase,
|
||||
}) {
|
||||
this.updateWebhookUrlUseCase = updateWebhookUrlUseCase;
|
||||
}
|
||||
|
||||
async execute(request: z.infer<typeof InputSchema>): Promise<void> {
|
||||
// parse input
|
||||
const result = InputSchema.safeParse(request);
|
||||
if (!result.success) {
|
||||
throw new BadRequestError(`Invalid request: ${JSON.stringify(result.error)}`);
|
||||
}
|
||||
// execute use case
|
||||
return await this.updateWebhookUrlUseCase.execute(result.data);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue