mostly because code is being autogenerated by all the AI stuff using this prefix. it's also used in the stack trace.
110 lines
3.9 KiB
JavaScript
110 lines
3.9 KiB
JavaScript
'use strict';
|
|
|
|
exports = module.exports = {
|
|
get,
|
|
getIcons,
|
|
getIcon,
|
|
add,
|
|
list,
|
|
listBackupIds,
|
|
del,
|
|
};
|
|
|
|
const assert = require('node:assert'),
|
|
BoxError = require('./boxerror.js'),
|
|
crypto = require('node:crypto'),
|
|
database = require('./database.js'),
|
|
eventlog = require('./eventlog.js'),
|
|
safe = require('safetydance');
|
|
|
|
const ARCHIVE_FIELDS = [ 'archives.id', 'backupId', 'archives.creationTime', 'backups.remotePath', 'backups.manifestJson', 'backups.appConfigJson', '(archives.icon IS NOT NULL) AS hasIcon', '(archives.appStoreIcon IS NOT NULL) AS hasAppStoreIcon' ];
|
|
|
|
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.hasAppStoreIcon ? `/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, appStoreIcon FROM archives WHERE id=?', [ id ]);
|
|
if (results.length === 0) return null;
|
|
return { icon: results[0].icon, appStoreIcon: results[0].appStoreIcon };
|
|
}
|
|
|
|
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.appStoreIcon) return icons.appStoreIcon;
|
|
|
|
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, appStoreIcon) VALUES (?, ?, ?, ?)',
|
|
[ id, backupId, data.icon, data.appStoreIcon ]));
|
|
|
|
if (error && error.code === 'ER_NO_REFERENCED_ROW_2') throw new BoxError(BoxError.NOT_FOUND, 'Backup not found');
|
|
if (error && error.code === '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 });
|
|
}
|