import path from 'path'; import fs from 'fs/promises'; import { nano } from '@iridiumcms/utils'; import { tarball } from './tarball'; import { lake, backup } from '@iridiumcms/config/server'; import { TEMP_DIR } from '@iridiumcms/constants'; import { tables, backupTable, } from './table-management'; export const generator = async function({ isDatabase, isDataLake, }: { isDatabase: boolean; isDataLake: boolean; }) { const id = nano(); const date = new Date(); const tbPath = path.resolve(TEMP_DIR, `${id}.tar.gz`); const destPath = path.resolve(TEMP_DIR, id); await fs.mkdir(destPath, { recursive: true }); return { get id() { return id; }, async*run() { if (isDatabase) { yield `database: starting copy`; const dbDest = path.resolve(destPath, 'database'); await fs.mkdir(dbDest, { recursive: true }); yield `database: ${tables.length} tables`; for (let i = 0; i < tables.length; i++) { const [filename, table] = tables[i]; const blob = await backupTable(table); await Bun.write(path.resolve(dbDest, filename), blob); yield `database: ${i + 1}/${tables.length} tables copied`; } yield `database: copy complete`; } if (isDataLake) { yield `data-lake: starting copy`; const dlDest = path.resolve(destPath, 'data-lake'); await fs.mkdir(dlDest, { recursive: true }); const files = await lake.list(); yield `data-lake: ${files.length} files found`; for (let i = 0; i < files.length; i++) { const file = files[i]; try { const data = await lake.get(file); await fs.writeFile(path.resolve(dlDest, file), data); } catch (err) { // if we fail to get the file, we don't want to fail the backup // // the file could not exist or be corrupted, to be fixed by an admin later console.error(err); } yield `data-lake: ${i + 1}/${files.length} files copied`; } yield `data-lake: copy complete`; } yield `compressing`; await tarball(destPath, tbPath); yield `compression complete`; yield `saving backup`; const zip = Bun.file(tbPath); await backup.set({ id, date, file: await zip.arrayBuffer(), isDatabase, isDataLake, }); yield `save complete`; yield `cleaning up`; await fs.rm(destPath, { recursive: true }); await fs.rm(tbPath); yield `cleanup complete`; }, }; };