Skip to content

Commit 762a9d9

Browse files
committed
fix(google-drive): validate export formats against Drive API docs, remove fallback
1 parent 40f3b45 commit 762a9d9

File tree

2 files changed

+74
-61
lines changed

2 files changed

+74
-61
lines changed

apps/sim/app/api/tools/google_drive/download/route.ts

Lines changed: 31 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
ALL_REVISION_FIELDS,
1414
DEFAULT_EXPORT_FORMATS,
1515
GOOGLE_WORKSPACE_MIME_TYPES,
16+
VALID_EXPORT_FORMATS,
1617
} from '@/tools/google_drive/utils'
1718

1819
export const dynamic = 'force-dynamic'
@@ -114,6 +115,24 @@ export async function POST(request: NextRequest) {
114115

115116
if (GOOGLE_WORKSPACE_MIME_TYPES.includes(fileMimeType)) {
116117
const exportFormat = exportMimeType || DEFAULT_EXPORT_FORMATS[fileMimeType] || 'text/plain'
118+
119+
const validFormats = VALID_EXPORT_FORMATS[fileMimeType]
120+
if (validFormats && !validFormats.includes(exportFormat)) {
121+
logger.warn(`[${requestId}] Unsupported export format requested`, {
122+
fileId,
123+
fileMimeType,
124+
requestedFormat: exportFormat,
125+
validFormats,
126+
})
127+
return NextResponse.json(
128+
{
129+
success: false,
130+
error: `Export format "${exportFormat}" is not supported for this file type. Supported formats: ${validFormats.join(', ')}`,
131+
},
132+
{ status: 400 }
133+
)
134+
}
135+
117136
finalMimeType = exportFormat
118137

119138
logger.info(`[${requestId}] Exporting Google Workspace file`, {
@@ -131,7 +150,7 @@ export async function POST(request: NextRequest) {
131150
)
132151
}
133152

134-
let exportResponse = await secureFetchWithPinnedIP(
153+
const exportResponse = await secureFetchWithPinnedIP(
135154
exportUrl,
136155
exportUrlValidation.resolvedIP!,
137156
{ headers: { Authorization: authHeader } }
@@ -141,64 +160,17 @@ export async function POST(request: NextRequest) {
141160
const exportError = (await exportResponse
142161
.json()
143162
.catch(() => ({}))) as GoogleApiErrorResponse
144-
const errorMessage = exportError.error?.message || ''
145-
146-
const defaultFormat = DEFAULT_EXPORT_FORMATS[fileMimeType]
147-
const canFallback = !!exportMimeType && !!defaultFormat && exportMimeType !== defaultFormat
148-
149-
if (canFallback) {
150-
logger.warn(`[${requestId}] Export failed with custom format, falling back to default`, {
151-
requestedFormat: exportFormat,
152-
fallbackFormat: defaultFormat,
153-
fileMimeType,
154-
status: exportResponse.status,
155-
})
156-
157-
finalMimeType = defaultFormat
158-
const fallbackUrl = `https://www.googleapis.com/drive/v3/files/${fileId}/export?mimeType=${encodeURIComponent(defaultFormat)}&supportsAllDrives=true`
159-
const fallbackUrlValidation = await validateUrlWithDNS(fallbackUrl, 'fallbackExportUrl')
160-
if (!fallbackUrlValidation.isValid) {
161-
return NextResponse.json(
162-
{ success: false, error: fallbackUrlValidation.error },
163-
{ status: 400 }
164-
)
165-
}
166-
167-
exportResponse = await secureFetchWithPinnedIP(
168-
fallbackUrl,
169-
fallbackUrlValidation.resolvedIP!,
170-
{ headers: { Authorization: authHeader } }
171-
)
172-
173-
if (!exportResponse.ok) {
174-
const fallbackError = (await exportResponse
175-
.json()
176-
.catch(() => ({}))) as GoogleApiErrorResponse
177-
logger.error(`[${requestId}] Fallback export also failed`, {
178-
status: exportResponse.status,
179-
error: fallbackError,
180-
})
181-
return NextResponse.json(
182-
{
183-
success: false,
184-
error: fallbackError.error?.message || 'Failed to export Google Workspace file',
185-
},
186-
{ status: 400 }
187-
)
188-
}
189-
} else {
190-
logger.error(`[${requestId}] Failed to export file`, {
191-
status: exportResponse.status,
192-
error: exportError,
193-
})
194-
return NextResponse.json(
195-
{
196-
success: false,
197-
error: errorMessage || 'Failed to export Google Workspace file',
198-
},
199-
{ status: 400 }
200-
)
201-
}
163+
logger.error(`[${requestId}] Failed to export file`, {
164+
status: exportResponse.status,
165+
error: exportError,
166+
})
167+
return NextResponse.json(
168+
{
169+
success: false,
170+
error: exportError.error?.message || 'Failed to export Google Workspace file',
171+
},
172+
{ status: 400 }
173+
)
202174
}
203175

204176
const arrayBuffer = await exportResponse.arrayBuffer()

apps/sim/tools/google_drive/utils.ts

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,8 +111,49 @@ export const DEFAULT_EXPORT_FORMATS: Record<string, string> = {
111111
'application/vnd.google-apps.spreadsheet': 'text/csv',
112112
'application/vnd.google-apps.presentation': 'text/plain',
113113
'application/vnd.google-apps.drawing': 'image/png',
114-
'application/vnd.google-apps.form': 'application/pdf',
115-
'application/vnd.google-apps.script': 'application/json',
114+
'application/vnd.google-apps.form': 'application/zip',
115+
'application/vnd.google-apps.script': 'application/vnd.google-apps.script+json',
116+
}
117+
118+
/**
119+
* Valid export formats per Google Workspace file type.
120+
* See: https://developers.google.com/drive/api/guides/ref-export-formats
121+
*/
122+
export const VALID_EXPORT_FORMATS: Record<string, string[]> = {
123+
'application/vnd.google-apps.document': [
124+
'text/plain',
125+
'text/html',
126+
'application/pdf',
127+
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
128+
'application/vnd.oasis.opendocument.text',
129+
'application/rtf',
130+
'application/epub+zip',
131+
],
132+
'application/vnd.google-apps.spreadsheet': [
133+
'text/csv',
134+
'text/tab-separated-values',
135+
'application/pdf',
136+
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
137+
'application/x-vnd.oasis.opendocument.spreadsheet',
138+
'application/zip',
139+
],
140+
'application/vnd.google-apps.presentation': [
141+
'text/plain',
142+
'application/pdf',
143+
'application/vnd.openxmlformats-officedocument.presentationml.presentation',
144+
'application/vnd.oasis.opendocument.presentation',
145+
'image/jpeg',
146+
'image/png',
147+
'image/svg+xml',
148+
],
149+
'application/vnd.google-apps.drawing': [
150+
'application/pdf',
151+
'image/jpeg',
152+
'image/png',
153+
'image/svg+xml',
154+
],
155+
'application/vnd.google-apps.form': ['application/zip'],
156+
'application/vnd.google-apps.script': ['application/vnd.google-apps.script+json'],
116157
}
117158

118159
export const SOURCE_MIME_TYPES: Record<string, string> = {

0 commit comments

Comments
 (0)