Skip to content

Commit 25769a4

Browse files
committed
fix(google-drive): add auto export format and Azure storage debug logging
1 parent 367415f commit 25769a4

File tree

4 files changed

+97
-15
lines changed

4 files changed

+97
-15
lines changed

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

Lines changed: 65 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -65,10 +65,12 @@ export async function POST(request: NextRequest) {
6565
const {
6666
accessToken,
6767
fileId,
68-
mimeType: exportMimeType,
68+
mimeType: rawExportMimeType,
6969
fileName,
7070
includeRevisions,
7171
} = validatedData
72+
const exportMimeType =
73+
rawExportMimeType && rawExportMimeType !== 'auto' ? rawExportMimeType : null
7274
const authHeader = `Bearer ${accessToken}`
7375

7476
logger.info(`[${requestId}] Getting file metadata from Google Drive`, { fileId })
@@ -129,7 +131,7 @@ export async function POST(request: NextRequest) {
129131
)
130132
}
131133

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

155207
const arrayBuffer = await exportResponse.arrayBuffer()

apps/sim/blocks/blocks/google_drive.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,7 @@ Return ONLY the query string - no explanations, no quotes around the whole thing
316316
title: 'Export Format',
317317
type: 'dropdown',
318318
options: [
319+
{ label: 'Auto (best format for file type)', id: 'auto' },
319320
{ label: 'Plain Text (text/plain)', id: 'text/plain' },
320321
{ label: 'HTML (text/html)', id: 'text/html' },
321322
{ label: 'PDF (application/pdf)', id: 'application/pdf' },
@@ -333,7 +334,8 @@ Return ONLY the query string - no explanations, no quotes around the whole thing
333334
},
334335
{ label: 'CSV (text/csv)', id: 'text/csv' },
335336
],
336-
placeholder: 'Optional: Choose export format for Google Docs/Sheets/Slides',
337+
value: 'auto',
338+
placeholder: 'Export format for Google Docs/Sheets/Slides',
337339
condition: { field: 'operation', value: 'download' },
338340
},
339341
{
@@ -867,7 +869,7 @@ Return ONLY the message text - no subject line, no greetings/signatures, no extr
867869
destinationFolderId: effectiveDestinationFolderId,
868870
file: normalizedFile,
869871
pageSize: rest.pageSize ? Number.parseInt(rest.pageSize as string, 10) : undefined,
870-
mimeType: mimeType,
872+
mimeType: mimeType === 'auto' ? undefined : mimeType,
871873
type: shareType, // Map shareType to type for share tool
872874
starred: starredValue,
873875
sendNotification: sendNotificationValue,

apps/sim/lib/uploads/contexts/workspace/workspace-file-manager.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,21 @@ export async function uploadWorkspaceFile(
247247
continue
248248
}
249249
logger.error(`Failed to upload workspace file ${fileName}:`, error)
250+
if (
251+
error instanceof Error &&
252+
'code' in error &&
253+
(error as { code?: string }).code === 'AuthorizationFailure'
254+
) {
255+
const { getStorageProvider, BLOB_CONFIG } = await import('@/lib/uploads/config')
256+
logger.error('Azure storage authorization diagnosis', {
257+
provider: getStorageProvider(),
258+
accountName: BLOB_CONFIG.accountName || '(empty)',
259+
containerName: BLOB_CONFIG.containerName || '(empty)',
260+
hasAccountKey: !!BLOB_CONFIG.accountKey,
261+
keyLength: BLOB_CONFIG.accountKey?.length || 0,
262+
hasConnectionString: !!BLOB_CONFIG.connectionString,
263+
})
264+
}
250265
throw new Error(
251266
`Failed to upload file: ${error instanceof Error ? error.message : 'Unknown error'}`
252267
)

apps/sim/lib/uploads/providers/blob/client.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,15 @@ export async function getBlobServiceClient(): Promise<BlobServiceClientInstance>
7272
const { accountName, accountKey, connectionString } = BLOB_CONFIG
7373

7474
if (connectionString) {
75+
logger.info('Initializing Azure Blob client using connection string', {
76+
accountName: accountName || '(from connection string)',
77+
})
7578
_blobServiceClient = BlobServiceClient.fromConnectionString(connectionString)
7679
} else if (accountName && accountKey) {
80+
logger.info('Initializing Azure Blob client using account name and key', {
81+
accountName,
82+
keyLength: accountKey.length,
83+
})
7784
const sharedKeyCredential = new StorageSharedKeyCredential(accountName, accountKey)
7885
_blobServiceClient = new BlobServiceClient(
7986
`https://${accountName}.blob.core.windows.net`,
@@ -131,6 +138,12 @@ export async function uploadToBlob(
131138
const uniqueKey = shouldPreserveKey ? fileName : `${Date.now()}-${safeFileName}`
132139

133140
const blobServiceClient = await getBlobServiceClient()
141+
logger.info('Uploading to Azure Blob Storage', {
142+
containerName: config.containerName,
143+
key: uniqueKey,
144+
contentType,
145+
fileSize,
146+
})
134147
const containerClient = blobServiceClient.getContainerClient(config.containerName)
135148
const blockBlobClient = containerClient.getBlockBlobClient(uniqueKey)
136149

0 commit comments

Comments
 (0)