"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
const ChildProcess = tslib_1.__importStar(require("child_process"));
const Path = tslib_1.__importStar(require("path"));
const FSE = tslib_1.__importStar(require("fs-extra"));
const js_yaml_1 = tslib_1.__importDefault(require("js-yaml"));
const lodash_1 = tslib_1.__importDefault(require("lodash"));
const v = tslib_1.__importStar(require("villa"));
const _utils_1 = require("../../@utils");
function NETWORK_NAME(workspace) {
    return `${workspace.id}-network`;
}
// workspace
function WORKSPACE_SOURCE_PATH(config, workspace) {
    return Path.join(config.dir, 'workspaces', workspace.id);
}
// workspace metadata
function WORKSPACE_METADATA_SOURCE_PATH(config, workspace) {
    return Path.join(WORKSPACE_SOURCE_PATH(config, workspace), 'metadata.json');
}
// repositories
class WorkspaceFiles {
    constructor(config) {
        this.config = config;
    }
    async update(workspaces) {
        let config = this.config;
        console.info('Updating authorized keys...');
        await _utils_1.writeTextFileToVolume(config.name, 'user-ssh', 'authorized_keys', await this.buildAuthorizedKeys());
        console.info('Updating initialize identity...');
        await _utils_1.writeTextFileToVolume(config.name, 'user-ssh', 'initialize-identity', config.identity, '600');
        console.info('Updating workspace metadata...');
        for (let workspace of workspaces) {
            await FSE.outputFile(WORKSPACE_METADATA_SOURCE_PATH(config, workspace), JSON.stringify(workspace.raw, undefined, 2));
        }
        console.info('Updating docker-compose.yml...');
        await FSE.outputFile(Path.join(config.dir, 'docker-compose.yml'), this.buildDockerComposeYAML(workspaces));
    }
    async prune(workspaces) {
        let workspacesPath = Path.join(this.config.dir, 'workspaces');
        let workspaceIdSet = new Set(workspaces.map(workspace => workspace.id));
        let ids;
        try {
            ids = await FSE.readdir(workspacesPath);
        }
        catch (_a) {
            ids = [];
        }
        let outdatedIds = ids.filter(id => !workspaceIdSet.has(id));
        for (let id of outdatedIds) {
            console.info(`Removing outdated workspace "${id}"...`);
            await FSE.remove(Path.join(workspacesPath, id));
        }
        console.info('Pruning docker containers...');
        await v.awaitable(ChildProcess.spawn('docker', ['container', 'prune', '--force']));
        console.info('Pruning docker networks...');
        await v.awaitable(ChildProcess.spawn('docker', ['network', 'prune', '--force']));
    }
    buildDockerComposeYAML(workspaces) {
        let config = this.config;
        let sharedVolumes = [
            {
                type: 'volume',
                source: 'repositories',
                target: '/root/repositories',
            },
            {
                type: 'volume',
                source: 'vscode-extensions',
                target: '/root/.vscode-server/extensions',
            },
            {
                type: 'volume',
                source: 'vscode-machine-data',
                target: '/root/.vscode-server/data/Machine',
            },
            {
                type: 'volume',
                source: 'user-ssh',
                target: '/root/.ssh',
            },
            {
                type: 'volume',
                source: 'ssh',
                target: '/etc/ssh',
            },
            ...config.sharedVolumes,
        ];
        let document = {
            version: '3.2',
            services: lodash_1.default.fromPairs(lodash_1.default.flatMap(workspaces, workspace => {
                return [
                    [
                        workspace.id,
                        {
                            image: workspace.image,
                            labels: {
                                // Recreate workspace container on workspace options change.
                                'remote-workspace.hash': workspace.hash,
                            },
                            restart: 'always',
                            volumes: [
                                {
                                    type: 'bind',
                                    source: WORKSPACE_SOURCE_PATH(config, workspace),
                                    target: '/root/workspace',
                                },
                                ...sharedVolumes,
                            ],
                            networks: [NETWORK_NAME(workspace)],
                            ports: [`${workspace.port}:22`],
                        },
                    ],
                    ...workspace.services.map(({ name, ...service }) => [
                        `${workspace.id}_${name}`,
                        {
                            ...service,
                            restart: 'always',
                            networks: {
                                [NETWORK_NAME(workspace)]: {
                                    aliases: [name],
                                },
                            },
                        },
                    ]),
                ];
            })),
            volumes: lodash_1.default.fromPairs(sharedVolumes
                .filter(volume => volume.type === 'volume')
                // tslint:disable-next-line: no-null-keyword
                .map(({ source }) => [source, null])),
            networks: lodash_1.default.fromPairs(workspaces.map(workspace => [
                NETWORK_NAME(workspace),
                // tslint:disable-next-line: no-null-keyword
                null,
            ])),
        };
        return [
            '# Generated file, changes directly made to this file will be overridden.',
            js_yaml_1.default.dump(document),
        ].join('\n');
    }
    async buildAuthorizedKeys() {
        let config = this.config;
        let lines = await v.map(config.users, async ({ name, email, publicKey, publicKeyFile }) => {
            if (publicKeyFile) {
                publicKey = await FSE.readFile(Path.join(config.dir, publicKeyFile), 'utf8');
            }
            if (!publicKey) {
                throw new Error(`Missing public key for user "${name}".`);
            }
            let line = [
                [
                    `environment="REMOTE_USER_NAME=${name}"`,
                    `environment="GIT_AUTHOR_NAME=${name}"`,
                    `environment="GIT_AUTHOR_EMAIL=${email}"`,
                    `environment="GIT_COMMITTER_NAME=${name}"`,
                    `environment="GIT_COMMITTER_EMAIL=${email}"`,
                ].join(','),
                publicKey.trim(),
            ].join(' ');
            return `${line}\n`;
        });
        return [
            '# Generated file, changes directly made to this file will be overridden.',
            lines.join(''),
        ].join('\n');
    }
}
exports.WorkspaceFiles = WorkspaceFiles;
//# sourceMappingURL=@workspace-files.js.map