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,319 @@
|
||||
import { z } from 'zod'
|
||||
import {
|
||||
introspectionResponseSchema,
|
||||
nonEmptyRecordSchema,
|
||||
sqlRowsResponseSchema,
|
||||
} from '@/lib/api/contracts/tools/databases/shared'
|
||||
import {
|
||||
type ContractBodyInput,
|
||||
type ContractJsonResponse,
|
||||
defineRouteContract,
|
||||
} from '@/lib/api/contracts/types'
|
||||
|
||||
const secureFlagSchema = z
|
||||
.union([z.boolean(), z.string()])
|
||||
.transform((value) => (typeof value === 'string' ? value.toLowerCase() === 'true' : value))
|
||||
.default(true)
|
||||
|
||||
export const clickhouseConnectionBodySchema = z.object({
|
||||
host: z.string().min(1, 'Host is required'),
|
||||
port: z.coerce.number().int().positive('Port must be a positive integer'),
|
||||
database: z.string().min(1, 'Database name is required'),
|
||||
username: z.string().min(1, 'Username is required'),
|
||||
password: z.string().default(''),
|
||||
secure: secureFlagSchema,
|
||||
})
|
||||
|
||||
export const clickhouseQueryBodySchema = clickhouseConnectionBodySchema.extend({
|
||||
query: z.string().min(1, 'Query is required'),
|
||||
})
|
||||
|
||||
export const clickhouseExecuteBodySchema = clickhouseQueryBodySchema
|
||||
|
||||
export const clickhouseInsertBodySchema = clickhouseConnectionBodySchema.extend({
|
||||
table: z.string().min(1, 'Table name is required'),
|
||||
data: nonEmptyRecordSchema('Data object cannot be empty'),
|
||||
})
|
||||
|
||||
export const clickhouseUpdateBodySchema = clickhouseConnectionBodySchema.extend({
|
||||
table: z.string().min(1, 'Table name is required'),
|
||||
data: nonEmptyRecordSchema('Data object cannot be empty'),
|
||||
where: z.string().min(1, 'WHERE clause is required'),
|
||||
})
|
||||
|
||||
export const clickhouseDeleteBodySchema = clickhouseConnectionBodySchema.extend({
|
||||
table: z.string().min(1, 'Table name is required'),
|
||||
where: z.string().min(1, 'WHERE clause is required'),
|
||||
})
|
||||
|
||||
export const clickhouseIntrospectBodySchema = clickhouseConnectionBodySchema
|
||||
|
||||
export const clickhouseQueryContract = defineRouteContract({
|
||||
method: 'POST',
|
||||
path: '/api/tools/clickhouse/query',
|
||||
body: clickhouseQueryBodySchema,
|
||||
response: { mode: 'json', schema: sqlRowsResponseSchema },
|
||||
})
|
||||
|
||||
export const clickhouseExecuteContract = defineRouteContract({
|
||||
method: 'POST',
|
||||
path: '/api/tools/clickhouse/execute',
|
||||
body: clickhouseExecuteBodySchema,
|
||||
response: { mode: 'json', schema: sqlRowsResponseSchema },
|
||||
})
|
||||
|
||||
export const clickhouseInsertContract = defineRouteContract({
|
||||
method: 'POST',
|
||||
path: '/api/tools/clickhouse/insert',
|
||||
body: clickhouseInsertBodySchema,
|
||||
response: { mode: 'json', schema: sqlRowsResponseSchema },
|
||||
})
|
||||
|
||||
export const clickhouseUpdateContract = defineRouteContract({
|
||||
method: 'POST',
|
||||
path: '/api/tools/clickhouse/update',
|
||||
body: clickhouseUpdateBodySchema,
|
||||
response: { mode: 'json', schema: sqlRowsResponseSchema },
|
||||
})
|
||||
|
||||
export const clickhouseDeleteContract = defineRouteContract({
|
||||
method: 'POST',
|
||||
path: '/api/tools/clickhouse/delete',
|
||||
body: clickhouseDeleteBodySchema,
|
||||
response: { mode: 'json', schema: sqlRowsResponseSchema },
|
||||
})
|
||||
|
||||
export const clickhouseIntrospectContract = defineRouteContract({
|
||||
method: 'POST',
|
||||
path: '/api/tools/clickhouse/introspect',
|
||||
body: clickhouseIntrospectBodySchema,
|
||||
response: { mode: 'json', schema: introspectionResponseSchema },
|
||||
})
|
||||
|
||||
const clickhouseTableBodySchema = clickhouseConnectionBodySchema.extend({
|
||||
table: z.string().min(1, 'Table name is required'),
|
||||
})
|
||||
|
||||
const clickhouseCountResponseSchema = z.object({
|
||||
message: z.string(),
|
||||
count: z.number(),
|
||||
})
|
||||
|
||||
const clickhouseDdlResponseSchema = z.object({
|
||||
message: z.string(),
|
||||
ddl: z.string(),
|
||||
})
|
||||
|
||||
export const clickhouseListDatabasesBodySchema = clickhouseConnectionBodySchema
|
||||
export const clickhouseListTablesBodySchema = clickhouseConnectionBodySchema
|
||||
export const clickhouseDescribeTableBodySchema = clickhouseTableBodySchema
|
||||
export const clickhouseShowCreateTableBodySchema = clickhouseTableBodySchema
|
||||
export const clickhouseCountRowsBodySchema = clickhouseTableBodySchema.extend({
|
||||
where: z.string().optional(),
|
||||
})
|
||||
export const clickhouseListPartitionsBodySchema = clickhouseTableBodySchema
|
||||
export const clickhouseListMutationsBodySchema = clickhouseConnectionBodySchema.extend({
|
||||
table: z.string().optional(),
|
||||
onlyRunning: z
|
||||
.union([z.boolean(), z.string()])
|
||||
.transform((value) => (typeof value === 'string' ? value.toLowerCase() === 'true' : value))
|
||||
.default(false),
|
||||
})
|
||||
export const clickhouseListRunningQueriesBodySchema = clickhouseConnectionBodySchema
|
||||
export const clickhouseTableStatsBodySchema = clickhouseConnectionBodySchema.extend({
|
||||
table: z.string().optional(),
|
||||
})
|
||||
export const clickhouseListClustersBodySchema = clickhouseConnectionBodySchema
|
||||
export const clickhouseCreateDatabaseBodySchema = clickhouseConnectionBodySchema.extend({
|
||||
name: z.string().min(1, 'Database name is required'),
|
||||
})
|
||||
export const clickhouseDropDatabaseBodySchema = clickhouseConnectionBodySchema.extend({
|
||||
name: z.string().min(1, 'Database name is required'),
|
||||
})
|
||||
export const clickhouseCreateTableBodySchema = clickhouseConnectionBodySchema.extend({
|
||||
table: z.string().min(1, 'Table name is required'),
|
||||
columns: z
|
||||
.array(
|
||||
z.object({
|
||||
name: z.string().min(1, 'Column name is required'),
|
||||
type: z.string().min(1, 'Column type is required'),
|
||||
})
|
||||
)
|
||||
.min(1, 'At least one column is required'),
|
||||
engine: z.string().min(1).default('MergeTree'),
|
||||
orderBy: z.string().min(1, 'ORDER BY expression is required'),
|
||||
partitionBy: z.string().optional(),
|
||||
})
|
||||
export const clickhouseDropTableBodySchema = clickhouseTableBodySchema
|
||||
export const clickhouseTruncateTableBodySchema = clickhouseTableBodySchema
|
||||
export const clickhouseRenameTableBodySchema = clickhouseTableBodySchema.extend({
|
||||
newTable: z.string().min(1, 'New table name is required'),
|
||||
})
|
||||
export const clickhouseOptimizeTableBodySchema = clickhouseTableBodySchema.extend({
|
||||
final: z
|
||||
.union([z.boolean(), z.string()])
|
||||
.transform((value) => (typeof value === 'string' ? value.toLowerCase() === 'true' : value))
|
||||
.default(false),
|
||||
})
|
||||
export const clickhouseDropPartitionBodySchema = clickhouseTableBodySchema.extend({
|
||||
partition: z.string().min(1, 'Partition expression is required'),
|
||||
})
|
||||
export const clickhouseKillQueryBodySchema = clickhouseConnectionBodySchema.extend({
|
||||
queryId: z.string().min(1, 'Query ID is required'),
|
||||
})
|
||||
export const clickhouseInsertRowsBodySchema = clickhouseTableBodySchema.extend({
|
||||
rows: z.array(z.record(z.string(), z.unknown())).min(1, 'At least one row is required'),
|
||||
})
|
||||
|
||||
export const clickhouseListDatabasesContract = defineRouteContract({
|
||||
method: 'POST',
|
||||
path: '/api/tools/clickhouse/list-databases',
|
||||
body: clickhouseListDatabasesBodySchema,
|
||||
response: { mode: 'json', schema: sqlRowsResponseSchema },
|
||||
})
|
||||
|
||||
export const clickhouseListTablesContract = defineRouteContract({
|
||||
method: 'POST',
|
||||
path: '/api/tools/clickhouse/list-tables',
|
||||
body: clickhouseListTablesBodySchema,
|
||||
response: { mode: 'json', schema: sqlRowsResponseSchema },
|
||||
})
|
||||
|
||||
export const clickhouseDescribeTableContract = defineRouteContract({
|
||||
method: 'POST',
|
||||
path: '/api/tools/clickhouse/describe-table',
|
||||
body: clickhouseDescribeTableBodySchema,
|
||||
response: { mode: 'json', schema: sqlRowsResponseSchema },
|
||||
})
|
||||
|
||||
export const clickhouseShowCreateTableContract = defineRouteContract({
|
||||
method: 'POST',
|
||||
path: '/api/tools/clickhouse/show-create-table',
|
||||
body: clickhouseShowCreateTableBodySchema,
|
||||
response: { mode: 'json', schema: clickhouseDdlResponseSchema },
|
||||
})
|
||||
|
||||
export const clickhouseCountRowsContract = defineRouteContract({
|
||||
method: 'POST',
|
||||
path: '/api/tools/clickhouse/count-rows',
|
||||
body: clickhouseCountRowsBodySchema,
|
||||
response: { mode: 'json', schema: clickhouseCountResponseSchema },
|
||||
})
|
||||
|
||||
export const clickhouseListPartitionsContract = defineRouteContract({
|
||||
method: 'POST',
|
||||
path: '/api/tools/clickhouse/list-partitions',
|
||||
body: clickhouseListPartitionsBodySchema,
|
||||
response: { mode: 'json', schema: sqlRowsResponseSchema },
|
||||
})
|
||||
|
||||
export const clickhouseListMutationsContract = defineRouteContract({
|
||||
method: 'POST',
|
||||
path: '/api/tools/clickhouse/list-mutations',
|
||||
body: clickhouseListMutationsBodySchema,
|
||||
response: { mode: 'json', schema: sqlRowsResponseSchema },
|
||||
})
|
||||
|
||||
export const clickhouseListRunningQueriesContract = defineRouteContract({
|
||||
method: 'POST',
|
||||
path: '/api/tools/clickhouse/list-running-queries',
|
||||
body: clickhouseListRunningQueriesBodySchema,
|
||||
response: { mode: 'json', schema: sqlRowsResponseSchema },
|
||||
})
|
||||
|
||||
export const clickhouseTableStatsContract = defineRouteContract({
|
||||
method: 'POST',
|
||||
path: '/api/tools/clickhouse/table-stats',
|
||||
body: clickhouseTableStatsBodySchema,
|
||||
response: { mode: 'json', schema: sqlRowsResponseSchema },
|
||||
})
|
||||
|
||||
export const clickhouseListClustersContract = defineRouteContract({
|
||||
method: 'POST',
|
||||
path: '/api/tools/clickhouse/list-clusters',
|
||||
body: clickhouseListClustersBodySchema,
|
||||
response: { mode: 'json', schema: sqlRowsResponseSchema },
|
||||
})
|
||||
|
||||
export const clickhouseCreateDatabaseContract = defineRouteContract({
|
||||
method: 'POST',
|
||||
path: '/api/tools/clickhouse/create-database',
|
||||
body: clickhouseCreateDatabaseBodySchema,
|
||||
response: { mode: 'json', schema: sqlRowsResponseSchema },
|
||||
})
|
||||
|
||||
export const clickhouseDropDatabaseContract = defineRouteContract({
|
||||
method: 'POST',
|
||||
path: '/api/tools/clickhouse/drop-database',
|
||||
body: clickhouseDropDatabaseBodySchema,
|
||||
response: { mode: 'json', schema: sqlRowsResponseSchema },
|
||||
})
|
||||
|
||||
export const clickhouseCreateTableContract = defineRouteContract({
|
||||
method: 'POST',
|
||||
path: '/api/tools/clickhouse/create-table',
|
||||
body: clickhouseCreateTableBodySchema,
|
||||
response: { mode: 'json', schema: sqlRowsResponseSchema },
|
||||
})
|
||||
|
||||
export const clickhouseDropTableContract = defineRouteContract({
|
||||
method: 'POST',
|
||||
path: '/api/tools/clickhouse/drop-table',
|
||||
body: clickhouseDropTableBodySchema,
|
||||
response: { mode: 'json', schema: sqlRowsResponseSchema },
|
||||
})
|
||||
|
||||
export const clickhouseTruncateTableContract = defineRouteContract({
|
||||
method: 'POST',
|
||||
path: '/api/tools/clickhouse/truncate-table',
|
||||
body: clickhouseTruncateTableBodySchema,
|
||||
response: { mode: 'json', schema: sqlRowsResponseSchema },
|
||||
})
|
||||
|
||||
export const clickhouseRenameTableContract = defineRouteContract({
|
||||
method: 'POST',
|
||||
path: '/api/tools/clickhouse/rename-table',
|
||||
body: clickhouseRenameTableBodySchema,
|
||||
response: { mode: 'json', schema: sqlRowsResponseSchema },
|
||||
})
|
||||
|
||||
export const clickhouseOptimizeTableContract = defineRouteContract({
|
||||
method: 'POST',
|
||||
path: '/api/tools/clickhouse/optimize-table',
|
||||
body: clickhouseOptimizeTableBodySchema,
|
||||
response: { mode: 'json', schema: sqlRowsResponseSchema },
|
||||
})
|
||||
|
||||
export const clickhouseDropPartitionContract = defineRouteContract({
|
||||
method: 'POST',
|
||||
path: '/api/tools/clickhouse/drop-partition',
|
||||
body: clickhouseDropPartitionBodySchema,
|
||||
response: { mode: 'json', schema: sqlRowsResponseSchema },
|
||||
})
|
||||
|
||||
export const clickhouseKillQueryContract = defineRouteContract({
|
||||
method: 'POST',
|
||||
path: '/api/tools/clickhouse/kill-query',
|
||||
body: clickhouseKillQueryBodySchema,
|
||||
response: { mode: 'json', schema: sqlRowsResponseSchema },
|
||||
})
|
||||
|
||||
export const clickhouseInsertRowsContract = defineRouteContract({
|
||||
method: 'POST',
|
||||
path: '/api/tools/clickhouse/insert-rows',
|
||||
body: clickhouseInsertRowsBodySchema,
|
||||
response: { mode: 'json', schema: sqlRowsResponseSchema },
|
||||
})
|
||||
|
||||
export type ClickHouseQueryRequest = ContractBodyInput<typeof clickhouseQueryContract>
|
||||
export type ClickHouseQueryResponse = ContractJsonResponse<typeof clickhouseQueryContract>
|
||||
export type ClickHouseExecuteRequest = ContractBodyInput<typeof clickhouseExecuteContract>
|
||||
export type ClickHouseExecuteResponse = ContractJsonResponse<typeof clickhouseExecuteContract>
|
||||
export type ClickHouseInsertRequest = ContractBodyInput<typeof clickhouseInsertContract>
|
||||
export type ClickHouseInsertResponse = ContractJsonResponse<typeof clickhouseInsertContract>
|
||||
export type ClickHouseUpdateRequest = ContractBodyInput<typeof clickhouseUpdateContract>
|
||||
export type ClickHouseUpdateResponse = ContractJsonResponse<typeof clickhouseUpdateContract>
|
||||
export type ClickHouseDeleteRequest = ContractBodyInput<typeof clickhouseDeleteContract>
|
||||
export type ClickHouseDeleteResponse = ContractJsonResponse<typeof clickhouseDeleteContract>
|
||||
export type ClickHouseIntrospectRequest = ContractBodyInput<typeof clickhouseIntrospectContract>
|
||||
export type ClickHouseIntrospectResponse = ContractJsonResponse<typeof clickhouseIntrospectContract>
|
||||
@@ -0,0 +1,8 @@
|
||||
export * from '@/lib/api/contracts/tools/databases/mongodb'
|
||||
export * from '@/lib/api/contracts/tools/databases/mysql'
|
||||
export * from '@/lib/api/contracts/tools/databases/neo4j'
|
||||
export * from '@/lib/api/contracts/tools/databases/postgresql'
|
||||
export * from '@/lib/api/contracts/tools/databases/rds'
|
||||
export * from '@/lib/api/contracts/tools/databases/redis'
|
||||
export * from '@/lib/api/contracts/tools/databases/shared'
|
||||
export * from '@/lib/api/contracts/tools/databases/supabase'
|
||||
@@ -0,0 +1,217 @@
|
||||
import { z } from 'zod'
|
||||
import {
|
||||
introspectionResponseSchema,
|
||||
mongoDocumentsResponseSchema,
|
||||
sslModeSchema,
|
||||
} from '@/lib/api/contracts/tools/databases/shared'
|
||||
import {
|
||||
type ContractBodyInput,
|
||||
type ContractJsonResponse,
|
||||
defineRouteContract,
|
||||
} from '@/lib/api/contracts/types'
|
||||
|
||||
// Un-refined base so the downstream operation schemas can .extend it; each
|
||||
// reattaches mongoUsernamePasswordPaired after its own .extend.
|
||||
const mongoConnectionBaseSchema = z.object({
|
||||
host: z.string().min(1, 'Host is required'),
|
||||
port: z.coerce.number().int().positive('Port must be a positive integer'),
|
||||
database: z.string().min(1, 'Database name is required'),
|
||||
username: z.string().min(1, 'Username is required').optional(),
|
||||
password: z.string().min(1, 'Password is required').optional(),
|
||||
authSource: z.string().optional(),
|
||||
ssl: sslModeSchema,
|
||||
})
|
||||
|
||||
const mongoUsernamePasswordPaired = (data: { username?: string; password?: string }) =>
|
||||
Boolean(data.username) === Boolean(data.password)
|
||||
const mongoUsernamePasswordPairedError = {
|
||||
message: 'Username and password must be provided together',
|
||||
path: ['password' as const],
|
||||
}
|
||||
|
||||
const mongoJsonStringOrObjectSchema = (message: string) =>
|
||||
z
|
||||
.union([z.string(), z.object({}).passthrough()])
|
||||
.transform((val) => {
|
||||
if (typeof val === 'object' && val !== null) {
|
||||
return JSON.stringify(val)
|
||||
}
|
||||
return val
|
||||
})
|
||||
.refine((val) => val && val.trim() !== '', { message })
|
||||
|
||||
const booleanStringSchema = z
|
||||
.union([z.boolean(), z.string(), z.undefined()])
|
||||
.optional()
|
||||
.transform((val) => {
|
||||
if (val === 'true' || val === true) return true
|
||||
if (val === 'false' || val === false) return false
|
||||
return false
|
||||
})
|
||||
|
||||
export const mongodbQueryBodySchema = mongoConnectionBaseSchema
|
||||
.extend({
|
||||
collection: z.string().min(1, 'Collection name is required'),
|
||||
query: z
|
||||
.union([z.string(), z.object({}).passthrough()])
|
||||
.optional()
|
||||
.default('{}')
|
||||
.transform((val) => {
|
||||
if (typeof val === 'object' && val !== null) {
|
||||
return JSON.stringify(val)
|
||||
}
|
||||
return val || '{}'
|
||||
}),
|
||||
limit: z
|
||||
.union([z.coerce.number().int().positive(), z.literal(''), z.undefined()])
|
||||
.optional()
|
||||
.transform((val) => {
|
||||
if (val === '' || val === undefined || val === null) {
|
||||
return 100
|
||||
}
|
||||
return val
|
||||
}),
|
||||
sort: z
|
||||
.union([z.string(), z.object({}).passthrough(), z.null()])
|
||||
.optional()
|
||||
.transform((val) => {
|
||||
if (typeof val === 'object' && val !== null) {
|
||||
return JSON.stringify(val)
|
||||
}
|
||||
return val
|
||||
}),
|
||||
})
|
||||
.refine(mongoUsernamePasswordPaired, mongoUsernamePasswordPairedError)
|
||||
|
||||
export const mongodbExecuteBodySchema = mongoConnectionBaseSchema
|
||||
.extend({
|
||||
collection: z.string().min(1, 'Collection name is required'),
|
||||
pipeline: z
|
||||
.union([z.string(), z.array(z.object({}).passthrough())])
|
||||
.transform((val) => {
|
||||
if (Array.isArray(val)) {
|
||||
return JSON.stringify(val)
|
||||
}
|
||||
return val
|
||||
})
|
||||
.refine((val) => val && val.trim() !== '', {
|
||||
message: 'Pipeline is required',
|
||||
}),
|
||||
})
|
||||
.refine(mongoUsernamePasswordPaired, mongoUsernamePasswordPairedError)
|
||||
|
||||
export const mongodbInsertBodySchema = mongoConnectionBaseSchema
|
||||
.extend({
|
||||
collection: z.string().min(1, 'Collection name is required'),
|
||||
documents: z
|
||||
.union([z.array(z.record(z.string(), z.unknown())), z.string()])
|
||||
.transform((val) => {
|
||||
if (typeof val === 'string') {
|
||||
try {
|
||||
const parsed = JSON.parse(val)
|
||||
return Array.isArray(parsed) ? parsed : [parsed]
|
||||
} catch {
|
||||
throw new Error('Invalid JSON in documents field')
|
||||
}
|
||||
}
|
||||
return val
|
||||
})
|
||||
.refine((val) => Array.isArray(val) && val.length > 0, {
|
||||
message: 'At least one document is required',
|
||||
}),
|
||||
})
|
||||
.refine(mongoUsernamePasswordPaired, mongoUsernamePasswordPairedError)
|
||||
|
||||
export const mongodbUpdateBodySchema = mongoConnectionBaseSchema
|
||||
.extend({
|
||||
collection: z.string().min(1, 'Collection name is required'),
|
||||
filter: mongoJsonStringOrObjectSchema('Filter is required for MongoDB Update').refine(
|
||||
(val) => val !== '{}',
|
||||
{ message: 'Filter is required for MongoDB Update' }
|
||||
),
|
||||
update: mongoJsonStringOrObjectSchema('Update is required'),
|
||||
upsert: booleanStringSchema,
|
||||
multi: booleanStringSchema,
|
||||
})
|
||||
.refine(mongoUsernamePasswordPaired, mongoUsernamePasswordPairedError)
|
||||
|
||||
export const mongodbDeleteBodySchema = mongoConnectionBaseSchema
|
||||
.extend({
|
||||
collection: z.string().min(1, 'Collection name is required'),
|
||||
filter: mongoJsonStringOrObjectSchema('Filter is required for MongoDB Delete').refine(
|
||||
(val) => val !== '{}',
|
||||
{ message: 'Filter is required for MongoDB Delete' }
|
||||
),
|
||||
multi: booleanStringSchema,
|
||||
})
|
||||
.refine(mongoUsernamePasswordPaired, mongoUsernamePasswordPairedError)
|
||||
|
||||
export const mongodbIntrospectBodySchema = z
|
||||
.object({
|
||||
host: z.string().min(1, 'Host is required'),
|
||||
port: z.coerce.number().int().positive('Port must be a positive integer'),
|
||||
database: z.string().optional(),
|
||||
username: z.string().optional(),
|
||||
password: z.string().optional(),
|
||||
authSource: z.string().optional(),
|
||||
ssl: sslModeSchema,
|
||||
})
|
||||
.refine((data) => Boolean(data.username) === Boolean(data.password), {
|
||||
message: 'Username and password must be provided together',
|
||||
path: ['password'],
|
||||
})
|
||||
|
||||
export const mongodbQueryContract = defineRouteContract({
|
||||
method: 'POST',
|
||||
path: '/api/tools/mongodb/query',
|
||||
body: mongodbQueryBodySchema,
|
||||
response: { mode: 'json', schema: mongoDocumentsResponseSchema },
|
||||
})
|
||||
|
||||
export const mongodbExecuteContract = defineRouteContract({
|
||||
method: 'POST',
|
||||
path: '/api/tools/mongodb/execute',
|
||||
body: mongodbExecuteBodySchema,
|
||||
response: { mode: 'json', schema: mongoDocumentsResponseSchema },
|
||||
})
|
||||
|
||||
export const mongodbInsertContract = defineRouteContract({
|
||||
method: 'POST',
|
||||
path: '/api/tools/mongodb/insert',
|
||||
body: mongodbInsertBodySchema,
|
||||
response: { mode: 'json', schema: mongoDocumentsResponseSchema },
|
||||
})
|
||||
|
||||
export const mongodbUpdateContract = defineRouteContract({
|
||||
method: 'POST',
|
||||
path: '/api/tools/mongodb/update',
|
||||
body: mongodbUpdateBodySchema,
|
||||
response: { mode: 'json', schema: mongoDocumentsResponseSchema },
|
||||
})
|
||||
|
||||
export const mongodbDeleteContract = defineRouteContract({
|
||||
method: 'POST',
|
||||
path: '/api/tools/mongodb/delete',
|
||||
body: mongodbDeleteBodySchema,
|
||||
response: { mode: 'json', schema: mongoDocumentsResponseSchema },
|
||||
})
|
||||
|
||||
export const mongodbIntrospectContract = defineRouteContract({
|
||||
method: 'POST',
|
||||
path: '/api/tools/mongodb/introspect',
|
||||
body: mongodbIntrospectBodySchema,
|
||||
response: { mode: 'json', schema: introspectionResponseSchema },
|
||||
})
|
||||
|
||||
export type MongoDBQueryRequest = ContractBodyInput<typeof mongodbQueryContract>
|
||||
export type MongoDBQueryResponse = ContractJsonResponse<typeof mongodbQueryContract>
|
||||
export type MongoDBExecuteRequest = ContractBodyInput<typeof mongodbExecuteContract>
|
||||
export type MongoDBExecuteResponse = ContractJsonResponse<typeof mongodbExecuteContract>
|
||||
export type MongoDBInsertRequest = ContractBodyInput<typeof mongodbInsertContract>
|
||||
export type MongoDBInsertResponse = ContractJsonResponse<typeof mongodbInsertContract>
|
||||
export type MongoDBUpdateRequest = ContractBodyInput<typeof mongodbUpdateContract>
|
||||
export type MongoDBUpdateResponse = ContractJsonResponse<typeof mongodbUpdateContract>
|
||||
export type MongoDBDeleteRequest = ContractBodyInput<typeof mongodbDeleteContract>
|
||||
export type MongoDBDeleteResponse = ContractJsonResponse<typeof mongodbDeleteContract>
|
||||
export type MongoDBIntrospectRequest = ContractBodyInput<typeof mongodbIntrospectContract>
|
||||
export type MongoDBIntrospectResponse = ContractJsonResponse<typeof mongodbIntrospectContract>
|
||||
@@ -0,0 +1,76 @@
|
||||
import {
|
||||
introspectionResponseSchema,
|
||||
sqlConnectionBodySchema,
|
||||
sqlDeleteBodySchema,
|
||||
sqlInsertBodySchema,
|
||||
sqlQueryBodySchema,
|
||||
sqlRowsResponseSchema,
|
||||
sqlUpdateBodySchema,
|
||||
} from '@/lib/api/contracts/tools/databases/shared'
|
||||
import {
|
||||
type ContractBodyInput,
|
||||
type ContractJsonResponse,
|
||||
defineRouteContract,
|
||||
} from '@/lib/api/contracts/types'
|
||||
|
||||
export const mysqlQueryBodySchema = sqlQueryBodySchema
|
||||
export const mysqlExecuteBodySchema = sqlQueryBodySchema
|
||||
export const mysqlInsertBodySchema = sqlInsertBodySchema
|
||||
export const mysqlUpdateBodySchema = sqlUpdateBodySchema
|
||||
export const mysqlDeleteBodySchema = sqlDeleteBodySchema
|
||||
export const mysqlIntrospectBodySchema = sqlConnectionBodySchema
|
||||
|
||||
export const mysqlQueryContract = defineRouteContract({
|
||||
method: 'POST',
|
||||
path: '/api/tools/mysql/query',
|
||||
body: mysqlQueryBodySchema,
|
||||
response: { mode: 'json', schema: sqlRowsResponseSchema },
|
||||
})
|
||||
|
||||
export const mysqlExecuteContract = defineRouteContract({
|
||||
method: 'POST',
|
||||
path: '/api/tools/mysql/execute',
|
||||
body: mysqlExecuteBodySchema,
|
||||
response: { mode: 'json', schema: sqlRowsResponseSchema },
|
||||
})
|
||||
|
||||
export const mysqlInsertContract = defineRouteContract({
|
||||
method: 'POST',
|
||||
path: '/api/tools/mysql/insert',
|
||||
body: mysqlInsertBodySchema,
|
||||
response: { mode: 'json', schema: sqlRowsResponseSchema },
|
||||
})
|
||||
|
||||
export const mysqlUpdateContract = defineRouteContract({
|
||||
method: 'POST',
|
||||
path: '/api/tools/mysql/update',
|
||||
body: mysqlUpdateBodySchema,
|
||||
response: { mode: 'json', schema: sqlRowsResponseSchema },
|
||||
})
|
||||
|
||||
export const mysqlDeleteContract = defineRouteContract({
|
||||
method: 'POST',
|
||||
path: '/api/tools/mysql/delete',
|
||||
body: mysqlDeleteBodySchema,
|
||||
response: { mode: 'json', schema: sqlRowsResponseSchema },
|
||||
})
|
||||
|
||||
export const mysqlIntrospectContract = defineRouteContract({
|
||||
method: 'POST',
|
||||
path: '/api/tools/mysql/introspect',
|
||||
body: mysqlIntrospectBodySchema,
|
||||
response: { mode: 'json', schema: introspectionResponseSchema },
|
||||
})
|
||||
|
||||
export type MySQLQueryRequest = ContractBodyInput<typeof mysqlQueryContract>
|
||||
export type MySQLQueryResponse = ContractJsonResponse<typeof mysqlQueryContract>
|
||||
export type MySQLExecuteRequest = ContractBodyInput<typeof mysqlExecuteContract>
|
||||
export type MySQLExecuteResponse = ContractJsonResponse<typeof mysqlExecuteContract>
|
||||
export type MySQLInsertRequest = ContractBodyInput<typeof mysqlInsertContract>
|
||||
export type MySQLInsertResponse = ContractJsonResponse<typeof mysqlInsertContract>
|
||||
export type MySQLUpdateRequest = ContractBodyInput<typeof mysqlUpdateContract>
|
||||
export type MySQLUpdateResponse = ContractJsonResponse<typeof mysqlUpdateContract>
|
||||
export type MySQLDeleteRequest = ContractBodyInput<typeof mysqlDeleteContract>
|
||||
export type MySQLDeleteResponse = ContractJsonResponse<typeof mysqlDeleteContract>
|
||||
export type MySQLIntrospectRequest = ContractBodyInput<typeof mysqlIntrospectContract>
|
||||
export type MySQLIntrospectResponse = ContractJsonResponse<typeof mysqlIntrospectContract>
|
||||
@@ -0,0 +1,99 @@
|
||||
import { z } from 'zod'
|
||||
import {
|
||||
introspectionResponseSchema,
|
||||
neo4jEncryptionSchema,
|
||||
neo4jResponseSchema,
|
||||
} from '@/lib/api/contracts/tools/databases/shared'
|
||||
import {
|
||||
type ContractBodyInput,
|
||||
type ContractJsonResponse,
|
||||
defineRouteContract,
|
||||
} from '@/lib/api/contracts/types'
|
||||
|
||||
export const neo4jConnectionBodySchema = z.object({
|
||||
host: z.string().min(1, 'Host is required'),
|
||||
port: z.coerce.number().int().positive('Port must be a positive integer'),
|
||||
database: z.string().min(1, 'Database name is required'),
|
||||
username: z.string().min(1, 'Username is required'),
|
||||
password: z.string().min(1, 'Password is required'),
|
||||
encryption: neo4jEncryptionSchema,
|
||||
})
|
||||
|
||||
export const neo4jCypherBodySchema = neo4jConnectionBodySchema.extend({
|
||||
cypherQuery: z.string().min(1, 'Cypher query is required'),
|
||||
parameters: z.record(z.string(), z.unknown()).nullable().optional().default({}),
|
||||
})
|
||||
|
||||
export const neo4jQueryBodySchema = neo4jCypherBodySchema
|
||||
export const neo4jExecuteBodySchema = neo4jCypherBodySchema
|
||||
export const neo4jCreateBodySchema = neo4jCypherBodySchema
|
||||
export const neo4jUpdateBodySchema = neo4jCypherBodySchema
|
||||
export const neo4jMergeBodySchema = neo4jCypherBodySchema
|
||||
export const neo4jDeleteBodySchema = neo4jCypherBodySchema.extend({
|
||||
detach: z.boolean().optional().default(false),
|
||||
})
|
||||
export const neo4jIntrospectBodySchema = neo4jConnectionBodySchema
|
||||
|
||||
export const neo4jQueryContract = defineRouteContract({
|
||||
method: 'POST',
|
||||
path: '/api/tools/neo4j/query',
|
||||
body: neo4jQueryBodySchema,
|
||||
response: { mode: 'json', schema: neo4jResponseSchema },
|
||||
})
|
||||
|
||||
export const neo4jExecuteContract = defineRouteContract({
|
||||
method: 'POST',
|
||||
path: '/api/tools/neo4j/execute',
|
||||
body: neo4jExecuteBodySchema,
|
||||
response: { mode: 'json', schema: neo4jResponseSchema },
|
||||
})
|
||||
|
||||
export const neo4jCreateContract = defineRouteContract({
|
||||
method: 'POST',
|
||||
path: '/api/tools/neo4j/create',
|
||||
body: neo4jCreateBodySchema,
|
||||
response: { mode: 'json', schema: neo4jResponseSchema },
|
||||
})
|
||||
|
||||
export const neo4jUpdateContract = defineRouteContract({
|
||||
method: 'POST',
|
||||
path: '/api/tools/neo4j/update',
|
||||
body: neo4jUpdateBodySchema,
|
||||
response: { mode: 'json', schema: neo4jResponseSchema },
|
||||
})
|
||||
|
||||
export const neo4jDeleteContract = defineRouteContract({
|
||||
method: 'POST',
|
||||
path: '/api/tools/neo4j/delete',
|
||||
body: neo4jDeleteBodySchema,
|
||||
response: { mode: 'json', schema: neo4jResponseSchema },
|
||||
})
|
||||
|
||||
export const neo4jMergeContract = defineRouteContract({
|
||||
method: 'POST',
|
||||
path: '/api/tools/neo4j/merge',
|
||||
body: neo4jMergeBodySchema,
|
||||
response: { mode: 'json', schema: neo4jResponseSchema },
|
||||
})
|
||||
|
||||
export const neo4jIntrospectContract = defineRouteContract({
|
||||
method: 'POST',
|
||||
path: '/api/tools/neo4j/introspect',
|
||||
body: neo4jIntrospectBodySchema,
|
||||
response: { mode: 'json', schema: introspectionResponseSchema },
|
||||
})
|
||||
|
||||
export type Neo4jQueryRequest = ContractBodyInput<typeof neo4jQueryContract>
|
||||
export type Neo4jQueryResponse = ContractJsonResponse<typeof neo4jQueryContract>
|
||||
export type Neo4jExecuteRequest = ContractBodyInput<typeof neo4jExecuteContract>
|
||||
export type Neo4jExecuteResponse = ContractJsonResponse<typeof neo4jExecuteContract>
|
||||
export type Neo4jCreateRequest = ContractBodyInput<typeof neo4jCreateContract>
|
||||
export type Neo4jCreateResponse = ContractJsonResponse<typeof neo4jCreateContract>
|
||||
export type Neo4jUpdateRequest = ContractBodyInput<typeof neo4jUpdateContract>
|
||||
export type Neo4jUpdateResponse = ContractJsonResponse<typeof neo4jUpdateContract>
|
||||
export type Neo4jDeleteRequest = ContractBodyInput<typeof neo4jDeleteContract>
|
||||
export type Neo4jDeleteResponse = ContractJsonResponse<typeof neo4jDeleteContract>
|
||||
export type Neo4jMergeRequest = ContractBodyInput<typeof neo4jMergeContract>
|
||||
export type Neo4jMergeResponse = ContractJsonResponse<typeof neo4jMergeContract>
|
||||
export type Neo4jIntrospectRequest = ContractBodyInput<typeof neo4jIntrospectContract>
|
||||
export type Neo4jIntrospectResponse = ContractJsonResponse<typeof neo4jIntrospectContract>
|
||||
@@ -0,0 +1,79 @@
|
||||
import { z } from 'zod'
|
||||
import {
|
||||
introspectionResponseSchema,
|
||||
sqlConnectionBodySchema,
|
||||
sqlDeleteBodySchema,
|
||||
sqlInsertBodySchema,
|
||||
sqlQueryBodySchema,
|
||||
sqlRowsResponseSchema,
|
||||
sqlUpdateBodySchema,
|
||||
} from '@/lib/api/contracts/tools/databases/shared'
|
||||
import {
|
||||
type ContractBodyInput,
|
||||
type ContractJsonResponse,
|
||||
defineRouteContract,
|
||||
} from '@/lib/api/contracts/types'
|
||||
|
||||
export const postgresqlQueryBodySchema = sqlQueryBodySchema
|
||||
export const postgresqlExecuteBodySchema = sqlQueryBodySchema
|
||||
export const postgresqlInsertBodySchema = sqlInsertBodySchema
|
||||
export const postgresqlUpdateBodySchema = sqlUpdateBodySchema
|
||||
export const postgresqlDeleteBodySchema = sqlDeleteBodySchema
|
||||
export const postgresqlIntrospectBodySchema = sqlConnectionBodySchema.extend({
|
||||
schema: z.string().default('public'),
|
||||
})
|
||||
|
||||
export const postgresqlQueryContract = defineRouteContract({
|
||||
method: 'POST',
|
||||
path: '/api/tools/postgresql/query',
|
||||
body: postgresqlQueryBodySchema,
|
||||
response: { mode: 'json', schema: sqlRowsResponseSchema },
|
||||
})
|
||||
|
||||
export const postgresqlExecuteContract = defineRouteContract({
|
||||
method: 'POST',
|
||||
path: '/api/tools/postgresql/execute',
|
||||
body: postgresqlExecuteBodySchema,
|
||||
response: { mode: 'json', schema: sqlRowsResponseSchema },
|
||||
})
|
||||
|
||||
export const postgresqlInsertContract = defineRouteContract({
|
||||
method: 'POST',
|
||||
path: '/api/tools/postgresql/insert',
|
||||
body: postgresqlInsertBodySchema,
|
||||
response: { mode: 'json', schema: sqlRowsResponseSchema },
|
||||
})
|
||||
|
||||
export const postgresqlUpdateContract = defineRouteContract({
|
||||
method: 'POST',
|
||||
path: '/api/tools/postgresql/update',
|
||||
body: postgresqlUpdateBodySchema,
|
||||
response: { mode: 'json', schema: sqlRowsResponseSchema },
|
||||
})
|
||||
|
||||
export const postgresqlDeleteContract = defineRouteContract({
|
||||
method: 'POST',
|
||||
path: '/api/tools/postgresql/delete',
|
||||
body: postgresqlDeleteBodySchema,
|
||||
response: { mode: 'json', schema: sqlRowsResponseSchema },
|
||||
})
|
||||
|
||||
export const postgresqlIntrospectContract = defineRouteContract({
|
||||
method: 'POST',
|
||||
path: '/api/tools/postgresql/introspect',
|
||||
body: postgresqlIntrospectBodySchema,
|
||||
response: { mode: 'json', schema: introspectionResponseSchema },
|
||||
})
|
||||
|
||||
export type PostgreSQLQueryRequest = ContractBodyInput<typeof postgresqlQueryContract>
|
||||
export type PostgreSQLQueryResponse = ContractJsonResponse<typeof postgresqlQueryContract>
|
||||
export type PostgreSQLExecuteRequest = ContractBodyInput<typeof postgresqlExecuteContract>
|
||||
export type PostgreSQLExecuteResponse = ContractJsonResponse<typeof postgresqlExecuteContract>
|
||||
export type PostgreSQLInsertRequest = ContractBodyInput<typeof postgresqlInsertContract>
|
||||
export type PostgreSQLInsertResponse = ContractJsonResponse<typeof postgresqlInsertContract>
|
||||
export type PostgreSQLUpdateRequest = ContractBodyInput<typeof postgresqlUpdateContract>
|
||||
export type PostgreSQLUpdateResponse = ContractJsonResponse<typeof postgresqlUpdateContract>
|
||||
export type PostgreSQLDeleteRequest = ContractBodyInput<typeof postgresqlDeleteContract>
|
||||
export type PostgreSQLDeleteResponse = ContractJsonResponse<typeof postgresqlDeleteContract>
|
||||
export type PostgreSQLIntrospectRequest = ContractBodyInput<typeof postgresqlIntrospectContract>
|
||||
export type PostgreSQLIntrospectResponse = ContractJsonResponse<typeof postgresqlIntrospectContract>
|
||||
@@ -0,0 +1,97 @@
|
||||
import { z } from 'zod'
|
||||
import {
|
||||
introspectionResponseSchema,
|
||||
nonEmptyRecordSchema,
|
||||
sqlRowsResponseSchema,
|
||||
} from '@/lib/api/contracts/tools/databases/shared'
|
||||
import {
|
||||
type ContractBodyInput,
|
||||
type ContractJsonResponse,
|
||||
defineRouteContract,
|
||||
} from '@/lib/api/contracts/types'
|
||||
|
||||
const rdsConnectionBodySchema = z.object({
|
||||
region: z.string().min(1, 'AWS region is required'),
|
||||
accessKeyId: z.string().min(1, 'AWS access key ID is required'),
|
||||
secretAccessKey: z.string().min(1, 'AWS secret access key is required'),
|
||||
resourceArn: z.string().min(1, 'Resource ARN is required'),
|
||||
secretArn: z.string().min(1, 'Secret ARN is required'),
|
||||
database: z.string().optional(),
|
||||
})
|
||||
|
||||
export const rdsQueryBodySchema = rdsConnectionBodySchema.extend({
|
||||
query: z.string().min(1, 'Query is required'),
|
||||
})
|
||||
|
||||
export const rdsInsertBodySchema = rdsConnectionBodySchema.extend({
|
||||
table: z.string().min(1, 'Table name is required'),
|
||||
data: nonEmptyRecordSchema('Data object must have at least one field'),
|
||||
})
|
||||
export const rdsUpdateBodySchema = rdsConnectionBodySchema.extend({
|
||||
table: z.string().min(1, 'Table name is required'),
|
||||
data: nonEmptyRecordSchema('Data object must have at least one field'),
|
||||
conditions: nonEmptyRecordSchema('At least one condition is required'),
|
||||
})
|
||||
export const rdsDeleteBodySchema = rdsConnectionBodySchema.extend({
|
||||
table: z.string().min(1, 'Table name is required'),
|
||||
conditions: nonEmptyRecordSchema('At least one condition is required'),
|
||||
})
|
||||
export const rdsIntrospectBodySchema = rdsConnectionBodySchema.extend({
|
||||
schema: z.string().optional(),
|
||||
engine: z.enum(['aurora-postgresql', 'aurora-mysql']).optional(),
|
||||
})
|
||||
|
||||
export const rdsQueryContract = defineRouteContract({
|
||||
method: 'POST',
|
||||
path: '/api/tools/rds/query',
|
||||
body: rdsQueryBodySchema,
|
||||
response: { mode: 'json', schema: sqlRowsResponseSchema },
|
||||
})
|
||||
|
||||
export const rdsExecuteContract = defineRouteContract({
|
||||
method: 'POST',
|
||||
path: '/api/tools/rds/execute',
|
||||
body: rdsQueryBodySchema,
|
||||
response: { mode: 'json', schema: sqlRowsResponseSchema },
|
||||
})
|
||||
|
||||
export const rdsInsertContract = defineRouteContract({
|
||||
method: 'POST',
|
||||
path: '/api/tools/rds/insert',
|
||||
body: rdsInsertBodySchema,
|
||||
response: { mode: 'json', schema: sqlRowsResponseSchema },
|
||||
})
|
||||
|
||||
export const rdsUpdateContract = defineRouteContract({
|
||||
method: 'POST',
|
||||
path: '/api/tools/rds/update',
|
||||
body: rdsUpdateBodySchema,
|
||||
response: { mode: 'json', schema: sqlRowsResponseSchema },
|
||||
})
|
||||
|
||||
export const rdsDeleteContract = defineRouteContract({
|
||||
method: 'POST',
|
||||
path: '/api/tools/rds/delete',
|
||||
body: rdsDeleteBodySchema,
|
||||
response: { mode: 'json', schema: sqlRowsResponseSchema },
|
||||
})
|
||||
|
||||
export const rdsIntrospectContract = defineRouteContract({
|
||||
method: 'POST',
|
||||
path: '/api/tools/rds/introspect',
|
||||
body: rdsIntrospectBodySchema,
|
||||
response: { mode: 'json', schema: introspectionResponseSchema },
|
||||
})
|
||||
|
||||
export type RdsQueryRequest = ContractBodyInput<typeof rdsQueryContract>
|
||||
export type RdsQueryResponse = ContractJsonResponse<typeof rdsQueryContract>
|
||||
export type RdsExecuteRequest = ContractBodyInput<typeof rdsExecuteContract>
|
||||
export type RdsExecuteResponse = ContractJsonResponse<typeof rdsExecuteContract>
|
||||
export type RdsInsertRequest = ContractBodyInput<typeof rdsInsertContract>
|
||||
export type RdsInsertResponse = ContractJsonResponse<typeof rdsInsertContract>
|
||||
export type RdsUpdateRequest = ContractBodyInput<typeof rdsUpdateContract>
|
||||
export type RdsUpdateResponse = ContractJsonResponse<typeof rdsUpdateContract>
|
||||
export type RdsDeleteRequest = ContractBodyInput<typeof rdsDeleteContract>
|
||||
export type RdsDeleteResponse = ContractJsonResponse<typeof rdsDeleteContract>
|
||||
export type RdsIntrospectRequest = ContractBodyInput<typeof rdsIntrospectContract>
|
||||
export type RdsIntrospectResponse = ContractJsonResponse<typeof rdsIntrospectContract>
|
||||
@@ -0,0 +1,23 @@
|
||||
import { z } from 'zod'
|
||||
import { redisExecuteResponseSchema } from '@/lib/api/contracts/tools/databases/shared'
|
||||
import {
|
||||
type ContractBodyInput,
|
||||
type ContractJsonResponse,
|
||||
defineRouteContract,
|
||||
} from '@/lib/api/contracts/types'
|
||||
|
||||
export const redisExecuteBodySchema = z.object({
|
||||
url: z.string().min(1, 'Redis connection URL is required'),
|
||||
command: z.string().min(1, 'Redis command is required'),
|
||||
args: z.array(z.union([z.string(), z.number()])).default([]),
|
||||
})
|
||||
|
||||
export const redisExecuteContract = defineRouteContract({
|
||||
method: 'POST',
|
||||
path: '/api/tools/redis/execute',
|
||||
body: redisExecuteBodySchema,
|
||||
response: { mode: 'json', schema: redisExecuteResponseSchema },
|
||||
})
|
||||
|
||||
export type RedisExecuteRequest = ContractBodyInput<typeof redisExecuteContract>
|
||||
export type RedisExecuteResponse = ContractJsonResponse<typeof redisExecuteContract>
|
||||
@@ -0,0 +1,106 @@
|
||||
import { getErrorMessage } from '@sim/utils/errors'
|
||||
import { z } from 'zod'
|
||||
|
||||
export const sslModeSchema = z.enum(['disabled', 'required', 'preferred']).default('preferred')
|
||||
export const neo4jEncryptionSchema = z.enum(['enabled', 'disabled']).default('disabled')
|
||||
|
||||
export const nonEmptyRecordSchema = (message: string) =>
|
||||
z.record(z.string(), z.unknown()).refine((obj) => Object.keys(obj).length > 0, { message })
|
||||
|
||||
const jsonObjectStringSchema = (message: string, includeReceivedValue = false) =>
|
||||
z
|
||||
.string()
|
||||
.min(1)
|
||||
.transform((str) => {
|
||||
try {
|
||||
const parsed = JSON.parse(str)
|
||||
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
|
||||
throw new Error('Data must be a JSON object')
|
||||
}
|
||||
return parsed
|
||||
} catch (error) {
|
||||
if (!includeReceivedValue) {
|
||||
throw new Error(message)
|
||||
}
|
||||
|
||||
const errorMessage = getErrorMessage(error, 'Unknown error')
|
||||
throw new Error(`${message}: ${errorMessage}. Received: ${str.substring(0, 100)}...`)
|
||||
}
|
||||
})
|
||||
|
||||
export const sqlConnectionBodySchema = z.object({
|
||||
host: z.string().min(1, 'Host is required'),
|
||||
port: z.coerce.number().int().positive('Port must be a positive integer'),
|
||||
database: z.string().min(1, 'Database name is required'),
|
||||
username: z.string().min(1, 'Username is required'),
|
||||
password: z.string().min(1, 'Password is required'),
|
||||
ssl: sslModeSchema,
|
||||
})
|
||||
|
||||
export const sqlQueryBodySchema = sqlConnectionBodySchema.extend({
|
||||
query: z.string().min(1, 'Query is required'),
|
||||
})
|
||||
|
||||
const sqlInsertDataSchema = z.union([
|
||||
nonEmptyRecordSchema('Data object cannot be empty'),
|
||||
jsonObjectStringSchema('Invalid JSON format in data field', true),
|
||||
])
|
||||
|
||||
const sqlUpdateDataSchema = z.union([
|
||||
nonEmptyRecordSchema('Data object cannot be empty'),
|
||||
jsonObjectStringSchema('Invalid JSON format in data field'),
|
||||
])
|
||||
|
||||
export const sqlInsertBodySchema = sqlConnectionBodySchema.extend({
|
||||
table: z.string().min(1, 'Table name is required'),
|
||||
data: sqlInsertDataSchema,
|
||||
})
|
||||
|
||||
export const sqlUpdateBodySchema = sqlConnectionBodySchema.extend({
|
||||
table: z.string().min(1, 'Table name is required'),
|
||||
data: sqlUpdateDataSchema,
|
||||
where: z.string().min(1, 'WHERE clause is required'),
|
||||
})
|
||||
|
||||
export const sqlDeleteBodySchema = sqlConnectionBodySchema.extend({
|
||||
table: z.string().min(1, 'Table name is required'),
|
||||
where: z.string().min(1, 'WHERE clause is required'),
|
||||
})
|
||||
|
||||
export const sqlRowsResponseSchema = z.object({
|
||||
message: z.string(),
|
||||
rows: z.array(z.unknown()),
|
||||
rowCount: z.number(),
|
||||
})
|
||||
|
||||
export const mongoDocumentsResponseSchema = z
|
||||
.object({
|
||||
message: z.string(),
|
||||
documents: z.array(z.unknown()).optional(),
|
||||
documentCount: z.number().optional(),
|
||||
})
|
||||
.passthrough()
|
||||
|
||||
export const neo4jResponseSchema = z
|
||||
.object({
|
||||
message: z.string(),
|
||||
})
|
||||
.passthrough()
|
||||
|
||||
export const introspectionResponseSchema = z
|
||||
.object({
|
||||
message: z.string(),
|
||||
})
|
||||
.passthrough()
|
||||
|
||||
export const redisExecuteResponseSchema = z.object({
|
||||
result: z.unknown(),
|
||||
})
|
||||
|
||||
export const supabaseStorageUploadResponseSchema = z.object({
|
||||
success: z.literal(true),
|
||||
output: z.object({
|
||||
message: z.string(),
|
||||
results: z.record(z.string(), z.unknown()),
|
||||
}),
|
||||
})
|
||||
@@ -0,0 +1,35 @@
|
||||
import { z } from 'zod'
|
||||
import { supabaseStorageUploadResponseSchema } from '@/lib/api/contracts/tools/databases/shared'
|
||||
import {
|
||||
type ContractBodyInput,
|
||||
type ContractJsonResponse,
|
||||
defineRouteContract,
|
||||
} from '@/lib/api/contracts/types'
|
||||
import { FileInputSchema } from '@/lib/uploads/utils/file-schemas'
|
||||
|
||||
export const supabaseStorageUploadBodySchema = z.object({
|
||||
projectId: z
|
||||
.string()
|
||||
.min(1, 'Project ID is required')
|
||||
.regex(/^[a-z0-9]+$/, 'Project ID must contain only lowercase alphanumeric characters'),
|
||||
apiKey: z.string().min(1, 'API key is required'),
|
||||
bucket: z.string().min(1, 'Bucket name is required'),
|
||||
fileName: z.string().min(1, 'File name is required'),
|
||||
path: z.string().optional().nullable(),
|
||||
fileData: FileInputSchema,
|
||||
contentType: z.string().optional().nullable(),
|
||||
cacheControl: z.string().optional().nullable(),
|
||||
upsert: z.boolean().optional().default(false),
|
||||
})
|
||||
|
||||
export const supabaseStorageUploadContract = defineRouteContract({
|
||||
method: 'POST',
|
||||
path: '/api/tools/supabase/storage-upload',
|
||||
body: supabaseStorageUploadBodySchema,
|
||||
response: { mode: 'json', schema: supabaseStorageUploadResponseSchema },
|
||||
})
|
||||
|
||||
export type SupabaseStorageUploadRequest = ContractBodyInput<typeof supabaseStorageUploadContract>
|
||||
export type SupabaseStorageUploadResponse = ContractJsonResponse<
|
||||
typeof supabaseStorageUploadContract
|
||||
>
|
||||
Reference in New Issue
Block a user