chore: import upstream snapshot with attribution
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled
Publish CLI Package / publish-npm (push) Has been cancelled
Publish Python SDK / publish-pypi (push) Has been cancelled
Publish TypeScript SDK / publish-npm (push) Has been cancelled

This commit is contained in:
wehub-resource-sync
2026-07-13 13:20:55 +08:00
commit d25d482dc2
13754 changed files with 4996608 additions and 0 deletions
@@ -0,0 +1,478 @@
import { AuditAction, AuditResourceType, recordAudit } from '@sim/audit'
import { db } from '@sim/db'
import { workflow, workflowFolder } from '@sim/db/schema'
import { createLogger } from '@sim/logger'
import { isFolderInWorkspace } from '@sim/platform-authz/workflow'
import { toError } from '@sim/utils/errors'
import { generateId } from '@sim/utils/id'
import { and, eq, isNull, min, ne } from 'drizzle-orm'
import { generateRequestId } from '@/lib/core/utils/request'
import { captureServerEvent } from '@/lib/posthog/server'
import { buildDefaultWorkflowArtifacts } from '@/lib/workflows/defaults'
import { archiveWorkflow, restoreWorkflow } from '@/lib/workflows/lifecycle'
import type { OrchestrationErrorCode } from '@/lib/workflows/orchestration/types'
import { saveWorkflowToNormalizedTables } from '@/lib/workflows/persistence/utils'
import { deduplicateWorkflowName } from '@/lib/workflows/utils'
const logger = createLogger('WorkflowLifecycle')
export interface PerformCreateWorkflowParams {
userId: string
workspaceId: string
name: string
id?: string
description?: string | null
folderId?: string | null
sortOrder?: number
deduplicate?: boolean
requestId?: string
}
export interface PerformCreateWorkflowResult {
success: boolean
error?: string
errorCode?: OrchestrationErrorCode
workflow?: {
id: string
name: string
description?: string | null
workspaceId: string
folderId?: string | null
sortOrder: number
createdAt: Date
updatedAt: Date
startBlockId?: string
subBlockValues: Record<string, unknown>
}
}
export interface PerformUpdateWorkflowParams {
workflowId: string
userId: string
workspaceId: string
currentName: string
currentFolderId?: string | null
/** Prior `locked` value, used to detect lock-state transitions for instrumentation. */
currentLocked?: boolean | null
name?: string
description?: string | null
folderId?: string | null
sortOrder?: number
locked?: boolean
requestId?: string
}
export interface PerformUpdateWorkflowResult {
success: boolean
error?: string
errorCode?: OrchestrationErrorCode
workflow?: {
id: string
name: string
description: string | null
workspaceId: string | null
folderId: string | null
sortOrder: number | null
locked: boolean | null
createdAt: Date
updatedAt: Date
archivedAt: Date | null
}
}
export interface PerformDeleteWorkflowParams {
workflowId: string
userId: string
requestId?: string
/** When true, allows deleting the last workflow in a workspace (used by admin API). */
skipLastWorkflowGuard?: boolean
/** Override the actor ID used in audit logs. Defaults to `userId`. */
actorId?: string
}
export interface PerformDeleteWorkflowResult {
success: boolean
error?: string
errorCode?: OrchestrationErrorCode
}
export interface PerformRestoreWorkflowParams {
workflowId: string
userId: string
requestId?: string
}
export interface PerformRestoreWorkflowResult {
success: boolean
error?: string
errorCode?: OrchestrationErrorCode
workflow?: Awaited<ReturnType<typeof restoreWorkflow>>['workflow']
}
async function nextWorkflowSortOrder(
workspaceId: string,
folderId: string | null | undefined
): Promise<number> {
const workflowParentCondition = folderId
? eq(workflow.folderId, folderId)
: isNull(workflow.folderId)
const folderParentCondition = folderId
? eq(workflowFolder.parentId, folderId)
: isNull(workflowFolder.parentId)
const [[workflowMinResult], [folderMinResult]] = await Promise.all([
db
.select({ minOrder: min(workflow.sortOrder) })
.from(workflow)
.where(
and(
eq(workflow.workspaceId, workspaceId),
workflowParentCondition,
isNull(workflow.archivedAt)
)
),
db
.select({ minOrder: min(workflowFolder.sortOrder) })
.from(workflowFolder)
.where(and(eq(workflowFolder.workspaceId, workspaceId), folderParentCondition)),
])
const minSortOrder = [workflowMinResult?.minOrder, folderMinResult?.minOrder].reduce<
number | null
>((currentMin, candidate) => {
if (candidate == null) return currentMin
if (currentMin == null) return candidate
return Math.min(currentMin, candidate)
}, null)
return minSortOrder != null ? minSortOrder - 1 : 0
}
async function workflowNameExistsInFolder(params: {
workspaceId: string
name: string
folderId?: string | null
excludeWorkflowId?: string
}): Promise<boolean> {
const conditions = [
eq(workflow.workspaceId, params.workspaceId),
isNull(workflow.archivedAt),
eq(workflow.name, params.name),
]
if (params.excludeWorkflowId) {
conditions.push(ne(workflow.id, params.excludeWorkflowId))
}
if (params.folderId) {
conditions.push(eq(workflow.folderId, params.folderId))
} else {
conditions.push(isNull(workflow.folderId))
}
const [duplicateWorkflow] = await db
.select({ id: workflow.id })
.from(workflow)
.where(and(...conditions))
.limit(1)
return Boolean(duplicateWorkflow)
}
export async function performCreateWorkflow(
params: PerformCreateWorkflowParams
): Promise<PerformCreateWorkflowResult> {
const requestId = params.requestId ?? generateRequestId()
const workflowId = params.id || generateId()
const folderId = params.folderId || null
try {
if (!(await isFolderInWorkspace(folderId, params.workspaceId))) {
return { success: false, error: 'Target folder not found', errorCode: 'validation' }
}
const name = params.deduplicate
? await deduplicateWorkflowName(params.name, params.workspaceId, folderId)
: params.name
if (!params.deduplicate) {
const duplicate = await workflowNameExistsInFolder({
workspaceId: params.workspaceId,
name,
folderId,
})
if (duplicate) {
return {
success: false,
error: `A workflow named "${name}" already exists in this folder`,
errorCode: 'conflict',
}
}
}
const sortOrder =
params.sortOrder !== undefined
? params.sortOrder
: await nextWorkflowSortOrder(params.workspaceId, folderId)
const now = new Date()
const { workflowState, subBlockValues, startBlockId } = buildDefaultWorkflowArtifacts()
await db.transaction(async (tx) => {
await tx.insert(workflow).values({
id: workflowId,
userId: params.userId,
workspaceId: params.workspaceId,
folderId,
sortOrder,
name,
description: params.description,
lastSynced: now,
createdAt: now,
updatedAt: now,
isDeployed: false,
runCount: 0,
variables: {},
})
await saveWorkflowToNormalizedTables(workflowId, workflowState, tx)
})
logger.info(`[${requestId}] Successfully created workflow ${workflowId}`)
recordAudit({
workspaceId: params.workspaceId,
actorId: params.userId,
action: AuditAction.WORKFLOW_CREATED,
resourceType: AuditResourceType.WORKFLOW,
resourceId: workflowId,
resourceName: name,
description: `Created workflow "${name}"`,
metadata: {
name,
description: params.description || undefined,
workspaceId: params.workspaceId,
folderId: folderId || undefined,
sortOrder,
},
})
return {
success: true,
workflow: {
id: workflowId,
name,
description: params.description,
workspaceId: params.workspaceId,
folderId,
sortOrder,
createdAt: now,
updatedAt: now,
startBlockId,
subBlockValues,
},
}
} catch (error) {
logger.error(`[${requestId}] Failed to create workflow`, { error })
return { success: false, error: toError(error).message, errorCode: 'internal' }
}
}
export async function performUpdateWorkflow(
params: PerformUpdateWorkflowParams
): Promise<PerformUpdateWorkflowResult> {
const requestId = params.requestId ?? generateRequestId()
try {
const targetName = params.name ?? params.currentName
const targetFolderId =
params.folderId !== undefined ? params.folderId || null : params.currentFolderId || null
if (
params.folderId !== undefined &&
!(await isFolderInWorkspace(targetFolderId, params.workspaceId))
) {
return { success: false, error: 'Target folder not found', errorCode: 'validation' }
}
if (params.name !== undefined || params.folderId !== undefined) {
const duplicate = await workflowNameExistsInFolder({
workspaceId: params.workspaceId,
name: targetName,
folderId: targetFolderId,
excludeWorkflowId: params.workflowId,
})
if (duplicate) {
return {
success: false,
error: `A workflow named "${targetName}" already exists in this folder`,
errorCode: 'conflict',
}
}
}
const updateData: Record<string, unknown> = { updatedAt: new Date() }
if (params.name !== undefined) updateData.name = params.name
if (params.description !== undefined) updateData.description = params.description
if (params.folderId !== undefined) updateData.folderId = params.folderId
if (params.sortOrder !== undefined) updateData.sortOrder = params.sortOrder
if (params.locked !== undefined) updateData.locked = params.locked
const [updatedWorkflow] = await db
.update(workflow)
.set(updateData)
.where(eq(workflow.id, params.workflowId))
.returning({
id: workflow.id,
name: workflow.name,
description: workflow.description,
workspaceId: workflow.workspaceId,
folderId: workflow.folderId,
sortOrder: workflow.sortOrder,
locked: workflow.locked,
createdAt: workflow.createdAt,
updatedAt: workflow.updatedAt,
archivedAt: workflow.archivedAt,
})
if (!updatedWorkflow) {
return { success: false, error: 'Workflow not found', errorCode: 'not_found' }
}
logger.info(`[${requestId}] Successfully updated workflow ${params.workflowId}`, {
updates: updateData,
})
if (params.locked !== undefined && params.locked !== (params.currentLocked ?? false)) {
const workspaceId = updatedWorkflow.workspaceId
recordAudit({
workspaceId: workspaceId ?? null,
actorId: params.userId,
action: params.locked ? AuditAction.WORKFLOW_LOCKED : AuditAction.WORKFLOW_UNLOCKED,
resourceType: AuditResourceType.WORKFLOW,
resourceId: params.workflowId,
resourceName: updatedWorkflow.name,
description: `${params.locked ? 'Locked' : 'Unlocked'} workflow "${updatedWorkflow.name}"`,
metadata: { locked: params.locked },
})
captureServerEvent(
params.userId,
'workflow_lock_toggled',
{
workflow_id: params.workflowId,
...(workspaceId ? { workspace_id: workspaceId } : {}),
locked: params.locked,
},
workspaceId ? { groups: { workspace: workspaceId } } : undefined
)
}
return { success: true, workflow: updatedWorkflow }
} catch (error) {
logger.error(`[${requestId}] Failed to update workflow ${params.workflowId}`, { error })
return { success: false, error: toError(error).message, errorCode: 'internal' }
}
}
/**
* Performs a full workflow deletion: enforces the last-workflow guard,
* archives the workflow via `archiveWorkflow`, and records an audit entry.
* Both the workflow API DELETE handler and the copilot delete_workflow tool
* must use this function.
*/
export async function performDeleteWorkflow(
params: PerformDeleteWorkflowParams
): Promise<PerformDeleteWorkflowResult> {
const { workflowId, userId, skipLastWorkflowGuard = false } = params
const actorId = params.actorId ?? userId
const requestId = params.requestId ?? generateRequestId()
const [workflowRecord] = await db
.select()
.from(workflow)
.where(eq(workflow.id, workflowId))
.limit(1)
if (!workflowRecord) {
return { success: false, error: 'Workflow not found', errorCode: 'not_found' }
}
if (!skipLastWorkflowGuard && workflowRecord.workspaceId) {
const totalWorkflows = await db
.select({ id: workflow.id })
.from(workflow)
.where(and(eq(workflow.workspaceId, workflowRecord.workspaceId), isNull(workflow.archivedAt)))
if (totalWorkflows.length <= 1) {
return {
success: false,
error: 'Cannot delete the only workflow in the workspace',
errorCode: 'validation',
}
}
}
const archiveResult = await archiveWorkflow(workflowId, { requestId })
if (!archiveResult.workflow) {
return { success: false, error: 'Workflow not found', errorCode: 'not_found' }
}
logger.info(`[${requestId}] Successfully archived workflow ${workflowId}`)
recordAudit({
workspaceId: workflowRecord.workspaceId || null,
actorId: actorId,
action: AuditAction.WORKFLOW_DELETED,
resourceType: AuditResourceType.WORKFLOW,
resourceId: workflowId,
resourceName: workflowRecord.name,
description: `Archived workflow "${workflowRecord.name}"`,
metadata: {
archived: archiveResult.archived,
},
})
return { success: true }
}
export async function performRestoreWorkflow(
params: PerformRestoreWorkflowParams
): Promise<PerformRestoreWorkflowResult> {
const { workflowId, userId } = params
const requestId = params.requestId ?? generateRequestId()
try {
const restoreResult = await restoreWorkflow(workflowId, { requestId })
if (!restoreResult.workflow) {
return { success: false, error: 'Workflow not found', errorCode: 'not_found' }
}
if (!restoreResult.restored) {
return {
success: false,
error: 'Workflow is not archived',
errorCode: 'validation',
workflow: restoreResult.workflow,
}
}
logger.info(`[${requestId}] Successfully restored workflow ${workflowId}`)
recordAudit({
workspaceId: restoreResult.workflow.workspaceId || null,
actorId: userId,
action: AuditAction.WORKFLOW_RESTORED,
resourceType: AuditResourceType.WORKFLOW,
resourceId: workflowId,
resourceName: restoreResult.workflow.name,
description: `Restored workflow "${restoreResult.workflow.name}"`,
metadata: {
workflowName: restoreResult.workflow.name,
workspaceId: restoreResult.workflow.workspaceId || undefined,
},
})
return { success: true, workflow: restoreResult.workflow }
} catch (error) {
logger.error(`[${requestId}] Failed to restore workflow ${workflowId}`, { error })
return { success: false, error: toError(error).message, errorCode: 'internal' }
}
}