"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
    __setModuleDefault(result, mod);
    return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getFilesFromStorage = exports.uploadMultiPartFile = exports.geThumbnailV2FromStorage = exports.geThumbnailFromStorage = exports.getFileV2FromStorage = exports.getFileFromStorage = exports.getUploadFileV2 = exports.handleListFilesREST = exports.handleGetThumbnail = exports.handleUploadFile = exports.handleGetFile = exports.multipartForm = void 0;
const rest_1 = require("../controllers/rest");
const storage_1 = require("../controllers/storage");
const http_status_codes_1 = __importStar(require("http-status-codes"));
const stream_1 = require("stream");
const buffer_1 = require("buffer");
const database_1 = require("../controllers/database");
const policy_1 = require("../controllers/policy");
const util_1 = require("util");
const fs_1 = require("fs");
const formidable_1 = __importDefault(require("formidable"));
const mime = __importStar(require("mime"));
function filePolicy(request, response, next, databaseAdapter, options) {
    (0, policy_1.ruleHasPermission)(request.body.ruleId, request.body.context, databaseAdapter, options).then(value => {
        if (value === true) {
            next();
        }
        else {
            throw { message: 'You can\'t access this file' };
        }
    }).catch(reason => {
        response.status(http_status_codes_1.StatusCodes.UNAUTHORIZED).send(reason);
    });
}
function multipartForm(request, response, _, filesAdapter, options) {
    const contentType = request.get('content-type').split(';')[0].toString().trim();
    if (contentType !== 'multipart/form-data'.trim()) {
        response.status(http_status_codes_1.StatusCodes.BAD_REQUEST).json({ message: 'Accept only multipart request' });
        return;
    }
    const form = (0, formidable_1.default)({
        multiples: true,
        maxFileSize: 10 * 1024 * 1024 * 1024,
        keepExtensions: true
    });
    form.parse(request, async (err, fields, files) => {
        try {
            if (err) {
                response.status(http_status_codes_1.StatusCodes.BAD_REQUEST).send(err.toString());
                return;
            }
            const urls = [];
            if (request && request.query && request.query.pn && request.query.pn.toString().trim().toLowerCase() === 'true') {
                request.body.context.storage = { preserveName: true };
            }
            else {
                request.body.context.storage = { preserveName: false };
            }
            const context = request.body.context;
            for (const file of Object.values(files)) {
                const fileMeta = { name: undefined, type: undefined };
                const regx = /[^0-9a-z.]/gi;
                fileMeta.name = file.originalFilename ? file.originalFilename : file.newFilename.toString().replace(regx, '');
                fileMeta.type = file.type;
                const data = {
                    data: await (0, util_1.promisify)(fs_1.readFile)(file.filepath),
                    type: fileMeta.type,
                    size: file.size,
                    name: fileMeta.name
                };
                const result = await (0, storage_1.saveFromBuffer)(data, context, filesAdapter, options);
                urls.push(result);
            }
            for (const f_key of Object.keys(fields)) {
                const fileMeta = { name: undefined, type: undefined };
                const regx = /[^0-9a-z.]/gi;
                fileMeta.name = f_key
                    .toString()
                    .replace(regx, '');
                const data = {
                    data: buffer_1.Buffer.from(fields[f_key]),
                    type: fileMeta.type,
                    size: fields[f_key]?.length,
                    name: fileMeta.name
                };
                fileMeta.type = mime.getType(f_key);
                const result = await (0, storage_1.saveFromBuffer)(data, context, filesAdapter, options);
                urls.push(result);
            }
            response.status(http_status_codes_1.StatusCodes.OK).json({ urls });
        }
        catch (e) {
            response.status(http_status_codes_1.StatusCodes.BAD_REQUEST).end(e.toString());
        }
    });
}
exports.multipartForm = multipartForm;
async function getStorage(id, databaseAdapter, options) {
    const wOptions = { bypassDomainVerification: true };
    const rule = { id: id, return: [] };
    const f = await (0, database_1.findDataByIdInStore)('_Storage', rule, databaseAdapter, wOptions, options);
    if (!f)
        throw { message: "File not found" };
    return f;
}
function handleGetFile(filesAdapter, databaseAdapter, optionsFn) {
    return [
        (request, _, next) => {
            request.body.applicationId = request.params.appId;
            request.body.ruleId = 'files.read';
            next();
        },
        (rq, rs, n) => (0, rest_1.verifyApplicationId)(rq, rs, n, optionsFn(rq)),
        (rq, rs, n) => (0, rest_1.verifyRequestToken)(rq, rs, n, optionsFn(rq)),
        (rq, rs, n) => filePolicy(rq, rs, n, databaseAdapter, optionsFn(rq)),
        (request, response) => {
            const options = optionsFn(request);
            if (request.method.toLowerCase() === 'head') {
                (0, storage_1.fileInfo)(request, response, filesAdapter, options);
            }
            else {
                const filename = request.params.filename;
                if ((0, storage_1.isS3)(filesAdapter) === true) {
                    return (0, storage_1.handleGetFileBySignedUrl)(filename, null, null, false, filesAdapter, options);
                }
                else {
                    getStorage(filename, databaseAdapter, options).then(f => {
                        response.set({
                            'Content-Type': typeof f.type === 'string' ? f.type : 'application/octet-stream',
                            'Content-Length': typeof f.size === 'number' ? f.size : 0,
                            'Content-Disposition': `attachment; filename="${f.name}.${f.extension}"`,
                        });
                        return (0, storage_1.handleGetFileRequest)(f, null, null, false, filesAdapter, options);
                    }).then(value => {
                        returnFile(value, response);
                    }).catch(reason => {
                        response.status(http_status_codes_1.default.BAD_REQUEST).send(reason);
                    });
                }
            }
        }
    ];
}
exports.handleGetFile = handleGetFile;
function returnFile(value, response) {
    if (typeof value === "string") {
        response.redirect(value);
    }
    else {
        if (value instanceof buffer_1.Buffer) {
            response.status(http_status_codes_1.default.OK).send(value);
        }
        else if (value instanceof stream_1.Readable) {
            value.pipe(response);
        }
        else {
            throw { message: 'file data can not be determined' };
        }
    }
}
function handleUploadFile(filesAdapter, databaseAdapter, optionsFn) {
    return [
        (request, response, next) => {
            request.body.applicationId = request.params.appId;
            request.body.ruleId = 'files.save';
            next();
        },
        (rq, rs, n) => (0, rest_1.verifyApplicationId)(rq, rs, n, optionsFn(rq)),
        (rq, rs, n) => (0, rest_1.verifyRequestToken)(rq, rs, n, optionsFn(rq)),
        (rq, rs, n) => filePolicy(rq, rs, n, databaseAdapter, optionsFn(rq)),
        (rq, rs, n) => multipartForm(rq, rs, n, filesAdapter, optionsFn(rq))
    ];
}
exports.handleUploadFile = handleUploadFile;
function handleGetThumbnail(filesAdapter, databaseAdapter, optionsFn) {
    return [
        (request, _, next) => {
            request.body.applicationId = request.params.appId;
            request.body.ruleId = 'files.read';
            next();
        },
        (rq, rs, n) => (0, rest_1.verifyApplicationId)(rq, rs, n, optionsFn(rq)),
        (rq, rs, n) => (0, rest_1.verifyRequestToken)(rq, rs, n, optionsFn(rq)),
        (rq, rs, n) => filePolicy(rq, rs, n, databaseAdapter, optionsFn(rq)),
        (request, response) => {
            const options = optionsFn(request);
            if (request.method.toLowerCase() === 'head') {
                (0, storage_1.fileInfo)(request, response, filesAdapter, options);
            }
            else {
                const width = parseInt(request.query.width ? request.query.width : 100);
                let height = parseInt(request.query.height ? request.query.height : -1);
                if (height === -1) {
                    height = undefined;
                }
                const filename = request.params.filename;
                if ((0, storage_1.isS3)(filesAdapter) === true) {
                    return (0, storage_1.handleGetFileBySignedUrl)(filename, null, null, true, filesAdapter, options);
                }
                else {
                    getStorage(filename, databaseAdapter, options).then(f => {
                        response.set({
                            'Content-Disposition': `attachment; filename="${f.name}.${f.extension}"`,
                        });
                        return (0, storage_1.handleGetFileRequest)(f, width, height, true, filesAdapter, options);
                    }).then(value => {
                        returnFile(value, response);
                    }).catch(reason => {
                        response.status(http_status_codes_1.default.BAD_REQUEST).send(reason);
                    });
                }
            }
        }
    ];
}
exports.handleGetThumbnail = handleGetThumbnail;
function handleListFilesREST(filesAdapter, databaseAdapter, optionsFn) {
    return [
        (request, _, next) => {
            request.body.applicationId = request.params.appId;
            request.body.ruleId = 'files.list';
            next();
        },
        (rq, rs, n) => (0, rest_1.verifyApplicationId)(rq, rs, n, optionsFn(rq)),
        (rq, rs, n) => (0, rest_1.verifyRequestToken)(rq, rs, n, optionsFn(rq)),
        (rq, rs, n) => filePolicy(rq, rs, n, databaseAdapter, optionsFn(rq)),
        (request, response) => {
            const options = optionsFn(request);
            const query = {
                skip: isNaN(Number(request.query.skip)) ? 0 : parseInt(request.query.skip),
                after: request.query.after ? request.query.after.toString() : '',
                size: isNaN(Number(request.query.size)) ? 20 : parseInt(request.query.size),
                prefix: request.query.prefix ? request.query.prefix.toString() : '',
            };
            (0, storage_1.listFilesFromStore)(query, filesAdapter, options).then(value => {
                response.json(value);
            }).catch(reason => {
                response.status(http_status_codes_1.StatusCodes.EXPECTATION_FAILED).send({ message: reason.toString() });
            });
        }
    ];
}
exports.handleListFilesREST = handleListFilesREST;
function getUploadFileV2(prefix = '/') {
    return {
        path: `${prefix}storage/:appId`,
        method: 'GET',
        onRequest: (request, response) => {
            response.writeHead(200, { 'content-type': 'text/html' });
            response.end(`
                <h2>With Node.js <code>"http"</code> module</h2>
                <form action="${prefix}storage/${request.params.appId}" enctype="multipart/form-data" method="post">
                  <div>File: <input type="file" name="multipleFiles" multiple="multiple" /></div>
                  <input type="submit" value="Upload" />
                </form>
            `);
        }
    };
}
exports.getUploadFileV2 = getUploadFileV2;
function getFileFromStorage(prefix = '/', filesAdapter, databaseAdapter, optionsFn) {
    return {
        path: `${prefix}storage/:appId/file/:filename`,
        method: 'GET',
        onRequest: handleGetFile(filesAdapter, databaseAdapter, optionsFn)
    };
}
exports.getFileFromStorage = getFileFromStorage;
function getFileV2FromStorage(prefix = '/', filesAdapter, databaseAdapter, optionsFn) {
    return {
        path: `${prefix}v2/storage/:appId/file/:filename`,
        method: 'GET',
        onRequest: handleGetFile(filesAdapter, databaseAdapter, optionsFn)
    };
}
exports.getFileV2FromStorage = getFileV2FromStorage;
function geThumbnailFromStorage(prefix = '/', filesAdapter, databaseAdapter, optionsFn) {
    return {
        path: `${prefix}storage/:appId/file/:filename/thumbnail`,
        method: 'GET',
        onRequest: handleGetThumbnail(filesAdapter, databaseAdapter, optionsFn)
    };
}
exports.geThumbnailFromStorage = geThumbnailFromStorage;
function geThumbnailV2FromStorage(prefix = '/', filesAdapter, databaseAdapter, optionsFn) {
    return {
        path: `${prefix}v2/storage/:appId/file/:filename/thumbnail`,
        method: 'GET',
        onRequest: handleGetThumbnail(filesAdapter, databaseAdapter, optionsFn)
    };
}
exports.geThumbnailV2FromStorage = geThumbnailV2FromStorage;
function uploadMultiPartFile(prefix = '/', filesAdapter, databaseAdapter, optionsFn) {
    return {
        path: `${prefix}storage/:appId`,
        method: 'POST',
        onRequest: handleUploadFile(filesAdapter, databaseAdapter, optionsFn)
    };
}
exports.uploadMultiPartFile = uploadMultiPartFile;
function getFilesFromStorage(prefix = '/', filesAdapter, databaseAdapter, optionsFn) {
    return {
        path: `${prefix}storage/:appId/list`,
        method: 'GET',
        onRequest: handleListFilesREST(filesAdapter, databaseAdapter, optionsFn)
    };
}
exports.getFilesFromStorage = getFilesFromStorage;
