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
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:
@@ -0,0 +1,55 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { getErrorMessage } from '@sim/utils/errors'
|
||||
import { generateId } from '@sim/utils/id'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { awsSecretsManagerCreateSecretContract } from '@/lib/api/contracts/tools/aws/secrets-manager-create-secret'
|
||||
import { parseToolRequest } from '@/lib/api/server'
|
||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
import { createSecret, createSecretsManagerClient } from '../utils'
|
||||
|
||||
const logger = createLogger('SecretsManagerCreateSecretAPI')
|
||||
|
||||
export const POST = withRouteHandler(async (request: NextRequest) => {
|
||||
const requestId = generateId().slice(0, 8)
|
||||
|
||||
const auth = await checkInternalAuth(request)
|
||||
if (!auth.success || !auth.userId) {
|
||||
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
||||
}
|
||||
|
||||
try {
|
||||
const parsed = await parseToolRequest(awsSecretsManagerCreateSecretContract, request, {
|
||||
errorFormat: 'details',
|
||||
logger,
|
||||
})
|
||||
if (!parsed.success) return parsed.response
|
||||
const params = parsed.data.body
|
||||
|
||||
logger.info(`[${requestId}] Creating secret ${params.name}`)
|
||||
|
||||
const client = createSecretsManagerClient({
|
||||
region: params.region,
|
||||
accessKeyId: params.accessKeyId,
|
||||
secretAccessKey: params.secretAccessKey,
|
||||
})
|
||||
|
||||
try {
|
||||
const result = await createSecret(client, params.name, params.secretValue, params.description)
|
||||
|
||||
logger.info(`[${requestId}] Secret created: ${result.name}`)
|
||||
|
||||
return NextResponse.json({
|
||||
message: `Secret "${result.name}" created successfully`,
|
||||
...result,
|
||||
})
|
||||
} finally {
|
||||
client.destroy()
|
||||
}
|
||||
} catch (error) {
|
||||
const errorMessage = getErrorMessage(error, 'Unknown error occurred')
|
||||
logger.error(`[${requestId}] Failed to create secret:`, error)
|
||||
|
||||
return NextResponse.json({ error: `Failed to create secret: ${errorMessage}` }, { status: 500 })
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,61 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { getErrorMessage } from '@sim/utils/errors'
|
||||
import { generateId } from '@sim/utils/id'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { awsSecretsManagerDeleteSecretContract } from '@/lib/api/contracts/tools/aws/secrets-manager-delete-secret'
|
||||
import { parseToolRequest } from '@/lib/api/server'
|
||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
import { createSecretsManagerClient, deleteSecret } from '../utils'
|
||||
|
||||
const logger = createLogger('SecretsManagerDeleteSecretAPI')
|
||||
|
||||
export const POST = withRouteHandler(async (request: NextRequest) => {
|
||||
const requestId = generateId().slice(0, 8)
|
||||
|
||||
const auth = await checkInternalAuth(request)
|
||||
if (!auth.success || !auth.userId) {
|
||||
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
||||
}
|
||||
|
||||
try {
|
||||
const parsed = await parseToolRequest(awsSecretsManagerDeleteSecretContract, request, {
|
||||
errorFormat: 'details',
|
||||
logger,
|
||||
})
|
||||
if (!parsed.success) return parsed.response
|
||||
const params = parsed.data.body
|
||||
|
||||
logger.info(`[${requestId}] Deleting secret ${params.secretId}`)
|
||||
|
||||
const client = createSecretsManagerClient({
|
||||
region: params.region,
|
||||
accessKeyId: params.accessKeyId,
|
||||
secretAccessKey: params.secretAccessKey,
|
||||
})
|
||||
|
||||
try {
|
||||
const result = await deleteSecret(
|
||||
client,
|
||||
params.secretId,
|
||||
params.recoveryWindowInDays,
|
||||
params.forceDelete
|
||||
)
|
||||
|
||||
const action = params.forceDelete ? 'permanently deleted' : 'scheduled for deletion'
|
||||
logger.info(`[${requestId}] Secret ${action}: ${result.name}`)
|
||||
|
||||
return NextResponse.json({
|
||||
message: `Secret "${result.name}" ${action}`,
|
||||
...result,
|
||||
})
|
||||
} finally {
|
||||
client.destroy()
|
||||
}
|
||||
} catch (error) {
|
||||
const errorMessage = getErrorMessage(error, 'Unknown error occurred')
|
||||
logger.error(`[${requestId}] Failed to delete secret:`, error)
|
||||
|
||||
return NextResponse.json({ error: `Failed to delete secret: ${errorMessage}` }, { status: 500 })
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,55 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { getErrorMessage } from '@sim/utils/errors'
|
||||
import { generateId } from '@sim/utils/id'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { awsSecretsManagerDescribeSecretContract } from '@/lib/api/contracts/tools/aws/secrets-manager-describe-secret'
|
||||
import { parseToolRequest } from '@/lib/api/server'
|
||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
import { createSecretsManagerClient, describeSecret } from '../utils'
|
||||
|
||||
const logger = createLogger('SecretsManagerDescribeSecretAPI')
|
||||
|
||||
export const POST = withRouteHandler(async (request: NextRequest) => {
|
||||
const requestId = generateId().slice(0, 8)
|
||||
|
||||
const auth = await checkInternalAuth(request)
|
||||
if (!auth.success || !auth.userId) {
|
||||
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
||||
}
|
||||
|
||||
try {
|
||||
const parsed = await parseToolRequest(awsSecretsManagerDescribeSecretContract, request, {
|
||||
errorFormat: 'details',
|
||||
logger,
|
||||
})
|
||||
if (!parsed.success) return parsed.response
|
||||
const params = parsed.data.body
|
||||
|
||||
logger.info(`[${requestId}] Describing secret ${params.secretId}`)
|
||||
|
||||
const client = createSecretsManagerClient({
|
||||
region: params.region,
|
||||
accessKeyId: params.accessKeyId,
|
||||
secretAccessKey: params.secretAccessKey,
|
||||
})
|
||||
|
||||
try {
|
||||
const result = await describeSecret(client, params.secretId)
|
||||
|
||||
logger.info(`[${requestId}] Described secret: ${result.name}`)
|
||||
|
||||
return NextResponse.json(result)
|
||||
} finally {
|
||||
client.destroy()
|
||||
}
|
||||
} catch (error) {
|
||||
const errorMessage = getErrorMessage(error, 'Unknown error occurred')
|
||||
logger.error(`[${requestId}] Failed to describe secret:`, error)
|
||||
|
||||
return NextResponse.json(
|
||||
{ error: `Failed to describe secret: ${errorMessage}` },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,60 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { getErrorMessage } from '@sim/utils/errors'
|
||||
import { generateId } from '@sim/utils/id'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { awsSecretsManagerGetSecretContract } from '@/lib/api/contracts/tools/aws/secrets-manager-get-secret'
|
||||
import { parseToolRequest } from '@/lib/api/server'
|
||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
import { createSecretsManagerClient, getSecretValue } from '../utils'
|
||||
|
||||
const logger = createLogger('SecretsManagerGetSecretAPI')
|
||||
|
||||
export const POST = withRouteHandler(async (request: NextRequest) => {
|
||||
const requestId = generateId().slice(0, 8)
|
||||
|
||||
const auth = await checkInternalAuth(request)
|
||||
if (!auth.success || !auth.userId) {
|
||||
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
||||
}
|
||||
|
||||
try {
|
||||
const parsed = await parseToolRequest(awsSecretsManagerGetSecretContract, request, {
|
||||
errorFormat: 'details',
|
||||
logger,
|
||||
})
|
||||
if (!parsed.success) return parsed.response
|
||||
const params = parsed.data.body
|
||||
|
||||
logger.info(`[${requestId}] Retrieving secret ${params.secretId}`)
|
||||
|
||||
const client = createSecretsManagerClient({
|
||||
region: params.region,
|
||||
accessKeyId: params.accessKeyId,
|
||||
secretAccessKey: params.secretAccessKey,
|
||||
})
|
||||
|
||||
try {
|
||||
const result = await getSecretValue(
|
||||
client,
|
||||
params.secretId,
|
||||
params.versionId,
|
||||
params.versionStage
|
||||
)
|
||||
|
||||
logger.info(`[${requestId}] Secret retrieved successfully`)
|
||||
|
||||
return NextResponse.json(result)
|
||||
} finally {
|
||||
client.destroy()
|
||||
}
|
||||
} catch (error) {
|
||||
const errorMessage = getErrorMessage(error, 'Unknown error occurred')
|
||||
logger.error(`[${requestId}] Failed to retrieve secret:`, error)
|
||||
|
||||
return NextResponse.json(
|
||||
{ error: `Failed to retrieve secret: ${errorMessage}` },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,52 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { getErrorMessage } from '@sim/utils/errors'
|
||||
import { generateId } from '@sim/utils/id'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { awsSecretsManagerListSecretsContract } from '@/lib/api/contracts/tools/aws/secrets-manager-list-secrets'
|
||||
import { parseToolRequest } from '@/lib/api/server'
|
||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
import { createSecretsManagerClient, listSecrets } from '../utils'
|
||||
|
||||
const logger = createLogger('SecretsManagerListSecretsAPI')
|
||||
|
||||
export const POST = withRouteHandler(async (request: NextRequest) => {
|
||||
const requestId = generateId().slice(0, 8)
|
||||
|
||||
const auth = await checkInternalAuth(request)
|
||||
if (!auth.success || !auth.userId) {
|
||||
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
||||
}
|
||||
|
||||
try {
|
||||
const parsed = await parseToolRequest(awsSecretsManagerListSecretsContract, request, {
|
||||
errorFormat: 'details',
|
||||
logger,
|
||||
})
|
||||
if (!parsed.success) return parsed.response
|
||||
const params = parsed.data.body
|
||||
|
||||
logger.info(`[${requestId}] Listing secrets`)
|
||||
|
||||
const client = createSecretsManagerClient({
|
||||
region: params.region,
|
||||
accessKeyId: params.accessKeyId,
|
||||
secretAccessKey: params.secretAccessKey,
|
||||
})
|
||||
|
||||
try {
|
||||
const result = await listSecrets(client, params.maxResults, params.nextToken)
|
||||
|
||||
logger.info(`[${requestId}] Listed ${result.count} secrets`)
|
||||
|
||||
return NextResponse.json(result)
|
||||
} finally {
|
||||
client.destroy()
|
||||
}
|
||||
} catch (error) {
|
||||
const errorMessage = getErrorMessage(error, 'Unknown error occurred')
|
||||
logger.error(`[${requestId}] Failed to list secrets:`, error)
|
||||
|
||||
return NextResponse.json({ error: `Failed to list secrets: ${errorMessage}` }, { status: 500 })
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,58 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { getErrorMessage } from '@sim/utils/errors'
|
||||
import { generateId } from '@sim/utils/id'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { awsSecretsManagerRestoreSecretContract } from '@/lib/api/contracts/tools/aws/secrets-manager-restore-secret'
|
||||
import { parseToolRequest } from '@/lib/api/server'
|
||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
import { createSecretsManagerClient, restoreSecret } from '../utils'
|
||||
|
||||
const logger = createLogger('SecretsManagerRestoreSecretAPI')
|
||||
|
||||
export const POST = withRouteHandler(async (request: NextRequest) => {
|
||||
const requestId = generateId().slice(0, 8)
|
||||
|
||||
const auth = await checkInternalAuth(request)
|
||||
if (!auth.success || !auth.userId) {
|
||||
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
||||
}
|
||||
|
||||
try {
|
||||
const parsed = await parseToolRequest(awsSecretsManagerRestoreSecretContract, request, {
|
||||
errorFormat: 'details',
|
||||
logger,
|
||||
})
|
||||
if (!parsed.success) return parsed.response
|
||||
const params = parsed.data.body
|
||||
|
||||
logger.info(`[${requestId}] Restoring secret ${params.secretId}`)
|
||||
|
||||
const client = createSecretsManagerClient({
|
||||
region: params.region,
|
||||
accessKeyId: params.accessKeyId,
|
||||
secretAccessKey: params.secretAccessKey,
|
||||
})
|
||||
|
||||
try {
|
||||
const result = await restoreSecret(client, params.secretId)
|
||||
|
||||
logger.info(`[${requestId}] Restored secret: ${result.name}`)
|
||||
|
||||
return NextResponse.json({
|
||||
message: `Secret "${result.name}" restored successfully`,
|
||||
...result,
|
||||
})
|
||||
} finally {
|
||||
client.destroy()
|
||||
}
|
||||
} catch (error) {
|
||||
const errorMessage = getErrorMessage(error, 'Unknown error occurred')
|
||||
logger.error(`[${requestId}] Failed to restore secret:`, error)
|
||||
|
||||
return NextResponse.json(
|
||||
{ error: `Failed to restore secret: ${errorMessage}` },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,66 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { getErrorMessage } from '@sim/utils/errors'
|
||||
import { generateId } from '@sim/utils/id'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { awsSecretsManagerRotateSecretContract } from '@/lib/api/contracts/tools/aws/secrets-manager-rotate-secret'
|
||||
import { parseToolRequest } from '@/lib/api/server'
|
||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
import { createSecretsManagerClient, rotateSecret } from '../utils'
|
||||
|
||||
const logger = createLogger('SecretsManagerRotateSecretAPI')
|
||||
|
||||
export const POST = withRouteHandler(async (request: NextRequest) => {
|
||||
const requestId = generateId().slice(0, 8)
|
||||
|
||||
const auth = await checkInternalAuth(request)
|
||||
if (!auth.success || !auth.userId) {
|
||||
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
||||
}
|
||||
|
||||
try {
|
||||
const parsed = await parseToolRequest(awsSecretsManagerRotateSecretContract, request, {
|
||||
errorFormat: 'details',
|
||||
logger,
|
||||
})
|
||||
if (!parsed.success) return parsed.response
|
||||
const params = parsed.data.body
|
||||
|
||||
logger.info(`[${requestId}] Rotating secret ${params.secretId}`)
|
||||
|
||||
const client = createSecretsManagerClient({
|
||||
region: params.region,
|
||||
accessKeyId: params.accessKeyId,
|
||||
secretAccessKey: params.secretAccessKey,
|
||||
})
|
||||
|
||||
try {
|
||||
const result = await rotateSecret(
|
||||
client,
|
||||
params.secretId,
|
||||
params.clientRequestToken,
|
||||
params.rotationLambdaARN,
|
||||
{
|
||||
automaticallyAfterDays: params.automaticallyAfterDays,
|
||||
duration: params.duration,
|
||||
scheduleExpression: params.scheduleExpression,
|
||||
},
|
||||
params.rotateImmediately
|
||||
)
|
||||
|
||||
logger.info(`[${requestId}] Rotation started for secret: ${result.name}`)
|
||||
|
||||
return NextResponse.json({
|
||||
message: `Rotation started for secret "${result.name}"`,
|
||||
...result,
|
||||
})
|
||||
} finally {
|
||||
client.destroy()
|
||||
}
|
||||
} catch (error) {
|
||||
const errorMessage = getErrorMessage(error, 'Unknown error occurred')
|
||||
logger.error(`[${requestId}] Failed to rotate secret:`, error)
|
||||
|
||||
return NextResponse.json({ error: `Failed to rotate secret: ${errorMessage}` }, { status: 500 })
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,59 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { getErrorMessage } from '@sim/utils/errors'
|
||||
import { generateId } from '@sim/utils/id'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { awsSecretsManagerTagResourceContract } from '@/lib/api/contracts/tools/aws/secrets-manager-tag-resource'
|
||||
import { parseToolRequest } from '@/lib/api/server'
|
||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
import { createSecretsManagerClient, tagResource } from '../utils'
|
||||
|
||||
const logger = createLogger('SecretsManagerTagResourceAPI')
|
||||
|
||||
export const POST = withRouteHandler(async (request: NextRequest) => {
|
||||
const requestId = generateId().slice(0, 8)
|
||||
|
||||
const auth = await checkInternalAuth(request)
|
||||
if (!auth.success || !auth.userId) {
|
||||
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
||||
}
|
||||
|
||||
try {
|
||||
const parsed = await parseToolRequest(awsSecretsManagerTagResourceContract, request, {
|
||||
errorFormat: 'details',
|
||||
logger,
|
||||
})
|
||||
if (!parsed.success) return parsed.response
|
||||
const params = parsed.data.body
|
||||
|
||||
logger.info(`[${requestId}] Tagging secret ${params.secretId}`)
|
||||
|
||||
const client = createSecretsManagerClient({
|
||||
region: params.region,
|
||||
accessKeyId: params.accessKeyId,
|
||||
secretAccessKey: params.secretAccessKey,
|
||||
})
|
||||
|
||||
try {
|
||||
const result = await tagResource(
|
||||
client,
|
||||
params.secretId,
|
||||
params.tags.map((t) => ({ Key: t.key, Value: t.value }))
|
||||
)
|
||||
|
||||
logger.info(`[${requestId}] Tagged secret: ${result.name}`)
|
||||
|
||||
return NextResponse.json({
|
||||
message: `Secret "${result.name}" tagged successfully`,
|
||||
...result,
|
||||
})
|
||||
} finally {
|
||||
client.destroy()
|
||||
}
|
||||
} catch (error) {
|
||||
const errorMessage = getErrorMessage(error, 'Unknown error occurred')
|
||||
logger.error(`[${requestId}] Failed to tag secret:`, error)
|
||||
|
||||
return NextResponse.json({ error: `Failed to tag secret: ${errorMessage}` }, { status: 500 })
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,55 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { getErrorMessage } from '@sim/utils/errors'
|
||||
import { generateId } from '@sim/utils/id'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { awsSecretsManagerUntagResourceContract } from '@/lib/api/contracts/tools/aws/secrets-manager-untag-resource'
|
||||
import { parseToolRequest } from '@/lib/api/server'
|
||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
import { createSecretsManagerClient, untagResource } from '../utils'
|
||||
|
||||
const logger = createLogger('SecretsManagerUntagResourceAPI')
|
||||
|
||||
export const POST = withRouteHandler(async (request: NextRequest) => {
|
||||
const requestId = generateId().slice(0, 8)
|
||||
|
||||
const auth = await checkInternalAuth(request)
|
||||
if (!auth.success || !auth.userId) {
|
||||
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
||||
}
|
||||
|
||||
try {
|
||||
const parsed = await parseToolRequest(awsSecretsManagerUntagResourceContract, request, {
|
||||
errorFormat: 'details',
|
||||
logger,
|
||||
})
|
||||
if (!parsed.success) return parsed.response
|
||||
const params = parsed.data.body
|
||||
|
||||
logger.info(`[${requestId}] Untagging secret ${params.secretId}`)
|
||||
|
||||
const client = createSecretsManagerClient({
|
||||
region: params.region,
|
||||
accessKeyId: params.accessKeyId,
|
||||
secretAccessKey: params.secretAccessKey,
|
||||
})
|
||||
|
||||
try {
|
||||
const result = await untagResource(client, params.secretId, params.tagKeys)
|
||||
|
||||
logger.info(`[${requestId}] Untagged secret: ${result.name}`)
|
||||
|
||||
return NextResponse.json({
|
||||
message: `Secret "${result.name}" untagged successfully`,
|
||||
...result,
|
||||
})
|
||||
} finally {
|
||||
client.destroy()
|
||||
}
|
||||
} catch (error) {
|
||||
const errorMessage = getErrorMessage(error, 'Unknown error occurred')
|
||||
logger.error(`[${requestId}] Failed to untag secret:`, error)
|
||||
|
||||
return NextResponse.json({ error: `Failed to untag secret: ${errorMessage}` }, { status: 500 })
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,60 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { getErrorMessage } from '@sim/utils/errors'
|
||||
import { generateId } from '@sim/utils/id'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { awsSecretsManagerUpdateSecretContract } from '@/lib/api/contracts/tools/aws/secrets-manager-update-secret'
|
||||
import { parseToolRequest } from '@/lib/api/server'
|
||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
import { createSecretsManagerClient, updateSecretValue } from '../utils'
|
||||
|
||||
const logger = createLogger('SecretsManagerUpdateSecretAPI')
|
||||
|
||||
export const POST = withRouteHandler(async (request: NextRequest) => {
|
||||
const requestId = generateId().slice(0, 8)
|
||||
|
||||
const auth = await checkInternalAuth(request)
|
||||
if (!auth.success || !auth.userId) {
|
||||
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
||||
}
|
||||
|
||||
try {
|
||||
const parsed = await parseToolRequest(awsSecretsManagerUpdateSecretContract, request, {
|
||||
errorFormat: 'details',
|
||||
logger,
|
||||
})
|
||||
if (!parsed.success) return parsed.response
|
||||
const params = parsed.data.body
|
||||
|
||||
logger.info(`[${requestId}] Updating secret ${params.secretId}`)
|
||||
|
||||
const client = createSecretsManagerClient({
|
||||
region: params.region,
|
||||
accessKeyId: params.accessKeyId,
|
||||
secretAccessKey: params.secretAccessKey,
|
||||
})
|
||||
|
||||
try {
|
||||
const result = await updateSecretValue(
|
||||
client,
|
||||
params.secretId,
|
||||
params.secretValue,
|
||||
params.description
|
||||
)
|
||||
|
||||
logger.info(`[${requestId}] Secret updated: ${result.name}`)
|
||||
|
||||
return NextResponse.json({
|
||||
message: `Secret "${result.name}" updated successfully`,
|
||||
...result,
|
||||
})
|
||||
} finally {
|
||||
client.destroy()
|
||||
}
|
||||
} catch (error) {
|
||||
const errorMessage = getErrorMessage(error, 'Unknown error occurred')
|
||||
logger.error(`[${requestId}] Failed to update secret:`, error)
|
||||
|
||||
return NextResponse.json({ error: `Failed to update secret: ${errorMessage}` }, { status: 500 })
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,266 @@
|
||||
import type { RotationRulesType, SecretListEntry, Tag } from '@aws-sdk/client-secrets-manager'
|
||||
import {
|
||||
CreateSecretCommand,
|
||||
DeleteSecretCommand,
|
||||
DescribeSecretCommand,
|
||||
GetSecretValueCommand,
|
||||
ListSecretsCommand,
|
||||
RestoreSecretCommand,
|
||||
RotateSecretCommand,
|
||||
SecretsManagerClient,
|
||||
TagResourceCommand,
|
||||
UntagResourceCommand,
|
||||
UpdateSecretCommand,
|
||||
} from '@aws-sdk/client-secrets-manager'
|
||||
import type { SecretsManagerConnectionConfig } from '@/tools/secrets_manager/types'
|
||||
|
||||
function mapRotationRules(rules: RotationRulesType | undefined) {
|
||||
if (!rules) return null
|
||||
return {
|
||||
automaticallyAfterDays: rules.AutomaticallyAfterDays ?? null,
|
||||
duration: rules.Duration ?? null,
|
||||
scheduleExpression: rules.ScheduleExpression ?? null,
|
||||
}
|
||||
}
|
||||
|
||||
export function createSecretsManagerClient(
|
||||
config: SecretsManagerConnectionConfig
|
||||
): SecretsManagerClient {
|
||||
return new SecretsManagerClient({
|
||||
region: config.region,
|
||||
credentials: {
|
||||
accessKeyId: config.accessKeyId,
|
||||
secretAccessKey: config.secretAccessKey,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export async function getSecretValue(
|
||||
client: SecretsManagerClient,
|
||||
secretId: string,
|
||||
versionId?: string | null,
|
||||
versionStage?: string | null
|
||||
) {
|
||||
const command = new GetSecretValueCommand({
|
||||
SecretId: secretId,
|
||||
...(versionId ? { VersionId: versionId } : {}),
|
||||
...(versionStage ? { VersionStage: versionStage } : {}),
|
||||
})
|
||||
|
||||
const response = await client.send(command)
|
||||
|
||||
if (!response.SecretString && response.SecretBinary) {
|
||||
throw new Error(
|
||||
'Secret is stored as binary (SecretBinary). This integration only supports string secrets.'
|
||||
)
|
||||
}
|
||||
|
||||
return {
|
||||
name: response.Name ?? '',
|
||||
secretValue: response.SecretString ?? '',
|
||||
arn: response.ARN ?? '',
|
||||
versionId: response.VersionId ?? '',
|
||||
versionStages: response.VersionStages ?? [],
|
||||
createdDate: response.CreatedDate?.toISOString() ?? null,
|
||||
}
|
||||
}
|
||||
|
||||
export async function listSecrets(
|
||||
client: SecretsManagerClient,
|
||||
maxResults?: number | null,
|
||||
nextToken?: string | null
|
||||
) {
|
||||
const command = new ListSecretsCommand({
|
||||
...(maxResults ? { MaxResults: maxResults } : {}),
|
||||
...(nextToken ? { NextToken: nextToken } : {}),
|
||||
})
|
||||
|
||||
const response = await client.send(command)
|
||||
const secrets = (response.SecretList ?? []).map((secret: SecretListEntry) => ({
|
||||
name: secret.Name ?? '',
|
||||
arn: secret.ARN ?? '',
|
||||
description: secret.Description ?? null,
|
||||
createdDate: secret.CreatedDate?.toISOString() ?? null,
|
||||
lastChangedDate: secret.LastChangedDate?.toISOString() ?? null,
|
||||
lastAccessedDate: secret.LastAccessedDate?.toISOString() ?? null,
|
||||
rotationEnabled: secret.RotationEnabled ?? false,
|
||||
tags: secret.Tags?.map((t: Tag) => ({ key: t.Key ?? '', value: t.Value ?? '' })) ?? [],
|
||||
rotationRules: mapRotationRules(secret.RotationRules),
|
||||
lastRotatedDate: secret.LastRotatedDate?.toISOString() ?? null,
|
||||
nextRotationDate: secret.NextRotationDate?.toISOString() ?? null,
|
||||
deletedDate: secret.DeletedDate?.toISOString() ?? null,
|
||||
secretVersionsToStages: secret.SecretVersionsToStages ?? null,
|
||||
}))
|
||||
|
||||
return {
|
||||
secrets,
|
||||
nextToken: response.NextToken ?? null,
|
||||
count: secrets.length,
|
||||
}
|
||||
}
|
||||
|
||||
export async function createSecret(
|
||||
client: SecretsManagerClient,
|
||||
name: string,
|
||||
secretValue: string,
|
||||
description?: string | null
|
||||
) {
|
||||
const command = new CreateSecretCommand({
|
||||
Name: name,
|
||||
SecretString: secretValue,
|
||||
...(description ? { Description: description } : {}),
|
||||
})
|
||||
|
||||
const response = await client.send(command)
|
||||
return {
|
||||
name: response.Name ?? '',
|
||||
arn: response.ARN ?? '',
|
||||
versionId: response.VersionId ?? '',
|
||||
}
|
||||
}
|
||||
|
||||
export async function updateSecretValue(
|
||||
client: SecretsManagerClient,
|
||||
secretId: string,
|
||||
secretValue: string,
|
||||
description?: string | null
|
||||
) {
|
||||
const command = new UpdateSecretCommand({
|
||||
SecretId: secretId,
|
||||
SecretString: secretValue,
|
||||
...(description ? { Description: description } : {}),
|
||||
})
|
||||
|
||||
const response = await client.send(command)
|
||||
return {
|
||||
name: response.Name ?? '',
|
||||
arn: response.ARN ?? '',
|
||||
versionId: response.VersionId ?? '',
|
||||
}
|
||||
}
|
||||
|
||||
export async function deleteSecret(
|
||||
client: SecretsManagerClient,
|
||||
secretId: string,
|
||||
recoveryWindowInDays?: number | null,
|
||||
forceDelete?: boolean | null
|
||||
) {
|
||||
const command = new DeleteSecretCommand({
|
||||
SecretId: secretId,
|
||||
...(forceDelete ? { ForceDeleteWithoutRecovery: true } : {}),
|
||||
...(!forceDelete && recoveryWindowInDays ? { RecoveryWindowInDays: recoveryWindowInDays } : {}),
|
||||
})
|
||||
|
||||
const response = await client.send(command)
|
||||
return {
|
||||
name: response.Name ?? '',
|
||||
arn: response.ARN ?? '',
|
||||
deletionDate: response.DeletionDate?.toISOString() ?? null,
|
||||
}
|
||||
}
|
||||
|
||||
export async function describeSecret(client: SecretsManagerClient, secretId: string) {
|
||||
const command = new DescribeSecretCommand({ SecretId: secretId })
|
||||
const response = await client.send(command)
|
||||
|
||||
return {
|
||||
name: response.Name ?? '',
|
||||
arn: response.ARN ?? '',
|
||||
description: response.Description ?? null,
|
||||
kmsKeyId: response.KmsKeyId ?? null,
|
||||
rotationEnabled: response.RotationEnabled ?? false,
|
||||
rotationLambdaARN: response.RotationLambdaARN ?? null,
|
||||
rotationRules: mapRotationRules(response.RotationRules),
|
||||
lastRotatedDate: response.LastRotatedDate?.toISOString() ?? null,
|
||||
lastChangedDate: response.LastChangedDate?.toISOString() ?? null,
|
||||
lastAccessedDate: response.LastAccessedDate?.toISOString() ?? null,
|
||||
deletedDate: response.DeletedDate?.toISOString() ?? null,
|
||||
nextRotationDate: response.NextRotationDate?.toISOString() ?? null,
|
||||
tags: response.Tags?.map((t: Tag) => ({ key: t.Key ?? '', value: t.Value ?? '' })) ?? [],
|
||||
versionIdsToStages: response.VersionIdsToStages ?? null,
|
||||
owningService: response.OwningService ?? null,
|
||||
createdDate: response.CreatedDate?.toISOString() ?? null,
|
||||
primaryRegion: response.PrimaryRegion ?? null,
|
||||
replicationStatus:
|
||||
response.ReplicationStatus?.map((r) => ({
|
||||
region: r.Region ?? '',
|
||||
kmsKeyId: r.KmsKeyId ?? null,
|
||||
status: r.Status ?? null,
|
||||
statusMessage: r.StatusMessage ?? null,
|
||||
lastAccessedDate: r.LastAccessedDate?.toISOString() ?? null,
|
||||
})) ?? [],
|
||||
}
|
||||
}
|
||||
|
||||
export async function tagResource(client: SecretsManagerClient, secretId: string, tags: Tag[]) {
|
||||
const command = new TagResourceCommand({ SecretId: secretId, Tags: tags })
|
||||
await client.send(command)
|
||||
return { name: secretId }
|
||||
}
|
||||
|
||||
export async function untagResource(
|
||||
client: SecretsManagerClient,
|
||||
secretId: string,
|
||||
tagKeys: string[]
|
||||
) {
|
||||
const command = new UntagResourceCommand({ SecretId: secretId, TagKeys: tagKeys })
|
||||
await client.send(command)
|
||||
return { name: secretId }
|
||||
}
|
||||
|
||||
export async function restoreSecret(client: SecretsManagerClient, secretId: string) {
|
||||
const command = new RestoreSecretCommand({ SecretId: secretId })
|
||||
const response = await client.send(command)
|
||||
return {
|
||||
name: response.Name ?? '',
|
||||
arn: response.ARN ?? '',
|
||||
}
|
||||
}
|
||||
|
||||
export async function rotateSecret(
|
||||
client: SecretsManagerClient,
|
||||
secretId: string,
|
||||
clientRequestToken?: string | null,
|
||||
rotationLambdaARN?: string | null,
|
||||
rotationRules?: {
|
||||
automaticallyAfterDays?: number | null
|
||||
duration?: string | null
|
||||
scheduleExpression?: string | null
|
||||
} | null,
|
||||
rotateImmediately?: boolean | null
|
||||
) {
|
||||
const hasRotationRules = Boolean(
|
||||
rotationRules?.automaticallyAfterDays ||
|
||||
rotationRules?.duration ||
|
||||
rotationRules?.scheduleExpression
|
||||
)
|
||||
|
||||
const command = new RotateSecretCommand({
|
||||
SecretId: secretId,
|
||||
...(clientRequestToken ? { ClientRequestToken: clientRequestToken } : {}),
|
||||
...(rotationLambdaARN ? { RotationLambdaARN: rotationLambdaARN } : {}),
|
||||
...(hasRotationRules
|
||||
? {
|
||||
RotationRules: {
|
||||
...(rotationRules?.automaticallyAfterDays
|
||||
? { AutomaticallyAfterDays: rotationRules.automaticallyAfterDays }
|
||||
: {}),
|
||||
...(rotationRules?.duration ? { Duration: rotationRules.duration } : {}),
|
||||
...(rotationRules?.scheduleExpression
|
||||
? { ScheduleExpression: rotationRules.scheduleExpression }
|
||||
: {}),
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
...(rotateImmediately === undefined || rotateImmediately === null
|
||||
? {}
|
||||
: { RotateImmediately: rotateImmediately }),
|
||||
})
|
||||
|
||||
const response = await client.send(command)
|
||||
return {
|
||||
name: response.Name ?? '',
|
||||
arn: response.ARN ?? '',
|
||||
versionId: response.VersionId ?? '',
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user