"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.createObjectReadStream = exports.listKeysOfPrefix = exports.updatePrefixACL = exports.updateObjectACL = exports.makeAvailable = exports.removePrefix = exports.removeObjectByFullKey = exports.removeObject = exports.storeObject = exports.buildKey = void 0;
const tslib_1 = require("tslib");
const bluebird_1 = require("bluebird");
const fs_extra_1 = require("fs-extra");
const path_1 = require("path");
const client_s3_1 = require("@aws-sdk/client-s3");
const lib_storage_1 = require("@aws-sdk/lib-storage");
const core_utils_1 = require("../../../helpers/core-utils");
const misc_1 = require("../../../helpers/custom-validators/misc");
const logger_1 = require("../../../helpers/logger");
const config_1 = require("../../../initializers/config");
const urls_1 = require("../urls");
const client_1 = require("./client");
const logger_2 = require("./logger");
function listKeysOfPrefix(prefix, bucketInfo) {
    return tslib_1.__awaiter(this, void 0, void 0, function* () {
        const s3Client = (0, client_1.getClient)();
        const commandPrefix = bucketInfo.PREFIX + prefix;
        const listCommand = new client_s3_1.ListObjectsV2Command({
            Bucket: bucketInfo.BUCKET_NAME,
            Prefix: commandPrefix
        });
        const listedObjects = yield s3Client.send(listCommand);
        if ((0, misc_1.isArray)(listedObjects.Contents) !== true)
            return [];
        return listedObjects.Contents.map(c => c.Key);
    });
}
exports.listKeysOfPrefix = listKeysOfPrefix;
function storeObject(options) {
    return tslib_1.__awaiter(this, void 0, void 0, function* () {
        const { inputPath, objectStorageKey, bucketInfo, isPrivate } = options;
        logger_1.logger.debug('Uploading file %s to %s%s in bucket %s', inputPath, bucketInfo.PREFIX, objectStorageKey, bucketInfo.BUCKET_NAME, (0, logger_2.lTags)());
        const fileStream = (0, fs_extra_1.createReadStream)(inputPath);
        return uploadToStorage({ objectStorageKey, content: fileStream, bucketInfo, isPrivate });
    });
}
exports.storeObject = storeObject;
function updateObjectACL(options) {
    const { objectStorageKey, bucketInfo, isPrivate } = options;
    const key = buildKey(objectStorageKey, bucketInfo);
    logger_1.logger.debug('Updating ACL file %s in bucket %s', key, bucketInfo.BUCKET_NAME, (0, logger_2.lTags)());
    const command = new client_s3_1.PutObjectAclCommand({
        Bucket: bucketInfo.BUCKET_NAME,
        Key: key,
        ACL: getACL(isPrivate)
    });
    return (0, client_1.getClient)().send(command);
}
exports.updateObjectACL = updateObjectACL;
function updatePrefixACL(options) {
    const { prefix, bucketInfo, isPrivate } = options;
    logger_1.logger.debug('Updating ACL of files in prefix %s in bucket %s', prefix, bucketInfo.BUCKET_NAME, (0, logger_2.lTags)());
    return applyOnPrefix({
        prefix,
        bucketInfo,
        commandBuilder: obj => {
            logger_1.logger.debug('Updating ACL of %s inside prefix %s in bucket %s', obj.Key, prefix, bucketInfo.BUCKET_NAME, (0, logger_2.lTags)());
            return new client_s3_1.PutObjectAclCommand({
                Bucket: bucketInfo.BUCKET_NAME,
                Key: obj.Key,
                ACL: getACL(isPrivate)
            });
        }
    });
}
exports.updatePrefixACL = updatePrefixACL;
function removeObject(objectStorageKey, bucketInfo) {
    const key = buildKey(objectStorageKey, bucketInfo);
    return removeObjectByFullKey(key, bucketInfo);
}
exports.removeObject = removeObject;
function removeObjectByFullKey(fullKey, bucketInfo) {
    logger_1.logger.debug('Removing file %s in bucket %s', fullKey, bucketInfo.BUCKET_NAME, (0, logger_2.lTags)());
    const command = new client_s3_1.DeleteObjectCommand({
        Bucket: bucketInfo.BUCKET_NAME,
        Key: fullKey
    });
    return (0, client_1.getClient)().send(command);
}
exports.removeObjectByFullKey = removeObjectByFullKey;
function removePrefix(prefix, bucketInfo) {
    return tslib_1.__awaiter(this, void 0, void 0, function* () {
        logger_1.logger.debug('Removing prefix %s in bucket %s', prefix, bucketInfo.BUCKET_NAME, (0, logger_2.lTags)());
        return applyOnPrefix({
            prefix,
            bucketInfo,
            commandBuilder: obj => {
                logger_1.logger.debug('Removing %s inside prefix %s in bucket %s', obj.Key, prefix, bucketInfo.BUCKET_NAME, (0, logger_2.lTags)());
                return new client_s3_1.DeleteObjectCommand({
                    Bucket: bucketInfo.BUCKET_NAME,
                    Key: obj.Key
                });
            }
        });
    });
}
exports.removePrefix = removePrefix;
function makeAvailable(options) {
    return tslib_1.__awaiter(this, void 0, void 0, function* () {
        const { key, destination, bucketInfo } = options;
        yield (0, fs_extra_1.ensureDir)((0, path_1.dirname)(options.destination));
        const command = new client_s3_1.GetObjectCommand({
            Bucket: bucketInfo.BUCKET_NAME,
            Key: buildKey(key, bucketInfo)
        });
        const response = yield (0, client_1.getClient)().send(command);
        const file = (0, fs_extra_1.createWriteStream)(destination);
        yield (0, core_utils_1.pipelinePromise)(response.Body, file);
        file.close();
    });
}
exports.makeAvailable = makeAvailable;
function buildKey(key, bucketInfo) {
    return bucketInfo.PREFIX + key;
}
exports.buildKey = buildKey;
function createObjectReadStream(options) {
    return tslib_1.__awaiter(this, void 0, void 0, function* () {
        const { key, bucketInfo, rangeHeader } = options;
        const command = new client_s3_1.GetObjectCommand({
            Bucket: bucketInfo.BUCKET_NAME,
            Key: buildKey(key, bucketInfo),
            Range: rangeHeader
        });
        const response = yield (0, client_1.getClient)().send(command);
        return {
            response,
            stream: response.Body
        };
    });
}
exports.createObjectReadStream = createObjectReadStream;
function uploadToStorage(options) {
    return tslib_1.__awaiter(this, void 0, void 0, function* () {
        const { content, objectStorageKey, bucketInfo, isPrivate } = options;
        const input = {
            Body: content,
            Bucket: bucketInfo.BUCKET_NAME,
            Key: buildKey(objectStorageKey, bucketInfo),
            ACL: getACL(isPrivate)
        };
        const parallelUploads3 = new lib_storage_1.Upload({
            client: (0, client_1.getClient)(),
            queueSize: 4,
            partSize: config_1.CONFIG.OBJECT_STORAGE.MAX_UPLOAD_PART,
            leavePartsOnError: true,
            params: input
        });
        const response = (yield parallelUploads3.done());
        if (!response.Bucket) {
            const message = `Error uploading ${objectStorageKey} to bucket ${bucketInfo.BUCKET_NAME}`;
            logger_1.logger.error(message, Object.assign({ response }, (0, logger_2.lTags)()));
            throw new Error(message);
        }
        logger_1.logger.debug('Completed %s%s in bucket %s', bucketInfo.PREFIX, objectStorageKey, bucketInfo.BUCKET_NAME, (0, logger_2.lTags)());
        return (0, urls_1.getInternalUrl)(bucketInfo, objectStorageKey);
    });
}
function applyOnPrefix(options) {
    return tslib_1.__awaiter(this, void 0, void 0, function* () {
        const { prefix, bucketInfo, commandBuilder, continuationToken } = options;
        const s3Client = (0, client_1.getClient)();
        const commandPrefix = buildKey(prefix, bucketInfo);
        const listCommand = new client_s3_1.ListObjectsV2Command({
            Bucket: bucketInfo.BUCKET_NAME,
            Prefix: commandPrefix,
            ContinuationToken: continuationToken
        });
        const listedObjects = yield s3Client.send(listCommand);
        if ((0, misc_1.isArray)(listedObjects.Contents) !== true) {
            const message = `Cannot apply function on ${commandPrefix} prefix in bucket ${bucketInfo.BUCKET_NAME}: no files listed.`;
            logger_1.logger.error(message, Object.assign({ response: listedObjects }, (0, logger_2.lTags)()));
            throw new Error(message);
        }
        yield (0, bluebird_1.map)(listedObjects.Contents, object => {
            const command = commandBuilder(object);
            return s3Client.send(command);
        }, { concurrency: 10 });
        if (listedObjects.IsTruncated) {
            yield applyOnPrefix(Object.assign(Object.assign({}, options), { continuationToken: listedObjects.ContinuationToken }));
        }
    });
}
function getACL(isPrivate) {
    return isPrivate
        ? config_1.CONFIG.OBJECT_STORAGE.UPLOAD_ACL.PRIVATE
        : config_1.CONFIG.OBJECT_STORAGE.UPLOAD_ACL.PUBLIC;
}
//# sourceMappingURL=object-storage-helpers.js.map