Execute reviewed tenant merges safely

This commit is contained in:
2026-08-06 12:34:52 +02:00
parent a2c5ad5dd9
commit e2fdfe9c0a
5 changed files with 440 additions and 11 deletions

View File

@@ -712,6 +712,12 @@ const prepareCommunicationRoomsForImport = (exportData: TenantFullExport) => {
}
}
export const prepareTenantFullExportRowsForImport = (exportData: TenantFullExport) => {
encryptEntityBankAccountRowsForImport(exportData)
prepareCommunicationRoomsForImport(exportData)
return exportData
}
const cleanupImportedCommunicationRooms = async (client: any, exportData: TenantFullExport) => {
const rows = exportData.tables.communication_rooms || []
if (!rows.length) return 0
@@ -900,8 +906,7 @@ export const importTenantFullExport = async (
}
const exportData = remapTenantScopedExport(rawExportData, options.targetTenantId)
encryptEntityBankAccountRowsForImport(exportData)
prepareCommunicationRoomsForImport(exportData)
prepareTenantFullExportRowsForImport(exportData)
const client = await pool.connect()
const importOrder = [
"tenants",
@@ -1099,3 +1104,53 @@ export const readTenantFullExportArchive = async (archiveBuffer: Buffer): Promis
await reader.close()
}
}
export const restoreTenantMergeArchiveFiles = async (
archiveBuffer: Buffer,
rawExportData: TenantFullExport,
targetTenantId: number,
selectedFileRefs: ReadonlySet<string>
) => {
const reader = new ZipReader(new BlobReader(new Blob([archiveBuffer])))
try {
const entries = await reader.getEntries()
const entriesByName = new Map(entries.map((entry: any) => [entry.filename, entry]))
const manifest = JSON.parse(await readZipTextEntry(entriesByName, "manifest.json")) as TenantArchiveManifest
const exportData = remapTenantScopedExport(rawExportData, targetTenantId)
const sourcePrefix = `${rawExportData.tenantId}/`
const targetPrefix = `${targetTenantId}/`
const filteredFiles = (manifest.files || [])
.filter((file) => selectedFileRefs.has(String(file.id)))
.map((file) => ({
...file,
path: file.path?.startsWith(sourcePrefix)
? `${targetPrefix}${file.path.slice(sourcePrefix.length)}`
: file.path,
}))
const filteredManifest: TenantArchiveManifest = {
...manifest,
tenantId: targetTenantId,
files: filteredFiles,
}
const filteredExportData: TenantFullExport = {
...exportData,
files: exportData.files.filter((file) => selectedFileRefs.has(String(file.id))),
}
return await restoreArchiveFiles(entriesByName, filteredExportData, filteredManifest)
} finally {
await reader.close()
}
}
export const restoreTenantMergeInlineFiles = async (
rawExportData: TenantFullExport,
targetTenantId: number,
selectedFileRefs: ReadonlySet<string>
) => {
const exportData = remapTenantScopedExport(rawExportData, targetTenantId)
return await restoreFiles({
...exportData,
files: exportData.files.filter((file) => selectedFileRefs.has(String(file.id))),
})
}