Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 33 additions & 4 deletions services/export/pod-export/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,15 @@
return rawFormat as ExportFormat
}

function toSafeFormatFileToken (format: ExportFormat): 'json' | 'csv' {

Check failure on line 267 in services/export/pod-export/src/server.ts

View workflow job for this annotation

GitHub Actions / build

Function lacks ending return statement and return type does not include 'undefined'.

Check failure on line 267 in services/export/pod-export/src/server.ts

View workflow job for this annotation

GitHub Actions / build

Function lacks ending return statement and return type does not include 'undefined'.
switch (format) {
case ExportFormat.JSON:
return 'json'
case ExportFormat.CSV:
return 'csv'
}
}

export function createServer (
storageConfig: StorageConfiguration,
dbUrl: string,
Expand Down Expand Up @@ -399,6 +408,7 @@
const txOperations = new TxOperations(platformClient, socialId)

const exportDir = await fs.mkdtemp(join(tmpdir(), 'export-'))
let archiveDir: string | undefined
try {
const exporter = new WorkspaceExporter(measureCtx, txOperations, storageAdapter, wsIds, config)
await exporter.export(_class, exportDir, { format, attributesOnly: attributesOnly ?? false, query })
Expand All @@ -408,17 +418,36 @@
throw new ApiError(400, 'No data to export')
}

if (files.length !== 1) {
throw new ApiError(400, 'Unexpected number of files exported')
let exportedFile: string
if (files.length === 1) {
// Single space exported: return its file directly.
exportedFile = join(exportDir, files[0])
} else {
// Pack all spaces into a single archive so the sync endpoint can still return exactly one downloadable file.
archiveDir = await fs.mkdtemp(join(tmpdir(), 'export-archive-'))
const safeFormatToken = toSafeFormatFileToken(format)
const archiveName = `export-${wsIds.uuid}-${safeFormatToken}-${Date.now()}.zip`
exportedFile = join(archiveDir, archiveName)
await saveToArchive(exportDir, exportedFile)
}

const exportedFile = join(exportDir, files[0])
res.download(exportedFile, basename(exportedFile), () => {})
await new Promise<void>((resolve, reject) => {
res.download(exportedFile, basename(exportedFile), (err) => {
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
if (err != null && !res.headersSent) {
reject(err)
} else {
resolve()
}
})
})
} catch (err: any) {
measureCtx.error('Export failed:', err)
throw err
} finally {
void fs.rm(exportDir, { recursive: true, force: true })
if (archiveDir !== undefined) {
void fs.rm(archiveDir, { recursive: true, force: true })
}
}
})
)
Expand Down
Loading