"use strict";
// This file is part of HFS - Copyright 2021-2023, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
Object.defineProperty(exports, "__esModule", { value: true });
exports.zipStreamFromFolder = zipStreamFromFolder;
const vfs_1 = require("./vfs");
const misc_1 = require("./misc");
const QuickZipStream_1 = require("./QuickZipStream");
const fs_1 = require("fs");
const config_1 = require("./config");
const path_1 = require("path");
const serveFile_1 = require("./serveFile");
const const_1 = require("./const");
const api_get_file_list_1 = require("./api.get_file_list");
const comments_1 = require("./comments");
const urlList_1 = require("./urlList");
// expects 'node' to have had permissions checked by caller
async function zipStreamFromFolder(node, ctx) {
    const list = (0, urlList_1.decodeUrlList)((0, misc_1.wantArray)(ctx.query.list)[0]);
    if (!list && (0, vfs_1.statusCodeForMissingPerm)(node, 'can_archive', ctx))
        return;
    ctx.status = const_1.HTTP_OK;
    ctx.mime = 'zip';
    // ctx.query.list is undefined | string | string[]
    const name = list?.length === 1 ? (0, misc_1.safeDecodeURIComponent)((0, path_1.basename)(list[0]), '') : (0, vfs_1.getNodeName)(node);
    try {
        (0, serveFile_1.forceDownload)(ctx, ((0, misc_1.isWindowsDrive)(name) ? name[0] : (name || 'archive')) + '.zip');
    }
    catch {
        return ctx.status = 400;
    }
    const { filterName, filterComment } = (0, api_get_file_list_1.paramsToFilter)(ctx.query);
    const walker = !list ? (0, vfs_1.walkNode)(node, { ctx, requiredPerm: 'can_archive' })
        : (async function* () {
            for await (const uri of list) {
                if (ctx.isAborted())
                    return;
                const subNode = await (0, vfs_1.urlToNode)(uri, ctx, node);
                if (!subNode)
                    continue;
                if ((0, vfs_1.nodeIsFolder)(subNode)) { // a directory needs to be walked
                    if ((0, vfs_1.hasPermission)(subNode, 'can_list', ctx) && (0, vfs_1.hasPermission)(subNode, 'can_archive', ctx)) {
                        yield subNode; // it could be empty
                        yield* (0, vfs_1.walkNode)(subNode, { ctx, prefixPath: (0, misc_1.pathDecodeSegments)(uri) + '/', requiredPerm: 'can_archive' });
                    }
                    continue;
                }
                let folder = (0, path_1.dirname)(decodeURIComponent(uri)); // decodeURI() won't account for %23=# . uri is safe because otherwise urlToNode above would have already returned undefined
                folder = folder === '.' ? '' : folder + '/';
                yield { ...subNode, name: folder + (0, vfs_1.getNodeName)(subNode) }; // reflect relative path in archive, otherwise way may have name-clashes
            }
        })();
    const mappedWalker = (0, misc_1.filterMapGenerator)(walker, async (el) => {
        if ((0, vfs_1.nodeIsLink)(el))
            return;
        if (!(0, vfs_1.hasPermission)(el, 'can_archive', ctx))
            return; // the fact you see it doesn't mean you can get it
        const { source } = el;
        let name = (0, vfs_1.getNodeName)(el);
        if (!const_1.IS_WINDOWS) // posix supports \ in file names, but zip tools don't
            name = name.replaceAll('\\', '_');
        if (filterName && !filterName(name)
            || filterComment && !filterComment(await (0, comments_1.getCommentFor)(source) || ''))
            return;
        try {
            if ((0, vfs_1.nodeIsFolder)(el))
                return { path: name + '/' };
            if (!source)
                return;
            const st = await (el.stats || (0, misc_1.statWithTimeout)(source));
            if (!st || !st.isFile())
                return;
            return {
                path: name,
                size: st.size,
                ts: st.mtime,
                mode: st.mode,
                sourcePath: source,
                getData: () => (0, fs_1.createReadStream)(source, { start: 0, end: Math.max(0, st.size - 1) })
            };
        }
        catch { }
    });
    const zip = new QuickZipStream_1.QuickZipStream(mappedWalker);
    const time = 1000 * zipSeconds.get();
    const size = await zip.calculateSize(time);
    const range = (0, serveFile_1.applyRange)(ctx, size); // keep var size as ctx.response.length won't preserve a NaN
    if (ctx.status >= 400)
        return;
    if (range)
        zip.applyRange(range.start, range.end);
    ctx.body = zip;
    ctx.req.on('close', () => zip.destroy());
    ctx.state.archive = 'zip';
    (0, serveFile_1.monitorAsDownload)(ctx, size, range?.start);
}
const zipSeconds = (0, config_1.defineConfig)('zip_calculate_size_for_seconds', 5);
