/*
 * Repo-specific actions - such as deactivation, deletion etc.
 * routes/api/repo.js
 */
const async = require('async');
const common = require('../../common');
const debug = require('debug')('strider:routes:api:repo');
const Job = require('../../models').Job;
const Project = require('../../models').Project;
const ssh = require('../../utils/ssh');
const User = require('../../models').User;
const utils = require('../../utils');
function makePlugins(plugins) {
    const configs = [];
    let plugin;
    for (let i = 0; i < plugins.length; i++) {
        plugin = common.extensions.job[plugins[i]];
        if (!plugin)
            return false;
        const config = utils.defaultSchema(plugin);
        configs.push({
            id: plugins[i],
            enabled: true,
            config: config,
        });
    }
    return configs;
}
/**
 * @api {delete} /:org/:repo/cache Clear Cache
 * @apiUse ProjectReference
 * @apiPermission ProjectAdmin
 * @apiDescription Clears/invalidates the cache for a project.
 * @apiName ClearCache
 * @apiGroup Repo
 * @apiVersion 1.0.0
 *
 * @apiExample {curl} CURL Example:
 *    curl -X DELETE http://localhost/api/strider-cd/strider/cache
 */
exports.clearCache = function (req, res) {
    clearProjectCache(req.project, function (err, result) {
        if (err) {
            return res.status(500).send('failed to clear cache');
        }
        if (result) {
            res.send(result);
        }
        else {
            res.sendStatus(204);
        }
    });
};
function clearProjectCache(project, cb) {
    const runners = [];
    const tasks = [];
    project.branches.forEach(function (branch) {
        const nonMasterMirrored = branch.name !== 'master' && branch.mirror_master;
        if (nonMasterMirrored || runners.indexOf(branch.runner.id) !== -1) {
            return;
        }
        runners.push(branch.runner.id);
    });
    runners.forEach(function (rid) {
        const runner = common.extensions.runner[rid];
        debug(rid, common.extensions.runner, project);
        if (!runner || !runner.clearCache)
            return;
        tasks.push(runner.clearCache.bind(runner, project));
    });
    if (!tasks.length) {
        return cb(undefined, 'No runners supported cache clearing');
    }
    async.parallel(tasks, cb);
}
/**
 * @api {put} /:org Create Repo
 * @apiDescription Create a new project for a repo.
 * @apiName CreateRepo
 * @apiGroup Repo
 * @apiVersion 1.0.0
 *
 * @apiParam (RequestBody) {String} name The name of the new branch
 * @apiParam (RequestBody) {String} display_name Human-readable project name
 * @apiParam (RequestBody) {String} display_url The URL for the repo (e.g. Github homepage)
 * @apiParam (RequestBody) {Boolean} public=false Whether this project is public or not.
 * @apiParam (RequestBody) {Boolean} prefetch_config=true Whether the strider.json should be fetched in advance.
 * @apiParam (RequestBody) {String} account The ID of provider account
 * @apiParam (RequestBody) {String} repo_id The ID of the repo
 * @apiParam (RequestBody) {Object} provider A json object with 'id' and 'config' properties.
 */
exports.createProject = function (req, res, next) {
    if (req.params.org === 'auth') {
        return next();
    }
    let name = `${req.params.org}/${req.params.repo}`;
    debug(`Setting up new project "${name}"...`);
    const display_name = req.body.display_name;
    const display_url = req.body.display_url;
    const isPublic = req.body.public === 'true' || req.body.public === '1';
    let prefetch_config = true;
    const project_type = req.body.project_type || 'node.js';
    if (req.body.prefetch_config === 'false' ||
        req.body.prefetch_config === '0') {
        prefetch_config = false;
    }
    const provider = req.body.provider;
    function error(code, str) {
        return res.status(code).json({
            results: [],
            status: 'error',
            errors: [{ code: code, reason: str }],
        });
    }
    if (!display_name) {
        return error(400, 'display_name is required');
    }
    if (!provider || !provider.id) {
        return error(400, 'provider.id is required');
    }
    if (common.extensions.provider[provider.id].hosted) {
        if (!provider.account) {
            return error(400, 'provider.account is required');
        }
        if (!provider.repo_id) {
            return error(400, 'provider.repo_id is required');
        }
    }
    if (!provider.config) {
        provider.config = utils.defaultSchema(provider.config);
    }
    if (!common.project_types[project_type]) {
        return error(400, 'Invalid project type specified');
    }
    const plugins = makePlugins(common.project_types[project_type].plugins);
    if (!plugins) {
        return error(400, 'Project type specified is not available; one or more required plugins is not installed');
    }
    function projectResult(err, project) {
        if (project) {
            debug(`User ${req.user.email} tried to create project for repo ${name}, but it already exists`);
            return error(409, 'project already exists');
        }
        return ssh.generateKeyPair(`${name}-${req.user.email}`, createProjectWithKey);
    }
    function createProjectWithKey(err, privkey, pubkey) {
        if (err)
            return error(500, 'Failed to generate ssh keypair');
        const project = {
            name: name,
            display_name: display_name,
            display_url: display_url,
            public: isPublic,
            prefetch_config: prefetch_config,
            creator: req.user._id,
            provider: provider,
            branches: [
                {
                    name: 'master',
                    active: true,
                    mirror_master: false,
                    deploy_on_green: true,
                    deploy_on_pull_request: false,
                    pubkey: pubkey,
                    privkey: privkey,
                    plugins: plugins,
                    runner: {
                        id: 'simple-runner',
                        config: { pty: false },
                    },
                },
                {
                    name: '*',
                    mirror_master: true,
                },
            ],
        };
        const plugin = common.extensions.provider[provider.id];
        if (!plugin.hosted || !plugin.setupRepo) {
            return Project.create(project, projectCreated);
        }
        debug(`Setting up repository "${project.name}" with provider "${provider.id}"...`);
        plugin.setupRepo(req.user.account(provider).config, provider.config, project, function (err, config) {
            if (err) {
                debug(`Setting up repository "${project.name}" failed!`, err.status, err.message);
                return error(500, `Failed to setup repo: ${err.message}`);
            }
            project.provider.config = config;
            Project.create(project, projectCreated);
        });
    }
    function projectCreated(err, p) {
        if (err) {
            debug(`Error creating repo ${name} for user ${req.user.email}: ${err}`);
            debug(err.stack);
            return error(500, 'internal server error');
        }
        // Project object created, add to User object
        User.updateOne({ _id: req.user._id }, {
            $push: {
                projects: {
                    name: name,
                    display_name: p.display_name,
                    access_level: 2,
                },
            },
        }, function (err, num) {
            if (err || !num)
                debug('Failed to give the creator repo access...');
            return res.json({
                project: {
                    _id: p._id,
                    name: p.name,
                    display_name: p.display_name,
                },
                results: [{ code: 200, message: 'project created' }],
                status: 'ok',
                errors: [],
            });
        });
    }
    name = name.toLowerCase().replace(/ /g, '-');
    Project.findOne({ name: name }, projectResult);
};
/**
 * @api {delete} /:org/:repo Delete Repo
 * @apiUse ProjectReference
 * @apiPermission ProjectAdmin
 * @apiDescription Deletes a repository/project. Also archives all jobs (marks as archived in DB which makes them hidden).
 * @apiName DeleteRepo
 * @apiGroup Repo
 * @apiVersion 1.0.0
 *
 * @apiExample {curl} CURL Example:
 *    curl -X DELETE http://localhost/api/strider-cd/strider
 */
exports.deleteProject = function (req, res) {
    async.parallel([
        function (next) {
            const provider = req.project.provider;
            const plugin = common.extensions.provider[provider.id];
            if (!plugin.hosted || !plugin.teardownRepo)
                return next();
            plugin.teardownRepo(req.project.creator.account(provider).config, provider.config, req.project, function (err) {
                if (err)
                    debug('Error while tearing down repo', req.project.name, provider.id, err);
                next();
            });
        },
        req.project.remove.bind(req.project),
        function (next) {
            clearProjectCache(req.project, function (error) {
                next(error);
            });
        },
        function (next) {
            const now = new Date();
            Job.updateOne({ project: req.project.name }, { $set: { archived: now } }, { multi: true }, next);
        },
    ], function (err) {
        if (err) {
            debug('repo.delete_index() - Error deleting repo config for url %s by user %s: %s', req.project.name, req.user.email, err);
            return res.status(500).send(`Failed to delete project: ${err.message}`);
        }
        const r = {
            errors: [],
            status: 'ok',
            results: [],
        };
        res.send(JSON.stringify(r, null, '\t'));
    });
};
//# sourceMappingURL=repo.js.map