projects.js 10.8 KB
'use strict';

import Router from 'koa-router';
import moment from 'moment';
import Rp from 'request-promise';
import Build from '../../ci/build';
import Deploy from '../../ci/deploy';
import Restart from '../../ci/restart';
import Operation from '../../logger/operation';
import ws from '../../../lib/ws';

import {
    Building,
    Project,
    Server,
    DeployInfo,
    RestartInfo
} from '../../models';

let r = new Router();

const colors = ['primary', 'success', 'info', 'warning', 'danger', 'success-alt', 'info-alt', 'warning-alt', 'danger-alt', 'primary-head', 'success-head', 'danger-head'];
const envs = {
    production: '线上环境',
    preview: '灰度环境',
    test: '测试环境'
};

const p = {
    /**
     * 所有项目首页
     */
    index_page: async(ctx, next) => {
        let projects = await Project.findAll();
        let awsProjects = [];
        let qcloudProject = [];

        projects.forEach((p, i) => {
            p.color = colors[i % colors.length];
            if (p.cloud === 'aws') {
                awsProjects.push(p);
            } else {
                qcloudProject.push(p);
            }
        });
        await ctx.render('action/projects', {
            awsProjects: awsProjects,
            qcloudProject: qcloudProject
        });
    },
    /**
     * 单个项目首页
     */
    project_index: async(ctx, next) => {
        let id = ctx.params.id;
        let env = ctx.request.query.env;
        let project = await Project.findById(id);
        let deploy = project.deploy[env];
        deploy.env = env;
        deploy.name = envs[env];


        let promises = deploy.target.map((host) => {
            console.log('read host :' + host);
            return DeployInfo.findOne({
                projectId: project._id,
                host: host,
                env: env
            }).then((info) => {
                return {
                    host: host,
                    hostFm: host.replace(/\./g, '-'),
                    info: info
                };
            });
        });
        let targets = await Promise.all(promises);

        await ctx.render('action/project_index', {
            project: project,
            deploy: deploy,
            targets: targets
        });
    },

    new_page: async(ctx, next) => {
        let serversAll = await Server.findAll();
        let servers = {};
        serversAll.forEach(s => {
            if (servers[s.env]) {
                servers[s.env].push(s);
            } else {
                servers[s.env] = [s];
            }
        });
        await ctx.render('action/project_form', {
            servers: servers,
            project: {
                deploy: {
                    production: {},
                    test: {}
                }
            }
        });

    },
    edit_page: async(ctx, next) => {
        let id = ctx.query.id;
        let project = await Project.findById(id);

        let serversAll = await Server.findAll();
        let servers = {};
        serversAll.forEach(s => {
            if (servers[s.env]) {
                servers[s.env].push(s);
            } else {
                servers[s.env] = [s];
            }
            if (project.deploy[s.env] && project.deploy[s.env].target && project.deploy[s.env].target.indexOf(s.host) >= 0) {
                s.checked = true;
            }
        });

        await ctx.render('action/project_form', {
            project: project,
            servers: servers
        });
    },
    save: async(ctx, next) => {
        let project = ctx.request.body;
        let id = project._id;
        delete project._id;
        if (id) {
            await Project.update({
                _id: id
            }, {
                $set: project
            });
            await Operation.action(ctx.session.user, 'EDIT_PROJECT_INFO', '修改项目信息', {
                _id: id,
                name: project.name
            });
        } else {
            await Project.insert(project);
            await Operation.action(ctx.session.user, 'NEW_PROJECT_INFO', '新增项目信息', {
                _id: id,
                name: project.name
            });
        }
        ctx.redirect('/projects');
        ctx.status = 301;
    },
    buildings_table: async(ctx, next) => {
        let env = ctx.request.query.env;
        let pid = ctx.params.id;
        let buildings = await Building.cfind({
            env: env,
            projectId: pid
        }).sort({
            buildTime: -1
        }).exec();
        buildings.forEach(b => {
            b.updatedAt = moment(b.updatedAt).format('YYYY-MM-DD HH:mm:ss');
        });
        ctx.body = {
            data: buildings
        };
    },
    project_build: async(ctx, next) => {
        let pid = ctx.params.pid;
        let env = ctx.request.body.env;
        let branch = ctx.request.body.branch;
        let p = await Project.findById(pid);
        let build = new Build(p);

        let {
            buildTime,
            distFile
        } = build.build(branch);
        let buildingDoc = await Building.insert({
            buildTime: buildTime,
            project: p.name,
            projectId: pid,
            branch: branch,
            env: env,
            distFile: distFile,
            state: 'waiting',
            createdAt: new Date(),
            updatedAt: new Date()
        });

        let id = buildingDoc[0]._id;

        build.run(id);

        await Operation.action(ctx.session.user, 'NEW_PROJECT_BUILDING', '新增项目构建', {
            _id: id,
            project: p.name,
            branch: branch,
            env: env
        });

        ctx.body = {
            code: 200,
            id: id
        };
        await next();
    },
    project_deploy: async(ctx) => {
        let buildingId = ctx.params.building;
        let building = await Building.findById(buildingId);
        if (!building) {
            ctx.body = {
                code: 201,
                msg: '该版本不存在'
            };
        } else if (building.state == 'success') {
            let project = await Project.findById(building.projectId);
            let targets = project.deploy[building.env].target;

            targets.forEach(async(host) => {

                let doc = await DeployInfo.findOne({
                    projectId: project._id,
                    host: host,
                    env: building.env
                });

                if (!doc || doc.state !== 'running' || doc.building !== building.buildTime) {
                    let info = {
                        projectId: project._id,
                        host: host,
                        env: building.env,
                        building: building.buildTime,
                        state: 'waiting'
                    };
                    info._id = await DeployInfo.insertOrUpdate(info);
                    let deploy = new Deploy(project, building);
                    deploy.deploy(info);
                }
            });

            await Operation.action(ctx.session.user, 'PROJECT_DEPLOY', '项目分发部署', {
                _id: buildingId,
                project: project.name,
                branch: building.branch,
                env: building.env
            });

            ctx.body = {
                code: 200,
                building: building
            };
        } else {
            ctx.body = {
                code: 201,
                msg: '该版本未构建成功,暂不能分发'
            };
        }
    },
    project_restart: async(ctx) => {
        const projectId = ctx.request.body.id;
        const host = ctx.request.body.host;
        const env = ctx.request.body.env;
        const project = await Project.findById(projectId);

        if (!project) {
            ctx.body = {
                code: 201,
                msg: '该项目不存在'
            };
            return;
        }

        let hosts = [];
        if (host === 'all') {
            // 全部重启
            hosts = project.deploy[env].target;
        } else {
            // 单台重启
            hosts.push(host);
        }

        hosts.forEach(async(host) => {
            let doc = await DeployInfo.findOne({
                projectId: projectId,
                host: host,
                env: env
            });

            if (!doc) {
                return;
            }

            let info = {
                projectId: projectId,
                host: host,
                env: env,
                createdAt: new Date(),
                updatedAt: new Date(),
                state: 'waiting'
            };

            let restartDoc = await RestartInfo.insert(info);
            let restart = new Restart(project);

            info._id = restartDoc[0]._id;
            restart.restart(info);

            await Operation.action(ctx.session.user, 'PROJECT_RESTART', '项目重启', {
                _id: info._id,
                project: project.name,
                branch: project.deploy[env].branchName,
                env: env
            });
        });

        ctx.body = {
            code: 200
        };
    },
    project_monit: async(ctx) => {
        const projectId = ctx.request.body.id;
        const env = ctx.request.body.env;
        const project = await Project.findById(projectId);
        if (!project) {
            ctx.body = {
                code: 201,
                msg: '该项目不存在'
            };
            return;
        }

        const hosts = project.deploy[env].target;
        hosts.forEach((host) => {
            var obj = {
                'total': 0,
                'status': {}
            };

            Rp({
                uri: `http://${host}:9615`,
                json: true
            }).then(function(data) {
                var processes = data.processes || [];
                processes.forEach(function(p) {
                    if (p.name === project.name) {
                        obj.total++;

                        if (!obj.status[p.pm2_env.status]) {
                            obj.status[p.pm2_env.status] = 1;
                        } else {
                            obj.status[p.pm2_env.status]++;
                        }
                    }
                });
            }).catch(function(err) {
                obj.errmsg = '获取监控状态失败'
            }).finally(function() {
                ws.broadcast(`/monit/${projectId}`, {
                    host: host,
                    monitdata: obj
                });
            });
        });

        ctx.body = {
            code: 200
        };
    }
};

r.get('/', p.index_page);
r.get('/new', p.new_page);
r.get('/edit', p.edit_page);
r.get('/:id', p.project_index);
r.get('/:id/buildings', p.buildings_table);
r.post('/save', p.save);
r.post('/build/:pid', p.project_build);
r.post('/deploy/:building', p.project_deploy);
r.post('/restart', p.project_restart);
r.post('/monit', p.project_monit);
export default r;