2024-12-10 10:06:52 +01:00
'use strict' ;
exports = module . exports = {
get ,
2024-12-10 17:30:23 +01:00
getIcons ,
2024-12-10 10:06:52 +01:00
getIcon ,
add ,
list ,
listBackupIds ,
2024-12-10 14:46:30 +01:00
del ,
2024-12-10 10:06:52 +01:00
} ;
2025-08-14 11:17:38 +05:30
const assert = require ( 'node:assert' ) ,
2024-12-10 10:06:52 +01:00
BoxError = require ( './boxerror.js' ) ,
2025-08-14 11:17:38 +05:30
crypto = require ( 'node:crypto' ) ,
2024-12-10 10:06:52 +01:00
database = require ( './database.js' ) ,
2024-12-10 11:01:10 +01:00
eventlog = require ( './eventlog.js' ) ,
2025-07-28 12:53:27 +02:00
safe = require ( 'safetydance' ) ;
2024-12-10 10:06:52 +01:00
2024-12-19 14:21:39 +01:00
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' ] ;
2024-12-10 10:06:52 +01:00
function postProcess ( result ) {
assert . strictEqual ( typeof result , 'object' ) ;
result . appConfig = result . appConfigJson ? safe . JSON . parse ( result . appConfigJson ) : null ;
delete result . appConfigJson ;
2024-12-19 14:21:39 +01:00
result . manifest = result . manifestJson ? safe . JSON . parse ( result . manifestJson ) : null ;
delete result . manifestJson ;
2024-12-10 11:53:29 +01:00
result . iconUrl = result . hasIcon || result . hasAppStoreIcon ? ` /api/v1/archives/ ${ result . id } /icon ` : null ;
2024-12-10 10:06:52 +01:00
return result ;
}
async function get ( id ) {
assert . strictEqual ( typeof id , 'string' ) ;
2024-12-10 20:52:29 +01:00
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 ] ) ;
2024-12-10 10:06:52 +01:00
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 ;
}
2024-12-10 11:01:10 +01:00
async function add ( backupId , data , auditSource ) {
2024-12-10 10:06:52 +01:00
assert . strictEqual ( typeof backupId , 'string' ) ;
assert . strictEqual ( typeof data , 'object' ) ;
2024-12-10 11:01:10 +01:00
assert ( auditSource && typeof auditSource === 'object' ) ;
2024-12-10 10:06:52 +01:00
2025-07-28 12:53:27 +02:00
const id = crypto . randomUUID ( ) ;
2024-12-10 10:06:52 +01:00
2024-12-10 20:52:29 +01:00
const [ error ] = await safe ( database . query ( 'INSERT INTO archives (id, backupId, icon, appStoreIcon) VALUES (?, ?, ?, ?)' ,
[ id , backupId , data . icon , data . appStoreIcon ] ) ) ;
2024-12-10 10:06:52 +01:00
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 ;
2024-12-10 11:01:10 +01:00
await eventlog . add ( eventlog . ACTION _ARCHIVES _ADD , auditSource , { id , backupId } ) ;
2024-12-10 10:06:52 +01:00
return id ;
}
async function list ( page , perPage ) {
assert ( typeof page === 'number' && page > 0 ) ;
assert ( typeof perPage === 'number' && perPage > 0 ) ;
2024-12-10 20:52:29 +01:00
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 ] ) ;
2024-12-10 10:06:52 +01:00
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 ) ;
}
2024-12-10 11:01:10 +01:00
async function del ( archive , auditSource ) {
assert . strictEqual ( typeof archive , 'object' ) ;
2024-12-10 10:06:52 +01:00
assert ( auditSource && typeof auditSource === 'object' ) ;
2024-12-10 11:01:10 +01:00
const result = await database . query ( 'DELETE FROM archives WHERE id=?' , [ archive . id ] ) ;
2024-12-10 10:06:52 +01:00
if ( result . affectedRows !== 1 ) throw new BoxError ( BoxError . NOT _FOUND , 'No such archive' ) ;
2024-12-10 11:01:10 +01:00
await eventlog . add ( eventlog . ACTION _ARCHIVES _DEL , auditSource , { id : archive . id , backupId : archive . backupId } ) ;
2024-12-10 10:06:52 +01:00
}