Files
simstudioai--sim/apps/sim/lib/api/contracts/admin.ts
T
wehub-resource-sync d25d482dc2
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
chore: import upstream snapshot with attribution
2026-07-13 13:20:55 +08:00

576 lines
15 KiB
TypeScript

import { z } from 'zod'
import { defineRouteContract } from '@/lib/api/contracts/types'
import { workflowStateSchema } from '@/lib/api/contracts/workflows'
import { workspacePermissionSchema } from '@/lib/api/contracts/workspaces'
const DEFAULT_LIMIT = 50
const MAX_LIMIT = 250
const lastQueryValue = (value: unknown) => (Array.isArray(value) ? value.at(-1) : value)
const queryStringSchema = z.preprocess(lastQueryValue, z.string().optional())
const adminPaginationValueSchema = (defaultValue: number, normalize: (value: number) => number) =>
z.preprocess(lastQueryValue, z.union([z.string(), z.number()]).optional()).transform((value) => {
const parsed =
typeof value === 'number'
? value
: typeof value === 'string'
? Number.parseInt(value, 10)
: Number.NaN
if (!Number.isFinite(parsed) || !Number.isInteger(parsed)) {
return defaultValue
}
return normalize(parsed)
})
export const adminPaginationQuerySchema = z.object({
limit: adminPaginationValueSchema(DEFAULT_LIMIT, (limit) =>
limit < 1 ? DEFAULT_LIMIT : Math.min(limit, MAX_LIMIT)
),
offset: adminPaginationValueSchema(0, (offset) => (offset < 0 ? 0 : offset)),
})
export const adminIdParamsSchema = z.object({
id: z.string().min(1),
})
export const adminWorkflowVersionParamsSchema = z.object({
id: z.string().min(1),
versionId: z
.string()
.transform((value) => Number(value))
.refine((value) => Number.isFinite(value) && value >= 1, {
error: 'Invalid version number',
}),
})
export const adminWorkspaceMemberParamsSchema = z.object({
id: z.string().min(1),
memberId: z.string().min(1),
})
export const adminExportFormatQuerySchema = z.object({
format: z.preprocess(lastQueryValue, z.enum(['zip', 'json']).catch('zip')),
})
export const adminDeleteWorkspaceMemberQuerySchema = z.object({
userId: z.preprocess(
lastQueryValue,
z
.string({ error: 'userId query parameter is required' })
.min(1, { error: 'userId query parameter is required' })
),
})
export const adminWorkspaceMemberBodySchema = z.object({
userId: z.string({ error: 'userId is required' }).min(1, { error: 'userId is required' }),
permissions: workspacePermissionSchema.refine((value) => value !== null, {
error: 'permissions must be "admin", "write", or "read"',
}),
})
export const adminUpdateWorkspaceMemberBodySchema = z.object({
permissions: workspacePermissionSchema.refine((value) => value !== null, {
error: 'permissions must be "admin", "write", or "read"',
}),
})
export const adminExportWorkflowsBodySchema = z.object({
ids: z
.array(z.string(), { error: 'ids must be a non-empty array of workflow IDs' })
.nonempty({ error: 'ids must be a non-empty array of workflow IDs' }),
})
export const adminWorkflowImportBodySchema = z.object({
workspaceId: z
.string({ error: 'workspaceId is required' })
.min(1, { error: 'workspaceId is required' }),
folderId: z.string().optional(),
name: z.string().optional(),
workflow: z.union([
z.string({ error: 'workflow is required' }).min(1, { error: 'workflow is required' }),
z.record(z.string(), z.unknown()),
]),
})
export const adminWorkspaceImportQuerySchema = z.object({
createFolders: z
.preprocess(lastQueryValue, z.enum(['true', 'false']).catch('true'))
.transform((value) => value !== 'false'),
rootFolderName: queryStringSchema,
})
export const adminWorkspaceImportBodySchema = z.object({
workflows: z.array(
z.object({
content: z.union([z.string(), z.record(z.string(), z.unknown())]),
name: z.string().optional(),
folderPath: z.array(z.string()).optional(),
}),
{ error: 'Invalid JSON body. Expected { workflows: [...] }' }
),
})
export const adminPaginationMetaSchema = z.object({
total: z.number(),
limit: z.number(),
offset: z.number(),
hasMore: z.boolean(),
})
export const adminWorkspaceSchema = z.object({
id: z.string(),
name: z.string(),
ownerId: z.string(),
createdAt: z.string(),
updatedAt: z.string(),
})
export const adminWorkspaceDetailSchema = adminWorkspaceSchema.extend({
workflowCount: z.number(),
folderCount: z.number(),
})
export const adminFolderSchema = z.object({
id: z.string(),
name: z.string(),
parentId: z.string().nullable(),
color: z.string().nullable(),
sortOrder: z.number(),
createdAt: z.string(),
updatedAt: z.string(),
})
export const adminWorkflowSchema = z.object({
id: z.string(),
name: z.string(),
description: z.string().nullable(),
workspaceId: z.string().nullable(),
folderId: z.string().nullable(),
isDeployed: z.boolean(),
deployedAt: z.string().nullable(),
runCount: z.number(),
lastRunAt: z.string().nullable(),
createdAt: z.string(),
updatedAt: z.string(),
})
export const adminWorkflowDetailSchema = adminWorkflowSchema.extend({
blockCount: z.number(),
edgeCount: z.number(),
})
export const adminWorkspaceMemberSchema = z.object({
id: z.string(),
workspaceId: z.string(),
userId: z.string(),
permissions: workspacePermissionSchema,
createdAt: z.string(),
updatedAt: z.string(),
userName: z.string(),
userEmail: z.string(),
userImage: z.string().nullable(),
})
export const adminWorkflowVariableSchema = z.object({
id: z.string(),
name: z.string(),
type: z.enum(['string', 'number', 'boolean', 'object', 'array', 'plain']),
value: z.unknown(),
})
export const adminWorkflowExportStateSchema = workflowStateSchema.extend({
metadata: z
.object({
name: z.string().optional(),
description: z.string().optional(),
exportedAt: z.string().optional(),
})
.optional(),
variables: z.record(z.string(), adminWorkflowVariableSchema).optional(),
})
export const adminWorkflowExportPayloadSchema = z.object({
version: z.literal('1.0'),
exportedAt: z.string(),
workflow: z.object({
id: z.string(),
name: z.string(),
description: z.string().nullable(),
workspaceId: z.string().nullable(),
folderId: z.string().nullable(),
}),
state: adminWorkflowExportStateSchema,
})
export const adminFolderExportPayloadSchema = z.object({
id: z.string(),
name: z.string(),
parentId: z.string().nullable(),
})
export const adminWorkspaceExportPayloadSchema = z.object({
version: z.literal('1.0'),
exportedAt: z.string(),
workspace: z.object({
id: z.string(),
name: z.string(),
}),
workflows: z.array(
z.object({
workflow: adminWorkflowExportPayloadSchema.shape.workflow,
state: adminWorkflowExportStateSchema,
})
),
folders: z.array(adminFolderExportPayloadSchema),
})
export const adminFolderFullExportPayloadSchema = z.object({
version: z.literal('1.0'),
exportedAt: z.string(),
folder: z.object({
id: z.string(),
name: z.string(),
}),
workflows: z.array(
z.object({
workflow: adminWorkflowExportPayloadSchema.shape.workflow.omit({ workspaceId: true }),
state: adminWorkflowExportStateSchema,
})
),
folders: z.array(adminFolderExportPayloadSchema),
})
export const adminImportResultSchema = z.object({
workflowId: z.string(),
name: z.string(),
success: z.boolean(),
error: z.string().optional(),
})
export const adminWorkflowImportResponseSchema = z.object({
workflowId: z.string(),
name: z.string(),
success: z.literal(true),
})
export const adminWorkspaceImportResponseSchema = z.object({
imported: z.number(),
failed: z.number(),
results: z.array(adminImportResultSchema),
})
export const adminDeploymentVersionSchema = z.object({
id: z.string(),
version: z.number(),
name: z.string().nullable(),
isActive: z.boolean(),
createdAt: z.string(),
createdBy: z.string().nullable(),
deployedByName: z.string().nullable(),
})
export const adminDeployResultSchema = z.object({
isDeployed: z.literal(true),
version: z.number(),
deployedAt: z.string(),
warnings: z.array(z.string()).optional(),
})
export const adminUndeployResultSchema = z.object({
isDeployed: z.literal(false),
})
const adminSingleResponseSchema = <TSchema extends z.ZodType>(schema: TSchema) =>
z.object({ data: schema })
const adminListResponseSchema = <TSchema extends z.ZodType>(schema: TSchema) =>
z.object({
data: z.array(schema),
pagination: adminPaginationMetaSchema,
})
export const adminListWorkflowsContract = defineRouteContract({
method: 'GET',
path: '/api/v1/admin/workflows',
query: adminPaginationQuerySchema,
response: {
mode: 'json',
schema: adminListResponseSchema(adminWorkflowSchema),
},
})
export const adminGetWorkflowContract = defineRouteContract({
method: 'GET',
path: '/api/v1/admin/workflows/[id]',
params: adminIdParamsSchema,
response: {
mode: 'json',
schema: adminSingleResponseSchema(adminWorkflowDetailSchema),
},
})
export const adminDeleteWorkflowContract = defineRouteContract({
method: 'DELETE',
path: '/api/v1/admin/workflows/[id]',
params: adminIdParamsSchema,
response: {
mode: 'json',
schema: z.object({
success: z.literal(true),
workflowId: z.string(),
}),
},
})
export const adminDeployWorkflowContract = defineRouteContract({
method: 'POST',
path: '/api/v1/admin/workflows/[id]/deploy',
params: adminIdParamsSchema,
response: {
mode: 'json',
schema: adminSingleResponseSchema(adminDeployResultSchema),
},
})
export const adminUndeployWorkflowContract = defineRouteContract({
method: 'DELETE',
path: '/api/v1/admin/workflows/[id]/deploy',
params: adminIdParamsSchema,
response: {
mode: 'json',
schema: adminSingleResponseSchema(adminUndeployResultSchema),
},
})
export const adminListWorkflowVersionsContract = defineRouteContract({
method: 'GET',
path: '/api/v1/admin/workflows/[id]/versions',
params: adminIdParamsSchema,
response: {
mode: 'json',
schema: adminSingleResponseSchema(
z.object({
versions: z.array(adminDeploymentVersionSchema),
})
),
},
})
export const adminActivateWorkflowVersionContract = defineRouteContract({
method: 'POST',
path: '/api/v1/admin/workflows/[id]/versions/[versionId]/activate',
params: adminWorkflowVersionParamsSchema,
response: {
mode: 'json',
schema: adminSingleResponseSchema(
z.object({
success: z.literal(true),
version: z.number(),
deployedAt: z.string(),
warnings: z.array(z.string()).optional(),
})
),
},
})
export const adminExportWorkflowContract = defineRouteContract({
method: 'GET',
path: '/api/v1/admin/workflows/[id]/export',
params: adminIdParamsSchema,
response: {
mode: 'json',
schema: adminSingleResponseSchema(adminWorkflowExportPayloadSchema),
},
})
export const adminExportWorkflowsContract = defineRouteContract({
method: 'POST',
path: '/api/v1/admin/workflows/export',
query: adminExportFormatQuerySchema,
body: adminExportWorkflowsBodySchema,
response: {
mode: 'binary',
},
})
export const adminImportWorkflowContract = defineRouteContract({
method: 'POST',
path: '/api/v1/admin/workflows/import',
body: adminWorkflowImportBodySchema,
response: {
mode: 'json',
schema: adminWorkflowImportResponseSchema,
},
})
export const adminListWorkspacesContract = defineRouteContract({
method: 'GET',
path: '/api/v1/admin/workspaces',
query: adminPaginationQuerySchema,
response: {
mode: 'json',
schema: adminListResponseSchema(adminWorkspaceSchema),
},
})
export const adminGetWorkspaceContract = defineRouteContract({
method: 'GET',
path: '/api/v1/admin/workspaces/[id]',
params: adminIdParamsSchema,
response: {
mode: 'json',
schema: adminSingleResponseSchema(adminWorkspaceDetailSchema),
},
})
export const adminListWorkspaceWorkflowsContract = defineRouteContract({
method: 'GET',
path: '/api/v1/admin/workspaces/[id]/workflows',
params: adminIdParamsSchema,
query: adminPaginationQuerySchema,
response: {
mode: 'json',
schema: adminListResponseSchema(adminWorkflowSchema),
},
})
export const adminDeleteWorkspaceWorkflowsContract = defineRouteContract({
method: 'DELETE',
path: '/api/v1/admin/workspaces/[id]/workflows',
params: adminIdParamsSchema,
response: {
mode: 'json',
schema: z.object({
success: z.literal(true),
deleted: z.number(),
}),
},
})
export const adminListWorkspaceFoldersContract = defineRouteContract({
method: 'GET',
path: '/api/v1/admin/workspaces/[id]/folders',
params: adminIdParamsSchema,
query: adminPaginationQuerySchema,
response: {
mode: 'json',
schema: adminListResponseSchema(adminFolderSchema),
},
})
export const adminExportWorkspaceContract = defineRouteContract({
method: 'GET',
path: '/api/v1/admin/workspaces/[id]/export',
params: adminIdParamsSchema,
query: adminExportFormatQuerySchema,
response: {
mode: 'binary',
},
})
export const adminImportWorkspaceContract = defineRouteContract({
method: 'POST',
path: '/api/v1/admin/workspaces/[id]/import',
params: adminIdParamsSchema,
query: adminWorkspaceImportQuerySchema,
response: {
mode: 'json',
schema: adminWorkspaceImportResponseSchema,
},
})
export const adminListWorkspaceMembersContract = defineRouteContract({
method: 'GET',
path: '/api/v1/admin/workspaces/[id]/members',
params: adminIdParamsSchema,
query: adminPaginationQuerySchema,
response: {
mode: 'json',
schema: adminListResponseSchema(adminWorkspaceMemberSchema),
},
})
export const adminCreateWorkspaceMemberContract = defineRouteContract({
method: 'POST',
path: '/api/v1/admin/workspaces/[id]/members',
params: adminIdParamsSchema,
body: adminWorkspaceMemberBodySchema,
response: {
mode: 'json',
schema: adminSingleResponseSchema(
adminWorkspaceMemberSchema.extend({
action: z.enum(['created', 'updated', 'already_member']),
})
),
},
})
export const adminDeleteWorkspaceMemberContract = defineRouteContract({
method: 'DELETE',
path: '/api/v1/admin/workspaces/[id]/members',
params: adminIdParamsSchema,
query: adminDeleteWorkspaceMemberQuerySchema,
response: {
mode: 'json',
schema: adminSingleResponseSchema(
z.object({
removed: z.literal(true),
userId: z.string(),
workspaceId: z.string(),
})
),
},
})
export const adminGetWorkspaceMemberContract = defineRouteContract({
method: 'GET',
path: '/api/v1/admin/workspaces/[id]/members/[memberId]',
params: adminWorkspaceMemberParamsSchema,
response: {
mode: 'json',
schema: adminSingleResponseSchema(adminWorkspaceMemberSchema),
},
})
export const adminUpdateWorkspaceMemberContract = defineRouteContract({
method: 'PATCH',
path: '/api/v1/admin/workspaces/[id]/members/[memberId]',
params: adminWorkspaceMemberParamsSchema,
body: adminUpdateWorkspaceMemberBodySchema,
response: {
mode: 'json',
schema: adminSingleResponseSchema(adminWorkspaceMemberSchema),
},
})
export const adminRemoveWorkspaceMemberContract = defineRouteContract({
method: 'DELETE',
path: '/api/v1/admin/workspaces/[id]/members/[memberId]',
params: adminWorkspaceMemberParamsSchema,
response: {
mode: 'json',
schema: adminSingleResponseSchema(
z.object({
removed: z.literal(true),
memberId: z.string(),
userId: z.string(),
workspaceId: z.string(),
})
),
},
})
export const adminExportFolderContract = defineRouteContract({
method: 'GET',
path: '/api/v1/admin/folders/[id]/export',
params: adminIdParamsSchema,
query: adminExportFormatQuerySchema,
response: {
mode: 'binary',
},
})