UNPKG

@bitblit/ratchet-aws

Version:

Common tools for use with AWS browser and node

128 lines 5.68 kB
import { RequireRatchet } from '@bitblit/ratchet-common/lang/require-ratchet'; import { Logger } from '@bitblit/ratchet-common/logger/logger'; import { StopWatch } from '@bitblit/ratchet-common/lang/stop-watch'; import { DateTime } from 'luxon'; import { StringRatchet } from '@bitblit/ratchet-common/lang/string-ratchet'; import { BackupResult } from '@bitblit/ratchet-common/network/remote-file-tracker/backup-result'; import { RemoteFileTracker } from '@bitblit/ratchet-common/network/remote-file-tracker/remote-file-tracker'; import { ErrorRatchet } from '@bitblit/ratchet-common/lang/error-ratchet'; import { FileTransferResultType } from '@bitblit/ratchet-common/network/remote-file-tracker/file-transfer-result-type'; export class S3RemoteFileTrackingProvider { opts; constructor(opts) { this.opts = opts; RequireRatchet.notNullOrUndefined(opts, 'opts'); RequireRatchet.notNullOrUndefined(opts.s3CacheRatchet, 'opts.s3CacheRatchet'); RequireRatchet.notNullOrUndefined(opts.s3CacheRatchet.getDefaultBucket(), 'opts.s3CacheRatchet must have default bucket set'); } async readRemoteStatus(key) { let rval = null; if (StringRatchet.trimToNull(key)) { const meta = await this.opts.s3CacheRatchet.fetchMetaForCacheFile(key); if (meta) { rval = { key: key, statusTakenEpochMs: Date.now(), remoteSizeInBytes: meta.ContentLength, remoteLastUpdatedEpochMs: meta.LastModified.getTime(), remoteHash: meta.ETag, }; } } return rval; } async pullRemoteData(key, ifNewerThan) { let rval = null; const sw = new StopWatch(); const req = { Bucket: this.opts.s3CacheRatchet.getDefaultBucket(), Key: key, IfModifiedSince: ifNewerThan?.remoteLastUpdatedEpochMs ? new Date(ifNewerThan.remoteLastUpdatedEpochMs) : null, }; const output = await this.opts.s3CacheRatchet.fetchCacheFilePassThru(req); if (output) { rval = { status: { key: key, statusTakenEpochMs: Date.now(), remoteSizeInBytes: output.ContentLength, remoteLastUpdatedEpochMs: output.LastModified.getTime(), remoteHash: output.ETag, }, content: output.Body.transformToWebStream(), }; Logger.info('Fetched remote to local, %d bytes in %s : %s', output.ContentLength, sw.dump(), key); } else { Logger.info('Did not pull %s - not modified', key); } return rval; } async sendDataToRemote(src, key, opts, checkStatus) { RequireRatchet.notNullOrUndefined(src, 'src'); RequireRatchet.notNullOrUndefined(key, 'key'); RequireRatchet.notNullOrUndefined(opts, 'opts'); const rval = { type: null, error: null, bytesTransferred: null, backupResult: BackupResult.NotRequested, }; const sw = new StopWatch(); Logger.info('Sending local data to remote : %s : %j : %j', key, opts, checkStatus); if (!opts.force && checkStatus?.remoteLastUpdatedEpochMs) { sw.start('statusCheck'); const current = await this.readRemoteStatus(key); if (!RemoteFileTracker.statusMatch(checkStatus, current)) { rval.type = FileTransferResultType.Error; rval.error = 'CheckStatus did not match, was ' + JSON.stringify(checkStatus) + ' but current is ' + JSON.stringify(current) + ' and force not specified'; rval.bytesTransferred = 0; return rval; } sw.stop('statusCheck'); Logger.info('Performed status check in %s', sw.dump('statusCheck')); } if (opts.backup) { sw.start('backup'); rval.backupResult = await this.backupRemote(key); sw.stop('backup'); Logger.info('Performed backup in %s', sw.dump('backup')); } try { sw.start('send'); const out = await this.opts.s3CacheRatchet.writeStreamToCacheFile(key, src); sw.stop('send'); Logger.info('Sent to remote in %s : %j', sw.dump('send'), out); } catch (err) { Logger.error('Failed to write %s - %s', key, err, err); rval.type = FileTransferResultType.Error; rval.error = ErrorRatchet.safeStringifyErr(err); rval.bytesTransferred = 0; } Logger.info('Overall timing : %s', sw.dump()); return rval; } async backupRemote(key) { let rval = null; try { const lastSlash = key.lastIndexOf('/'); const datePart = '/backup/' + DateTime.now().toFormat('yyyy/MM/dd/HH/mm/ss') + '/'; const newPath = lastSlash > -1 ? key.substring(0, lastSlash) + datePart + key.substring(lastSlash + 1) : datePart + key; Logger.info('Backing up path %s to %s', key, newPath); await this.opts.s3CacheRatchet.copyFile(key, newPath); rval = BackupResult.Success; } catch (err) { Logger.error('Failed to backup %s : %s', key, err, err); rval = BackupResult.Error; } return rval; } } //# sourceMappingURL=s3-remote-file-tracking-provider.js.map