import fs from 'fs' ; import path from 'path' ; export const DATA_FOLDER_NAME = 'data' ; const EXT = '.pm' ; const SAFE_TIME = 60 * 60 * 1000 ; const EXPIRATION = 7 * 24 * 60 * 60 * 1000 ; function checkExistence ( path ) { return new Promise ( resolve => { fs . exists ( path , res => { resolve ( res ) ; } ) ; } ) ; } export async function createDataFolder ( dir ) { const exists = await checkExistence ( dir ) ; if ( ! exists ) { return new Promise ( ( resolve , reject ) => { fs . mkdir ( dir , err => { if ( err ) reject ( err ) ; else resolve ( ) ; } ) ; } ) ; } } export function save ( path , blob ) { return new Promise ( ( resolve , reject ) => { fs . writeFile ( path , blob , err => { if ( err ) reject ( err ) ; else resolve ( ) ; } ) ; } ) ; } export function open ( path ) { return new Promise ( ( resolve , reject ) => { fs . readFile ( path , ( err , data ) => { if ( err ) reject ( err ) ; else resolve ( data ) ; } ) ; } ) ; } export function saveBackup ( dir , blob ) { const timestamp = Date . now ( ) ; return save ( path . join ( dir , timestamp + EXT ) , blob ) ; } function getFilepaths ( dir ) { return new Promise ( ( resolve , reject ) => { fs . readdir ( dir , ( err , files ) => { if ( err ) reject ( err ) ; else resolve ( files ) ; } ) ; } ) ; } async function getTimestamps ( dir ) { const filepaths = await getFilepaths ( dir ) ; const filenames = filepaths . filter ( p => p . endsWith ( EXT ) ) ; return filenames . map ( n => parseInt ( n . substring ( 0 , n . length - EXT . length ) ) ) ; } function getDataFrom ( dir , ts ) { return new Promise ( ( resolve , reject ) => { fs . readFile ( path . join ( dir , ts + EXT ) , ( err , data ) => { if ( err ) reject ( err ) ; else resolve ( data ) ; } ) ; } ) ; } export async function getMostRecent ( dir ) { const timestamps = await getTimestamps ( dir ) ; if ( timestamps . length > 0 ) { const max = Math . max ( ... timestamps ) ; return getDataFrom ( dir , max ) ; } else { return null ; } } function timestampsToKeep ( timestamps , now ) { const [ toKeep ] = timestamps . sort ( ) . reverse ( ) . reduce ( ( [ keep , stored ] , ts ) => { if ( stored && ( ts > stored - SAFE_TIME || ts < now - EXPIRATION ) ) { return [ keep , stored ] ; } else { keep . add ( ts ) ; return [ keep , ts ] ; } } , [ new Set ( ) , null ] as [ Set < number > , number | null ] ) ; return toKeep ; } function remove ( dir , ts ) { return new Promise ( ( resolve , reject ) => { fs . unlink ( path . join ( dir , ts + EXT ) , err => { if ( err ) reject ( err ) ; else resolve ( ) ; } ) ; } ) ; } export async function cleanup ( dir ) { const timestamps = await getTimestamps ( dir ) ; if ( timestamps . length === 0 ) return ; const now = Date . now ( ) ; const toKeep = timestampsToKeep ( timestamps , now ) ; const toRemove = timestamps . filter ( ts => ! toKeep . has ( ts ) ) ; await Promise . all ( toRemove . map ( ts => remove ( dir , ts ) ) ) ; }