mirror of
https://github.com/simstudioai/sim.git
synced 2026-02-19 02:34:37 -05:00
fix(audit-log): address PR review — nullable workspaceId, enum usage, remove redundant queries
- Make audit_log.workspace_id nullable with ON DELETE SET NULL (logs survive workspace/user deletion)
- Make audit_log.actor_id nullable with ON DELETE SET NULL
- Replace all 53 routes' string literal action/resourceType with AuditAction.X and AuditResourceType.X enums
- Fix empty workspaceId ('') → null for OAuth, form, and org routes to avoid FK violations
- Remove redundant DB queries in chat manage route (use checkChatAccess return data)
- Fix organization routes to pass workspaceId: null instead of organizationId
This commit is contained in:
@@ -4,7 +4,7 @@ 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 { recordAudit } from '@/lib/audit/log'
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import { generateRequestId } from '@/lib/core/utils/request'
|
||||
import { syncAllWebhooksForCredentialSet } from '@/lib/webhooks/utils.server'
|
||||
@@ -120,10 +120,10 @@ export async function POST(request: NextRequest) {
|
||||
}
|
||||
|
||||
recordAudit({
|
||||
workspaceId: '',
|
||||
workspaceId: null,
|
||||
actorId: session.user.id,
|
||||
action: 'oauth.disconnected',
|
||||
resourceType: 'oauth',
|
||||
action: AuditAction.OAUTH_DISCONNECTED,
|
||||
resourceType: AuditResourceType.OAUTH,
|
||||
resourceId: providerId ?? provider,
|
||||
actorName: session.user.name ?? undefined,
|
||||
actorEmail: session.user.email ?? undefined,
|
||||
|
||||
@@ -50,14 +50,7 @@ describe('Chat Edit API Route', () => {
|
||||
}))
|
||||
|
||||
vi.doMock('@sim/db/schema', () => ({
|
||||
chat: {
|
||||
id: 'id',
|
||||
identifier: 'identifier',
|
||||
userId: 'userId',
|
||||
workflowId: 'workflowId',
|
||||
title: 'title',
|
||||
},
|
||||
workflow: { id: 'id', workspaceId: 'workspaceId' },
|
||||
chat: { id: 'id', identifier: 'identifier', userId: 'userId' },
|
||||
}))
|
||||
|
||||
// Mock logger - use loggerMock from @sim/testing
|
||||
@@ -225,8 +218,11 @@ describe('Chat Edit API Route', () => {
|
||||
workflowId: 'workflow-123',
|
||||
}
|
||||
|
||||
mockCheckChatAccess.mockResolvedValue({ hasAccess: true, chat: mockChat })
|
||||
mockLimit.mockResolvedValueOnce([{ workspaceId: 'workspace-123' }])
|
||||
mockCheckChatAccess.mockResolvedValue({
|
||||
hasAccess: true,
|
||||
chat: mockChat,
|
||||
workspaceId: 'workspace-123',
|
||||
})
|
||||
|
||||
const req = new NextRequest('http://localhost:3000/api/chat/manage/chat-123', {
|
||||
method: 'PATCH',
|
||||
@@ -320,8 +316,11 @@ describe('Chat Edit API Route', () => {
|
||||
workflowId: 'workflow-123',
|
||||
}
|
||||
|
||||
mockCheckChatAccess.mockResolvedValue({ hasAccess: true, chat: mockChat })
|
||||
mockLimit.mockResolvedValueOnce([{ workspaceId: 'workspace-123' }])
|
||||
mockCheckChatAccess.mockResolvedValue({
|
||||
hasAccess: true,
|
||||
chat: mockChat,
|
||||
workspaceId: 'workspace-123',
|
||||
})
|
||||
|
||||
const req = new NextRequest('http://localhost:3000/api/chat/manage/chat-123', {
|
||||
method: 'PATCH',
|
||||
@@ -380,9 +379,11 @@ describe('Chat Edit API Route', () => {
|
||||
}),
|
||||
}))
|
||||
|
||||
mockCheckChatAccess.mockResolvedValue({ hasAccess: true })
|
||||
mockLimit.mockResolvedValueOnce([{ workflowId: 'workflow-123', title: 'Test Chat' }])
|
||||
mockLimit.mockResolvedValueOnce([{ workspaceId: 'workspace-123' }])
|
||||
mockCheckChatAccess.mockResolvedValue({
|
||||
hasAccess: true,
|
||||
chat: { title: 'Test Chat', workflowId: 'workflow-123' },
|
||||
workspaceId: 'workspace-123',
|
||||
})
|
||||
|
||||
const req = new NextRequest('http://localhost:3000/api/chat/manage/chat-123', {
|
||||
method: 'DELETE',
|
||||
@@ -403,9 +404,11 @@ describe('Chat Edit API Route', () => {
|
||||
}),
|
||||
}))
|
||||
|
||||
mockCheckChatAccess.mockResolvedValue({ hasAccess: true })
|
||||
mockLimit.mockResolvedValueOnce([{ workflowId: 'workflow-123', title: 'Test Chat' }])
|
||||
mockLimit.mockResolvedValueOnce([{ workspaceId: 'workspace-123' }])
|
||||
mockCheckChatAccess.mockResolvedValue({
|
||||
hasAccess: true,
|
||||
chat: { title: 'Test Chat', workflowId: 'workflow-123' },
|
||||
workspaceId: 'workspace-123',
|
||||
})
|
||||
|
||||
const req = new NextRequest('http://localhost:3000/api/chat/manage/chat-123', {
|
||||
method: 'DELETE',
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { db } from '@sim/db'
|
||||
import { chat, workflow } from '@sim/db/schema'
|
||||
import { chat } from '@sim/db/schema'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { eq } from 'drizzle-orm'
|
||||
import type { NextRequest } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { recordAudit } from '@/lib/audit/log'
|
||||
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'
|
||||
@@ -104,7 +104,11 @@ export async function PATCH(request: NextRequest, { params }: { params: Promise<
|
||||
try {
|
||||
const validatedData = chatUpdateSchema.parse(body)
|
||||
|
||||
const { hasAccess, chat: existingChatRecord } = await checkChatAccess(chatId, session.user.id)
|
||||
const {
|
||||
hasAccess,
|
||||
chat: existingChatRecord,
|
||||
workspaceId: chatWorkspaceId,
|
||||
} = await checkChatAccess(chatId, session.user.id)
|
||||
|
||||
if (!hasAccess || !existingChatRecord) {
|
||||
return createErrorResponse('Chat not found or access denied', 404)
|
||||
@@ -218,19 +222,13 @@ export async function PATCH(request: NextRequest, { params }: { params: Promise<
|
||||
|
||||
logger.info(`Chat "${chatId}" updated successfully`)
|
||||
|
||||
const [workflowRecord] = await db
|
||||
.select({ workspaceId: workflow.workspaceId })
|
||||
.from(workflow)
|
||||
.where(eq(workflow.id, existingChat[0].workflowId))
|
||||
.limit(1)
|
||||
|
||||
recordAudit({
|
||||
workspaceId: workflowRecord?.workspaceId || '',
|
||||
workspaceId: chatWorkspaceId || null,
|
||||
actorId: session.user.id,
|
||||
actorName: session.user.name,
|
||||
actorEmail: session.user.email,
|
||||
action: 'chat.updated',
|
||||
resourceType: 'chat',
|
||||
action: AuditAction.CHAT_UPDATED,
|
||||
resourceType: AuditResourceType.CHAT,
|
||||
resourceId: chatId,
|
||||
resourceName: title || existingChat[0].title,
|
||||
description: `Updated chat deployment "${title || existingChat[0].title}"`,
|
||||
@@ -272,37 +270,27 @@ export async function DELETE(
|
||||
return createErrorResponse('Unauthorized', 401)
|
||||
}
|
||||
|
||||
const { hasAccess } = await checkChatAccess(chatId, session.user.id)
|
||||
const {
|
||||
hasAccess,
|
||||
chat: chatRecord,
|
||||
workspaceId: chatWorkspaceId,
|
||||
} = await checkChatAccess(chatId, session.user.id)
|
||||
|
||||
if (!hasAccess) {
|
||||
return createErrorResponse('Chat not found or access denied', 404)
|
||||
}
|
||||
|
||||
const [chatRecord] = await db
|
||||
.select({ workflowId: chat.workflowId, title: chat.title })
|
||||
.from(chat)
|
||||
.where(eq(chat.id, chatId))
|
||||
.limit(1)
|
||||
|
||||
const [workflowRecord] = chatRecord
|
||||
? await db
|
||||
.select({ workspaceId: workflow.workspaceId })
|
||||
.from(workflow)
|
||||
.where(eq(workflow.id, chatRecord.workflowId))
|
||||
.limit(1)
|
||||
: [undefined]
|
||||
|
||||
await db.delete(chat).where(eq(chat.id, chatId))
|
||||
|
||||
logger.info(`Chat "${chatId}" deleted successfully`)
|
||||
|
||||
recordAudit({
|
||||
workspaceId: workflowRecord?.workspaceId || '',
|
||||
workspaceId: chatWorkspaceId || null,
|
||||
actorId: session.user.id,
|
||||
actorName: session.user.name,
|
||||
actorEmail: session.user.email,
|
||||
action: 'chat.deleted',
|
||||
resourceType: 'chat',
|
||||
action: AuditAction.CHAT_DELETED,
|
||||
resourceType: AuditResourceType.CHAT,
|
||||
resourceId: chatId,
|
||||
resourceName: chatRecord?.title || chatId,
|
||||
description: `Deleted chat deployment "${chatRecord?.title || chatId}"`,
|
||||
|
||||
@@ -5,7 +5,7 @@ import { eq } from 'drizzle-orm'
|
||||
import type { NextRequest } from 'next/server'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import { z } from 'zod'
|
||||
import { recordAudit } from '@/lib/audit/log'
|
||||
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'
|
||||
@@ -175,7 +175,7 @@ export async function POST(request: NextRequest) {
|
||||
userId: session.user.id,
|
||||
identifier,
|
||||
title,
|
||||
description: description || '',
|
||||
description: description || null,
|
||||
customizations: mergedCustomizations,
|
||||
isActive: true,
|
||||
authType,
|
||||
@@ -226,12 +226,12 @@ export async function POST(request: NextRequest) {
|
||||
}
|
||||
|
||||
recordAudit({
|
||||
workspaceId: workflowRecord.workspaceId || '',
|
||||
workspaceId: workflowRecord.workspaceId || null,
|
||||
actorId: session.user.id,
|
||||
actorName: session.user.name,
|
||||
actorEmail: session.user.email,
|
||||
action: 'chat.deployed',
|
||||
resourceType: 'chat',
|
||||
action: AuditAction.CHAT_DEPLOYED,
|
||||
resourceType: AuditResourceType.CHAT,
|
||||
resourceId: id,
|
||||
resourceName: title,
|
||||
description: `Deployed chat "${title}"`,
|
||||
|
||||
@@ -52,7 +52,7 @@ export async function checkWorkflowAccessForChatCreation(
|
||||
export async function checkChatAccess(
|
||||
chatId: string,
|
||||
userId: string
|
||||
): Promise<{ hasAccess: boolean; chat?: any }> {
|
||||
): Promise<{ hasAccess: boolean; chat?: any; workspaceId?: string }> {
|
||||
const chatData = await db
|
||||
.select({
|
||||
chat: chat,
|
||||
@@ -78,7 +78,9 @@ export async function checkChatAccess(
|
||||
action: 'admin',
|
||||
})
|
||||
|
||||
return authorization.allowed ? { hasAccess: true, chat: chatRecord } : { hasAccess: false }
|
||||
return authorization.allowed
|
||||
? { hasAccess: true, chat: chatRecord, workspaceId: workflowWorkspaceId }
|
||||
: { hasAccess: false }
|
||||
}
|
||||
|
||||
export async function validateChatAuth(
|
||||
|
||||
@@ -5,7 +5,7 @@ import { and, eq } from 'drizzle-orm'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { getEmailSubject, renderPollingGroupInvitationEmail } from '@/components/emails'
|
||||
import { recordAudit } from '@/lib/audit/log'
|
||||
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'
|
||||
@@ -179,8 +179,8 @@ export async function POST(req: NextRequest, { params }: { params: Promise<{ id:
|
||||
recordAudit({
|
||||
workspaceId: result.set.organizationId,
|
||||
actorId: session.user.id,
|
||||
action: 'credential_set_invitation.created',
|
||||
resourceType: 'credential_set',
|
||||
action: AuditAction.CREDENTIAL_SET_INVITATION_CREATED,
|
||||
resourceType: AuditResourceType.CREDENTIAL_SET,
|
||||
resourceId: id,
|
||||
actorName: session.user.name ?? undefined,
|
||||
actorEmail: session.user.email ?? undefined,
|
||||
@@ -252,8 +252,8 @@ export async function DELETE(req: NextRequest, { params }: { params: Promise<{ i
|
||||
recordAudit({
|
||||
workspaceId: result.set.organizationId,
|
||||
actorId: session.user.id,
|
||||
action: 'credential_set_invitation.revoked',
|
||||
resourceType: 'credential_set',
|
||||
action: AuditAction.CREDENTIAL_SET_INVITATION_REVOKED,
|
||||
resourceType: AuditResourceType.CREDENTIAL_SET,
|
||||
resourceId: id,
|
||||
actorName: session.user.name ?? undefined,
|
||||
actorEmail: session.user.email ?? undefined,
|
||||
|
||||
@@ -3,7 +3,7 @@ import { account, credentialSet, credentialSetMember, member, user } from '@sim/
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { and, eq, inArray } from 'drizzle-orm'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { recordAudit } from '@/lib/audit/log'
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import { hasCredentialSetsAccess } from '@/lib/billing'
|
||||
import { syncAllWebhooksForCredentialSet } from '@/lib/webhooks/utils.server'
|
||||
@@ -181,8 +181,8 @@ export async function DELETE(req: NextRequest, { params }: { params: Promise<{ i
|
||||
recordAudit({
|
||||
workspaceId: result.set.organizationId,
|
||||
actorId: session.user.id,
|
||||
action: 'credential_set_member.removed',
|
||||
resourceType: 'credential_set',
|
||||
action: AuditAction.CREDENTIAL_SET_MEMBER_REMOVED,
|
||||
resourceType: AuditResourceType.CREDENTIAL_SET,
|
||||
resourceId: id,
|
||||
actorName: session.user.name ?? undefined,
|
||||
actorEmail: session.user.email ?? undefined,
|
||||
|
||||
@@ -4,7 +4,7 @@ import { createLogger } from '@sim/logger'
|
||||
import { and, eq } from 'drizzle-orm'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { recordAudit } from '@/lib/audit/log'
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import { hasCredentialSetsAccess } from '@/lib/billing'
|
||||
|
||||
@@ -135,8 +135,8 @@ export async function PUT(req: NextRequest, { params }: { params: Promise<{ id:
|
||||
recordAudit({
|
||||
workspaceId: result.set.organizationId,
|
||||
actorId: session.user.id,
|
||||
action: 'credential_set.updated',
|
||||
resourceType: 'credential_set',
|
||||
action: AuditAction.CREDENTIAL_SET_UPDATED,
|
||||
resourceType: AuditResourceType.CREDENTIAL_SET,
|
||||
resourceId: id,
|
||||
actorName: session.user.name ?? undefined,
|
||||
actorEmail: session.user.email ?? undefined,
|
||||
@@ -192,8 +192,8 @@ export async function DELETE(req: NextRequest, { params }: { params: Promise<{ i
|
||||
recordAudit({
|
||||
workspaceId: result.set.organizationId,
|
||||
actorId: session.user.id,
|
||||
action: 'credential_set.deleted',
|
||||
resourceType: 'credential_set',
|
||||
action: AuditAction.CREDENTIAL_SET_DELETED,
|
||||
resourceType: AuditResourceType.CREDENTIAL_SET,
|
||||
resourceId: id,
|
||||
actorName: session.user.name ?? undefined,
|
||||
actorEmail: session.user.email ?? undefined,
|
||||
|
||||
@@ -4,7 +4,7 @@ import { createLogger } from '@sim/logger'
|
||||
import { and, count, desc, eq } from 'drizzle-orm'
|
||||
import { NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { recordAudit } from '@/lib/audit/log'
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import { hasCredentialSetsAccess } from '@/lib/billing'
|
||||
|
||||
@@ -169,8 +169,8 @@ export async function POST(req: Request) {
|
||||
recordAudit({
|
||||
workspaceId: organizationId,
|
||||
actorId: session.user.id,
|
||||
action: 'credential_set.created',
|
||||
resourceType: 'credential_set',
|
||||
action: AuditAction.CREDENTIAL_SET_CREATED,
|
||||
resourceType: AuditResourceType.CREDENTIAL_SET,
|
||||
resourceId: newCredentialSet.id,
|
||||
actorName: session.user.name ?? undefined,
|
||||
actorEmail: session.user.email ?? undefined,
|
||||
|
||||
@@ -4,7 +4,7 @@ import { createLogger } from '@sim/logger'
|
||||
import { and, eq } from 'drizzle-orm'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { recordAudit } from '@/lib/audit/log'
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import { generateRequestId } from '@/lib/core/utils/request'
|
||||
import { duplicateWorkflow } from '@/lib/workflows/persistence/duplicate'
|
||||
@@ -119,8 +119,8 @@ export async function POST(req: NextRequest, { params }: { params: Promise<{ id:
|
||||
recordAudit({
|
||||
workspaceId: targetWorkspaceId,
|
||||
actorId: session.user.id,
|
||||
action: 'folder.duplicated',
|
||||
resourceType: 'folder',
|
||||
action: AuditAction.FOLDER_DUPLICATED,
|
||||
resourceType: AuditResourceType.FOLDER,
|
||||
resourceId: newFolderId,
|
||||
actorName: session.user.name ?? undefined,
|
||||
actorEmail: session.user.email ?? undefined,
|
||||
|
||||
@@ -4,7 +4,7 @@ import { createLogger } from '@sim/logger'
|
||||
import { and, eq } from 'drizzle-orm'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { recordAudit } from '@/lib/audit/log'
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import { getUserEntityPermissions } from '@/lib/workspaces/permissions/utils'
|
||||
|
||||
@@ -173,8 +173,8 @@ export async function DELETE(
|
||||
actorId: session.user.id,
|
||||
actorName: session.user.name,
|
||||
actorEmail: session.user.email,
|
||||
action: 'folder.deleted',
|
||||
resourceType: 'folder',
|
||||
action: AuditAction.FOLDER_DELETED,
|
||||
resourceType: AuditResourceType.FOLDER,
|
||||
resourceId: id,
|
||||
resourceName: existingFolder.name,
|
||||
description: `Deleted folder "${existingFolder.name}"`,
|
||||
|
||||
@@ -3,7 +3,7 @@ import { workflowFolder } from '@sim/db/schema'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { and, asc, desc, eq, isNull } from 'drizzle-orm'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { recordAudit } from '@/lib/audit/log'
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import { getUserEntityPermissions } from '@/lib/workspaces/permissions/utils'
|
||||
|
||||
@@ -125,8 +125,8 @@ export async function POST(request: NextRequest) {
|
||||
actorId: session.user.id,
|
||||
actorName: session.user.name,
|
||||
actorEmail: session.user.email,
|
||||
action: 'folder.created',
|
||||
resourceType: 'folder',
|
||||
action: AuditAction.FOLDER_CREATED,
|
||||
resourceType: AuditResourceType.FOLDER,
|
||||
resourceId: id,
|
||||
resourceName: name.trim(),
|
||||
description: `Created folder "${name.trim()}"`,
|
||||
|
||||
@@ -4,7 +4,7 @@ import { createLogger } from '@sim/logger'
|
||||
import { eq } from 'drizzle-orm'
|
||||
import type { NextRequest } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { recordAudit } from '@/lib/audit/log'
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import { encryptSecret } from '@/lib/core/security/encryption'
|
||||
import { checkFormAccess, DEFAULT_FORM_CUSTOMIZATIONS } from '@/app/api/form/utils'
|
||||
@@ -186,10 +186,10 @@ export async function PATCH(request: NextRequest, { params }: { params: Promise<
|
||||
logger.info(`Form ${id} updated successfully`)
|
||||
|
||||
recordAudit({
|
||||
workspaceId: '',
|
||||
workspaceId: null,
|
||||
actorId: session.user.id,
|
||||
action: 'form.updated',
|
||||
resourceType: 'form',
|
||||
action: AuditAction.FORM_UPDATED,
|
||||
resourceType: AuditResourceType.FORM,
|
||||
resourceId: id,
|
||||
actorName: session.user.name ?? undefined,
|
||||
actorEmail: session.user.email ?? undefined,
|
||||
@@ -238,10 +238,10 @@ export async function DELETE(
|
||||
logger.info(`Form ${id} deleted (soft delete)`)
|
||||
|
||||
recordAudit({
|
||||
workspaceId: '',
|
||||
workspaceId: null,
|
||||
actorId: session.user.id,
|
||||
action: 'form.deleted',
|
||||
resourceType: 'form',
|
||||
action: AuditAction.FORM_DELETED,
|
||||
resourceType: AuditResourceType.FORM,
|
||||
resourceId: id,
|
||||
actorName: session.user.name ?? undefined,
|
||||
actorEmail: session.user.email ?? undefined,
|
||||
|
||||
@@ -5,7 +5,7 @@ import { eq } from 'drizzle-orm'
|
||||
import type { NextRequest } from 'next/server'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import { z } from 'zod'
|
||||
import { recordAudit } from '@/lib/audit/log'
|
||||
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'
|
||||
@@ -179,7 +179,7 @@ export async function POST(request: NextRequest) {
|
||||
userId: session.user.id,
|
||||
identifier,
|
||||
title,
|
||||
description: description || '',
|
||||
description: description || null,
|
||||
customizations: mergedCustomizations,
|
||||
isActive: true,
|
||||
authType,
|
||||
@@ -199,8 +199,8 @@ export async function POST(request: NextRequest) {
|
||||
recordAudit({
|
||||
workspaceId: workflowRecord.workspaceId ?? '',
|
||||
actorId: session.user.id,
|
||||
action: 'form.created',
|
||||
resourceType: 'form',
|
||||
action: AuditAction.FORM_CREATED,
|
||||
resourceType: AuditResourceType.FORM,
|
||||
resourceId: id,
|
||||
actorName: session.user.name ?? undefined,
|
||||
actorEmail: session.user.email ?? undefined,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { recordAudit } from '@/lib/audit/log'
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
|
||||
import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { generateRequestId } from '@/lib/core/utils/request'
|
||||
import {
|
||||
@@ -201,8 +201,8 @@ export async function PUT(
|
||||
recordAudit({
|
||||
workspaceId: accessCheck.knowledgeBase?.workspaceId ?? '',
|
||||
actorId: userId,
|
||||
action: 'document.updated',
|
||||
resourceType: 'document',
|
||||
action: AuditAction.DOCUMENT_UPDATED,
|
||||
resourceType: AuditResourceType.DOCUMENT,
|
||||
resourceId: documentId,
|
||||
resourceName: validatedData.filename ?? accessCheck.document?.filename,
|
||||
description: `Updated document "${documentId}" in knowledge base "${knowledgeBaseId}"`,
|
||||
@@ -272,8 +272,8 @@ export async function DELETE(
|
||||
recordAudit({
|
||||
workspaceId: accessCheck.knowledgeBase?.workspaceId ?? '',
|
||||
actorId: userId,
|
||||
action: 'document.deleted',
|
||||
resourceType: 'document',
|
||||
action: AuditAction.DOCUMENT_DELETED,
|
||||
resourceType: AuditResourceType.DOCUMENT,
|
||||
resourceId: documentId,
|
||||
resourceName: accessCheck.document?.filename,
|
||||
description: `Deleted document "${documentId}" from knowledge base "${knowledgeBaseId}"`,
|
||||
|
||||
@@ -2,7 +2,7 @@ import { randomUUID } from 'crypto'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { recordAudit } from '@/lib/audit/log'
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid'
|
||||
import {
|
||||
@@ -248,8 +248,8 @@ export async function POST(req: NextRequest, { params }: { params: Promise<{ id:
|
||||
recordAudit({
|
||||
workspaceId: accessCheck.knowledgeBase?.workspaceId ?? '',
|
||||
actorId: userId,
|
||||
action: 'document.uploaded',
|
||||
resourceType: 'document',
|
||||
action: AuditAction.DOCUMENT_UPLOADED,
|
||||
resourceType: AuditResourceType.DOCUMENT,
|
||||
resourceId: knowledgeBaseId,
|
||||
resourceName: `${createdDocuments.length} document(s)`,
|
||||
description: `Uploaded ${createdDocuments.length} document(s) to knowledge base "${knowledgeBaseId}"`,
|
||||
@@ -307,8 +307,8 @@ export async function POST(req: NextRequest, { params }: { params: Promise<{ id:
|
||||
recordAudit({
|
||||
workspaceId: accessCheck.knowledgeBase?.workspaceId ?? '',
|
||||
actorId: userId,
|
||||
action: 'document.uploaded',
|
||||
resourceType: 'document',
|
||||
action: AuditAction.DOCUMENT_UPLOADED,
|
||||
resourceType: AuditResourceType.DOCUMENT,
|
||||
resourceId: knowledgeBaseId,
|
||||
resourceName: validatedData.filename,
|
||||
description: `Uploaded document "${validatedData.filename}" to knowledge base "${knowledgeBaseId}"`,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { recordAudit } from '@/lib/audit/log'
|
||||
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'
|
||||
@@ -139,8 +139,8 @@ export async function PUT(req: NextRequest, { params }: { params: Promise<{ id:
|
||||
recordAudit({
|
||||
workspaceId: accessCheck.knowledgeBase.workspaceId ?? '',
|
||||
actorId: userId,
|
||||
action: 'knowledge_base.updated',
|
||||
resourceType: 'knowledge_base',
|
||||
action: AuditAction.KNOWLEDGE_BASE_UPDATED,
|
||||
resourceType: AuditResourceType.KNOWLEDGE_BASE,
|
||||
resourceId: id,
|
||||
resourceName: validatedData.name ?? updatedKnowledgeBase.name,
|
||||
description: `Updated knowledge base "${validatedData.name ?? updatedKnowledgeBase.name}"`,
|
||||
@@ -212,8 +212,8 @@ export async function DELETE(
|
||||
recordAudit({
|
||||
workspaceId: accessCheck.knowledgeBase.workspaceId ?? '',
|
||||
actorId: userId,
|
||||
action: 'knowledge_base.deleted',
|
||||
resourceType: 'knowledge_base',
|
||||
action: AuditAction.KNOWLEDGE_BASE_DELETED,
|
||||
resourceType: AuditResourceType.KNOWLEDGE_BASE,
|
||||
resourceId: id,
|
||||
description: `Deleted knowledge base "${id}"`,
|
||||
request: _request,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { recordAudit } from '@/lib/audit/log'
|
||||
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'
|
||||
@@ -115,8 +115,8 @@ export async function POST(req: NextRequest) {
|
||||
actorId: session.user.id,
|
||||
actorName: session.user.name,
|
||||
actorEmail: session.user.email,
|
||||
action: 'knowledge_base.created',
|
||||
resourceType: 'knowledge_base',
|
||||
action: AuditAction.KNOWLEDGE_BASE_CREATED,
|
||||
resourceType: AuditResourceType.KNOWLEDGE_BASE,
|
||||
resourceId: newKnowledgeBase.id,
|
||||
resourceName: validatedData.name,
|
||||
description: `Created knowledge base "${validatedData.name}"`,
|
||||
|
||||
@@ -3,7 +3,7 @@ import { mcpServers } from '@sim/db/schema'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { and, eq, isNull } from 'drizzle-orm'
|
||||
import type { NextRequest } from 'next/server'
|
||||
import { recordAudit } from '@/lib/audit/log'
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
|
||||
import { McpDomainNotAllowedError, validateMcpDomain } from '@/lib/mcp/domain-check'
|
||||
import { getParsedBody, withMcpAuth } from '@/lib/mcp/middleware'
|
||||
import { mcpService } from '@/lib/mcp/service'
|
||||
@@ -90,8 +90,8 @@ export const PATCH = withMcpAuth<{ id: string }>('write')(
|
||||
recordAudit({
|
||||
workspaceId,
|
||||
actorId: userId,
|
||||
action: 'mcp_server.updated',
|
||||
resourceType: 'mcp_server',
|
||||
action: AuditAction.MCP_SERVER_UPDATED,
|
||||
resourceType: AuditResourceType.MCP_SERVER,
|
||||
resourceId: serverId,
|
||||
resourceName: updatedServer.name || serverId,
|
||||
description: `Updated MCP server "${updatedServer.name || serverId}"`,
|
||||
|
||||
@@ -3,7 +3,7 @@ import { mcpServers } from '@sim/db/schema'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { and, eq, isNull } from 'drizzle-orm'
|
||||
import type { NextRequest } from 'next/server'
|
||||
import { recordAudit } from '@/lib/audit/log'
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
|
||||
import { McpDomainNotAllowedError, validateMcpDomain } from '@/lib/mcp/domain-check'
|
||||
import { getParsedBody, withMcpAuth } from '@/lib/mcp/middleware'
|
||||
import { mcpService } from '@/lib/mcp/service'
|
||||
@@ -165,8 +165,8 @@ export const POST = withMcpAuth('write')(
|
||||
recordAudit({
|
||||
workspaceId,
|
||||
actorId: userId,
|
||||
action: 'mcp_server.added',
|
||||
resourceType: 'mcp_server',
|
||||
action: AuditAction.MCP_SERVER_ADDED,
|
||||
resourceType: AuditResourceType.MCP_SERVER,
|
||||
resourceId: serverId,
|
||||
resourceName: body.name,
|
||||
description: `Added MCP server "${body.name}"`,
|
||||
@@ -225,8 +225,8 @@ export const DELETE = withMcpAuth('admin')(
|
||||
recordAudit({
|
||||
workspaceId,
|
||||
actorId: userId,
|
||||
action: 'mcp_server.removed',
|
||||
resourceType: 'mcp_server',
|
||||
action: AuditAction.MCP_SERVER_REMOVED,
|
||||
resourceType: AuditResourceType.MCP_SERVER,
|
||||
resourceId: serverId!,
|
||||
resourceName: deletedServer.name,
|
||||
description: `Removed MCP server "${deletedServer.name}"`,
|
||||
|
||||
@@ -3,7 +3,7 @@ import { workflowMcpServer, workflowMcpTool } from '@sim/db/schema'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { and, eq } from 'drizzle-orm'
|
||||
import type { NextRequest } from 'next/server'
|
||||
import { recordAudit } from '@/lib/audit/log'
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
|
||||
import { getParsedBody, withMcpAuth } from '@/lib/mcp/middleware'
|
||||
import { mcpPubSub } from '@/lib/mcp/pubsub'
|
||||
import { createMcpErrorResponse, createMcpSuccessResponse } from '@/lib/mcp/utils'
|
||||
@@ -116,8 +116,8 @@ export const PATCH = withMcpAuth<RouteParams>('write')(
|
||||
recordAudit({
|
||||
workspaceId,
|
||||
actorId: userId,
|
||||
action: 'mcp_server.updated',
|
||||
resourceType: 'mcp_server',
|
||||
action: AuditAction.MCP_SERVER_UPDATED,
|
||||
resourceType: AuditResourceType.MCP_SERVER,
|
||||
resourceId: serverId,
|
||||
resourceName: updatedServer.name,
|
||||
description: `Updated workflow MCP server "${updatedServer.name}"`,
|
||||
@@ -164,8 +164,8 @@ export const DELETE = withMcpAuth<RouteParams>('admin')(
|
||||
recordAudit({
|
||||
workspaceId,
|
||||
actorId: userId,
|
||||
action: 'mcp_server.removed',
|
||||
resourceType: 'mcp_server',
|
||||
action: AuditAction.MCP_SERVER_REMOVED,
|
||||
resourceType: AuditResourceType.MCP_SERVER,
|
||||
resourceId: serverId,
|
||||
resourceName: deletedServer.name,
|
||||
description: `Unpublished workflow MCP server "${deletedServer.name}"`,
|
||||
|
||||
@@ -3,7 +3,7 @@ import { workflowMcpServer, workflowMcpTool } from '@sim/db/schema'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { and, eq } from 'drizzle-orm'
|
||||
import type { NextRequest } from 'next/server'
|
||||
import { recordAudit } from '@/lib/audit/log'
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
|
||||
import { getParsedBody, withMcpAuth } from '@/lib/mcp/middleware'
|
||||
import { mcpPubSub } from '@/lib/mcp/pubsub'
|
||||
import { createMcpErrorResponse, createMcpSuccessResponse } from '@/lib/mcp/utils'
|
||||
@@ -122,8 +122,8 @@ export const PATCH = withMcpAuth<RouteParams>('write')(
|
||||
recordAudit({
|
||||
workspaceId,
|
||||
actorId: userId,
|
||||
action: 'mcp_server.updated',
|
||||
resourceType: 'mcp_server',
|
||||
action: AuditAction.MCP_SERVER_UPDATED,
|
||||
resourceType: AuditResourceType.MCP_SERVER,
|
||||
resourceId: serverId,
|
||||
description: `Updated tool "${updatedTool.toolName}" in MCP server`,
|
||||
metadata: { toolId, toolName: updatedTool.toolName },
|
||||
@@ -180,8 +180,8 @@ export const DELETE = withMcpAuth<RouteParams>('write')(
|
||||
recordAudit({
|
||||
workspaceId,
|
||||
actorId: userId,
|
||||
action: 'mcp_server.updated',
|
||||
resourceType: 'mcp_server',
|
||||
action: AuditAction.MCP_SERVER_UPDATED,
|
||||
resourceType: AuditResourceType.MCP_SERVER,
|
||||
resourceId: serverId,
|
||||
description: `Removed tool "${deletedTool.toolName}" from MCP server`,
|
||||
metadata: { toolId, toolName: deletedTool.toolName },
|
||||
|
||||
@@ -3,7 +3,7 @@ import { workflow, workflowMcpServer, workflowMcpTool } from '@sim/db/schema'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { and, eq } from 'drizzle-orm'
|
||||
import type { NextRequest } from 'next/server'
|
||||
import { recordAudit } from '@/lib/audit/log'
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
|
||||
import { getParsedBody, withMcpAuth } from '@/lib/mcp/middleware'
|
||||
import { mcpPubSub } from '@/lib/mcp/pubsub'
|
||||
import { createMcpErrorResponse, createMcpSuccessResponse } from '@/lib/mcp/utils'
|
||||
@@ -201,8 +201,8 @@ export const POST = withMcpAuth<RouteParams>('write')(
|
||||
recordAudit({
|
||||
workspaceId,
|
||||
actorId: userId,
|
||||
action: 'mcp_server.updated',
|
||||
resourceType: 'mcp_server',
|
||||
action: AuditAction.MCP_SERVER_UPDATED,
|
||||
resourceType: AuditResourceType.MCP_SERVER,
|
||||
resourceId: serverId,
|
||||
description: `Added tool "${toolName}" to MCP server`,
|
||||
metadata: { toolId, toolName, workflowId: body.workflowId },
|
||||
|
||||
@@ -3,7 +3,7 @@ import { workflow, workflowMcpServer, workflowMcpTool } from '@sim/db/schema'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { eq, inArray, sql } from 'drizzle-orm'
|
||||
import type { NextRequest } from 'next/server'
|
||||
import { recordAudit } from '@/lib/audit/log'
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
|
||||
import { getParsedBody, withMcpAuth } from '@/lib/mcp/middleware'
|
||||
import { mcpPubSub } from '@/lib/mcp/pubsub'
|
||||
import { createMcpErrorResponse, createMcpSuccessResponse } from '@/lib/mcp/utils'
|
||||
@@ -192,8 +192,8 @@ export const POST = withMcpAuth('write')(
|
||||
recordAudit({
|
||||
workspaceId,
|
||||
actorId: userId,
|
||||
action: 'mcp_server.added',
|
||||
resourceType: 'mcp_server',
|
||||
action: AuditAction.MCP_SERVER_ADDED,
|
||||
resourceType: AuditResourceType.MCP_SERVER,
|
||||
resourceId: serverId,
|
||||
resourceName: body.name.trim(),
|
||||
description: `Published workflow MCP server "${body.name.trim()}" with ${addedTools.length} tool(s)`,
|
||||
|
||||
@@ -18,7 +18,7 @@ import { and, eq } from 'drizzle-orm'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { getEmailSubject, renderInvitationEmail } from '@/components/emails'
|
||||
import { recordAudit } from '@/lib/audit/log'
|
||||
import { AuditResourceType, recordAudit } from '@/lib/audit/log'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import { hasAccessControlAccess } from '@/lib/billing'
|
||||
import { syncUsageLimitsFromSubscription } from '@/lib/billing/core/usage'
|
||||
@@ -554,10 +554,10 @@ export async function PUT(
|
||||
})
|
||||
|
||||
recordAudit({
|
||||
workspaceId: organizationId,
|
||||
workspaceId: null,
|
||||
actorId: session.user.id,
|
||||
action: status === 'accepted' ? 'org_invitation.accepted' : 'org_invitation.updated',
|
||||
resourceType: 'organization',
|
||||
resourceType: AuditResourceType.ORGANIZATION,
|
||||
resourceId: organizationId,
|
||||
actorName: session.user.name ?? undefined,
|
||||
actorEmail: session.user.email ?? undefined,
|
||||
|
||||
@@ -17,7 +17,7 @@ import {
|
||||
renderBatchInvitationEmail,
|
||||
renderInvitationEmail,
|
||||
} from '@/components/emails'
|
||||
import { recordAudit } from '@/lib/audit/log'
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import {
|
||||
validateBulkInvitations,
|
||||
@@ -414,10 +414,10 @@ export async function POST(request: NextRequest, { params }: { params: Promise<{
|
||||
|
||||
for (const inv of invitationsToCreate) {
|
||||
recordAudit({
|
||||
workspaceId: organizationId,
|
||||
workspaceId: null,
|
||||
actorId: session.user.id,
|
||||
action: 'org_invitation.created',
|
||||
resourceType: 'organization',
|
||||
action: AuditAction.ORG_INVITATION_CREATED,
|
||||
resourceType: AuditResourceType.ORGANIZATION,
|
||||
resourceId: organizationId,
|
||||
actorName: session.user.name ?? undefined,
|
||||
actorEmail: session.user.email ?? undefined,
|
||||
@@ -550,10 +550,10 @@ export async function DELETE(
|
||||
})
|
||||
|
||||
recordAudit({
|
||||
workspaceId: organizationId,
|
||||
workspaceId: null,
|
||||
actorId: session.user.id,
|
||||
action: 'org_invitation.revoked',
|
||||
resourceType: 'organization',
|
||||
action: AuditAction.ORG_INVITATION_REVOKED,
|
||||
resourceType: AuditResourceType.ORGANIZATION,
|
||||
resourceId: organizationId,
|
||||
actorName: session.user.name ?? undefined,
|
||||
actorEmail: session.user.email ?? undefined,
|
||||
|
||||
@@ -4,7 +4,7 @@ import { createLogger } from '@sim/logger'
|
||||
import { and, eq } from 'drizzle-orm'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { recordAudit } from '@/lib/audit/log'
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import { getUserUsageData } from '@/lib/billing/core/usage'
|
||||
import { removeUserFromOrganization } from '@/lib/billing/organizations/membership'
|
||||
@@ -215,10 +215,10 @@ export async function PUT(
|
||||
})
|
||||
|
||||
recordAudit({
|
||||
workspaceId: organizationId,
|
||||
workspaceId: null,
|
||||
actorId: session.user.id,
|
||||
action: 'org_member.role_changed',
|
||||
resourceType: 'organization',
|
||||
action: AuditAction.ORG_MEMBER_ROLE_CHANGED,
|
||||
resourceType: AuditResourceType.ORGANIZATION,
|
||||
resourceId: organizationId,
|
||||
actorName: session.user.name ?? undefined,
|
||||
actorEmail: session.user.email ?? undefined,
|
||||
@@ -320,10 +320,10 @@ export async function DELETE(
|
||||
})
|
||||
|
||||
recordAudit({
|
||||
workspaceId: organizationId,
|
||||
workspaceId: null,
|
||||
actorId: session.user.id,
|
||||
action: 'org_member.removed',
|
||||
resourceType: 'organization',
|
||||
action: AuditAction.ORG_MEMBER_REMOVED,
|
||||
resourceType: AuditResourceType.ORGANIZATION,
|
||||
resourceId: organizationId,
|
||||
actorName: session.user.name ?? undefined,
|
||||
actorEmail: session.user.email ?? undefined,
|
||||
|
||||
@@ -5,7 +5,7 @@ import { createLogger } from '@sim/logger'
|
||||
import { and, eq } from 'drizzle-orm'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { getEmailSubject, renderInvitationEmail } from '@/components/emails'
|
||||
import { recordAudit } from '@/lib/audit/log'
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import { getUserUsageData } from '@/lib/billing/core/usage'
|
||||
import { validateSeatAvailability } from '@/lib/billing/validation/seat-management'
|
||||
@@ -287,10 +287,10 @@ export async function POST(request: NextRequest, { params }: { params: Promise<{
|
||||
}
|
||||
|
||||
recordAudit({
|
||||
workspaceId: organizationId,
|
||||
workspaceId: null,
|
||||
actorId: session.user.id,
|
||||
action: 'org_member.added',
|
||||
resourceType: 'organization',
|
||||
action: AuditAction.ORG_MEMBER_ADDED,
|
||||
resourceType: AuditResourceType.ORGANIZATION,
|
||||
resourceId: organizationId,
|
||||
actorName: session.user.name ?? undefined,
|
||||
actorEmail: session.user.email ?? undefined,
|
||||
|
||||
@@ -4,7 +4,7 @@ import { createLogger } from '@sim/logger'
|
||||
import { and, eq, ne } from 'drizzle-orm'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { recordAudit } from '@/lib/audit/log'
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import {
|
||||
getOrganizationSeatAnalytics,
|
||||
@@ -194,10 +194,10 @@ export async function PUT(request: NextRequest, { params }: { params: Promise<{
|
||||
})
|
||||
|
||||
recordAudit({
|
||||
workspaceId: organizationId,
|
||||
workspaceId: null,
|
||||
actorId: session.user.id,
|
||||
action: 'organization.updated',
|
||||
resourceType: 'organization',
|
||||
action: AuditAction.ORGANIZATION_UPDATED,
|
||||
resourceType: AuditResourceType.ORGANIZATION,
|
||||
resourceId: organizationId,
|
||||
actorName: session.user.name ?? undefined,
|
||||
actorEmail: session.user.email ?? undefined,
|
||||
|
||||
@@ -3,7 +3,7 @@ import { member, organization } from '@sim/db/schema'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { and, eq, or } from 'drizzle-orm'
|
||||
import { NextResponse } from 'next/server'
|
||||
import { recordAudit } from '@/lib/audit/log'
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import { createOrganizationForTeamPlan } from '@/lib/billing/organization'
|
||||
|
||||
@@ -117,10 +117,10 @@ export async function POST(request: Request) {
|
||||
})
|
||||
|
||||
recordAudit({
|
||||
workspaceId: organizationId,
|
||||
workspaceId: null,
|
||||
actorId: user.id,
|
||||
action: 'organization.created',
|
||||
resourceType: 'organization',
|
||||
action: AuditAction.ORGANIZATION_CREATED,
|
||||
resourceType: AuditResourceType.ORGANIZATION,
|
||||
resourceId: organizationId,
|
||||
actorName: user.name ?? undefined,
|
||||
actorEmail: user.email ?? undefined,
|
||||
|
||||
@@ -4,7 +4,7 @@ import { createLogger } from '@sim/logger'
|
||||
import { and, eq } from 'drizzle-orm'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { recordAudit } from '@/lib/audit/log'
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import { hasAccessControlAccess } from '@/lib/billing'
|
||||
|
||||
@@ -153,10 +153,10 @@ export async function POST(req: NextRequest, { params }: { params: Promise<{ id:
|
||||
})
|
||||
|
||||
recordAudit({
|
||||
workspaceId: result.group.organizationId,
|
||||
workspaceId: null,
|
||||
actorId: session.user.id,
|
||||
action: 'permission_group_member.added',
|
||||
resourceType: 'permission_group',
|
||||
action: AuditAction.PERMISSION_GROUP_MEMBER_ADDED,
|
||||
resourceType: AuditResourceType.PERMISSION_GROUP,
|
||||
resourceId: id,
|
||||
actorName: session.user.name ?? undefined,
|
||||
actorEmail: session.user.email ?? undefined,
|
||||
@@ -236,10 +236,10 @@ export async function DELETE(req: NextRequest, { params }: { params: Promise<{ i
|
||||
})
|
||||
|
||||
recordAudit({
|
||||
workspaceId: result.group.organizationId,
|
||||
workspaceId: null,
|
||||
actorId: session.user.id,
|
||||
action: 'permission_group_member.removed',
|
||||
resourceType: 'permission_group',
|
||||
action: AuditAction.PERMISSION_GROUP_MEMBER_REMOVED,
|
||||
resourceType: AuditResourceType.PERMISSION_GROUP,
|
||||
resourceId: id,
|
||||
actorName: session.user.name ?? undefined,
|
||||
actorEmail: session.user.email ?? undefined,
|
||||
|
||||
@@ -4,7 +4,7 @@ import { createLogger } from '@sim/logger'
|
||||
import { and, eq } from 'drizzle-orm'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { recordAudit } from '@/lib/audit/log'
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import { hasAccessControlAccess } from '@/lib/billing'
|
||||
import {
|
||||
@@ -183,10 +183,10 @@ export async function PUT(req: NextRequest, { params }: { params: Promise<{ id:
|
||||
.limit(1)
|
||||
|
||||
recordAudit({
|
||||
workspaceId: result.group.organizationId,
|
||||
workspaceId: null,
|
||||
actorId: session.user.id,
|
||||
action: 'permission_group.updated',
|
||||
resourceType: 'permission_group',
|
||||
action: AuditAction.PERMISSION_GROUP_UPDATED,
|
||||
resourceType: AuditResourceType.PERMISSION_GROUP,
|
||||
resourceId: id,
|
||||
actorName: session.user.name ?? undefined,
|
||||
actorEmail: session.user.email ?? undefined,
|
||||
@@ -244,10 +244,10 @@ export async function DELETE(req: NextRequest, { params }: { params: Promise<{ i
|
||||
logger.info('Deleted permission group', { permissionGroupId: id, userId: session.user.id })
|
||||
|
||||
recordAudit({
|
||||
workspaceId: result.group.organizationId,
|
||||
workspaceId: null,
|
||||
actorId: session.user.id,
|
||||
action: 'permission_group.deleted',
|
||||
resourceType: 'permission_group',
|
||||
action: AuditAction.PERMISSION_GROUP_DELETED,
|
||||
resourceType: AuditResourceType.PERMISSION_GROUP,
|
||||
resourceId: id,
|
||||
actorName: session.user.name ?? undefined,
|
||||
actorEmail: session.user.email ?? undefined,
|
||||
|
||||
@@ -4,7 +4,7 @@ import { createLogger } from '@sim/logger'
|
||||
import { and, count, desc, eq } from 'drizzle-orm'
|
||||
import { NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { recordAudit } from '@/lib/audit/log'
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import { hasAccessControlAccess } from '@/lib/billing'
|
||||
import {
|
||||
@@ -200,10 +200,10 @@ export async function POST(req: Request) {
|
||||
})
|
||||
|
||||
recordAudit({
|
||||
workspaceId: organizationId,
|
||||
workspaceId: null,
|
||||
actorId: session.user.id,
|
||||
action: 'permission_group.created',
|
||||
resourceType: 'permission_group',
|
||||
action: AuditAction.PERMISSION_GROUP_CREATED,
|
||||
resourceType: AuditResourceType.PERMISSION_GROUP,
|
||||
resourceId: newGroup.id,
|
||||
actorName: session.user.name ?? undefined,
|
||||
actorEmail: session.user.email ?? undefined,
|
||||
|
||||
@@ -4,7 +4,7 @@ import { createLogger } from '@sim/logger'
|
||||
import { eq } from 'drizzle-orm'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { recordAudit } from '@/lib/audit/log'
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import { generateRequestId } from '@/lib/core/utils/request'
|
||||
import { validateCronExpression } from '@/lib/workflows/schedules/utils'
|
||||
@@ -110,8 +110,8 @@ export async function PUT(request: NextRequest, { params }: { params: Promise<{
|
||||
recordAudit({
|
||||
workspaceId: authorization.workflow.workspaceId ?? '',
|
||||
actorId: session.user.id,
|
||||
action: 'schedule.updated',
|
||||
resourceType: 'schedule',
|
||||
action: AuditAction.SCHEDULE_UPDATED,
|
||||
resourceType: AuditResourceType.SCHEDULE,
|
||||
resourceId: scheduleId,
|
||||
actorName: session.user.name ?? undefined,
|
||||
actorEmail: session.user.email ?? undefined,
|
||||
|
||||
@@ -3,7 +3,7 @@ import { apiKey } from '@sim/db/schema'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { and, eq } from 'drizzle-orm'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { recordAudit } from '@/lib/audit/log'
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import { generateRequestId } from '@/lib/core/utils/request'
|
||||
|
||||
@@ -42,10 +42,10 @@ export async function DELETE(
|
||||
}
|
||||
|
||||
recordAudit({
|
||||
workspaceId: '',
|
||||
workspaceId: null,
|
||||
actorId: userId,
|
||||
action: 'personal_api_key.revoked',
|
||||
resourceType: 'api_key',
|
||||
action: AuditAction.PERSONAL_API_KEY_REVOKED,
|
||||
resourceType: AuditResourceType.API_KEY,
|
||||
resourceId: keyId,
|
||||
actorName: session.user.name ?? undefined,
|
||||
actorEmail: session.user.email ?? undefined,
|
||||
|
||||
@@ -5,7 +5,7 @@ import { and, eq } from 'drizzle-orm'
|
||||
import { nanoid } from 'nanoid'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { createApiKey, getApiKeyDisplayFormat } from '@/lib/api-key/auth'
|
||||
import { recordAudit } from '@/lib/audit/log'
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
|
||||
import { getSession } from '@/lib/auth'
|
||||
|
||||
const logger = createLogger('ApiKeysAPI')
|
||||
@@ -112,10 +112,10 @@ export async function POST(request: NextRequest) {
|
||||
})
|
||||
|
||||
recordAudit({
|
||||
workspaceId: '',
|
||||
workspaceId: null,
|
||||
actorId: userId,
|
||||
action: 'personal_api_key.created',
|
||||
resourceType: 'api_key',
|
||||
action: AuditAction.PERSONAL_API_KEY_CREATED,
|
||||
resourceType: AuditResourceType.API_KEY,
|
||||
resourceId: newKey.id,
|
||||
actorName: session.user.name ?? undefined,
|
||||
actorEmail: session.user.email ?? undefined,
|
||||
|
||||
@@ -3,7 +3,7 @@ import { webhook, workflow } from '@sim/db/schema'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { and, eq } from 'drizzle-orm'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { recordAudit } from '@/lib/audit/log'
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
|
||||
import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { validateInteger } from '@/lib/core/security/input-validation'
|
||||
import { PlatformEvents } from '@/lib/core/telemetry'
|
||||
@@ -263,10 +263,10 @@ export async function DELETE(
|
||||
}
|
||||
|
||||
recordAudit({
|
||||
workspaceId: webhookData.workflow.workspaceId || '',
|
||||
workspaceId: webhookData.workflow.workspaceId || null,
|
||||
actorId: userId,
|
||||
action: 'webhook.deleted',
|
||||
resourceType: 'webhook',
|
||||
action: AuditAction.WEBHOOK_DELETED,
|
||||
resourceType: AuditResourceType.WEBHOOK,
|
||||
resourceId: id,
|
||||
resourceName: foundWebhook.provider || 'generic',
|
||||
description: 'Deleted webhook',
|
||||
|
||||
@@ -4,7 +4,7 @@ import { createLogger } from '@sim/logger'
|
||||
import { and, desc, eq, inArray, isNull, or } from 'drizzle-orm'
|
||||
import { nanoid } from 'nanoid'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { recordAudit } from '@/lib/audit/log'
|
||||
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'
|
||||
@@ -681,10 +681,10 @@ export async function POST(request: NextRequest) {
|
||||
}
|
||||
|
||||
recordAudit({
|
||||
workspaceId: workflowRecord.workspaceId || '',
|
||||
workspaceId: workflowRecord.workspaceId || null,
|
||||
actorId: userId,
|
||||
action: 'webhook.created',
|
||||
resourceType: 'webhook',
|
||||
action: AuditAction.WEBHOOK_CREATED,
|
||||
resourceType: AuditResourceType.WEBHOOK,
|
||||
resourceId: savedWebhook.id,
|
||||
resourceName: provider || 'generic',
|
||||
description: `Created ${provider || 'generic'} webhook`,
|
||||
|
||||
@@ -2,7 +2,7 @@ import { db, workflow, workflowDeploymentVersion } from '@sim/db'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { and, desc, eq } from 'drizzle-orm'
|
||||
import type { NextRequest } from 'next/server'
|
||||
import { recordAudit } from '@/lib/audit/log'
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
|
||||
import { generateRequestId } from '@/lib/core/utils/request'
|
||||
import { removeMcpToolsForWorkflow, syncMcpToolsForWorkflow } from '@/lib/mcp/workflow-mcp-sync'
|
||||
import {
|
||||
@@ -260,12 +260,12 @@ export async function POST(request: NextRequest, { params }: { params: Promise<{
|
||||
await syncMcpToolsForWorkflow({ workflowId: id, requestId, context: 'deploy' })
|
||||
|
||||
recordAudit({
|
||||
workspaceId: workflowData?.workspaceId || '',
|
||||
workspaceId: workflowData?.workspaceId || null,
|
||||
actorId: actorUserId,
|
||||
actorName: session?.user?.name,
|
||||
actorEmail: session?.user?.email,
|
||||
action: 'workflow.deployed',
|
||||
resourceType: 'workflow',
|
||||
action: AuditAction.WORKFLOW_DEPLOYED,
|
||||
resourceType: AuditResourceType.WORKFLOW,
|
||||
resourceId: id,
|
||||
resourceName: workflowData?.name,
|
||||
description: `Deployed workflow "${workflowData?.name || id}"`,
|
||||
@@ -340,12 +340,12 @@ export async function DELETE(
|
||||
}
|
||||
|
||||
recordAudit({
|
||||
workspaceId: workflowData?.workspaceId || '',
|
||||
workspaceId: workflowData?.workspaceId || null,
|
||||
actorId: session?.user?.id || '',
|
||||
actorName: session?.user?.name,
|
||||
actorEmail: session?.user?.email,
|
||||
action: 'workflow.undeployed',
|
||||
resourceType: 'workflow',
|
||||
action: AuditAction.WORKFLOW_UNDEPLOYED,
|
||||
resourceType: AuditResourceType.WORKFLOW,
|
||||
resourceId: id,
|
||||
resourceName: workflowData?.name,
|
||||
description: `Undeployed workflow "${workflowData?.name || id}"`,
|
||||
|
||||
@@ -2,7 +2,7 @@ import { db, workflow, workflowDeploymentVersion } from '@sim/db'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { and, eq } from 'drizzle-orm'
|
||||
import type { NextRequest } from 'next/server'
|
||||
import { recordAudit } from '@/lib/audit/log'
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
|
||||
import { env } from '@/lib/core/config/env'
|
||||
import { generateRequestId } from '@/lib/core/utils/request'
|
||||
import { syncMcpToolsForWorkflow } from '@/lib/mcp/workflow-mcp-sync'
|
||||
@@ -115,8 +115,8 @@ export async function POST(
|
||||
recordAudit({
|
||||
workspaceId: workflowRecord?.workspaceId ?? '',
|
||||
actorId: session!.user.id,
|
||||
action: 'workflow.deployment_reverted',
|
||||
resourceType: 'workflow',
|
||||
action: AuditAction.WORKFLOW_DEPLOYMENT_REVERTED,
|
||||
resourceType: AuditResourceType.WORKFLOW,
|
||||
resourceId: id,
|
||||
actorName: session!.user.name ?? undefined,
|
||||
actorEmail: session!.user.email ?? undefined,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { recordAudit } from '@/lib/audit/log'
|
||||
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'
|
||||
@@ -63,10 +63,10 @@ export async function POST(req: NextRequest, { params }: { params: Promise<{ id:
|
||||
)
|
||||
|
||||
recordAudit({
|
||||
workspaceId: workspaceId || '',
|
||||
workspaceId: workspaceId || null,
|
||||
actorId: userId,
|
||||
action: 'workflow.duplicated',
|
||||
resourceType: 'workflow',
|
||||
action: AuditAction.WORKFLOW_DUPLICATED,
|
||||
resourceType: AuditResourceType.WORKFLOW,
|
||||
resourceId: result.id,
|
||||
resourceName: result.name,
|
||||
description: `Duplicated workflow from ${sourceWorkflowId}`,
|
||||
|
||||
@@ -4,7 +4,7 @@ import { createLogger } from '@sim/logger'
|
||||
import { eq } from 'drizzle-orm'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { recordAudit } from '@/lib/audit/log'
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
|
||||
import { checkHybridAuth, checkSessionOrInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { env } from '@/lib/core/config/env'
|
||||
import { PlatformEvents } from '@/lib/core/telemetry'
|
||||
@@ -338,10 +338,10 @@ export async function DELETE(
|
||||
}
|
||||
|
||||
recordAudit({
|
||||
workspaceId: workflowData.workspaceId || '',
|
||||
workspaceId: workflowData.workspaceId || null,
|
||||
actorId: userId,
|
||||
action: 'workflow.deleted',
|
||||
resourceType: 'workflow',
|
||||
action: AuditAction.WORKFLOW_DELETED,
|
||||
resourceType: AuditResourceType.WORKFLOW,
|
||||
resourceId: workflowId,
|
||||
resourceName: workflowData.name,
|
||||
description: `Deleted workflow "${workflowData.name}"`,
|
||||
|
||||
@@ -4,7 +4,7 @@ import { createLogger } from '@sim/logger'
|
||||
import { eq } from 'drizzle-orm'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { recordAudit } from '@/lib/audit/log'
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
|
||||
import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { generateRequestId } from '@/lib/core/utils/request'
|
||||
import { authorizeWorkflowByWorkspacePermission } from '@/lib/workflows/utils'
|
||||
@@ -83,8 +83,8 @@ export async function POST(req: NextRequest, { params }: { params: Promise<{ id:
|
||||
recordAudit({
|
||||
workspaceId: workflowData.workspaceId ?? '',
|
||||
actorId: userId,
|
||||
action: 'workflow.variables_updated',
|
||||
resourceType: 'workflow',
|
||||
action: AuditAction.WORKFLOW_VARIABLES_UPDATED,
|
||||
resourceType: AuditResourceType.WORKFLOW,
|
||||
resourceId: workflowId,
|
||||
resourceName: workflowData.name ?? undefined,
|
||||
description: `Updated workflow variables`,
|
||||
|
||||
@@ -4,7 +4,7 @@ import { createLogger } from '@sim/logger'
|
||||
import { and, asc, eq, inArray, isNull, min } from 'drizzle-orm'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { recordAudit } from '@/lib/audit/log'
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
|
||||
import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { generateRequestId } from '@/lib/core/utils/request'
|
||||
import { getUserEntityPermissions, workspaceExists } from '@/lib/workspaces/permissions/utils'
|
||||
@@ -192,8 +192,8 @@ export async function POST(req: NextRequest) {
|
||||
recordAudit({
|
||||
workspaceId,
|
||||
actorId: userId,
|
||||
action: 'workflow.created',
|
||||
resourceType: 'workflow',
|
||||
action: AuditAction.WORKFLOW_CREATED,
|
||||
resourceType: AuditResourceType.WORKFLOW,
|
||||
resourceId: workflowId,
|
||||
resourceName: name,
|
||||
description: `Created workflow "${name}"`,
|
||||
|
||||
@@ -4,7 +4,7 @@ import { createLogger } from '@sim/logger'
|
||||
import { and, eq, not } from 'drizzle-orm'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { recordAudit } from '@/lib/audit/log'
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import { generateRequestId } from '@/lib/core/utils/request'
|
||||
import { getUserEntityPermissions } from '@/lib/workspaces/permissions/utils'
|
||||
@@ -90,8 +90,8 @@ export async function PUT(
|
||||
recordAudit({
|
||||
workspaceId,
|
||||
actorId: userId,
|
||||
action: 'api_key.updated',
|
||||
resourceType: 'api_key',
|
||||
action: AuditAction.API_KEY_UPDATED,
|
||||
resourceType: AuditResourceType.API_KEY,
|
||||
resourceId: keyId,
|
||||
actorName: session.user.name ?? undefined,
|
||||
actorEmail: session.user.email ?? undefined,
|
||||
@@ -146,8 +146,8 @@ export async function DELETE(
|
||||
recordAudit({
|
||||
workspaceId,
|
||||
actorId: userId,
|
||||
action: 'api_key.revoked',
|
||||
resourceType: 'api_key',
|
||||
action: AuditAction.API_KEY_REVOKED,
|
||||
resourceType: AuditResourceType.API_KEY,
|
||||
resourceId: keyId,
|
||||
actorName: session.user.name ?? undefined,
|
||||
actorEmail: session.user.email ?? undefined,
|
||||
|
||||
@@ -6,7 +6,7 @@ import { nanoid } from 'nanoid'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { createApiKey, getApiKeyDisplayFormat } from '@/lib/api-key/auth'
|
||||
import { recordAudit } from '@/lib/audit/log'
|
||||
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'
|
||||
@@ -165,8 +165,8 @@ export async function POST(request: NextRequest, { params }: { params: Promise<{
|
||||
actorId: userId,
|
||||
actorName: session?.user?.name,
|
||||
actorEmail: session?.user?.email,
|
||||
action: 'api_key.created',
|
||||
resourceType: 'api_key',
|
||||
action: AuditAction.API_KEY_CREATED,
|
||||
resourceType: AuditResourceType.API_KEY,
|
||||
resourceId: newKey.id,
|
||||
resourceName: name,
|
||||
description: `Created API key "${name}"`,
|
||||
@@ -243,8 +243,8 @@ export async function DELETE(
|
||||
actorId: userId,
|
||||
actorName: session?.user?.name,
|
||||
actorEmail: session?.user?.email,
|
||||
action: 'api_key.revoked',
|
||||
resourceType: 'api_key',
|
||||
action: AuditAction.API_KEY_REVOKED,
|
||||
resourceType: AuditResourceType.API_KEY,
|
||||
description: `Revoked ${deletedCount} API key(s)`,
|
||||
metadata: { keyIds: keys, deletedCount },
|
||||
request,
|
||||
|
||||
@@ -5,7 +5,7 @@ import { and, eq } from 'drizzle-orm'
|
||||
import { nanoid } from 'nanoid'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { recordAudit } from '@/lib/audit/log'
|
||||
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'
|
||||
@@ -191,8 +191,8 @@ export async function POST(request: NextRequest, { params }: { params: Promise<{
|
||||
actorId: userId,
|
||||
actorName: session?.user?.name,
|
||||
actorEmail: session?.user?.email,
|
||||
action: 'byok_key.created',
|
||||
resourceType: 'byok_key',
|
||||
action: AuditAction.BYOK_KEY_CREATED,
|
||||
resourceType: AuditResourceType.BYOK_KEY,
|
||||
resourceId: newKey.id,
|
||||
resourceName: providerId,
|
||||
description: `Added BYOK key for ${providerId}`,
|
||||
@@ -262,8 +262,8 @@ export async function DELETE(
|
||||
actorId: userId,
|
||||
actorName: session?.user?.name,
|
||||
actorEmail: session?.user?.email,
|
||||
action: 'byok_key.deleted',
|
||||
resourceType: 'byok_key',
|
||||
action: AuditAction.BYOK_KEY_DELETED,
|
||||
resourceType: AuditResourceType.BYOK_KEY,
|
||||
resourceName: providerId,
|
||||
description: `Removed BYOK key for ${providerId}`,
|
||||
metadata: { providerId },
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { recordAudit } from '@/lib/audit/log'
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import { generateRequestId } from '@/lib/core/utils/request'
|
||||
import { duplicateWorkspace } from '@/lib/workspaces/duplicate'
|
||||
@@ -51,8 +51,8 @@ export async function POST(req: NextRequest, { params }: { params: Promise<{ id:
|
||||
actorId: session.user.id,
|
||||
actorName: session.user.name,
|
||||
actorEmail: session.user.email,
|
||||
action: 'workspace.duplicated',
|
||||
resourceType: 'workspace',
|
||||
action: AuditAction.WORKSPACE_DUPLICATED,
|
||||
resourceType: AuditResourceType.WORKSPACE,
|
||||
resourceId: result.id,
|
||||
resourceName: name,
|
||||
description: `Duplicated workspace to "${name}"`,
|
||||
|
||||
@@ -4,7 +4,7 @@ import { createLogger } from '@sim/logger'
|
||||
import { eq } from 'drizzle-orm'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { recordAudit } from '@/lib/audit/log'
|
||||
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'
|
||||
@@ -162,8 +162,8 @@ export async function PUT(request: NextRequest, { params }: { params: Promise<{
|
||||
actorId: userId,
|
||||
actorName: session?.user?.name,
|
||||
actorEmail: session?.user?.email,
|
||||
action: 'environment.updated',
|
||||
resourceType: 'environment',
|
||||
action: AuditAction.ENVIRONMENT_UPDATED,
|
||||
resourceType: AuditResourceType.ENVIRONMENT,
|
||||
resourceId: workspaceId,
|
||||
description: `Updated environment variables`,
|
||||
metadata: { keysUpdated: Object.keys(variables) },
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { recordAudit } from '@/lib/audit/log'
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import { generateRequestId } from '@/lib/core/utils/request'
|
||||
import { deleteWorkspaceFile } from '@/lib/uploads/contexts/workspace'
|
||||
@@ -45,8 +45,8 @@ export async function DELETE(
|
||||
actorId: session.user.id,
|
||||
actorName: session.user.name,
|
||||
actorEmail: session.user.email,
|
||||
action: 'file.deleted',
|
||||
resourceType: 'file',
|
||||
action: AuditAction.FILE_DELETED,
|
||||
resourceType: AuditResourceType.FILE,
|
||||
resourceId: fileId,
|
||||
description: `Deleted file "${fileId}"`,
|
||||
request,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { recordAudit } from '@/lib/audit/log'
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import { generateRequestId } from '@/lib/core/utils/request'
|
||||
import { listWorkspaceFiles, uploadWorkspaceFile } from '@/lib/uploads/contexts/workspace'
|
||||
@@ -110,8 +110,8 @@ export async function POST(request: NextRequest, { params }: { params: Promise<{
|
||||
actorId: session.user.id,
|
||||
actorName: session.user.name,
|
||||
actorEmail: session.user.email,
|
||||
action: 'file.uploaded',
|
||||
resourceType: 'file',
|
||||
action: AuditAction.FILE_UPLOADED,
|
||||
resourceType: AuditResourceType.FILE,
|
||||
resourceId: userFile.id,
|
||||
resourceName: file.name,
|
||||
description: `Uploaded file "${file.name}"`,
|
||||
|
||||
@@ -4,7 +4,7 @@ import { createLogger } from '@sim/logger'
|
||||
import { and, eq, inArray } from 'drizzle-orm'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { recordAudit } from '@/lib/audit/log'
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import { encryptSecret } from '@/lib/core/security/encryption'
|
||||
import { getUserEntityPermissions } from '@/lib/workspaces/permissions/utils'
|
||||
@@ -255,8 +255,8 @@ export async function PUT(request: NextRequest, { params }: RouteParams) {
|
||||
recordAudit({
|
||||
workspaceId,
|
||||
actorId: session.user.id,
|
||||
action: 'notification.updated',
|
||||
resourceType: 'notification',
|
||||
action: AuditAction.NOTIFICATION_UPDATED,
|
||||
resourceType: AuditResourceType.NOTIFICATION,
|
||||
resourceId: notificationId,
|
||||
actorName: session.user.name ?? undefined,
|
||||
actorEmail: session.user.email ?? undefined,
|
||||
@@ -327,8 +327,8 @@ export async function DELETE(request: NextRequest, { params }: RouteParams) {
|
||||
recordAudit({
|
||||
workspaceId,
|
||||
actorId: session.user.id,
|
||||
action: 'notification.deleted',
|
||||
resourceType: 'notification',
|
||||
action: AuditAction.NOTIFICATION_DELETED,
|
||||
resourceType: AuditResourceType.NOTIFICATION,
|
||||
resourceId: notificationId,
|
||||
actorName: session.user.name ?? undefined,
|
||||
actorEmail: session.user.email ?? undefined,
|
||||
|
||||
@@ -5,7 +5,7 @@ import { and, eq, inArray } from 'drizzle-orm'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import { z } from 'zod'
|
||||
import { recordAudit } from '@/lib/audit/log'
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import { encryptSecret } from '@/lib/core/security/encryption'
|
||||
import { getUserEntityPermissions } from '@/lib/workspaces/permissions/utils'
|
||||
@@ -260,8 +260,8 @@ export async function POST(request: NextRequest, { params }: { params: Promise<{
|
||||
recordAudit({
|
||||
workspaceId,
|
||||
actorId: session.user.id,
|
||||
action: 'notification.created',
|
||||
resourceType: 'notification',
|
||||
action: AuditAction.NOTIFICATION_CREATED,
|
||||
resourceType: AuditResourceType.NOTIFICATION,
|
||||
resourceId: subscription.id,
|
||||
actorName: session.user.name ?? undefined,
|
||||
actorEmail: session.user.email ?? undefined,
|
||||
|
||||
@@ -5,7 +5,7 @@ import { createLogger } from '@sim/logger'
|
||||
import { and, eq } from 'drizzle-orm'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { recordAudit } from '@/lib/audit/log'
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import {
|
||||
getUsersWithPermissions,
|
||||
@@ -161,8 +161,8 @@ export async function PATCH(request: NextRequest, { params }: { params: Promise<
|
||||
recordAudit({
|
||||
workspaceId,
|
||||
actorId: session.user.id,
|
||||
action: 'member.role_changed',
|
||||
resourceType: 'workspace',
|
||||
action: AuditAction.MEMBER_ROLE_CHANGED,
|
||||
resourceType: AuditResourceType.WORKSPACE,
|
||||
resourceId: workspaceId,
|
||||
actorName: session.user.name ?? undefined,
|
||||
actorEmail: session.user.email ?? undefined,
|
||||
|
||||
@@ -3,7 +3,7 @@ import { createLogger } from '@sim/logger'
|
||||
import { and, eq, inArray } from 'drizzle-orm'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { recordAudit } from '@/lib/audit/log'
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
|
||||
import { getSession } from '@/lib/auth'
|
||||
|
||||
const logger = createLogger('WorkspaceByIdAPI')
|
||||
@@ -287,8 +287,8 @@ export async function DELETE(
|
||||
actorId: session.user.id,
|
||||
actorName: session.user.name,
|
||||
actorEmail: session.user.email,
|
||||
action: 'workspace.deleted',
|
||||
resourceType: 'workspace',
|
||||
action: AuditAction.WORKSPACE_DELETED,
|
||||
resourceType: AuditResourceType.WORKSPACE,
|
||||
resourceId: workspaceId,
|
||||
description: 'Deleted workspace',
|
||||
request,
|
||||
|
||||
@@ -12,7 +12,7 @@ import { createLogger } from '@sim/logger'
|
||||
import { and, eq } from 'drizzle-orm'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { WorkspaceInvitationEmail } from '@/components/emails'
|
||||
import { recordAudit } from '@/lib/audit/log'
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import { getBaseUrl } from '@/lib/core/utils/urls'
|
||||
import { sendEmail } from '@/lib/messaging/email/mailer'
|
||||
@@ -166,8 +166,8 @@ export async function GET(
|
||||
recordAudit({
|
||||
workspaceId: invitation.workspaceId,
|
||||
actorId: session.user.id,
|
||||
action: 'invitation.accepted',
|
||||
resourceType: 'workspace',
|
||||
action: AuditAction.INVITATION_ACCEPTED,
|
||||
resourceType: AuditResourceType.WORKSPACE,
|
||||
resourceId: invitation.workspaceId,
|
||||
actorName: session.user.name ?? undefined,
|
||||
actorEmail: session.user.email ?? undefined,
|
||||
@@ -233,8 +233,8 @@ export async function DELETE(
|
||||
recordAudit({
|
||||
workspaceId: invitation.workspaceId,
|
||||
actorId: session.user.id,
|
||||
action: 'invitation.revoked',
|
||||
resourceType: 'workspace',
|
||||
action: AuditAction.INVITATION_REVOKED,
|
||||
resourceType: AuditResourceType.WORKSPACE,
|
||||
resourceId: invitation.workspaceId,
|
||||
actorName: session.user.name ?? undefined,
|
||||
actorEmail: session.user.email ?? undefined,
|
||||
|
||||
@@ -13,7 +13,7 @@ import { createLogger } from '@sim/logger'
|
||||
import { and, eq, inArray } from 'drizzle-orm'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { WorkspaceInvitationEmail } from '@/components/emails'
|
||||
import { recordAudit } from '@/lib/audit/log'
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import { PlatformEvents } from '@/lib/core/telemetry'
|
||||
import { getBaseUrl } from '@/lib/core/utils/urls'
|
||||
@@ -220,8 +220,8 @@ export async function POST(req: NextRequest) {
|
||||
actorId: session.user.id,
|
||||
actorName: session.user.name,
|
||||
actorEmail: session.user.email,
|
||||
action: 'member.invited',
|
||||
resourceType: 'workspace',
|
||||
action: AuditAction.MEMBER_INVITED,
|
||||
resourceType: AuditResourceType.WORKSPACE,
|
||||
resourceId: workspaceId,
|
||||
resourceName: email,
|
||||
description: `Invited ${email} as ${permission}`,
|
||||
|
||||
@@ -4,7 +4,7 @@ import { createLogger } from '@sim/logger'
|
||||
import { and, eq } from 'drizzle-orm'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { recordAudit } from '@/lib/audit/log'
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import { hasWorkspaceAdminAccess } from '@/lib/workspaces/permissions/utils'
|
||||
|
||||
@@ -107,8 +107,8 @@ export async function DELETE(req: NextRequest, { params }: { params: Promise<{ i
|
||||
actorId: session.user.id,
|
||||
actorName: session.user.name,
|
||||
actorEmail: session.user.email,
|
||||
action: 'member.removed',
|
||||
resourceType: 'workspace',
|
||||
action: AuditAction.MEMBER_REMOVED,
|
||||
resourceType: AuditResourceType.WORKSPACE,
|
||||
resourceId: workspaceId,
|
||||
description: isSelf ? 'Left the workspace' : 'Removed a member from the workspace',
|
||||
metadata: { removedUserId: userId, selfRemoval: isSelf },
|
||||
|
||||
@@ -4,7 +4,7 @@ import { createLogger } from '@sim/logger'
|
||||
import { and, desc, eq, isNull } from 'drizzle-orm'
|
||||
import { NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { recordAudit } from '@/lib/audit/log'
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import { PlatformEvents } from '@/lib/core/telemetry'
|
||||
import { buildDefaultWorkflowArtifacts } from '@/lib/workflows/defaults'
|
||||
@@ -74,8 +74,8 @@ export async function POST(req: Request) {
|
||||
actorId: session.user.id,
|
||||
actorName: session.user.name,
|
||||
actorEmail: session.user.email,
|
||||
action: 'workspace.created',
|
||||
resourceType: 'workspace',
|
||||
action: AuditAction.WORKSPACE_CREATED,
|
||||
resourceType: AuditResourceType.WORKSPACE,
|
||||
resourceId: newWorkspace.id,
|
||||
resourceName: newWorkspace.name,
|
||||
description: `Created workspace "${newWorkspace.name}"`,
|
||||
|
||||
@@ -110,9 +110,12 @@ const logger = createLogger('McpSettings')
|
||||
* Checks if a URL's hostname is in the allowed domains list.
|
||||
* Returns true if no allowlist is configured (null) or the domain matches.
|
||||
*/
|
||||
const ENV_VAR_PATTERN = /\{\{[^}]+\}\}/
|
||||
|
||||
function isDomainAllowed(url: string | undefined, allowedDomains: string[] | null): boolean {
|
||||
if (allowedDomains === null) return true
|
||||
if (!url) return false
|
||||
if (ENV_VAR_PATTERN.test(url)) return true
|
||||
try {
|
||||
const hostname = new URL(url).hostname.toLowerCase()
|
||||
return allowedDomains.includes(hostname)
|
||||
|
||||
@@ -151,7 +151,7 @@ export const AuditResourceType = {
|
||||
export type AuditResourceTypeValue = (typeof AuditResourceType)[keyof typeof AuditResourceType]
|
||||
|
||||
interface AuditLogParams {
|
||||
workspaceId: string
|
||||
workspaceId?: string | null
|
||||
actorId: string
|
||||
action: AuditActionType
|
||||
resourceType: AuditResourceTypeValue
|
||||
@@ -177,7 +177,7 @@ export function recordAudit(params: AuditLogParams): void {
|
||||
db.insert(auditLog)
|
||||
.values({
|
||||
id: nanoid(),
|
||||
workspaceId: params.workspaceId,
|
||||
workspaceId: params.workspaceId || null,
|
||||
actorId: params.actorId,
|
||||
action: params.action,
|
||||
resourceType: params.resourceType,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
CREATE TABLE "audit_log" (
|
||||
"id" text PRIMARY KEY NOT NULL,
|
||||
"workspace_id" text NOT NULL,
|
||||
"actor_id" text NOT NULL,
|
||||
"workspace_id" text,
|
||||
"actor_id" text,
|
||||
"action" text NOT NULL,
|
||||
"resource_type" text NOT NULL,
|
||||
"resource_id" text,
|
||||
@@ -15,8 +15,8 @@ CREATE TABLE "audit_log" (
|
||||
"created_at" timestamp DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
ALTER TABLE "audit_log" ADD CONSTRAINT "audit_log_workspace_id_workspace_id_fk" FOREIGN KEY ("workspace_id") REFERENCES "public"."workspace"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "audit_log" ADD CONSTRAINT "audit_log_actor_id_user_id_fk" FOREIGN KEY ("actor_id") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "audit_log" ADD CONSTRAINT "audit_log_workspace_id_workspace_id_fk" FOREIGN KEY ("workspace_id") REFERENCES "public"."workspace"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "audit_log" ADD CONSTRAINT "audit_log_actor_id_user_id_fk" FOREIGN KEY ("actor_id") REFERENCES "public"."user"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
|
||||
CREATE INDEX "audit_log_workspace_created_idx" ON "audit_log" USING btree ("workspace_id","created_at");--> statement-breakpoint
|
||||
CREATE INDEX "audit_log_actor_created_idx" ON "audit_log" USING btree ("actor_id","created_at");--> statement-breakpoint
|
||||
CREATE INDEX "audit_log_resource_idx" ON "audit_log" USING btree ("resource_type","resource_id");--> statement-breakpoint
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"id": "a5703ff6-4481-4c0e-abc3-218e55a84376",
|
||||
"id": "878a3e46-0446-46a9-9577-663699ae0373",
|
||||
"prevId": "49f580f7-7eba-4431-bdf4-61db0e606546",
|
||||
"version": "7",
|
||||
"dialect": "postgresql",
|
||||
@@ -977,13 +977,13 @@
|
||||
"name": "workspace_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
"notNull": false
|
||||
},
|
||||
"actor_id": {
|
||||
"name": "actor_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
"notNull": false
|
||||
},
|
||||
"action": {
|
||||
"name": "action",
|
||||
@@ -1141,7 +1141,7 @@
|
||||
"tableTo": "workspace",
|
||||
"columnsFrom": ["workspace_id"],
|
||||
"columnsTo": ["id"],
|
||||
"onDelete": "cascade",
|
||||
"onDelete": "set null",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
"audit_log_actor_id_user_id_fk": {
|
||||
@@ -1150,7 +1150,7 @@
|
||||
"tableTo": "user",
|
||||
"columnsFrom": ["actor_id"],
|
||||
"columnsTo": ["id"],
|
||||
"onDelete": "cascade",
|
||||
"onDelete": "set null",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1083,8 +1083,8 @@
|
||||
{
|
||||
"idx": 155,
|
||||
"version": "7",
|
||||
"when": 1771384748779,
|
||||
"tag": "0155_lush_legion",
|
||||
"when": 1771387813205,
|
||||
"tag": "0155_ambiguous_blade",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
|
||||
@@ -2030,12 +2030,8 @@ export const auditLog = pgTable(
|
||||
'audit_log',
|
||||
{
|
||||
id: text('id').primaryKey(),
|
||||
workspaceId: text('workspace_id')
|
||||
.notNull()
|
||||
.references(() => workspace.id, { onDelete: 'cascade' }),
|
||||
actorId: text('actor_id')
|
||||
.notNull()
|
||||
.references(() => user.id, { onDelete: 'cascade' }),
|
||||
workspaceId: text('workspace_id').references(() => workspace.id, { onDelete: 'set null' }),
|
||||
actorId: text('actor_id').references(() => user.id, { onDelete: 'set null' }),
|
||||
action: text('action').notNull(),
|
||||
resourceType: text('resource_type').notNull(),
|
||||
resourceId: text('resource_id'),
|
||||
|
||||
Reference in New Issue
Block a user