<repo_name>chenboxiang/koa-test<file_sep>/build/task/version.js
/**
 * 1. 生成带版本号的资源
 * 2. 修改需要引用这些资源的文件中引用的资源链接
 *
 * Author: chenboxiang
 * Date: 14-5-25
 * Time: 下午5:42
 */
'use strict';

module.exports = function(grunt) {

    var path = require('path');
    var glob = require('glob');
    var crypto = require('crypto');
    var util = require('util');
    var fs = require('fs');

    /**
     * 将str中的正则元字符转义
     * @param str
     * @returns {*|XML|string|void}
     */
    function quoteRegExp(str) {
        return str.replace(/([.?*+^$[\]\\(){}|-])/g, '\\$1');
    };

    /**
     * 参数
     * resources: 资源文件，也就是需要被计算签名的文件对象
     * 对象结构
     *  {
     *      // 就是glob的cwd option
     *      // 这个对匹配出来的路径会有影响，此处匹配出来的路径会作为替换refs文件内容的字符串
     *      cwd:
     *      // glob pattern
     *      pattern:
     *  }
     * refs: 引用资源文件的文件
     */
    grunt.registerMultiTask('version', function() {
        var resources = this.data.resources;
        if (!resources) {
            grunt.fail.fatal('the property "resources" is empty');
            return;
        }
        if (!Array.isArray(resources)) {
            resources = [resources];
        }
        var refs = this.data.refs;
        if (!refs) {
            grunt.fail.fatal('the property "refs" is empty');
            return;
        }
        if (!Array.isArray(refs)) {
            refs = [refs];
        }

        // --------- parse files
        var resFiles = [];
        resources.forEach(function(o) {
            var files = glob.sync(o.pattern, {
                cwd: o.cwd
            })
            resFiles.push({
                files: files,
                cwd: o.cwd
            })
        });
        grunt.verbose.ok('resources: ' + JSON.stringify(resFiles, null, 4));

        var refFiles = [];
        refs.forEach(function(o) {
            var files = glob.sync(o.pattern, {
                cwd: o.cwd
            })
            refFiles.push({
                files: files,
                cwd: o.cwd
            })
        });
        grunt.verbose.ok('refs: ' + JSON.stringify(resFiles, null, 4));

        // ---------- generate digest resources
        var resMap = {};
        resFiles.forEach(function(o) {
            var files = o.files;
            var cwd = o.cwd;
            if (files) {
                files.forEach(function(file) {
                    // 二进制数据
                    var data = fs.readFileSync(path.join(cwd, file));
                    // count digest
                    var digest = crypto.createHash('sha1').update(data).digest('hex');
                    var extname = path.extname(file);
                    var targetFile = util.format('%s/%s.%s%s',
                        path.dirname(file), path.basename(file, extname), digest, extname);
                    resMap[file] = targetFile;
                    fs.writeFileSync(path.join(cwd, targetFile), data);
                })
            }
        })
        grunt.log.ok('resources generate completed. resource map: ' + JSON.stringify(resMap, null, 4));

        // ---------- replace ref files reference
        refFiles.forEach(function(o) {
            var files = o.files;
            var cwd = o.cwd;
            if (files) {
                files.forEach(function(file) {
                    var absolutePath = path.join(cwd, file);
                    // 文本数据
                    var data = grunt.file.read(absolutePath);
                    Object.keys(resMap).forEach(function(origin) {
                        var target = resMap[origin];
                        data = data.replace(new RegExp(quoteRegExp(origin), 'g'), target);
                    })
                    // 回写
                    grunt.file.write(absolutePath, data);
                })
            }
        })
        grunt.log.ok('refs replace completed.');
    })
}<file_sep>/model/user.js
/**
 * Author: chenboxiang
 * Date: 14-1-29
 * Time: 下午8:37
 */
'use strict';

var db = require('koa-db');

module.exports = db.defineModel('user', {
    email: {
        validation: ['notEmpty', 'email']
    },

    emailChecked: {}
});<file_sep>/cluster_test/worker_pm.js
/**
 * Author: chenboxiang
 * Date: 14-5-2
 * Time: 下午8:03
 */
'use strict';

var http = require('http');
var koa = require('koa');
var config = require('./config/' + (process.env.NODE_ENV || 'development'));
var logger = require('tracer').console(config.log);
var middlewares = require('koa-middlewares');
var pm = require('pm');

var app = new koa();

//session
var redisStore = new middlewares.redisStore(config.session.redisStore);
redisStore.on('connect', function() {
    logger.info('Redis store is connected!');
})
redisStore.on('disconnect', function() {
    logger.error('Redis store is disconnected!');
})
app.use(middlewares.session({
    store: redisStore,
    cookie: config.session.cookie
}))

app.use(function* hello() {
    this.body = 'hello world';
})

var server = http.createServer(function() {
    logger.info('handled by child, pid is ' + process.pid);
    app.callback().apply(this, arguments);
});

var worker = pm.createWorker({
    'terminate_timeout': 15000
});

worker.ready(function(socket, port) {
    server.emit('connection', socket);
})<file_sep>/routes.js
/**
 * Author: chenboxiang
 * Date: 14-5-4
 * Time: 下午1:42
 */
'use strict';

var _s = require('underscore.string');
var utils = require('./lib/utils');
var path = require('path');
var logger = G.logger;

module.exports = function(app) {
    // filters

    // controllers
    var paths = utils.getFilePathsSync(path.join(__dirname, 'controller'), function(p, stats) {
        return _s.endsWith(p, '.js') && stats.isFile();
    });
    logger.info('controllers:\n', paths.join('\n '));
    paths.forEach(function(path) {
        require(path)(app);
    });
}<file_sep>/static/dist/js/seajs_config.js
/**
 * Author: chenboxiang
 * Date: 14-3-27
 * Time: 下午8:19
 */
seajs.config({
    alias: {

    }
})
;seajs.config({
    "map": [
        [
            "/js/business/hw",
            "/js/business/hw?c73a189185d167eef531267e15da25d9b17e1347"
        ],
        [
            "/js/business/hw1",
            "/js/business/hw1?56e6789c49ed36eb5279514ad181ce4f922dd8c4"
        ]
    ]
})<file_sep>/co_test/co_test.js
/**
 * Author: chenboxiang
 * Date: 14-4-22
 * Time: 上午10:16
 */
'use strict';

//var co = require('co');
//
//co(function *() {
//    yield function *() {
//        req + 'hi'; // ReferenceError: req is not defined
//        return req;
//    }
//})(function(err) {
//    console.log(err); // if I remove throw err, it will log [ReferenceError: req is not defined]
//    console.log('hi');
//    throw err;
//});

//co(function *() {
//    req + 'hi';
//})(function(err) {
//    throw err; // properly throws
//});

var co = require('co');
var fs = require('fs');

function size(file) {
    return function(fn){
        fs.stat(file, function(err, stat){
            if (err) return fn(err);
            fn(null, stat.size);
        });
    }
}

co(function *(){
    var a = size('app.js');
    var b = size('co_test.js');
    var c = size('generator_test.js');
    var res = yield [a, b, c];
    console.log(res);
    // => [ 13, 1687, 129 ]
    return res;
})(function() {
    console.log(arguments);
})<file_sep>/worker.js
/**
 * Author: chenboxiang
 * Date: 14-5-4
 * Time: 下午12:46
 */
'use strict';

var graceful = require('graceful');
var pm = require('pm');
var http = require('http');
var config = require('./config/' + (process.env.NODE_ENV || 'development'));
var logger = require('tracer').console(config.log);

var worker = pm.createWorker({
    'terminate_timeout': 5000
});
var killTimeout = 10000;
var app = require('./app');

app.on('ready', function() {
    var server = http.createServer(app.callback());
    // hack for pm, because server._handle is empty.
    server.close = function () {};

    graceful({
        server: server,
        worker: worker,
        error: function (err) {
            logger.error('[worker:%s] error: %s', process.pid, err.stack);
        },
        killTimeout: killTimeout
    });

    worker.ready(function (socket, port) {
        server.emit('connection', socket);
    });
})

app.on('startError', function(err) {
    logger.error('[worker:%s] error on start: %s', process.pid, err.stack);
    worker.disconnect();
    // make sure we close down within `killTimeout` seconds
    var killTimer = setTimeout(function () {
        logger.info('[worker:%s] kill timeout, exit now.', process.pid);
        process.exit(1);
    }, killTimeout);

    // But don't keep the process open just for that!
    // If there is no more io waiting, just let process exit normally.
    if (typeof killTimer.unref === 'function') {
        // only worked on node 0.10+
        killTimer.unref();
    }
})<file_sep>/middleware/ejs.js
/**
 * Author: chenboxiang
 * Date: 14-4-20
 * Time: 下午12:18
 */
'use strict';

var _ = require('lodash');
var _s = require('underscore.string');
var fs = require('co-fs');
var path = require('path');
var ejs = require('ejs-remix');

var defaultConfig = {
    cache: false,
    compileOptions: {
        compileDebug: false,
        debug: false,
        escape: function(html) {
            if (!html) {
                return "";
            }
            return String(html)
                .replace(/&/g, '&amp;')
                .replace(/</g, '&lt;')
                .replace(/>/g, '&gt;')
                .replace(/"/g, '&quot;');
        }
    },
    // 去多余空白符
    strip: true,
    ext: '.ejs',
    // default global property
    locals: {}
}

// template cache
var cache = {}

/**
 * response the html to browser
 * @param {Object} ctx  koa context
 * @param {String} html
 */
function response(ctx, html) {
    ctx.type = 'html';
    ctx.length = html.length;
    ctx.body = html;
}

module.exports = function(app, config) {
    config = _.extend({}, defaultConfig, config);

    if (!config.root) {
        throw new Error('config.root is required');
    }

    if (config.strip) {
        var parse = ejs.parse;
        ejs.parse = function(str, options) {
            str = str.replace(/(<\/?[^<>]+>)\s+/g, '$1');

            return parse.apply(this, [str, options]);
        }
    }

    if (config.ext.charAt(0) !== '.') {
        config.ext = "." + config.ext;
    }

    app.context.render = function *(view, options) {
        options = _.extend({}, config.locals, options);
        if (!_s.endsWith(view, config.ext)) {
            view += config.ext;
        }
        var template = cache[view];
        if (!template) {
            var absolutePath = path.join(config.root, view);
            var fileData = yield fs.readFile(absolutePath, {encoding: 'utf8'});
            template = ejs.compile(fileData, _.extend({filename: absolutePath}, config.compileOptions));
            if (config.cache) {
                cache[view] = template;
            }
        }
        var html = template.call(options.scope, options);
        response(this, html);
    }
}<file_sep>/static/js/business/hw1.js
/**
 * Author: chenboxiang
 * Date: 14-3-26
 * Time: 下午3:24
 */
define(function(require, exports, module) {
    "use strict";

    var hw = require("./hw");
    module.exports = function() {
        hw();
        console.log("hello world 1");
    }
})<file_sep>/dispatch.js
/**
 * Author: chenboxiang
 * Date: 14-5-4
 * Time: 下午12:43
 */
'use strict';

var config = require('./config/' + (process.env.NODE_ENV || 'development'));
var pm = require('pm');
var logger = require('tracer').console(config.log);

var master = pm.createMaster();

master.on('giveup', function(name, fatals, pause) {
    logger.info('[master:%s] giveup to restart "%s" process after %d times. pm will try after %d ms.',
        process.pid, name, fatals, pause);
});

master.on('disconnect', function(name, pid) {
    logger.error('[master:%s] worker:%s disconnect!',
        process.pid, pid);
});

master.on('fork', function(name, pid) {
    logger.info('[master:%s] new %s:worker:%s fork',
        process.pid, name, pid);
});

master.on('quit', function(name, pid, code, signal) {
    logger.info('[master:%s] %s:worker:%s quit, code: %s, signal: %s',
        process.pid, name, pid, code, signal);
});

master.register('web', __dirname + '/worker.js', {
    listen: config.port
});

master.dispatch();

if (module.parent) module.exports = master;<file_sep>/Gruntfile.js
'use strict';

var config = require('./build/config/' + (process.env.NODE_ENV || 'development'));
var path = require('path');

module.exports = function(grunt) {
    // 项目配置
    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json'),

        less: {
            options: {
                strictMath: true,
                modifyVars: config.lessModifyVars
            },

            production: {
                files: [
                    {
                        expand: true,     // Enable dynamic expansion.
                        cwd: 'static/less/',      // Src matches are relative to this path.
                        src: ['**/*.less', '!base/*', 'base/index.less'], // Actual pattern(s) to match.
                        dest: 'static/dist/css/',   // Destination path prefix.
                        ext: '.css'    // Dest filepaths will have this extension.
                    }
                ]
            },

            development: {
                files: [
                    {
                        expand: true,     // Enable dynamic expansion.
                        cwd: 'static/less/',      // Src matches are relative to this path.
                        src: ['**/*.less', '!base/*', 'base/index.less'], // Actual pattern(s) to match.
                        dest: 'static/css/',   // Destination path prefix.
                        ext: '.css'    // Dest filepaths will have this extension.
                    }
                ]
            }
        },

        watch: {
            less: {
                files: ['static/less/**/*.less'],
                tasks: ['less:development']
            }
        },

        clean: {
            js: ['.tmp*'],
            build: ['static/dist']
        },

        // seajs transport
        transport: {
            js: {
                options: {
                    paths: ['static/js/'],
                    idleading: '/js/'
                },
                files: [
                    {
                        expand: true,
                        cwd: 'static/js/',
                        src: ['**/*.js', '!seajs_config.js'],
                        filter: function(path) {
                            return path.indexOf('sea-modules') < 0;
                        },
                        dest: '.tmp1'
                    }
                ]
            }
        },

        // seajs concat
        concat: {
            cmd: {
                options: {
                    include: 'relative'
                },
                files: [
                    {
                        expand: true,
                        cwd: '.tmp1',
                        src: ['**/*.js'],
                        filter: function(filepath) {
                            return !/-debug\.js$/.test(filepath);
                        },
                        dest: '.tmp2'
                    }
                ]
            }
        },

        uglify: {
            js: {
                files: [
                    {
                        expand: true,
                        cwd: '.tmp2',
                        src: '**/*.js',
                        dest: 'static/dist/js'
                    }
                ]
            }
        },

        // 生成seajs config文件
        seajsConfig: {
            config: {
                options: {
                    src: 'static/js/seajs_config.js',
                    dest: 'static/dist/js/seajs_config.js'
                },
                files: [
                    {
                        expand: true,
                        cwd: 'static/dist/js/',
                        src: ['**/*.js', '!seajs_config.js'],
                        filter: function(path) {
                            return path.indexOf('sea-modules') < 0;
                        }
                    }
                ]
            }
        },

        copy: {
            main: {
                expand: true,
                cwd: 'static/',
                src: ['font/**', 'image/**', 'swf/**'],
                dest: 'static/dist/'
            }
        },

        version: {
            main: {
                // 需要生成带hash号的资源
                resources: {
                    cwd: path.join(__dirname, 'static/dist/'),
                    pattern: 'image/**/*.*'
                },
                // 需要替换资源为带版本号资源的文件
                refs: {
                    cwd: path.join(__dirname, 'static/dist/'),
                    pattern: 'css/**/*.css'
                }
            }
        },

        // upload to qiniu cdn
        qiniu: {
            deploy: {
                options: {
                    accessKey: config.qiniu.accessKey,
                    secretKey: config.qiniu.secretKey,
                    bucket: config.qiniu.bucket,
                    domain: config.qiniu.domain,
                    resources: [
                        {
                            cwd: 'static/dist',
                            pattern: '**/*.*'
                        }
                    ]
                }
            }
        }
    });

    grunt.loadNpmTasks('grunt-contrib-uglify');
    grunt.loadNpmTasks('grunt-contrib-less');
    grunt.loadNpmTasks('grunt-contrib-watch');
    grunt.loadNpmTasks('grunt-contrib-clean');
    grunt.loadNpmTasks('grunt-contrib-copy');
    grunt.loadNpmTasks('grunt-cmd-transport');
    grunt.loadNpmTasks('grunt-cmd-concat');
    grunt.loadNpmTasks('grunt-seajs-config');
    grunt.loadNpmTasks('grunt-qiniu-deploy');

    grunt.loadTasks('build/task');

    // 默认任务
    grunt.registerTask('default', ['watch']);

    grunt.registerTask('buildJs', ['clean:js', 'transport', 'concat', 'uglify', 'seajsConfig', 'clean:js']);
    grunt.registerTask('buildCss', ['less']);
    grunt.registerTask('build', ['clean:build', 'buildJs', 'buildCss', 'copy', 'assetsMap', 'version', 'qiniu']);
}<file_sep>/static/js/seajs_config.js
/**
 * Author: chenboxiang
 * Date: 14-3-27
 * Time: 下午8:19
 */
seajs.config({
    alias: {

    }
})<file_sep>/bin/server.js
#!/usr/bin/env node

var program = require('commander');

program
    .version('1.0.0')
    .usage('<start|stop|restart>')

program.parse(process.argv);<file_sep>/static/js/business/hw.js
/**
 * Author: chenboxiang
 * Date: 14-3-26
 * Time: 下午3:22
 */
"use strict";

define(function(require, exports, module) {
    module.exports = function() {
        console.log("say hello12121");
    }
})<file_sep>/controller/index.js
/**
 * Author: chenboxiang
 * Date: 14-1-26
 * Time: 下午2:38
 */
'use strict';

var User = require('../model/user');
var db = require('koa-db');
var parse = require('co-busboy');
var fs = require('fs');
var dao = db.dao;
var logger = G.logger;
var path = require('path');
var saveTo = require('save-to');

module.exports = function(app) {
    app.get('/', function *(next) {
        logger.info('index page');
        this.session.name = 'koa-redis';
        var user = new User({
            email: '<EMAIL>',
            emailChecked: true
        });
        yield dao.trans(function *(t) {
            yield dao.select(User, {email: '<EMAIL>'}, t).forUpdate();
            yield dao.insert(user, t);
            yield dao.update(user, t);
            yield dao.del(user, t);
        })
//        var user = yield dao.select(User, 1);
        yield* this.render('index')
    })

    app.get('/todo', function *() {
        yield* this.render('index', {
            user: {id: 1}
        })
    })

    app.post('/file/upload', function *(next) {
        // the body isn't multipart, so busboy can't parse it
        if (!this.request.is('multipart/*')) return yield* next;

        var parts = parse(this, {
            autoFields: true
        });
        var part;
        while (part = yield parts) {
            yield saveTo(part, path.join(process.cwd(), 'test.jpg'));
        }
//        console.log(parts.field);
//        console.log(parts.fields);
        this.body = {code: 200};
    })
}<file_sep>/co_test/generator_test.js
/**
 * Author: chenboxiang
 * Date: 14-4-17
 * Time: 下午12:43
 */
"use strict";

//function delay(time, callback) {
//    setTimeout(function() {
//        if (typeof callback === "function") {
//            callback("Sleep for " + time);
//        }
//    }, time);
//
//    return time;
//}
//
//function *myDelayedMessages(resume) {
//    console.log(yield delay(1000, resume));
//    console.log(yield delay(1200, resume));
//}
//
//function run(generatorFunction) {
//    var generatorItr = generatorFunction(resume);
//
//    function resume(callbackValue) {
//        console.log(generatorItr.next(callbackValue));
//    }
//
//    console.log("xx", generatorItr.next());
//}
//
//run(myDelayedMessages);

//var generatorItr = myDelayedMessages();
//console.log(generatorItr.next());
//console.log(generatorItr.next());
//console.log(generatorItr.next());

//function* genFunc () {
//    console.log(yield 1)
//    console.log(yield 2)
//}
//var gen = genFunc()
//console.log(gen.next()) // 此时generator内部执行到 yield 1 并暂停，但还未对result赋值！
//// 即使异步也可以！
//setTimeout(function () {
//    gen.next(123) // 给result赋值并继续执行，输出: 123
//}, 1000)

function *example() {
    console.log("he", yield 'he');
    var hello = yield 'hello';
    console.log("he", hello);
    var world = yield 'world';
    console.log("world", world);
}
var gen = example();
var ret = gen.next();
//try {
    ret = gen.throw(new Error("throw error"));
//} catch (err) {
//    console.log("error", err);
//}

console.log(ret);

//ret = gen.next(ret.value);
//console.log(ret);
////ret = gen.next();
////console.log(ret);
//ret = gen.next(ret.value);
//console.log(ret);<file_sep>/lib/constants.js
/**
 * Author: chenboxiang
 * Date: 14-1-29
 * Time: 下午3:33
 */
"use strict";

module.exports = {

};<file_sep>/build/config/development.js
/**
 * Author: chenboxiang
 * Date: 14-5-24
 * Time: 下午12:15
 */
'use strict';

module.exports = {
    lessModifyVars: {
        staticUrl: '"/"'
    },
    qiniu: {
        accessKey: <KEY>',
        secretKey: '<KEY>',
        bucket: 'chenboxiang',
        domain: 'http://{bucket}.qiniudn.com'
    }
}<file_sep>/cluster_test/master.js
/**
 * Author: chenboxiang
 * Date: 14-5-2
 * Time: 上午11:56
 */
'use strict';

var cp = require('child_process');
require('cluster');
var cpus = require('os').cpus();

var server = require('net').createServer();
server.listen(3000);

var workers = {};

function createWorker() {
    var worker = cp.fork(__dirname + '/worker.js');
    worker.on('exit', function() {
        console.log('Worker ' + worker.pid + ' exited.');
        delete workers[worker.pid];
    })

    worker.send('server', server);
    workers[worker.pid] = worker;
    console.log('Create worker, pid: ' + worker.pid);
}

for (var i = 0; i < cpus.length; i++) {
    createWorker();
}

process.on('exit', function() {
    Object.keys(workers).forEach(function(pid) {
        workers[pid].kill();
    })
})<file_sep>/app.js
/**
 * Author: chenboxiang
 * Date: 14-4-18
 * Time: 下午5:23
 */
'use strict';

var path = require('path');
var http = require('http');
var koa = require('koa');
var middlewares = require('koa-middlewares');
var _ = require('lodash');
var async = require('async');
var Loader = require('loader');
var config = require('./config/' + (process.env.NODE_ENV || 'development'));
var logger = require('tracer').console(config.log);
var fs = require('fs');
var crypto = require('crypto')

// 全局对象
global.G = {};
G.logger = logger;

// -- 初始化数据库 -----------------------
var mysqlIsInit = false;
// TODO 需在测试环境确认mysql未启动的情况是否会报错
var initMysqlConnectionPool = function(callback) {
    var db = require('koa-db');
    config.db.pool.afterCreate = function(connection, cb) {
        if (!mysqlIsInit) {
            logger.info('Mysql connection pool init success!');
            mysqlIsInit = true;
            callback();
        }
        cb(null, connection);
    }
    db.buildDao(_.extend({name: "def"}, config.db));
}

// -- new app instance ----------------
var app = new koa();
require('koa-trace')(app)
app.debug()
app.keys = config.keys;

// -- 加载中间件 ------------------------
var useMiddlewares = function(callback) {
    app.use(function* (next) {
        try {
            yield next;
        } catch (err) {
            logger.error(err);
            this.status = err.status || 500;
            this.body = err.message || require('http').STATUS_CODES[this.status];
            this.app.emit('error', err, this);
        }
    });
    // response time trace
    app.use(middlewares.rt());

    // static file
    var serve = require('koa-static');
    app.use(serve(path.join(__dirname, config.staticDir), config.static));

    // logger
    app.use(middlewares.logger());

    app.use(function* (next) {
        // give each request some sort of ID
        this.id = crypto.randomBytes(12)

        // log events with optional arguments
        this.trace('start')
        yield* next
        this.trace('finish')
    })
    app.use(function* (next) {
        this.trace('something', 1, 2, 3)
        yield* next
    })

    // session
    var redisStore = new middlewares.redisStore(config.session.redisStore);
    redisStore.on('connect', function() {
        logger.info('Redis store is connected!');
        callback(null);
    })
    redisStore.on('disconnect', function() {
        logger.error('Redis store is disconnected!');
        callback(new Error('Redis store is disconnected!'));
    })
    app.use(middlewares.session({
        store: redisStore,
        cookie: config.session.cookie
    }))

    // body parser
    app.use(middlewares.bodyParser());

    // router
    app.use(middlewares.router(app));
    require('koa-router-namespace')(app);


    // view engine
    var assetsMap = {};
    if (config.assets.combo) {
        try {
            assetsMap = JSON.parse(fs.readFileSync(path.join(__dirname, 'static/dist/assets.json')));
        } catch (e) {
            logger.error('You must execute `make build` before start app when mini_assets is true.');
            throw e;
        }
    }
    var middlewareEjs = require('./middleware/ejs');
    middlewareEjs(app, {
        root: path.join(__dirname, 'view'),
        cache: app.env !== 'development',
        ext: '.html',
        locals: {
            config: config,
            Loader: Loader,
            assetsMap: assetsMap
        }
    });
    //app.use(middlewares.conditional());
    //app.use(middlewares.etag());
    //app.use(middlewares.compress());
    //middlewares.csrf(app);
}

app.on('error', function (err, ctx) {
    err.url = err.url || ctx.request.url;
    logger.error(err);
});

// -- 启动服务器 -------------------------
async.parallel(
    [initMysqlConnectionPool, useMiddlewares],
    function(err) {
        if (!err) {
            // -- 加载路由配置信息 -------------------------
            require('./routes')(app);
            app.emit('ready');

        } else {
            app.emit('startError', err);
        }
    }
);

if (module.parent) module.exports = app;

<file_sep>/build/task/assets_map.js
/**
 * Author: chenboxiang
 * Date: 14-5-25
 * Time: 下午2:55
 */
'use strict';

module.exports = function(grunt) {
    var path = require('path');
    var fs = require('fs');
    var Loader = require('loader');
    var cwd = process.cwd();
    
    grunt.registerTask('assetsMap', 'generate assets.json', function() {
        // 视图目录
        var viewsDir = path.join(cwd, './view');
        // 资源目录
        var baseDir = path.join(cwd, './static/dist');

        // scan views folder, get the assets map
        var scanned = Loader.scanDir(viewsDir);
        grunt.log.ok('Scaned.');

        // combo？md5 hash
        var minified = Loader.minify(baseDir, scanned);
        grunt.log.ok(JSON.stringify(minified, null, 4));
        grunt.log.ok('Compile static assets done.');

        // write the assets mapping into assets.json
        var assets = path.join(baseDir, 'assets.json');
        grunt.log.ok('assets.json is here: ' + assets);
        fs.writeFileSync(assets, JSON.stringify(Loader.map(minified)));
        grunt.log.ok('write assets.json done. assets.json: ');
        grunt.log.ok(fs.readFileSync(assets, 'utf-8'));
    })
}<file_sep>/cluster_test/master_pm.js
/**
 * Author: chenboxiang
 * Date: 14-5-3
 * Time: 下午12:08
 */
'use strict';

var pm = require('pm');

var master = pm.createMaster();

master.on('giveup', function (name, fatals, pause) {
    console.log('[%s] [master:%s] giveup to restart "%s" process after %d times. pm will try after %d ms.',
        new Date(), process.pid, name, fatals, pause);
});

master.on('disconnect', function (name, pid) {
    // console.log('%s %s disconnect', name, pid)
    var w = master.fork(name);
    console.error('[%s] [master:%s] worker:%s disconnect! new worker:%s fork',
        new Date(), process.pid, pid, w.process.pid);
});

master.on('fork', function (name, pid) {
    console.log('[%s] [master:%s] new %s:worker:%s fork',
        new Date(), process.pid, name, pid);
});

master.on('quit', function (name, pid, code, signal) {
    console.log('[%s] [master:%s] %s:worker:%s quit, code: %s, signal: %s',
        new Date(), process.pid, name, pid, code, signal);
});

master.register('web', __dirname + '/worker_pm.js', {
    listen: 3000
});

master.dispatch();<file_sep>/config/development.js
'use strict';

module.exports = {
    // session
    session: {
        secret: 'koa test',
        redisStore: {
            host: 'localhost',
            port: '6379',
            db: 0,
            // session过期时间: 4hour
            ttl: 14400,
            prefix: 'sess:'
        },
        cookie: {
            signed: false
        }
    },
    keys: ['koa', 'test'],
    log: {
        format: [
            // 产品环境需去掉file和line以提高性能
            '{{timestamp}} <{{title}}> {{file}}:{{line}} {{message}}', //default format
            {
                error: '{{timestamp}} <{{title}}> {{file}}:{{line}} {{message}} \nCall Stack:\n{{stack}}' // error format
            }
        ],
        dateformat: 'yyyy-mm-dd HH:MM:ss.l "GMT"o',
        level: 'debug'
    },

    db: {
        client: 'mysql',
        connection: {
            host: '127.0.0.1',
            port: '3306',
            user: 'root',
            password: '',
            database: 'zhanguoce',
            // When dealing with big numbers (BIGINT and DECIMAL columns) in the database, you should enable this option
            supportBigNumbers: true,
            // Enabling both supportBigNumbers and bigNumberStrings forces big numbers (BIGINT and DECIMAL columns)
            // to be always returned as JavaScript String objects (Default: false).
            // Enabling supportBigNumbers but leaving bigNumberStrings disabled will return big numbers as String objects
            // only when they cannot be accurately represented with JavaScript Number objects (which happens when they exceed the [-2^53, +2^53] range),
            // otherwise they will be returned as Number objects. This option is ignored if supportBigNumbers is disabled
            bigNumberStrings: true,
            debug: false
        },
        pool: {
            min: 2,
            max: 50,
            // boolean that specifies whether idle resources at or below the min threshold
            // should be destroyed/re-created.  optional (default=true)
            refreshIdle: true,
            // max milliseconds a resource can go unused before it should be destroyed
            // (default 30000)
            idleTimeoutMillis: 60 * 60 * 1000,
            // frequency to check for idle resources (default 1000)
            reapIntervalMillis: 60 * 1000,
            // true/false or function -
            // If a log is a function, it will be called with two parameters:
            //   - log string
            //   - log level ('verbose', 'info', 'warn', 'error')
            // Else if log is true, verbose log info will be sent to console.log()
            // Else internal log messages be ignored (this is the default)
            log: false
        },
        debug: true
    },

    port: 3000,

    // 静态资源缓存
    'static': {
        maxage: 0
    },

    staticDir: 'static',

    assets: {
        combo: false,
        url: ''
    }
};<file_sep>/static/js/sea-modules/chenboxiang/seajs-nocache/1.0.0/seajs-nocache-debug.js
(function() {
    // copy from https://github.com/seajs/seajs/issues/264#issuecomment-20719662
    var TIME_STAMP = "?t=" + new Date().getTime()

    seajs.on("fetch", function(data) {
        if (data.uri) {
            data.requestUri = data.uri + TIME_STAMP
        }
    })

    seajs.on("define", function(data) {
        if (data.uri) {
            data.uri = data.uri.replace(TIME_STAMP, "")
        }
    })
    define("chenboxiang/seajs-nocache/1.0.0/seajs-nocache-debug", [], {});
})();