v0.6.55: standardize monorepo conventions, api key hash, thinking text for mothership

This commit is contained in:
Waleed
2026-04-22 23:25:27 -07:00
committed by GitHub
372 changed files with 19403 additions and 1776 deletions

View File

@@ -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)` |

View File

@@ -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)` |

View File

@@ -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..."

View File

@@ -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'

View File

@@ -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`)

View 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

View 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
View 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,
})

View File

@@ -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')

View File

@@ -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
View 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
}

View File

@@ -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')

View 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)
}

View File

@@ -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')

View File

@@ -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')

View File

@@ -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')

View File

@@ -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')

View File

@@ -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

View File

@@ -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')

View File

@@ -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',

View File

@@ -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,

View File

@@ -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')

View File

@@ -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', () => {

View File

@@ -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')

View 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'

View File

@@ -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')

View File

@@ -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')

View File

@@ -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

View File

@@ -0,0 +1,11 @@
{
"extends": "@sim/tsconfig/base.json",
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["src/*"]
}
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}

View 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') },
],
},
})

View 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'

View File

@@ -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]/`:

View File

@@ -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'

View File

@@ -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'

View File

@@ -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'

View File

@@ -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'

View File

@@ -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) => {

View File

@@ -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'

View File

@@ -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,

View File

@@ -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'

View File

@@ -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,
}))

View File

@@ -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')

View File

@@ -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')

View File

@@ -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({

View File

@@ -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, {

View File

@@ -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')

View File

@@ -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,
})

View File

@@ -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')

View File

@@ -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,
})
})

View File

@@ -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')

View File

@@ -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'

View File

@@ -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'

View File

@@ -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'

View File

@@ -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'

View File

@@ -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'

View File

@@ -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'

View File

@@ -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'

View File

@@ -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'

View File

@@ -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'

View File

@@ -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'

View File

@@ -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 }

View File

@@ -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'

View File

@@ -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'

View File

@@ -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(),

View File

@@ -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 })),

View File

@@ -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'

View File

@@ -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'

View File

@@ -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'

View File

@@ -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')

View File

@@ -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,

View File

@@ -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'

View File

@@ -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'

View File

@@ -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'

View File

@@ -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'

View File

@@ -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',

View File

@@ -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)
}

View File

@@ -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'

View File

@@ -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'

View File

@@ -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'

View File

@@ -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'

View File

@@ -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'

View File

@@ -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'

View File

@@ -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'

View File

@@ -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'

View File

@@ -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,

View File

@@ -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'

View File

@@ -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,

View File

@@ -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')

View File

@@ -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')

View File

@@ -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'

View File

@@ -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')>()

View File

@@ -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'

View File

@@ -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)

View File

@@ -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'

View File

@@ -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',

View File

@@ -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,

View File

@@ -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()

View File

@@ -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