Files
cloudron-box/src/archives.js
T
Girish Ramakrishnan 96dc79cfe6 Migrate codebase from CommonJS to ES Modules
- Convert all require()/module.exports to import/export across 260+ files
- Add "type": "module" to package.json to enable ESM by default
- Add migrations/package.json with "type": "commonjs" to keep db-migrate compatible
- Convert eslint.config.js to ESM with sourceType: "module"
- Replace __dirname/__filename with import.meta.dirname/import.meta.filename
- Replace require.main === module with process.argv[1] === import.meta.filename
- Remove 'use strict' directives (implicit in ESM)
- Convert dynamic require() in switch statements to static import lookup maps
  (dns.js, domains.js, backupformats.js, backupsites.js, network.js)
- Extract self-referencing exports.CONSTANT patterns into standalone const
  declarations (apps.js, services.js, locks.js, users.js, mail.js, etc.)
- Lazify SERVICES object in services.js to avoid circular dependency TDZ issues
- Add clearMailQueue() to mailer.js for ESM-safe queue clearing in tests
- Add _setMockApp() to ldapserver.js for ESM-safe test mocking
- Add _setMockResolve() wrapper to dig.js for ESM-safe DNS mocking in tests
- Convert backupupload.js to use dynamic imports so --check exits before
  loading the module graph (which requires BOX_ENV)
- Update check-install to use ESM import for infra_version.js
- Convert scripts/ (hotfix, release, remote_hotfix.js, find-unused-translations)
- All 1315 tests passing

Migration stats (AI-assisted using Cursor with Claude):
- Wall clock time: ~3-4 hours
- Assistant completions: ~80-100
- Estimated token usage: ~1-2M tokens

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-14 15:11:45 +01:00

108 lines
3.8 KiB
JavaScript

import assert from 'node:assert';
import BoxError from './boxerror.js';
import crypto from 'node:crypto';
import * as database from './database.js';
import eventlog from './eventlog.js';
import safe from 'safetydance';
export {
get,
getIcons,
getIcon,
add,
list,
listBackupIds,
del,
};
const ARCHIVE_FIELDS = [ 'archives.id', 'backupId', 'archives.creationTime', 'backups.remotePath', 'backups.siteId', 'backups.manifestJson', 'backups.appConfigJson', '(archives.icon IS NOT NULL) AS hasIcon', '(archives.packageIcon IS NOT NULL) AS hasPackageIcon' ];
function postProcess(result) {
assert.strictEqual(typeof result, 'object');
result.appConfig = result.appConfigJson ? safe.JSON.parse(result.appConfigJson) : null;
delete result.appConfigJson;
result.manifest = result.manifestJson ? safe.JSON.parse(result.manifestJson) : null;
delete result.manifestJson;
result.iconUrl = result.hasIcon || result.hasPackageIcon ? `/api/v1/archives/${result.id}/icon` : null;
return result;
}
async function get(id) {
assert.strictEqual(typeof id, 'string');
const result = await database.query(`SELECT ${ARCHIVE_FIELDS} FROM archives LEFT JOIN backups ON archives.backupId = backups.id WHERE archives.id = ? ORDER BY creationTime DESC`, [ id ]);
if (result.length === 0) return null;
return postProcess(result[0]);
}
async function getIcons(id) {
assert.strictEqual(typeof id, 'string');
const results = await database.query('SELECT icon, packageIcon FROM archives WHERE id=?', [ id ]);
if (results.length === 0) return null;
return { icon: results[0].icon, packageIcon: results[0].packageIcon };
}
async function getIcon(id, options) {
assert.strictEqual(typeof id, 'string');
assert.strictEqual(typeof options, 'object');
const icons = await getIcons(id);
if (!icons) throw new BoxError(BoxError.NOT_FOUND, 'No such backup');
if (!options.original && icons.icon) return icons.icon;
if (icons.packageIcon) return icons.packageIcon;
return null;
}
async function add(backupId, data, auditSource) {
assert.strictEqual(typeof backupId, 'string');
assert.strictEqual(typeof data, 'object');
assert(auditSource && typeof auditSource === 'object');
const id = crypto.randomUUID();
const [error] = await safe(database.query('INSERT INTO archives (id, backupId, icon, packageIcon) VALUES (?, ?, ?, ?)',
[ id, backupId, data.icon, data.packageIcon ]));
if (error && error.sqlCode === 'ER_NO_REFERENCED_ROW_2') throw new BoxError(BoxError.NOT_FOUND, 'Backup not found');
if (error && error.sqlCode === 'ER_DUP_ENTRY') throw new BoxError(BoxError.ALREADY_EXISTS, 'Archive already exists');
if (error) throw error;
await eventlog.add(eventlog.ACTION_ARCHIVES_ADD, auditSource, { id, backupId });
return id;
}
async function list(page, perPage) {
assert(typeof page === 'number' && page > 0);
assert(typeof perPage === 'number' && perPage > 0);
const results = await database.query(`SELECT ${ARCHIVE_FIELDS} FROM archives LEFT JOIN backups ON archives.backupId = backups.id ORDER BY creationTime DESC LIMIT ?,?`, [ (page-1)*perPage, perPage ]);
results.forEach(function (result) { postProcess(result); });
return results;
}
async function listBackupIds() {
const results = await database.query(`SELECT backupId FROM archives`, []);
return results.map(r => r.backupId);
}
async function del(archive, auditSource) {
assert.strictEqual(typeof archive, 'object');
assert(auditSource && typeof auditSource === 'object');
const result = await database.query('DELETE FROM archives WHERE id=?', [ archive.id ]);
if (result.affectedRows !== 1) throw new BoxError(BoxError.NOT_FOUND, 'No such archive');
await eventlog.add(eventlog.ACTION_ARCHIVES_DEL, auditSource, { id: archive.id, backupId: archive.backupId });
}