Skip to content

Commit 89caa68

Browse files
Bill Leoutsakoscursoragent
authored andcommitted
fix(api): Zod-contract storage-status and ratchet validation baseline
Wire /api/files/storage-status through a shared route contract so the strict API validation audit stays at zero non-Zod routes after the new Instagram cloud-storage check. Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 7a6f33a commit 89caa68

4 files changed

Lines changed: 24 additions & 11 deletions

File tree

apps/sim/app/api/files/storage-status/route.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
import { NextResponse } from 'next/server'
1+
import { type NextRequest, NextResponse } from 'next/server'
2+
import { fileStorageStatusContract } from '@/lib/api/contracts/storage-transfer'
3+
import { parseRequest } from '@/lib/api/server'
24
import { getSession } from '@/lib/auth'
35
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
46
import { hasCloudStorage } from '@/lib/uploads/core/storage-service'
@@ -9,11 +11,14 @@ export const dynamic = 'force-dynamic'
911
* GET /api/files/storage-status
1012
* Whether S3 or Azure Blob is configured (needed for Instagram file-upload publish).
1113
*/
12-
export const GET = withRouteHandler(async () => {
14+
export const GET = withRouteHandler(async (request: NextRequest) => {
1315
const session = await getSession()
1416
if (!session?.user?.id) {
1517
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
1618
}
1719

20+
const parsed = await parseRequest(fileStorageStatusContract, request, {})
21+
if (!parsed.success) return parsed.response
22+
1823
return NextResponse.json({ cloudConfigured: hasCloudStorage() })
1924
})

apps/sim/hooks/queries/workspace-files.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { backoffWithJitter } from '@sim/utils/retry'
66
import { keepPreviousData, useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
77
import { ApiClientError, isApiClientError } from '@/lib/api/client/errors'
88
import { requestJson } from '@/lib/api/client/request'
9+
import { fileStorageStatusContract } from '@/lib/api/contracts/storage-transfer'
910
import { getUsageLimitsContract } from '@/lib/api/contracts/usage-limits'
1011
import {
1112
deleteWorkspaceFileContract,
@@ -298,12 +299,7 @@ export function useStorageInfo(enabled = true) {
298299
}
299300

300301
async function fetchCloudStorageConfigured(signal?: AbortSignal): Promise<boolean> {
301-
// boundary-raw-fetch: tiny status endpoint, no shared contract yet
302-
const response = await fetch('/api/files/storage-status', { signal, cache: 'no-store' })
303-
if (!response.ok) {
304-
throw new Error('Failed to check cloud storage status')
305-
}
306-
const data = (await response.json()) as { cloudConfigured?: boolean }
302+
const data = await requestJson(fileStorageStatusContract, { signal })
307303
return data.cloudConfigured === true
308304
}
309305

apps/sim/lib/api/contracts/storage-transfer.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -745,6 +745,18 @@ export const fileExportContract = defineRouteContract({
745745
response: { mode: 'binary' },
746746
})
747747

748+
export const fileStorageStatusResponseSchema = z.object({
749+
cloudConfigured: z.boolean(),
750+
})
751+
752+
export const fileStorageStatusContract = defineRouteContract({
753+
method: 'GET',
754+
path: '/api/files/storage-status',
755+
response: { mode: 'json', schema: fileStorageStatusResponseSchema },
756+
})
757+
758+
export type FileStorageStatusResponse = ContractJsonResponse<typeof fileStorageStatusContract>
759+
748760
export type BoxUploadBody = ContractBodyInput<typeof boxUploadContract>
749761
export type BoxUploadResponse = ContractJsonResponse<typeof boxUploadContract>
750762
export type DropboxUploadBody = ContractBodyInput<typeof dropboxUploadContract>

scripts/check-api-validation-contracts.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ const QUERY_HOOKS_DIR = path.join(ROOT, 'apps/sim/hooks/queries')
99
const SELECTOR_HOOKS_DIR = path.join(ROOT, 'apps/sim/hooks/selectors')
1010

1111
const BASELINE = {
12-
totalRoutes: 927,
13-
zodRoutes: 927,
12+
totalRoutes: 935,
13+
zodRoutes: 935,
1414
nonZodRoutes: 0,
1515
} as const
1616

@@ -25,7 +25,7 @@ const BOUNDARY_POLICY_BASELINE = {
2525
clientHookRawFetches: 0,
2626
clientSameOriginApiFetches: 0,
2727
doubleCasts: 8,
28-
rawJsonReads: 8,
28+
rawJsonReads: 6,
2929
untypedResponses: 0,
3030
annotationsMissingReason: 0,
3131
} as const

0 commit comments

Comments
 (0)