@bitblit/ratchet-aws
Version:
Common tools for use with AWS browser and node
128 lines • 5.68 kB
JavaScript
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