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,200 @@
import { AuditAction, AuditResourceType, recordAudit } from '@sim/audit'
import { db } from '@sim/db'
import { apiKey } from '@sim/db/schema'
import { createLogger } from '@sim/logger'
import { getErrorMessage } from '@sim/utils/errors'
import { and, eq, not } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
import { updateWorkspaceApiKeyContract } from '@/lib/api/contracts/api-keys'
import { parseRequest } from '@/lib/api/server'
import { getSession } from '@/lib/auth'
import { generateRequestId } from '@/lib/core/utils/request'
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
import { captureServerEvent } from '@/lib/posthog/server'
import { getUserEntityPermissions } from '@/lib/workspaces/permissions/utils'
const logger = createLogger('WorkspaceApiKeyAPI')
export const PUT = withRouteHandler(
async (request: NextRequest, context: { params: Promise<{ id: string; keyId: string }> }) => {
const requestId = generateRequestId()
const { id: workspaceId, keyId } = await context.params
try {
const session = await getSession()
if (!session?.user?.id) {
logger.warn(`[${requestId}] Unauthorized workspace API key update attempt`)
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const userId = session.user.id
const permission = await getUserEntityPermissions(userId, 'workspace', workspaceId)
if (permission !== 'admin') {
return NextResponse.json({ error: 'Forbidden' }, { status: 403 })
}
const parsed = await parseRequest(updateWorkspaceApiKeyContract, request, context)
if (!parsed.success) return parsed.response
const { name } = parsed.data.body
const existingKey = await db
.select()
.from(apiKey)
.where(
and(
eq(apiKey.workspaceId, workspaceId),
eq(apiKey.id, keyId),
eq(apiKey.type, 'workspace')
)
)
.limit(1)
if (existingKey.length === 0) {
return NextResponse.json({ error: 'API key not found' }, { status: 404 })
}
const conflictingKey = await db
.select()
.from(apiKey)
.where(
and(
eq(apiKey.workspaceId, workspaceId),
eq(apiKey.name, name),
eq(apiKey.type, 'workspace'),
not(eq(apiKey.id, keyId))
)
)
.limit(1)
if (conflictingKey.length > 0) {
return NextResponse.json(
{ error: 'A workspace API key with this name already exists' },
{ status: 400 }
)
}
const [updatedKey] = await db
.update(apiKey)
.set({
name,
updatedAt: new Date(),
})
.where(
and(
eq(apiKey.workspaceId, workspaceId),
eq(apiKey.id, keyId),
eq(apiKey.type, 'workspace')
)
)
.returning({
id: apiKey.id,
name: apiKey.name,
createdAt: apiKey.createdAt,
updatedAt: apiKey.updatedAt,
})
recordAudit({
workspaceId,
actorId: userId,
action: AuditAction.API_KEY_UPDATED,
resourceType: AuditResourceType.API_KEY,
resourceId: keyId,
actorName: session.user.name ?? undefined,
actorEmail: session.user.email ?? undefined,
resourceName: name,
description: `Renamed workspace API key from "${existingKey[0].name}" to "${name}"`,
metadata: {
keyType: 'workspace',
previousName: existingKey[0].name,
newName: name,
},
request,
})
logger.info(`[${requestId}] Updated workspace API key: ${keyId} in workspace ${workspaceId}`)
return NextResponse.json({ key: updatedKey })
} catch (error: unknown) {
logger.error(`[${requestId}] Workspace API key PUT error`, error)
return NextResponse.json(
{ error: getErrorMessage(error, 'Failed to update workspace API key') },
{ status: 500 }
)
}
}
)
export const DELETE = withRouteHandler(
async (request: NextRequest, { params }: { params: Promise<{ id: string; keyId: string }> }) => {
const requestId = generateRequestId()
const { id: workspaceId, keyId } = await params
try {
const session = await getSession()
if (!session?.user?.id) {
logger.warn(`[${requestId}] Unauthorized workspace API key deletion attempt`)
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const userId = session.user.id
const permission = await getUserEntityPermissions(userId, 'workspace', workspaceId)
if (permission !== 'admin') {
return NextResponse.json({ error: 'Forbidden' }, { status: 403 })
}
const deletedRows = await db
.delete(apiKey)
.where(
and(
eq(apiKey.workspaceId, workspaceId),
eq(apiKey.id, keyId),
eq(apiKey.type, 'workspace')
)
)
.returning({ id: apiKey.id, name: apiKey.name, lastUsed: apiKey.lastUsed })
if (deletedRows.length === 0) {
return NextResponse.json({ error: 'API key not found' }, { status: 404 })
}
const deletedKey = deletedRows[0]
captureServerEvent(
userId,
'api_key_revoked',
{ workspace_id: workspaceId, key_name: deletedKey.name },
{ groups: { workspace: workspaceId } }
)
recordAudit({
workspaceId,
actorId: userId,
action: AuditAction.API_KEY_REVOKED,
resourceType: AuditResourceType.API_KEY,
resourceId: keyId,
actorName: session.user.name ?? undefined,
actorEmail: session.user.email ?? undefined,
resourceName: deletedKey.name,
description: `Revoked workspace API key: ${deletedKey.name}`,
metadata: {
keyType: 'workspace',
keyName: deletedKey.name,
lastUsed: deletedKey.lastUsed?.toISOString() ?? null,
},
request,
})
logger.info(
`[${requestId}] Deleted workspace API key: ${keyId} from workspace ${workspaceId}`
)
return NextResponse.json({ success: true })
} catch (error: unknown) {
logger.error(`[${requestId}] Workspace API key DELETE error`, error)
return NextResponse.json(
{ error: getErrorMessage(error, 'Failed to delete workspace API key') },
{ status: 500 }
)
}
}
)