mirror of
https://github.com/simstudioai/sim.git
synced 2026-04-28 03:00:29 -04:00
v0.6.55: standardize monorepo conventions, api key hash, thinking text for mothership
This commit is contained in:
@@ -144,7 +144,7 @@ vi.useFakeTimers()
|
||||
| `@/app/api/auth/oauth/utils` | `authOAuthUtilsMock`, `authOAuthUtilsMockFns` | `vi.mock('@/app/api/auth/oauth/utils', () => authOAuthUtilsMock)` |
|
||||
| `@/app/api/knowledge/utils` | `knowledgeApiUtilsMock`, `knowledgeApiUtilsMockFns` | `vi.mock('@/app/api/knowledge/utils', () => knowledgeApiUtilsMock)` |
|
||||
| `@/app/api/workflows/utils` | `workflowsApiUtilsMock`, `workflowsApiUtilsMockFns` | `vi.mock('@/app/api/workflows/utils', () => workflowsApiUtilsMock)` |
|
||||
| `@/lib/audit/log` | `auditMock`, `auditMockFns` | `vi.mock('@/lib/audit/log', () => auditMock)` |
|
||||
| `@sim/audit` | `auditMock`, `auditMockFns` | `vi.mock('@sim/audit', () => auditMock)` |
|
||||
| `@/lib/auth` | `authMock`, `authMockFns` | `vi.mock('@/lib/auth', () => authMock)` |
|
||||
| `@/lib/auth/hybrid` | `hybridAuthMock`, `hybridAuthMockFns` | `vi.mock('@/lib/auth/hybrid', () => hybridAuthMock)` |
|
||||
| `@/lib/copilot/request/http` | `copilotHttpMock`, `copilotHttpMockFns` | `vi.mock('@/lib/copilot/request/http', () => copilotHttpMock)` |
|
||||
|
||||
@@ -144,7 +144,7 @@ vi.useFakeTimers()
|
||||
| `@/app/api/auth/oauth/utils` | `authOAuthUtilsMock`, `authOAuthUtilsMockFns` | `vi.mock('@/app/api/auth/oauth/utils', () => authOAuthUtilsMock)` |
|
||||
| `@/app/api/knowledge/utils` | `knowledgeApiUtilsMock`, `knowledgeApiUtilsMockFns` | `vi.mock('@/app/api/knowledge/utils', () => knowledgeApiUtilsMock)` |
|
||||
| `@/app/api/workflows/utils` | `workflowsApiUtilsMock`, `workflowsApiUtilsMockFns` | `vi.mock('@/app/api/workflows/utils', () => workflowsApiUtilsMock)` |
|
||||
| `@/lib/audit/log` | `auditMock`, `auditMockFns` | `vi.mock('@/lib/audit/log', () => auditMock)` |
|
||||
| `@sim/audit` | `auditMock`, `auditMockFns` | `vi.mock('@sim/audit', () => auditMock)` |
|
||||
| `@/lib/auth` | `authMock`, `authMockFns` | `vi.mock('@/lib/auth', () => authMock)` |
|
||||
| `@/lib/auth/hybrid` | `hybridAuthMock`, `hybridAuthMockFns` | `vi.mock('@/lib/auth/hybrid', () => hybridAuthMock)` |
|
||||
| `@/lib/copilot/request/http` | `copilotHttpMock`, `copilotHttpMockFns` | `vi.mock('@/lib/copilot/request/http', () => copilotHttpMock)` |
|
||||
|
||||
@@ -71,7 +71,7 @@ fi
|
||||
|
||||
# Set up environment variables if .env doesn't exist for the sim app
|
||||
if [ ! -f "apps/sim/.env" ]; then
|
||||
echo "📄 Creating .env file from template..."
|
||||
echo "📄 Creating apps/sim/.env from template..."
|
||||
if [ -f "apps/sim/.env.example" ]; then
|
||||
cp apps/sim/.env.example apps/sim/.env
|
||||
else
|
||||
@@ -79,6 +79,18 @@ if [ ! -f "apps/sim/.env" ]; then
|
||||
fi
|
||||
fi
|
||||
|
||||
# Set up env for the realtime server (must match the shared values in apps/sim/.env)
|
||||
if [ ! -f "apps/realtime/.env" ] && [ -f "apps/realtime/.env.example" ]; then
|
||||
echo "📄 Creating apps/realtime/.env from template..."
|
||||
cp apps/realtime/.env.example apps/realtime/.env
|
||||
fi
|
||||
|
||||
# Set up packages/db/.env for drizzle-kit and migration scripts
|
||||
if [ ! -f "packages/db/.env" ] && [ -f "packages/db/.env.example" ]; then
|
||||
echo "📄 Creating packages/db/.env from template..."
|
||||
cp packages/db/.env.example packages/db/.env
|
||||
fi
|
||||
|
||||
# Generate schema and run database migrations
|
||||
echo "🗃️ Running database schema generation and migrations..."
|
||||
echo "Generating schema..."
|
||||
|
||||
9
.github/workflows/test-build.yml
vendored
9
.github/workflows/test-build.yml
vendored
@@ -103,6 +103,15 @@ jobs:
|
||||
- name: Lint code
|
||||
run: bun run lint:check
|
||||
|
||||
- name: Enforce monorepo boundaries
|
||||
run: bun run check:boundaries
|
||||
|
||||
- name: Verify realtime prune graph
|
||||
run: bun run check:realtime-prune
|
||||
|
||||
- name: Type-check realtime server
|
||||
run: bunx turbo run type-check --filter=@sim/realtime
|
||||
|
||||
- name: Run tests with coverage
|
||||
env:
|
||||
NODE_OPTIONS: '--no-warnings --max-old-space-size=8192'
|
||||
|
||||
45
AGENTS.md
45
AGENTS.md
@@ -20,19 +20,42 @@ You are a professional software engineer. All code must follow best practices: a
|
||||
|
||||
### Root Structure
|
||||
```
|
||||
apps/sim/
|
||||
├── app/ # Next.js app router (pages, API routes)
|
||||
├── blocks/ # Block definitions and registry
|
||||
├── components/ # Shared UI (emcn/, ui/)
|
||||
├── executor/ # Workflow execution engine
|
||||
├── hooks/ # Shared hooks (queries/, selectors/)
|
||||
├── lib/ # App-wide utilities
|
||||
├── providers/ # LLM provider integrations
|
||||
├── stores/ # Zustand stores
|
||||
├── tools/ # Tool definitions
|
||||
└── triggers/ # Trigger definitions
|
||||
apps/
|
||||
├── sim/ # Next.js app (UI + API routes + workflow editor)
|
||||
│ ├── app/ # Next.js app router (pages, API routes)
|
||||
│ ├── blocks/ # Block definitions and registry
|
||||
│ ├── components/ # Shared UI (emcn/, ui/)
|
||||
│ ├── executor/ # Workflow execution engine
|
||||
│ ├── hooks/ # Shared hooks (queries/, selectors/)
|
||||
│ ├── lib/ # App-wide utilities
|
||||
│ ├── providers/ # LLM provider integrations
|
||||
│ ├── stores/ # Zustand stores
|
||||
│ ├── tools/ # Tool definitions
|
||||
│ └── triggers/ # Trigger definitions
|
||||
└── realtime/ # Bun Socket.IO server (collaborative canvas)
|
||||
└── src/ # auth, config, database, handlers, middleware,
|
||||
# rooms, routes, internal/webhook-cleanup.ts
|
||||
|
||||
packages/
|
||||
├── audit/ # @sim/audit — recordAudit + AuditAction + AuditResourceType
|
||||
├── auth/ # @sim/auth — @sim/auth/verify (shared Better Auth verifier)
|
||||
├── db/ # @sim/db — drizzle schema + client
|
||||
├── logger/ # @sim/logger
|
||||
├── realtime-protocol/ # @sim/realtime-protocol — socket operation constants + zod schemas
|
||||
├── security/ # @sim/security — safeCompare
|
||||
├── tsconfig/ # shared tsconfig presets
|
||||
├── utils/ # @sim/utils
|
||||
├── workflow-authz/ # @sim/workflow-authz — authorizeWorkflowByWorkspacePermission
|
||||
├── workflow-persistence/ # @sim/workflow-persistence — raw load/save + subflow helpers
|
||||
└── workflow-types/ # @sim/workflow-types — pure BlockState/Loop/Parallel/... types
|
||||
```
|
||||
|
||||
### Package boundaries
|
||||
- `apps/* → packages/*` only. Packages never import from `apps/*`.
|
||||
- Each package has explicit subpath `exports` maps; no barrels that accidentally pull in heavy halves.
|
||||
- `apps/realtime` intentionally avoids Next.js, React, the block/tool registry, provider SDKs, and the executor. CI enforces this via `scripts/check-monorepo-boundaries.ts` and `scripts/check-realtime-prune-graph.ts`.
|
||||
- Auth is shared across services via the Better Auth "Shared Database Session" pattern: both apps read the same `BETTER_AUTH_SECRET` and point at the same DB via `@sim/db`.
|
||||
|
||||
### Naming Conventions
|
||||
- Components: PascalCase (`WorkflowList`)
|
||||
- Hooks: `use` prefix (`useWorkflowOperations`)
|
||||
|
||||
30
apps/realtime/.env.example
Normal file
30
apps/realtime/.env.example
Normal file
@@ -0,0 +1,30 @@
|
||||
# Environment variables required by the @sim/realtime (Socket.IO) server.
|
||||
# These MUST match the corresponding values in apps/sim/.env for auth to work.
|
||||
# See apps/realtime/src/env.ts for the full zod schema.
|
||||
|
||||
# Core
|
||||
NODE_ENV=development
|
||||
PORT=3002
|
||||
|
||||
# Database — must point at the same Postgres as the main app
|
||||
DATABASE_URL=postgresql://postgres:postgres@localhost:5432/simstudio
|
||||
|
||||
# Auth — shared with apps/sim (Better Auth "Shared Database Session" pattern)
|
||||
BETTER_AUTH_URL=http://localhost:3000
|
||||
BETTER_AUTH_SECRET=your_better_auth_secret_min_32_chars
|
||||
|
||||
# Internal RPC — shared with apps/sim
|
||||
INTERNAL_API_SECRET=your_internal_api_secret_min_32_chars
|
||||
|
||||
# Public app URL — used for CORS allow-list and base URL resolution
|
||||
NEXT_PUBLIC_APP_URL=http://localhost:3000
|
||||
|
||||
# Optional: Redis for cross-pod room management
|
||||
# Leave unset for single-pod / in-memory rooms
|
||||
# REDIS_URL=redis://localhost:6379
|
||||
|
||||
# Optional: extra Socket.IO CORS allow-list (comma-separated)
|
||||
# ALLOWED_ORIGINS=https://embed.example.com,https://admin.example.com
|
||||
|
||||
# Optional: disable auth entirely for trusted private networks
|
||||
# DISABLE_AUTH=true
|
||||
48
apps/realtime/package.json
Normal file
48
apps/realtime/package.json
Normal file
@@ -0,0 +1,48 @@
|
||||
{
|
||||
"name": "@sim/realtime",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"license": "Apache-2.0",
|
||||
"type": "module",
|
||||
"engines": {
|
||||
"bun": ">=1.2.13",
|
||||
"node": ">=20.0.0"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "bun --watch src/index.ts",
|
||||
"start": "bun src/index.ts",
|
||||
"type-check": "tsc --noEmit",
|
||||
"lint": "biome check --write --unsafe .",
|
||||
"lint:check": "biome check .",
|
||||
"format": "biome format --write .",
|
||||
"format:check": "biome format .",
|
||||
"test": "vitest run",
|
||||
"test:watch": "vitest"
|
||||
},
|
||||
"dependencies": {
|
||||
"@sim/audit": "workspace:*",
|
||||
"@sim/auth": "workspace:*",
|
||||
"@sim/db": "workspace:*",
|
||||
"@sim/logger": "workspace:*",
|
||||
"@sim/realtime-protocol": "workspace:*",
|
||||
"@sim/security": "workspace:*",
|
||||
"@sim/utils": "workspace:*",
|
||||
"@sim/workflow-authz": "workspace:*",
|
||||
"@sim/workflow-persistence": "workspace:*",
|
||||
"@sim/workflow-types": "workspace:*",
|
||||
"@socket.io/redis-adapter": "8.3.0",
|
||||
"drizzle-orm": "^0.45.2",
|
||||
"postgres": "^3.4.5",
|
||||
"redis": "5.10.0",
|
||||
"socket.io": "^4.8.1",
|
||||
"zod": "^3.24.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@sim/testing": "workspace:*",
|
||||
"@sim/tsconfig": "workspace:*",
|
||||
"@types/node": "24.2.1",
|
||||
"socket.io-client": "4.8.1",
|
||||
"typescript": "^5.7.3",
|
||||
"vitest": "^3.0.8"
|
||||
}
|
||||
}
|
||||
17
apps/realtime/src/auth.ts
Normal file
17
apps/realtime/src/auth.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { createVerifyAuth } from '@sim/auth/verify'
|
||||
import { env } from '@/env'
|
||||
|
||||
export const ANONYMOUS_USER_ID = '00000000-0000-0000-0000-000000000000'
|
||||
|
||||
export const ANONYMOUS_USER = {
|
||||
id: ANONYMOUS_USER_ID,
|
||||
name: 'Anonymous',
|
||||
email: 'anonymous@localhost',
|
||||
emailVerified: true,
|
||||
image: null,
|
||||
} as const
|
||||
|
||||
export const auth = createVerifyAuth({
|
||||
secret: env.BETTER_AUTH_SECRET,
|
||||
baseURL: env.BETTER_AUTH_URL,
|
||||
})
|
||||
@@ -3,9 +3,7 @@ import { createLogger } from '@sim/logger'
|
||||
import { createAdapter } from '@socket.io/redis-adapter'
|
||||
import { createClient, type RedisClientType } from 'redis'
|
||||
import { Server } from 'socket.io'
|
||||
import { env } from '@/lib/core/config/env'
|
||||
import { isProd } from '@/lib/core/config/feature-flags'
|
||||
import { getBaseUrl } from '@/lib/core/utils/urls'
|
||||
import { env, getBaseUrl, isProd } from '@/env'
|
||||
|
||||
const logger = createLogger('SocketIOConfig')
|
||||
|
||||
@@ -1,15 +1,7 @@
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@sim/audit'
|
||||
import * as schema from '@sim/db'
|
||||
import { webhook, workflow, workflowBlocks, workflowEdges, workflowSubflows } from '@sim/db'
|
||||
import { workflow, workflowBlocks, workflowEdges, workflowSubflows } from '@sim/db'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { and, eq, inArray, isNull, or, sql } from 'drizzle-orm'
|
||||
import { drizzle } from 'drizzle-orm/postgres-js'
|
||||
import postgres from 'postgres'
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
|
||||
import { env } from '@/lib/core/config/env'
|
||||
import { cleanupExternalWebhook } from '@/lib/webhooks/provider-subscriptions'
|
||||
import { getActiveWorkflowContext } from '@/lib/workflows/active-context'
|
||||
import { loadWorkflowFromNormalizedTables } from '@/lib/workflows/persistence/utils'
|
||||
import { mergeSubBlockValues } from '@/lib/workflows/subblocks'
|
||||
import {
|
||||
BLOCK_OPERATIONS,
|
||||
BLOCKS_OPERATIONS,
|
||||
@@ -19,7 +11,14 @@ import {
|
||||
SUBFLOW_OPERATIONS,
|
||||
VARIABLE_OPERATIONS,
|
||||
WORKFLOW_OPERATIONS,
|
||||
} from '@/socket/constants'
|
||||
} from '@sim/realtime-protocol/constants'
|
||||
import { getActiveWorkflowContext } from '@sim/workflow-authz'
|
||||
import { loadWorkflowFromNormalizedTablesRaw } from '@sim/workflow-persistence/load'
|
||||
import { mergeSubBlockValues } from '@sim/workflow-persistence/subblocks'
|
||||
import { and, eq, inArray, isNull, or, sql } from 'drizzle-orm'
|
||||
import { drizzle } from 'drizzle-orm/postgres-js'
|
||||
import postgres from 'postgres'
|
||||
import { env } from '@/env'
|
||||
|
||||
const logger = createLogger('SocketDatabase')
|
||||
|
||||
@@ -182,7 +181,7 @@ export async function getWorkflowState(workflowId: string) {
|
||||
throw new Error(`Workflow ${workflowId} not found`)
|
||||
}
|
||||
|
||||
const normalizedData = await loadWorkflowFromNormalizedTables(workflowId)
|
||||
const normalizedData = await loadWorkflowFromNormalizedTablesRaw(workflowId)
|
||||
|
||||
if (normalizedData) {
|
||||
const finalState = {
|
||||
@@ -915,30 +914,10 @@ async function handleBlocksOperationTx(
|
||||
}
|
||||
}
|
||||
|
||||
// Clean up external webhooks
|
||||
const webhooksToCleanup = await tx
|
||||
.select({
|
||||
webhook: webhook,
|
||||
workflow: {
|
||||
id: workflow.id,
|
||||
userId: workflow.userId,
|
||||
workspaceId: workflow.workspaceId,
|
||||
},
|
||||
})
|
||||
.from(webhook)
|
||||
.innerJoin(workflow, eq(webhook.workflowId, workflow.id))
|
||||
.where(and(eq(webhook.workflowId, workflowId), inArray(webhook.blockId, blockIdsArray)))
|
||||
|
||||
if (webhooksToCleanup.length > 0) {
|
||||
const requestId = `socket-batch-${workflowId}-${Date.now()}`
|
||||
for (const { webhook: wh, workflow: wf } of webhooksToCleanup) {
|
||||
try {
|
||||
await cleanupExternalWebhook(wh, wf, requestId)
|
||||
} catch (error) {
|
||||
logger.error(`Failed to cleanup webhook ${wh.id}:`, error)
|
||||
}
|
||||
}
|
||||
}
|
||||
// Webhook rows are only created at deploy time (saveTriggerWebhooksForDeploy in
|
||||
// lib/webhooks/deploy.ts) with deploymentVersionId set; their external-subscription
|
||||
// lifecycle is managed by deploy.ts, lifecycle.ts, and the /api/webhooks/[id] route.
|
||||
// Removing a trigger block from the draft canvas does not touch any webhook rows.
|
||||
|
||||
// Delete edges connected to any of the blocks
|
||||
await tx
|
||||
44
apps/realtime/src/env.ts
Normal file
44
apps/realtime/src/env.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import { z } from 'zod'
|
||||
|
||||
const EnvSchema = z.object({
|
||||
NODE_ENV: z.enum(['development', 'test', 'production']).default('development'),
|
||||
DATABASE_URL: z.string().url(),
|
||||
REDIS_URL: z.string().url().optional(),
|
||||
BETTER_AUTH_URL: z.string().url(),
|
||||
BETTER_AUTH_SECRET: z.string().min(32),
|
||||
INTERNAL_API_SECRET: z.string().min(32),
|
||||
NEXT_PUBLIC_APP_URL: z.string().url(),
|
||||
ALLOWED_ORIGINS: z.string().optional(),
|
||||
PORT: z.coerce.number().int().positive().default(3002),
|
||||
DISABLE_AUTH: z
|
||||
.string()
|
||||
.optional()
|
||||
.transform((value) => value === 'true' || value === '1'),
|
||||
})
|
||||
|
||||
function parseEnv() {
|
||||
const parsed = EnvSchema.safeParse(process.env)
|
||||
if (!parsed.success) {
|
||||
const formatted = parsed.error.format()
|
||||
throw new Error(`Invalid realtime server environment: ${JSON.stringify(formatted, null, 2)}`)
|
||||
}
|
||||
return parsed.data
|
||||
}
|
||||
|
||||
export const env = parseEnv()
|
||||
|
||||
export const isProd = env.NODE_ENV === 'production'
|
||||
export const isDev = env.NODE_ENV === 'development'
|
||||
export const isTest = env.NODE_ENV === 'test'
|
||||
|
||||
let appHostname = ''
|
||||
try {
|
||||
appHostname = new URL(env.NEXT_PUBLIC_APP_URL).hostname
|
||||
} catch {}
|
||||
export const isHosted = appHostname === 'sim.ai' || appHostname.endsWith('.sim.ai')
|
||||
|
||||
export const isAuthDisabled = env.DISABLE_AUTH === true && !isHosted
|
||||
|
||||
export function getBaseUrl(): string {
|
||||
return env.NEXT_PUBLIC_APP_URL
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { cleanupPendingSubblocksForSocket } from '@/socket/handlers/subblocks'
|
||||
import { cleanupPendingVariablesForSocket } from '@/socket/handlers/variables'
|
||||
import type { AuthenticatedSocket } from '@/socket/middleware/auth'
|
||||
import type { IRoomManager } from '@/socket/rooms'
|
||||
import { cleanupPendingSubblocksForSocket } from '@/handlers/subblocks'
|
||||
import { cleanupPendingVariablesForSocket } from '@/handlers/variables'
|
||||
import type { AuthenticatedSocket } from '@/middleware/auth'
|
||||
import type { IRoomManager } from '@/rooms'
|
||||
|
||||
const logger = createLogger('ConnectionHandlers')
|
||||
|
||||
17
apps/realtime/src/handlers/index.ts
Normal file
17
apps/realtime/src/handlers/index.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { setupConnectionHandlers } from '@/handlers/connection'
|
||||
import { setupOperationsHandlers } from '@/handlers/operations'
|
||||
import { setupPresenceHandlers } from '@/handlers/presence'
|
||||
import { setupSubblocksHandlers } from '@/handlers/subblocks'
|
||||
import { setupVariablesHandlers } from '@/handlers/variables'
|
||||
import { setupWorkflowHandlers } from '@/handlers/workflow'
|
||||
import type { AuthenticatedSocket } from '@/middleware/auth'
|
||||
import type { IRoomManager } from '@/rooms'
|
||||
|
||||
export function setupAllHandlers(socket: AuthenticatedSocket, roomManager: IRoomManager) {
|
||||
setupWorkflowHandlers(socket, roomManager)
|
||||
setupOperationsHandlers(socket, roomManager)
|
||||
setupSubblocksHandlers(socket, roomManager)
|
||||
setupVariablesHandlers(socket, roomManager)
|
||||
setupPresenceHandlers(socket, roomManager)
|
||||
setupConnectionHandlers(socket, roomManager)
|
||||
}
|
||||
@@ -1,6 +1,4 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { generateId } from '@sim/utils/id'
|
||||
import { ZodError } from 'zod'
|
||||
import {
|
||||
BLOCK_OPERATIONS,
|
||||
BLOCKS_OPERATIONS,
|
||||
@@ -9,12 +7,14 @@ import {
|
||||
VARIABLE_OPERATIONS,
|
||||
type VariableOperation,
|
||||
WORKFLOW_OPERATIONS,
|
||||
} from '@/socket/constants'
|
||||
import { persistWorkflowOperation } from '@/socket/database/operations'
|
||||
import type { AuthenticatedSocket } from '@/socket/middleware/auth'
|
||||
import { checkRolePermission } from '@/socket/middleware/permissions'
|
||||
import type { IRoomManager, UserSession } from '@/socket/rooms'
|
||||
import { WorkflowOperationSchema } from '@/socket/validation/schemas'
|
||||
} from '@sim/realtime-protocol/constants'
|
||||
import { WorkflowOperationSchema } from '@sim/realtime-protocol/schemas'
|
||||
import { generateId } from '@sim/utils/id'
|
||||
import { ZodError } from 'zod'
|
||||
import { persistWorkflowOperation } from '@/database/operations'
|
||||
import type { AuthenticatedSocket } from '@/middleware/auth'
|
||||
import { checkRolePermission } from '@/middleware/permissions'
|
||||
import type { IRoomManager, UserSession } from '@/rooms'
|
||||
|
||||
const logger = createLogger('OperationsHandlers')
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import type { AuthenticatedSocket } from '@/socket/middleware/auth'
|
||||
import type { IRoomManager } from '@/socket/rooms'
|
||||
import type { AuthenticatedSocket } from '@/middleware/auth'
|
||||
import type { IRoomManager } from '@/rooms'
|
||||
|
||||
const logger = createLogger('PresenceHandlers')
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { db } from '@sim/db'
|
||||
import { workflow, workflowBlocks } from '@sim/db/schema'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { SUBBLOCK_OPERATIONS } from '@sim/realtime-protocol/constants'
|
||||
import { and, eq } from 'drizzle-orm'
|
||||
import { SUBBLOCK_OPERATIONS } from '@/socket/constants'
|
||||
import type { AuthenticatedSocket } from '@/socket/middleware/auth'
|
||||
import { checkRolePermission } from '@/socket/middleware/permissions'
|
||||
import type { IRoomManager } from '@/socket/rooms'
|
||||
import type { AuthenticatedSocket } from '@/middleware/auth'
|
||||
import { checkRolePermission } from '@/middleware/permissions'
|
||||
import type { IRoomManager } from '@/rooms'
|
||||
|
||||
const logger = createLogger('SubblocksHandlers')
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { db } from '@sim/db'
|
||||
import { workflow } from '@sim/db/schema'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { VARIABLE_OPERATIONS } from '@sim/realtime-protocol/constants'
|
||||
import { eq } from 'drizzle-orm'
|
||||
import { VARIABLE_OPERATIONS } from '@/socket/constants'
|
||||
import type { AuthenticatedSocket } from '@/socket/middleware/auth'
|
||||
import { checkRolePermission } from '@/socket/middleware/permissions'
|
||||
import type { IRoomManager } from '@/socket/rooms'
|
||||
import type { AuthenticatedSocket } from '@/middleware/auth'
|
||||
import { checkRolePermission } from '@/middleware/permissions'
|
||||
import type { IRoomManager } from '@/rooms'
|
||||
|
||||
const logger = createLogger('VariablesHandlers')
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* @vitest-environment node
|
||||
*/
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import type { IRoomManager } from '@/socket/rooms'
|
||||
import type { IRoomManager } from '@/rooms'
|
||||
|
||||
const { mockGetWorkflowState, mockVerifyWorkflowAccess } = vi.hoisted(() => ({
|
||||
mockGetWorkflowState: vi.fn(),
|
||||
@@ -14,15 +14,15 @@ vi.mock('@sim/db', () => ({
|
||||
user: { image: 'image' },
|
||||
}))
|
||||
|
||||
vi.mock('@/socket/database/operations', () => ({
|
||||
vi.mock('@/database/operations', () => ({
|
||||
getWorkflowState: mockGetWorkflowState,
|
||||
}))
|
||||
|
||||
vi.mock('@/socket/middleware/permissions', () => ({
|
||||
vi.mock('@/middleware/permissions', () => ({
|
||||
verifyWorkflowAccess: mockVerifyWorkflowAccess,
|
||||
}))
|
||||
|
||||
import { setupWorkflowHandlers } from '@/socket/handlers/workflow'
|
||||
import { setupWorkflowHandlers } from '@/handlers/workflow'
|
||||
|
||||
interface JoinWorkflowPayload {
|
||||
workflowId: string
|
||||
@@ -1,10 +1,10 @@
|
||||
import { db, user } from '@sim/db'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { eq } from 'drizzle-orm'
|
||||
import { getWorkflowState } from '@/socket/database/operations'
|
||||
import type { AuthenticatedSocket } from '@/socket/middleware/auth'
|
||||
import { verifyWorkflowAccess } from '@/socket/middleware/permissions'
|
||||
import type { IRoomManager, UserPresence } from '@/socket/rooms'
|
||||
import { getWorkflowState } from '@/database/operations'
|
||||
import type { AuthenticatedSocket } from '@/middleware/auth'
|
||||
import { verifyWorkflowAccess } from '@/middleware/permissions'
|
||||
import type { IRoomManager, UserPresence } from '@/rooms'
|
||||
|
||||
const logger = createLogger('WorkflowHandlers')
|
||||
|
||||
@@ -4,21 +4,28 @@
|
||||
* @vitest-environment node
|
||||
*/
|
||||
import { createServer, request as httpRequest } from 'http'
|
||||
import { createEnvMock, createMockLogger } from '@sim/testing'
|
||||
import { createMockLogger } from '@sim/testing'
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { createSocketIOServer } from '@/socket/config/socket'
|
||||
import { MemoryRoomManager } from '@/socket/rooms'
|
||||
import { createHttpHandler } from '@/socket/routes/http'
|
||||
import { createSocketIOServer } from '@/config/socket'
|
||||
import { MemoryRoomManager } from '@/rooms'
|
||||
import { createHttpHandler } from '@/routes/http'
|
||||
|
||||
vi.mock('@/lib/auth', () => ({
|
||||
vi.mock('@/auth', () => ({
|
||||
auth: {
|
||||
api: {
|
||||
verifyOneTimeToken: vi.fn(),
|
||||
},
|
||||
},
|
||||
ANONYMOUS_USER_ID: '00000000-0000-0000-0000-000000000000',
|
||||
ANONYMOUS_USER: {
|
||||
id: '00000000-0000-0000-0000-000000000000',
|
||||
name: 'Anonymous',
|
||||
email: 'anonymous@localhost',
|
||||
emailVerified: true,
|
||||
image: null,
|
||||
},
|
||||
}))
|
||||
|
||||
// Mock redis package to prevent actual Redis connections
|
||||
vi.mock('redis', () => ({
|
||||
createClient: vi.fn(() => ({
|
||||
on: vi.fn(),
|
||||
@@ -28,15 +35,27 @@ vi.mock('redis', () => ({
|
||||
})),
|
||||
}))
|
||||
|
||||
vi.mock('@/lib/core/config/env', () =>
|
||||
createEnvMock({
|
||||
vi.mock('@/env', () => ({
|
||||
env: {
|
||||
DATABASE_URL: 'postgres://localhost/test',
|
||||
NODE_ENV: 'test',
|
||||
REDIS_URL: undefined,
|
||||
})
|
||||
)
|
||||
BETTER_AUTH_URL: 'http://localhost:3000',
|
||||
BETTER_AUTH_SECRET: 'test-better-auth-secret-at-least-32-chars',
|
||||
INTERNAL_API_SECRET: 'test-internal-api-secret-at-least-32-chars',
|
||||
NEXT_PUBLIC_APP_URL: 'http://localhost:3000',
|
||||
PORT: 3002,
|
||||
DISABLE_AUTH: false,
|
||||
},
|
||||
isProd: false,
|
||||
isDev: false,
|
||||
isTest: true,
|
||||
isHosted: false,
|
||||
isAuthDisabled: false,
|
||||
getBaseUrl: () => 'http://localhost:3000',
|
||||
}))
|
||||
|
||||
vi.mock('@/socket/middleware/auth', () => ({
|
||||
vi.mock('@/middleware/auth', () => ({
|
||||
authenticateSocket: vi.fn((socket, next) => {
|
||||
socket.userId = 'test-user-id'
|
||||
socket.userName = 'Test User'
|
||||
@@ -45,7 +64,7 @@ vi.mock('@/socket/middleware/auth', () => ({
|
||||
}),
|
||||
}))
|
||||
|
||||
vi.mock('@/socket/middleware/permissions', () => ({
|
||||
vi.mock('@/middleware/permissions', () => ({
|
||||
verifyWorkflowAccess: vi.fn().mockResolvedValue({
|
||||
hasAccess: true,
|
||||
role: 'admin',
|
||||
@@ -55,7 +74,7 @@ vi.mock('@/socket/middleware/permissions', () => ({
|
||||
}),
|
||||
}))
|
||||
|
||||
vi.mock('@/socket/database/operations', () => ({
|
||||
vi.mock('@/database/operations', () => ({
|
||||
getWorkflowState: vi.fn().mockResolvedValue({
|
||||
id: 'test-workflow',
|
||||
name: 'Test Workflow',
|
||||
@@ -275,13 +294,13 @@ describe('Socket Server Index Integration', () => {
|
||||
|
||||
describe('Module Integration', () => {
|
||||
it.concurrent('should properly import all extracted modules', async () => {
|
||||
const { createSocketIOServer } = await import('@/socket/config/socket')
|
||||
const { createHttpHandler } = await import('@/socket/routes/http')
|
||||
const { MemoryRoomManager, RedisRoomManager } = await import('@/socket/rooms')
|
||||
const { authenticateSocket } = await import('@/socket/middleware/auth')
|
||||
const { verifyWorkflowAccess } = await import('@/socket/middleware/permissions')
|
||||
const { getWorkflowState } = await import('@/socket/database/operations')
|
||||
const { WorkflowOperationSchema } = await import('@/socket/validation/schemas')
|
||||
const { createSocketIOServer } = await import('@/config/socket')
|
||||
const { createHttpHandler } = await import('@/routes/http')
|
||||
const { MemoryRoomManager, RedisRoomManager } = await import('@/rooms')
|
||||
const { authenticateSocket } = await import('@/middleware/auth')
|
||||
const { verifyWorkflowAccess } = await import('@/middleware/permissions')
|
||||
const { getWorkflowState } = await import('@/database/operations')
|
||||
const { WorkflowOperationSchema } = await import('@sim/realtime-protocol/schemas')
|
||||
|
||||
expect(createSocketIOServer).toBeTypeOf('function')
|
||||
expect(createHttpHandler).toBeTypeOf('function')
|
||||
@@ -332,7 +351,7 @@ describe('Socket Server Index Integration', () => {
|
||||
|
||||
describe('Validation and Utils', () => {
|
||||
it.concurrent('should validate workflow operations', async () => {
|
||||
const { WorkflowOperationSchema } = await import('@/socket/validation/schemas')
|
||||
const { WorkflowOperationSchema } = await import('@sim/realtime-protocol/schemas')
|
||||
|
||||
const validOperation = {
|
||||
operation: 'batch-add-blocks',
|
||||
@@ -358,7 +377,7 @@ describe('Socket Server Index Integration', () => {
|
||||
})
|
||||
|
||||
it.concurrent('should validate batch-add-blocks with edges', async () => {
|
||||
const { WorkflowOperationSchema } = await import('@/socket/validation/schemas')
|
||||
const { WorkflowOperationSchema } = await import('@sim/realtime-protocol/schemas')
|
||||
|
||||
const validOperationWithEdge = {
|
||||
operation: 'batch-add-blocks',
|
||||
@@ -393,7 +412,7 @@ describe('Socket Server Index Integration', () => {
|
||||
})
|
||||
|
||||
it.concurrent('should validate edge operations', async () => {
|
||||
const { WorkflowOperationSchema } = await import('@/socket/validation/schemas')
|
||||
const { WorkflowOperationSchema } = await import('@sim/realtime-protocol/schemas')
|
||||
|
||||
const validEdgeOperation = {
|
||||
operation: 'add',
|
||||
@@ -410,7 +429,7 @@ describe('Socket Server Index Integration', () => {
|
||||
})
|
||||
|
||||
it('should validate subflow operations', async () => {
|
||||
const { WorkflowOperationSchema } = await import('@/socket/validation/schemas')
|
||||
const { WorkflowOperationSchema } = await import('@sim/realtime-protocol/schemas')
|
||||
|
||||
const validSubflowOperation = {
|
||||
operation: 'update',
|
||||
@@ -1,12 +1,12 @@
|
||||
import { createServer } from 'http'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import type { Server as SocketIOServer } from 'socket.io'
|
||||
import { env } from '@/lib/core/config/env'
|
||||
import { createSocketIOServer, shutdownSocketIOAdapter } from '@/socket/config/socket'
|
||||
import { setupAllHandlers } from '@/socket/handlers'
|
||||
import { type AuthenticatedSocket, authenticateSocket } from '@/socket/middleware/auth'
|
||||
import { type IRoomManager, MemoryRoomManager, RedisRoomManager } from '@/socket/rooms'
|
||||
import { createHttpHandler } from '@/socket/routes/http'
|
||||
import { createSocketIOServer, shutdownSocketIOAdapter } from '@/config/socket'
|
||||
import { env } from '@/env'
|
||||
import { setupAllHandlers } from '@/handlers'
|
||||
import { type AuthenticatedSocket, authenticateSocket } from '@/middleware/auth'
|
||||
import { type IRoomManager, MemoryRoomManager, RedisRoomManager } from '@/rooms'
|
||||
import { createHttpHandler } from '@/routes/http'
|
||||
|
||||
const logger = createLogger('CollaborativeSocketServer')
|
||||
|
||||
@@ -29,7 +29,7 @@ async function createRoomManager(io: SocketIOServer): Promise<IRoomManager> {
|
||||
|
||||
async function main() {
|
||||
const httpServer = createServer()
|
||||
const PORT = Number(env.PORT || env.SOCKET_PORT || 3002)
|
||||
const PORT = env.PORT
|
||||
|
||||
logger.info('Starting Socket.IO server...', {
|
||||
port: PORT,
|
||||
@@ -1,9 +1,8 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { toError } from '@sim/utils/errors'
|
||||
import type { Socket } from 'socket.io'
|
||||
import { auth } from '@/lib/auth'
|
||||
import { ANONYMOUS_USER, ANONYMOUS_USER_ID } from '@/lib/auth/constants'
|
||||
import { isAuthDisabled } from '@/lib/core/config/feature-flags'
|
||||
import { ANONYMOUS_USER, ANONYMOUS_USER_ID, auth } from '@/auth'
|
||||
import { isAuthDisabled } from '@/env'
|
||||
|
||||
const logger = createLogger('SocketAuth')
|
||||
|
||||
@@ -14,7 +14,7 @@ import {
|
||||
SOCKET_OPERATIONS,
|
||||
} from '@sim/testing'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { checkRolePermission } from '@/socket/middleware/permissions'
|
||||
import { checkRolePermission } from '@/middleware/permissions'
|
||||
|
||||
describe('checkRolePermission', () => {
|
||||
describe('admin role', () => {
|
||||
@@ -1,8 +1,6 @@
|
||||
import { db } from '@sim/db'
|
||||
import { workflow } from '@sim/db/schema'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { and, eq, isNull } from 'drizzle-orm'
|
||||
import { authorizeWorkflowByWorkspacePermission } from '@/lib/workflows/utils'
|
||||
import {
|
||||
BLOCK_OPERATIONS,
|
||||
BLOCKS_OPERATIONS,
|
||||
@@ -12,7 +10,9 @@ import {
|
||||
SUBFLOW_OPERATIONS,
|
||||
VARIABLE_OPERATIONS,
|
||||
WORKFLOW_OPERATIONS,
|
||||
} from '@/socket/constants'
|
||||
} from '@sim/realtime-protocol/constants'
|
||||
import { authorizeWorkflowByWorkspacePermission } from '@sim/workflow-authz'
|
||||
import { and, eq, isNull } from 'drizzle-orm'
|
||||
|
||||
const logger = createLogger('SocketPermissions')
|
||||
|
||||
3
apps/realtime/src/rooms/index.ts
Normal file
3
apps/realtime/src/rooms/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export { MemoryRoomManager } from '@/rooms/memory-manager'
|
||||
export { RedisRoomManager } from '@/rooms/redis-manager'
|
||||
export type { IRoomManager, UserPresence, UserSession, WorkflowRoom } from '@/rooms/types'
|
||||
@@ -1,6 +1,6 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import type { Server } from 'socket.io'
|
||||
import type { IRoomManager, UserPresence, UserSession, WorkflowRoom } from '@/socket/rooms/types'
|
||||
import type { IRoomManager, UserPresence, UserSession, WorkflowRoom } from '@/rooms/types'
|
||||
|
||||
const logger = createLogger('MemoryRoomManager')
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { createClient, type RedisClientType } from 'redis'
|
||||
import type { Server } from 'socket.io'
|
||||
import type { IRoomManager, UserPresence, UserSession } from '@/socket/rooms/types'
|
||||
import type { IRoomManager, UserPresence, UserSession } from '@/rooms/types'
|
||||
|
||||
const logger = createLogger('RedisRoomManager')
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { IncomingMessage, ServerResponse } from 'http'
|
||||
import { env } from '@/lib/core/config/env'
|
||||
import { safeCompare } from '@/lib/core/security/encryption'
|
||||
import type { IRoomManager } from '@/socket/rooms'
|
||||
import { safeCompare } from '@sim/security/compare'
|
||||
import { env } from '@/env'
|
||||
import type { IRoomManager } from '@/rooms'
|
||||
|
||||
interface Logger {
|
||||
info: (message: string, ...args: unknown[]) => void
|
||||
11
apps/realtime/tsconfig.json
Normal file
11
apps/realtime/tsconfig.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"extends": "@sim/tsconfig/base.json",
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["src/*"]
|
||||
}
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
27
apps/realtime/vitest.config.ts
Normal file
27
apps/realtime/vitest.config.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import path from 'node:path'
|
||||
import { defineConfig } from 'vitest/config'
|
||||
|
||||
export default defineConfig({
|
||||
test: {
|
||||
globals: true,
|
||||
environment: 'node',
|
||||
include: ['**/*.test.{ts,tsx}'],
|
||||
exclude: ['**/node_modules/**', '**/dist/**'],
|
||||
setupFiles: ['./vitest.setup.ts'],
|
||||
pool: 'threads',
|
||||
testTimeout: 10000,
|
||||
},
|
||||
resolve: {
|
||||
alias: [
|
||||
{
|
||||
find: '@sim/db',
|
||||
replacement: path.resolve(__dirname, '../../packages/db'),
|
||||
},
|
||||
{
|
||||
find: '@sim/logger',
|
||||
replacement: path.resolve(__dirname, '../../packages/logger/src'),
|
||||
},
|
||||
{ find: '@', replacement: path.resolve(__dirname, 'src') },
|
||||
],
|
||||
},
|
||||
})
|
||||
6
apps/realtime/vitest.setup.ts
Normal file
6
apps/realtime/vitest.setup.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
process.env.DATABASE_URL ??= 'postgres://localhost/test'
|
||||
process.env.NODE_ENV ??= 'test'
|
||||
process.env.BETTER_AUTH_URL ??= 'http://localhost:3000'
|
||||
process.env.BETTER_AUTH_SECRET ??= 'test-better-auth-secret-at-least-32-chars'
|
||||
process.env.INTERNAL_API_SECRET ??= 'test-internal-api-secret-at-least-32-chars'
|
||||
process.env.NEXT_PUBLIC_APP_URL ??= 'http://localhost:3000'
|
||||
@@ -26,6 +26,13 @@ apps/sim/
|
||||
└── triggers/ # Trigger definitions
|
||||
```
|
||||
|
||||
The Socket.IO collaborative-canvas server lives in a separate workspace at
|
||||
`apps/realtime/`. It shares DB + auth with `apps/sim` via the `@sim/*`
|
||||
packages. Do not add imports from `@/lib/webhooks/providers/*`, `@/executor/*`,
|
||||
`@/blocks/*`, or `@/tools/*` to any package consumed by `apps/realtime` —
|
||||
those heavyweight registries stay in this app. `apps/realtime` calls back
|
||||
into this app only over internal HTTP with `INTERNAL_API_SECRET`.
|
||||
|
||||
### Feature Organization
|
||||
|
||||
Features live under `app/workspace/[workspaceId]/`:
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@sim/audit'
|
||||
import { db } from '@sim/db'
|
||||
import { user } from '@sim/db/schema'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { eq } from 'drizzle-orm'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
|
||||
import { auth } from '@/lib/auth'
|
||||
import { isSameOrigin } from '@/lib/core/utils/validation'
|
||||
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { db } from '@sim/db'
|
||||
import { account, credential, credentialMember } from '@sim/db/schema'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { authorizeWorkflowByWorkspacePermission } from '@sim/workflow-authz'
|
||||
import { and, eq } from 'drizzle-orm'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
@@ -12,7 +13,6 @@ import {
|
||||
getCanonicalScopesForProvider,
|
||||
getServiceAccountProviderForProviderId,
|
||||
} from '@/lib/oauth/utils'
|
||||
import { authorizeWorkflowByWorkspacePermission } from '@/lib/workflows/utils'
|
||||
import { checkWorkspaceAccess } from '@/lib/workspaces/permissions/utils'
|
||||
|
||||
export const dynamic = 'force-dynamic'
|
||||
|
||||
@@ -23,7 +23,7 @@ vi.mock('@/lib/webhooks/utils.server', () => ({
|
||||
syncAllWebhooksForCredentialSet: mockSyncAllWebhooksForCredentialSet,
|
||||
}))
|
||||
|
||||
vi.mock('@/lib/audit/log', () => auditMock)
|
||||
vi.mock('@sim/audit', () => auditMock)
|
||||
|
||||
import { POST } from '@/app/api/auth/oauth/disconnect/route'
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@sim/audit'
|
||||
import { db } from '@sim/db'
|
||||
import { account, credentialSet, credentialSetMember } from '@sim/db/schema'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { and, eq, like, or } from 'drizzle-orm'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import { generateRequestId } from '@/lib/core/utils/request'
|
||||
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import crypto from 'crypto'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { safeCompare } from '@sim/security/compare'
|
||||
import { hmacSha256Hex } from '@sim/security/hmac'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import { env } from '@/lib/core/config/env'
|
||||
@@ -34,13 +35,9 @@ function validateHmac(searchParams: URLSearchParams, clientSecret: string): bool
|
||||
.map((key) => `${key}=${params[key]}`)
|
||||
.join('&')
|
||||
|
||||
const generatedHmac = crypto.createHmac('sha256', clientSecret).update(message).digest('hex')
|
||||
const generatedHmac = hmacSha256Hex(message, clientSecret)
|
||||
|
||||
try {
|
||||
return crypto.timingSafeEqual(Buffer.from(hmac, 'hex'), Buffer.from(generatedHmac, 'hex'))
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
return safeCompare(hmac, generatedHmac)
|
||||
}
|
||||
|
||||
export const GET = withRouteHandler(async (request: NextRequest) => {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@sim/audit'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import { getCreditBalance } from '@/lib/billing/credits/balance'
|
||||
import { purchaseCredits } from '@/lib/billing/credits/purchase'
|
||||
|
||||
@@ -33,7 +33,7 @@ const mockPerformChatUndeploy = workflowsOrchestrationMockFns.mockPerformChatUnd
|
||||
const mockNotifySocketDeploymentChanged =
|
||||
workflowsOrchestrationMockFns.mockNotifySocketDeploymentChanged
|
||||
|
||||
vi.mock('@/lib/audit/log', () => auditMock)
|
||||
vi.mock('@sim/audit', () => auditMock)
|
||||
vi.mock('@/lib/core/config/feature-flags', () => ({
|
||||
isDev: true,
|
||||
isHosted: false,
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@sim/audit'
|
||||
import { db } from '@sim/db'
|
||||
import { chat } from '@sim/db/schema'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { and, eq, isNull } from 'drizzle-orm'
|
||||
import type { NextRequest } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import { isDev } from '@/lib/core/config/feature-flags'
|
||||
import { encryptSecret } from '@/lib/core/security/encryption'
|
||||
|
||||
@@ -40,7 +40,7 @@ vi.mock('@/serializer', () => ({
|
||||
Serializer: vi.fn(),
|
||||
}))
|
||||
|
||||
vi.mock('@/lib/workflows/subblocks', () => ({
|
||||
vi.mock('@sim/workflow-persistence/subblocks', () => ({
|
||||
mergeSubblockStateWithValues: mockMergeSubblockStateWithValues,
|
||||
mergeSubBlockValues: mockMergeSubBlockValues,
|
||||
}))
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { db } from '@sim/db'
|
||||
import { chat, workflow } from '@sim/db/schema'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { authorizeWorkflowByWorkspacePermission } from '@sim/workflow-authz'
|
||||
import { and, eq, isNull } from 'drizzle-orm'
|
||||
import type { NextRequest, NextResponse } from 'next/server'
|
||||
import {
|
||||
@@ -9,7 +10,6 @@ import {
|
||||
validateAuthToken,
|
||||
} from '@/lib/core/security/deployment'
|
||||
import { decryptSecret } from '@/lib/core/security/encryption'
|
||||
import { authorizeWorkflowByWorkspacePermission } from '@/lib/workflows/utils'
|
||||
|
||||
const logger = createLogger('ChatAuthUtils')
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import { db } from '@sim/db'
|
||||
import { copilotChats } from '@sim/db/schema'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { toError } from '@sim/utils/errors'
|
||||
import { authorizeWorkflowByWorkspacePermission } from '@sim/workflow-authz'
|
||||
import { and, desc, eq } from 'drizzle-orm'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { getLatestRunForStream } from '@/lib/copilot/async-runs/repository'
|
||||
@@ -17,7 +18,6 @@ import {
|
||||
import { readFilePreviewSessions } from '@/lib/copilot/request/session'
|
||||
import { readEvents } from '@/lib/copilot/request/session/buffer'
|
||||
import { toStreamBatchEvent } from '@/lib/copilot/request/session/types'
|
||||
import { authorizeWorkflowByWorkspacePermission } from '@/lib/workflows/utils'
|
||||
import { assertActiveWorkspaceAccess } from '@/lib/workspaces/permissions/utils'
|
||||
|
||||
const logger = createLogger('CopilotChatAPI')
|
||||
|
||||
@@ -52,6 +52,8 @@ const ContentBlockSchema = z.object({
|
||||
lifecycle: z.enum(['start', 'end']).optional(),
|
||||
status: z.enum(['complete', 'error', 'cancelled']).optional(),
|
||||
toolCall: StoredToolCallSchema.optional(),
|
||||
timestamp: z.number().optional(),
|
||||
endedAt: z.number().optional(),
|
||||
})
|
||||
|
||||
const StopSchema = z.object({
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { context as otelContext, trace } from '@opentelemetry/api'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { sleep } from '@sim/utils/helpers'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { getLatestRunForStream } from '@/lib/copilot/async-runs/repository'
|
||||
import {
|
||||
@@ -407,7 +408,7 @@ async function handleResumeRequestBody({
|
||||
break
|
||||
}
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS))
|
||||
await sleep(POLL_INTERVAL_MS)
|
||||
}
|
||||
if (!controllerClosed && Date.now() - startTime >= MAX_STREAM_MS) {
|
||||
emitTerminalIfMissing(MothershipStreamV1CompletionStatus.error, {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { db } from '@sim/db'
|
||||
import { copilotChats, permissions, workflow, workspace } from '@sim/db/schema'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { authorizeWorkflowByWorkspacePermission } from '@sim/workflow-authz'
|
||||
import { and, desc, eq, isNull, or, sql } from 'drizzle-orm'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
@@ -13,7 +14,6 @@ import {
|
||||
} from '@/lib/copilot/request/http'
|
||||
import { taskPubSub } from '@/lib/copilot/tasks'
|
||||
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
import { authorizeWorkflowByWorkspacePermission } from '@/lib/workflows/utils'
|
||||
import { assertActiveWorkspaceAccess } from '@/lib/workspaces/permissions/utils'
|
||||
|
||||
const logger = createLogger('CopilotChatsListAPI')
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* @vitest-environment node
|
||||
*/
|
||||
import { authMockFns, workflowsUtilsMock, workflowsUtilsMockFns } from '@sim/testing'
|
||||
import { authMockFns, workflowAuthzMockFns, workflowsUtilsMock } from '@sim/testing'
|
||||
import { NextRequest } from 'next/server'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
|
||||
@@ -63,7 +63,7 @@ describe('Copilot Checkpoints Revert API Route', () => {
|
||||
|
||||
authMockFns.mockGetSession.mockResolvedValue(null)
|
||||
|
||||
workflowsUtilsMockFns.mockAuthorizeWorkflowByWorkspacePermission.mockResolvedValue({
|
||||
workflowAuthzMockFns.mockAuthorizeWorkflowByWorkspacePermission.mockResolvedValue({
|
||||
allowed: true,
|
||||
status: 200,
|
||||
})
|
||||
@@ -251,7 +251,7 @@ describe('Copilot Checkpoints Revert API Route', () => {
|
||||
thenResults.push(mockCheckpoint) // Checkpoint found
|
||||
thenResults.push(mockWorkflow) // Workflow found but different user
|
||||
|
||||
workflowsUtilsMockFns.mockAuthorizeWorkflowByWorkspacePermission.mockResolvedValueOnce({
|
||||
workflowAuthzMockFns.mockAuthorizeWorkflowByWorkspacePermission.mockResolvedValueOnce({
|
||||
allowed: false,
|
||||
status: 403,
|
||||
})
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { db } from '@sim/db'
|
||||
import { workflowCheckpoints, workflow as workflowTable } from '@sim/db/schema'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { authorizeWorkflowByWorkspacePermission } from '@sim/workflow-authz'
|
||||
import { and, eq } from 'drizzle-orm'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
@@ -14,7 +15,6 @@ import {
|
||||
} from '@/lib/copilot/request/http'
|
||||
import { getInternalApiBaseUrl } from '@/lib/core/utils/urls'
|
||||
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
import { authorizeWorkflowByWorkspacePermission } from '@/lib/workflows/utils'
|
||||
import { isUuidV4 } from '@/executor/constants'
|
||||
|
||||
const logger = createLogger('CheckpointRevertAPI')
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* @vitest-environment node
|
||||
*/
|
||||
import { authMockFns, workflowsUtilsMock, workflowsUtilsMockFns } from '@sim/testing'
|
||||
import { authMockFns, workflowAuthzMockFns, workflowsUtilsMock } from '@sim/testing'
|
||||
import { NextRequest } from 'next/server'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
|
||||
@@ -79,7 +79,7 @@ describe('Copilot Checkpoints API Route', () => {
|
||||
userId: 'user-123',
|
||||
workflowId: 'workflow-123',
|
||||
})
|
||||
workflowsUtilsMockFns.mockAuthorizeWorkflowByWorkspacePermission.mockResolvedValue({
|
||||
workflowAuthzMockFns.mockAuthorizeWorkflowByWorkspacePermission.mockResolvedValue({
|
||||
allowed: true,
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { db } from '@sim/db'
|
||||
import { workflowCheckpoints } from '@sim/db/schema'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { authorizeWorkflowByWorkspacePermission } from '@sim/workflow-authz'
|
||||
import { and, desc, eq } from 'drizzle-orm'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
@@ -13,7 +14,6 @@ import {
|
||||
createUnauthorizedResponse,
|
||||
} from '@/lib/copilot/request/http'
|
||||
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
import { authorizeWorkflowByWorkspacePermission } from '@/lib/workflows/utils'
|
||||
|
||||
const logger = createLogger('WorkflowCheckpointsAPI')
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@sim/audit'
|
||||
import { db } from '@sim/db'
|
||||
import { credentialSet, credentialSetInvitation, member, organization, user } from '@sim/db/schema'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { and, eq } from 'drizzle-orm'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { getEmailSubject, renderPollingGroupInvitationEmail } from '@/components/emails'
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import { hasCredentialSetsAccess } from '@/lib/billing'
|
||||
import { getBaseUrl } from '@/lib/core/utils/urls'
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@sim/audit'
|
||||
import { db } from '@sim/db'
|
||||
import { credentialSet, credentialSetInvitation, member, organization, user } from '@sim/db/schema'
|
||||
import { createLogger } from '@sim/logger'
|
||||
@@ -6,7 +7,6 @@ import { and, eq } from 'drizzle-orm'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { getEmailSubject, renderPollingGroupInvitationEmail } from '@/components/emails'
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import { hasCredentialSetsAccess } from '@/lib/billing'
|
||||
import { getBaseUrl } from '@/lib/core/utils/urls'
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@sim/audit'
|
||||
import { db } from '@sim/db'
|
||||
import { account, credentialSet, credentialSetMember, member, user } from '@sim/db/schema'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { generateId } from '@sim/utils/id'
|
||||
import { and, eq, inArray } from 'drizzle-orm'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import { hasCredentialSetsAccess } from '@/lib/billing'
|
||||
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@sim/audit'
|
||||
import { db } from '@sim/db'
|
||||
import { credentialSet, credentialSetMember, member } from '@sim/db/schema'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { and, eq } from 'drizzle-orm'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import { hasCredentialSetsAccess } from '@/lib/billing'
|
||||
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@sim/audit'
|
||||
import { db } from '@sim/db'
|
||||
import {
|
||||
credentialSet,
|
||||
@@ -9,7 +10,6 @@ import { createLogger } from '@sim/logger'
|
||||
import { generateId } from '@sim/utils/id'
|
||||
import { and, eq } from 'drizzle-orm'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
import { syncAllWebhooksForCredentialSet } from '@/lib/webhooks/utils.server'
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@sim/audit'
|
||||
import { db } from '@sim/db'
|
||||
import { credentialSet, credentialSetMember, organization } from '@sim/db/schema'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { generateId } from '@sim/utils/id'
|
||||
import { and, eq } from 'drizzle-orm'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
import { syncAllWebhooksForCredentialSet } from '@/lib/webhooks/utils.server'
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@sim/audit'
|
||||
import { db } from '@sim/db'
|
||||
import { credentialSet, credentialSetMember, member, organization, user } from '@sim/db/schema'
|
||||
import { createLogger } from '@sim/logger'
|
||||
@@ -5,7 +6,6 @@ import { generateId } from '@sim/utils/id'
|
||||
import { and, count, desc, eq } from 'drizzle-orm'
|
||||
import { NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import { hasCredentialSetsAccess } from '@/lib/billing'
|
||||
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@sim/audit'
|
||||
import { db } from '@sim/db'
|
||||
import { credential, credentialMember, environment, workspaceEnvironment } from '@sim/db/schema'
|
||||
import { createLogger } from '@sim/logger'
|
||||
@@ -5,7 +6,6 @@ import { generateId } from '@sim/utils/id'
|
||||
import { and, eq } from 'drizzle-orm'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import { encryptSecret } from '@/lib/core/security/encryption'
|
||||
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@sim/audit'
|
||||
import { db } from '@sim/db'
|
||||
import { account, credential, credentialMember, workspace } from '@sim/db/schema'
|
||||
import { createLogger } from '@sim/logger'
|
||||
@@ -5,7 +6,6 @@ import { generateId } from '@sim/utils/id'
|
||||
import { and, eq } from 'drizzle-orm'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import { encryptSecret } from '@/lib/core/security/encryption'
|
||||
import { generateRequestId } from '@/lib/core/utils/request'
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@sim/audit'
|
||||
import { db } from '@sim/db'
|
||||
import { environment } from '@sim/db/schema'
|
||||
import { createLogger } from '@sim/logger'
|
||||
@@ -5,7 +6,6 @@ import { generateId } from '@sim/utils/id'
|
||||
import { eq } from 'drizzle-orm'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import { decryptSecret, encryptSecret } from '@/lib/core/security/encryption'
|
||||
import { generateRequestId } from '@/lib/core/utils/request'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { createHash } from 'crypto'
|
||||
import { readFile } from 'fs/promises'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { sha256Hex } from '@sim/security/hash'
|
||||
import type { NextRequest } from 'next/server'
|
||||
import { NextResponse } from 'next/server'
|
||||
import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid'
|
||||
@@ -80,11 +80,7 @@ async function compileDocumentIfNeeded(
|
||||
}
|
||||
|
||||
const code = buffer.toString('utf-8')
|
||||
const cacheKey = createHash('sha256')
|
||||
.update(ext)
|
||||
.update(code)
|
||||
.update(workspaceId ?? '')
|
||||
.digest('hex')
|
||||
const cacheKey = sha256Hex(`${ext}${code}${workspaceId ?? ''}`)
|
||||
const cached = compiledDocCache.get(cacheKey)
|
||||
if (cached) {
|
||||
return { buffer: cached, contentType: format.contentType }
|
||||
|
||||
@@ -2,7 +2,7 @@ import { createLogger } from '@sim/logger'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { sanitizeFileName } from '@/executor/constants'
|
||||
import '@/lib/uploads/core/setup.server'
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@sim/audit'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
import { captureServerEvent } from '@/lib/posthog/server'
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@sim/audit'
|
||||
import { db } from '@sim/db'
|
||||
import { workflow, workflowFolder } from '@sim/db/schema'
|
||||
import { createLogger } from '@sim/logger'
|
||||
@@ -5,7 +6,6 @@ import { generateId } from '@sim/utils/id'
|
||||
import { and, eq, isNull, min } from 'drizzle-orm'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import { generateRequestId } from '@/lib/core/utils/request'
|
||||
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
|
||||
@@ -37,7 +37,7 @@ const mockPerformDeleteFolder = workflowsOrchestrationMockFns.mockPerformDeleteF
|
||||
|
||||
const mockGetUserEntityPermissions = permissionsMockFns.mockGetUserEntityPermissions
|
||||
|
||||
vi.mock('@/lib/audit/log', () => auditMock)
|
||||
vi.mock('@sim/audit', () => auditMock)
|
||||
vi.mock('@sim/logger', () => ({
|
||||
createLogger: vi.fn().mockReturnValue(mockLogger),
|
||||
runWithRequestContext: <T>(_ctx: unknown, fn: () => T): T => fn(),
|
||||
|
||||
@@ -30,7 +30,7 @@ const { mockLogger } = vi.hoisted(() => {
|
||||
|
||||
const mockGetUserEntityPermissions = permissionsMockFns.mockGetUserEntityPermissions
|
||||
|
||||
vi.mock('@/lib/audit/log', () => auditMock)
|
||||
vi.mock('@sim/audit', () => auditMock)
|
||||
vi.mock('drizzle-orm', () => ({
|
||||
...drizzleOrmMock,
|
||||
min: vi.fn((field) => ({ type: 'min', field })),
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@sim/audit'
|
||||
import { db } from '@sim/db'
|
||||
import { workflow, workflowFolder } from '@sim/db/schema'
|
||||
import { createLogger } from '@sim/logger'
|
||||
@@ -5,7 +6,6 @@ import { generateId } from '@sim/utils/id'
|
||||
import { and, asc, eq, isNotNull, isNull, min } from 'drizzle-orm'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
import { captureServerEvent } from '@/lib/posthog/server'
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@sim/audit'
|
||||
import { db } from '@sim/db'
|
||||
import { form } from '@sim/db/schema'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { and, eq, isNull } from 'drizzle-orm'
|
||||
import type { NextRequest } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import { encryptSecret } from '@/lib/core/security/encryption'
|
||||
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@sim/audit'
|
||||
import { db } from '@sim/db'
|
||||
import { form } from '@sim/db/schema'
|
||||
import { createLogger } from '@sim/logger'
|
||||
@@ -5,7 +6,6 @@ import { generateId } from '@sim/utils/id'
|
||||
import { and, eq, isNull } from 'drizzle-orm'
|
||||
import type { NextRequest } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import { isDev } from '@/lib/core/config/feature-flags'
|
||||
import { encryptSecret } from '@/lib/core/security/encryption'
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { db } from '@sim/db'
|
||||
import { form, workflow } from '@sim/db/schema'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { authorizeWorkflowByWorkspacePermission } from '@sim/workflow-authz'
|
||||
import { and, eq, isNull } from 'drizzle-orm'
|
||||
import type { NextRequest, NextResponse } from 'next/server'
|
||||
import {
|
||||
@@ -9,7 +10,6 @@ import {
|
||||
validateAuthToken,
|
||||
} from '@/lib/core/security/deployment'
|
||||
import { decryptSecret } from '@/lib/core/security/encryption'
|
||||
import { authorizeWorkflowByWorkspacePermission } from '@/lib/workflows/utils'
|
||||
|
||||
const logger = createLogger('FormAuthUtils')
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { authorizeWorkflowByWorkspacePermission } from '@sim/workflow-authz'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { generateRequestId } from '@/lib/core/utils/request'
|
||||
@@ -7,7 +8,6 @@ import { validateHallucination } from '@/lib/guardrails/validate_hallucination'
|
||||
import { validateJson } from '@/lib/guardrails/validate_json'
|
||||
import { validatePII } from '@/lib/guardrails/validate_pii'
|
||||
import { validateRegex } from '@/lib/guardrails/validate_regex'
|
||||
import { authorizeWorkflowByWorkspacePermission } from '@/lib/workflows/utils'
|
||||
import {
|
||||
assertPermissionsAllowed,
|
||||
ProviderNotAllowedError,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@sim/audit'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
import { acceptInvitation } from '@/lib/invitations/core'
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@sim/audit'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
import { rejectInvitation } from '@/lib/invitations/core'
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@sim/audit'
|
||||
import { db } from '@sim/db'
|
||||
import { user } from '@sim/db/schema'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { eq } from 'drizzle-orm'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import { getOrganizationSubscription } from '@/lib/billing/core/billing'
|
||||
import { isOrganizationOwnerOrAdmin } from '@/lib/billing/core/organization'
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@sim/audit'
|
||||
import { db } from '@sim/db'
|
||||
import { invitation, invitationWorkspaceGrant } from '@sim/db/schema'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { and, eq } from 'drizzle-orm'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import { isOrganizationOwnerOrAdmin } from '@/lib/billing/core/organization'
|
||||
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
|
||||
@@ -5,9 +5,9 @@ import { hybridAuthMockFns, workflowsUtilsMock, workflowsUtilsMockFns } from '@s
|
||||
import type { NextRequest } from 'next/server'
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
|
||||
const { mockGetJobQueue, mockVerifyWorkflowAccess, mockGetJob } = vi.hoisted(() => ({
|
||||
const { mockGetJobQueue, mockAuthorizeWorkflow, mockGetJob } = vi.hoisted(() => ({
|
||||
mockGetJobQueue: vi.fn(),
|
||||
mockVerifyWorkflowAccess: vi.fn(),
|
||||
mockAuthorizeWorkflow: vi.fn(),
|
||||
mockGetJob: vi.fn(),
|
||||
}))
|
||||
|
||||
@@ -15,8 +15,8 @@ vi.mock('@/lib/core/async-jobs', () => ({
|
||||
getJobQueue: mockGetJobQueue,
|
||||
}))
|
||||
|
||||
vi.mock('@/socket/middleware/permissions', () => ({
|
||||
verifyWorkflowAccess: mockVerifyWorkflowAccess,
|
||||
vi.mock('@sim/workflow-authz', () => ({
|
||||
authorizeWorkflowByWorkspacePermission: mockAuthorizeWorkflow,
|
||||
}))
|
||||
|
||||
vi.mock('@/lib/workflows/utils', () => workflowsUtilsMock)
|
||||
@@ -42,7 +42,7 @@ describe('GET /api/jobs/[jobId]', () => {
|
||||
workspaceId: undefined,
|
||||
})
|
||||
|
||||
mockVerifyWorkflowAccess.mockResolvedValue({ hasAccess: true })
|
||||
mockAuthorizeWorkflow.mockResolvedValue({ allowed: true, status: 200 })
|
||||
workflowsUtilsMockFns.mockGetWorkflowById.mockResolvedValue({
|
||||
id: 'workflow-1',
|
||||
workspaceId: 'workspace-1',
|
||||
|
||||
@@ -33,12 +33,13 @@ export const GET = withRouteHandler(
|
||||
const metadataToCheck = job.metadata
|
||||
|
||||
if (metadataToCheck?.workflowId) {
|
||||
const { verifyWorkflowAccess } = await import('@/socket/middleware/permissions')
|
||||
const accessCheck = await verifyWorkflowAccess(
|
||||
authenticatedUserId,
|
||||
metadataToCheck.workflowId as string
|
||||
)
|
||||
if (!accessCheck.hasAccess) {
|
||||
const { authorizeWorkflowByWorkspacePermission } = await import('@sim/workflow-authz')
|
||||
const accessCheck = await authorizeWorkflowByWorkspacePermission({
|
||||
userId: authenticatedUserId,
|
||||
workflowId: metadataToCheck.workflowId as string,
|
||||
action: 'read',
|
||||
})
|
||||
if (!accessCheck.allowed) {
|
||||
logger.warn(`[${requestId}] Access denied to workflow ${metadataToCheck.workflowId}`)
|
||||
return createErrorResponse('Access denied', 403)
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ const mockCheckWriteAccess = knowledgeApiUtilsMockFns.mockCheckKnowledgeBaseWrit
|
||||
|
||||
vi.mock('@sim/db', () => ({ db: mockDbChain }))
|
||||
vi.mock('@/app/api/knowledge/utils', () => knowledgeApiUtilsMock)
|
||||
vi.mock('@/lib/audit/log', () => auditMock)
|
||||
vi.mock('@sim/audit', () => auditMock)
|
||||
|
||||
import { GET, PATCH } from '@/app/api/knowledge/[id]/connectors/[connectorId]/documents/route'
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@sim/audit'
|
||||
import { db } from '@sim/db'
|
||||
import { document, knowledgeConnector } from '@sim/db/schema'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { and, eq, inArray, isNull } from 'drizzle-orm'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
|
||||
import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { generateRequestId } from '@/lib/core/utils/request'
|
||||
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
|
||||
@@ -50,7 +50,7 @@ vi.mock('@/lib/knowledge/tags/service', () => ({
|
||||
vi.mock('@/lib/knowledge/documents/service', () => ({
|
||||
deleteDocumentStorageFiles: vi.fn().mockResolvedValue(undefined),
|
||||
}))
|
||||
vi.mock('@/lib/audit/log', () => auditMock)
|
||||
vi.mock('@sim/audit', () => auditMock)
|
||||
|
||||
import { DELETE, GET, PATCH } from '@/app/api/knowledge/[id]/connectors/[connectorId]/route'
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@sim/audit'
|
||||
import { db } from '@sim/db'
|
||||
import {
|
||||
document,
|
||||
@@ -11,7 +12,6 @@ import { and, desc, eq, inArray, isNull, sql } from 'drizzle-orm'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { decryptApiKey } from '@/lib/api-key/crypto'
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
|
||||
import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { hasLiveSyncAccess } from '@/lib/billing/core/subscription'
|
||||
import { generateRequestId } from '@/lib/core/utils/request'
|
||||
|
||||
@@ -34,7 +34,7 @@ vi.mock('@/app/api/knowledge/utils', () => knowledgeApiUtilsMock)
|
||||
vi.mock('@/lib/knowledge/connectors/sync-engine', () => ({
|
||||
dispatchSync: mockDispatchSync,
|
||||
}))
|
||||
vi.mock('@/lib/audit/log', () => auditMock)
|
||||
vi.mock('@sim/audit', () => auditMock)
|
||||
|
||||
import { POST } from '@/app/api/knowledge/[id]/connectors/[connectorId]/sync/route'
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@sim/audit'
|
||||
import { db } from '@sim/db'
|
||||
import { knowledgeConnector } from '@sim/db/schema'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { and, eq, isNull } from 'drizzle-orm'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
|
||||
import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { generateRequestId } from '@/lib/core/utils/request'
|
||||
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@sim/audit'
|
||||
import { db } from '@sim/db'
|
||||
import { knowledgeBase, knowledgeBaseTagDefinitions, knowledgeConnector } from '@sim/db/schema'
|
||||
import { createLogger } from '@sim/logger'
|
||||
@@ -6,7 +7,6 @@ import { and, desc, eq, isNull, sql } from 'drizzle-orm'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { encryptApiKey } from '@/lib/api-key/crypto'
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
|
||||
import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { hasLiveSyncAccess } from '@/lib/billing/core/subscription'
|
||||
import { generateRequestId } from '@/lib/core/utils/request'
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { authorizeWorkflowByWorkspacePermission } from '@sim/workflow-authz'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { generateRequestId } from '@/lib/core/utils/request'
|
||||
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
import { batchChunkOperation, createChunk, queryChunks } from '@/lib/knowledge/chunks/service'
|
||||
import { authorizeWorkflowByWorkspacePermission } from '@/lib/workflows/utils'
|
||||
import { checkDocumentAccess, checkDocumentWriteAccess } from '@/app/api/knowledge/utils'
|
||||
import { calculateCost } from '@/providers/utils'
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ vi.mock('@/lib/knowledge/documents/service', () => ({
|
||||
processDocumentAsync: vi.fn(),
|
||||
}))
|
||||
|
||||
vi.mock('@/lib/audit/log', () => auditMock)
|
||||
vi.mock('@sim/audit', () => auditMock)
|
||||
|
||||
import {
|
||||
deleteDocument,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@sim/audit'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
|
||||
import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { generateRequestId } from '@/lib/core/utils/request'
|
||||
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
|
||||
@@ -42,7 +42,7 @@ vi.mock('@/lib/knowledge/documents/service', () => ({
|
||||
retryDocumentProcessing: vi.fn(),
|
||||
}))
|
||||
|
||||
vi.mock('@/lib/audit/log', () => auditMock)
|
||||
vi.mock('@sim/audit', () => auditMock)
|
||||
|
||||
import {
|
||||
createDocumentRecords,
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@sim/audit'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { generateId } from '@sim/utils/id'
|
||||
import { authorizeWorkflowByWorkspacePermission } from '@sim/workflow-authz'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
@@ -18,7 +19,6 @@ import {
|
||||
} from '@/lib/knowledge/documents/service'
|
||||
import type { DocumentSortField, SortOrder } from '@/lib/knowledge/documents/types'
|
||||
import { captureServerEvent } from '@/lib/posthog/server'
|
||||
import { authorizeWorkflowByWorkspacePermission } from '@/lib/workflows/utils'
|
||||
import { checkKnowledgeBaseAccess, checkKnowledgeBaseWriteAccess } from '@/app/api/knowledge/utils'
|
||||
|
||||
const logger = createLogger('DocumentsAPI')
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@sim/audit'
|
||||
import { db } from '@sim/db'
|
||||
import { document } from '@sim/db/schema'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { generateId } from '@sim/utils/id'
|
||||
import { authorizeWorkflowByWorkspacePermission } from '@sim/workflow-authz'
|
||||
import { and, eq, isNull } from 'drizzle-orm'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
|
||||
import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
import {
|
||||
@@ -14,7 +15,6 @@ import {
|
||||
getProcessingConfig,
|
||||
processDocumentsWithQueue,
|
||||
} from '@/lib/knowledge/documents/service'
|
||||
import { authorizeWorkflowByWorkspacePermission } from '@/lib/workflows/utils'
|
||||
import { checkKnowledgeBaseWriteAccess } from '@/app/api/knowledge/utils'
|
||||
|
||||
const logger = createLogger('DocumentUpsertAPI')
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@sim/audit'
|
||||
import { db } from '@sim/db'
|
||||
import { knowledgeBase } from '@sim/db/schema'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { eq } from 'drizzle-orm'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
|
||||
import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { generateRequestId } from '@/lib/core/utils/request'
|
||||
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
|
||||
@@ -22,7 +22,7 @@ vi.mock('@sim/db', () => ({
|
||||
db: mockDbChain,
|
||||
}))
|
||||
|
||||
vi.mock('@/lib/audit/log', () => auditMock)
|
||||
vi.mock('@sim/audit', () => auditMock)
|
||||
|
||||
vi.mock('@/lib/knowledge/service', async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import('@/lib/knowledge/service')>()
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@sim/audit'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
|
||||
import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { PlatformEvents } from '@/lib/core/telemetry'
|
||||
import { generateRequestId } from '@/lib/core/utils/request'
|
||||
|
||||
@@ -31,7 +31,7 @@ vi.mock('@sim/db', () => ({
|
||||
db: mockDbChain,
|
||||
}))
|
||||
|
||||
vi.mock('@/lib/audit/log', () => auditMock)
|
||||
vi.mock('@sim/audit', () => auditMock)
|
||||
|
||||
vi.mock('@/lib/workspaces/permissions/utils', () => permissionsMock)
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@sim/audit'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import { PlatformEvents } from '@/lib/core/telemetry'
|
||||
import { generateRequestId } from '@/lib/core/utils/request'
|
||||
|
||||
@@ -11,8 +11,8 @@ import {
|
||||
hybridAuthMockFns,
|
||||
knowledgeApiUtilsMock,
|
||||
knowledgeApiUtilsMockFns,
|
||||
workflowAuthzMockFns,
|
||||
workflowsUtilsMock,
|
||||
workflowsUtilsMockFns,
|
||||
} from '@sim/testing'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
|
||||
@@ -169,7 +169,7 @@ describe('Knowledge Search API Route', () => {
|
||||
userId: 'user-123',
|
||||
authType: 'session',
|
||||
})
|
||||
workflowsUtilsMockFns.mockAuthorizeWorkflowByWorkspacePermission.mockClear().mockResolvedValue({
|
||||
workflowAuthzMockFns.mockAuthorizeWorkflowByWorkspacePermission.mockClear().mockResolvedValue({
|
||||
allowed: true,
|
||||
status: 200,
|
||||
})
|
||||
@@ -324,13 +324,11 @@ describe('Knowledge Search API Route', () => {
|
||||
|
||||
expect(response.status).toBe(200)
|
||||
expect(data.success).toBe(true)
|
||||
expect(workflowsUtilsMockFns.mockAuthorizeWorkflowByWorkspacePermission).toHaveBeenCalledWith(
|
||||
{
|
||||
workflowId: 'workflow-123',
|
||||
userId: 'user-123',
|
||||
action: 'read',
|
||||
}
|
||||
)
|
||||
expect(workflowAuthzMockFns.mockAuthorizeWorkflowByWorkspacePermission).toHaveBeenCalledWith({
|
||||
workflowId: 'workflow-123',
|
||||
userId: 'user-123',
|
||||
action: 'read',
|
||||
})
|
||||
})
|
||||
|
||||
it.concurrent('should return unauthorized for unauthenticated request', async () => {
|
||||
@@ -353,7 +351,7 @@ describe('Knowledge Search API Route', () => {
|
||||
workflowId: 'nonexistent-workflow',
|
||||
}
|
||||
|
||||
workflowsUtilsMockFns.mockAuthorizeWorkflowByWorkspacePermission.mockResolvedValueOnce({
|
||||
workflowAuthzMockFns.mockAuthorizeWorkflowByWorkspacePermission.mockResolvedValueOnce({
|
||||
allowed: false,
|
||||
status: 404,
|
||||
message: 'Workflow not found',
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { authorizeWorkflowByWorkspacePermission } from '@sim/workflow-authz'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid'
|
||||
@@ -10,7 +11,6 @@ import { getDocumentTagDefinitions } from '@/lib/knowledge/tags/service'
|
||||
import { buildUndefinedTagsError, validateTagValue } from '@/lib/knowledge/tags/utils'
|
||||
import type { StructuredFilter } from '@/lib/knowledge/types'
|
||||
import { estimateTokenCount } from '@/lib/tokenization/estimators'
|
||||
import { authorizeWorkflowByWorkspacePermission } from '@/lib/workflows/utils'
|
||||
import {
|
||||
generateSearchEmbedding,
|
||||
getDocumentNamesByIds,
|
||||
|
||||
@@ -15,6 +15,7 @@ import { userStats } from '@sim/db/schema'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { toError } from '@sim/utils/errors'
|
||||
import { generateId } from '@sim/utils/id'
|
||||
import { authorizeWorkflowByWorkspacePermission } from '@sim/workflow-authz'
|
||||
import { eq, sql } from 'drizzle-orm'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { validateOAuthAccessToken } from '@/lib/auth/oauth-token'
|
||||
@@ -31,10 +32,7 @@ import { env } from '@/lib/core/config/env'
|
||||
import { RateLimiter } from '@/lib/core/rate-limiter'
|
||||
import { getBaseUrl } from '@/lib/core/utils/urls'
|
||||
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
import {
|
||||
authorizeWorkflowByWorkspacePermission,
|
||||
resolveWorkflowIdForUser,
|
||||
} from '@/lib/workflows/utils'
|
||||
import { resolveWorkflowIdForUser } from '@/lib/workflows/utils'
|
||||
|
||||
const logger = createLogger('CopilotMcpAPI')
|
||||
const mcpRateLimiter = new RateLimiter()
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@sim/audit'
|
||||
import { db } from '@sim/db'
|
||||
import { mcpServers } from '@sim/db/schema'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { toError } from '@sim/utils/errors'
|
||||
import { and, eq, isNull } from 'drizzle-orm'
|
||||
import type { NextRequest } from 'next/server'
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
|
||||
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
import {
|
||||
McpDnsResolutionError,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user