/**
* 这是通过AJAX的方式调用的API的入口。所有AJAX请求的最终处理会交由`core`模块处理。其中`params`
* 参数包括了以下部分:
* - ip {string} 对方的IP
* - transport {string} `ajax`
* - auth {object} 可选,对方如果提供了`authorization`头,则为解析出的JWT的数据。
* 如果解析的过程中出错则会直接返回错误。数据包含以下字段:
* - uid {string} 用户ID
* - jti {string} JWT的ID
* - role {Number} 权限
* - iat {Number}
* - exp {Number}
* - query {object} 请求中的query string解析出的对象,支持数组和嵌套对象
* - data {object} POST等请求中的body数据,只支持json和urlencoded,某些接口会支持multipart
* - id {string} 可选,对于某些会包含ID的请求类型,这是URL中的ID
* - file|files 可选,上传的文件,其中文件的值的内容见[Multer文档](https://github.com/expressjs/multer)
* - _files {Array.<string>} 这次请求创建的所有临时文件的路径(不仅是上传的文件,比如还包含处理过的图片),
* 如果返回值不是200或有异常抛出,上述_files中的文件将被删除
* - _dirs {Array.<string>} 这次请求创建的所有临时文件夹,如果返回值不是200或有异常抛出,
* 上述_dirs将被递归删除
* @module api
*/
const fs = require('fs');
const rimraf = require('rimraf');
const logger = require('winston');
const Router = require('koa-router');
const bodyParser = require('koa-bodyparser');
const {errorsEnum, coreThrow} = require('../core/errors');
const UserRouter = require('./user');
const EmailRouter = require('./email');
const AuthRouter = require('./auth');
const TaskRouter = require('./task');
const AssignmentRouter = require('./assignment');
const TaskTypeRouter = require('./task-type');
async function errorHandler(ctx, next) {
try {
await next();
} catch (err) {
if (err.expose === true) {
ctx.status = err.status || 500;
ctx.type = 'json';
if (err.data)
ctx.body = JSON.stringify(err.data);
else
ctx.body = err.message;
} else {
ctx.status = 500;
ctx.type = 'json';
ctx.body = JSON.stringify({
code: 500,
type: 'INTERNAL',
message: 'Internal server error'
});
ctx.app.emit('error', err, ctx);
}
}
}
function cleanFiles(params) {
params._files.forEach(file =>
fs.unlink(file, err => {
if (err) {
logger.error(`Failed to delete file "${file}".`);
logger.error(err);
}
})
);
params._dirs.forEach(dir =>
rimraf(dir, err => {
if (err) {
logger.error(`Failed to delete directory "${dir}".`);
logger.error(err);
}
})
);
}
module.exports = function (global) {
const router = new Router();
const userRouter = new UserRouter(global);
const emailRouter = new EmailRouter();
const authRouter = new AuthRouter();
const taskRouter = new TaskRouter(global);
const assignmentRouter = new AssignmentRouter();
const taskTypeRouter = new TaskTypeRouter(global);
router.use(errorHandler);
router.use(bodyParser({
onerror: (e, ctx) => coreThrow(errorsEnum.PARSE, 'Cannot parse body')
}));
router.use(async (ctx, next) => {
ctx.params = {
ip: ctx.ip,
transport: 'ajax',
_files: [],
_dirs: []
};
if (ctx.request.body)
ctx.params.data = ctx.request.body;
if (ctx.query)
ctx.params.query = ctx.query;
let token = ctx.headers['authorization'];
if (token) {
try {
token = token.split(/\s+/);
token = token[token.length - 1];
ctx.params.auth = await ctx.global.jwt.verify(token);
} catch (err) {
ctx.set('WWW-Authenticate', 'Bearer');
coreThrow(errorsEnum.AUTH, err.message);
}
}
try {
await next();
if (ctx.status !== 200)
cleanFiles(ctx.params);
} catch (err) {
cleanFiles(ctx.params);
throw err;
}
});
router.use('/user', userRouter.routes(), userRouter.allowedMethods());
router.use('/email', emailRouter.routes(), emailRouter.allowedMethods());
router.use('/auth', authRouter.routes(), authRouter.allowedMethods());
router.use('/task', taskRouter.routes(), taskRouter.allowedMethods());
router.use('/assignment', assignmentRouter.routes(), assignmentRouter.allowedMethods());
router.use('/tasktype', taskTypeRouter.routes(), taskTypeRouter.allowedMethods());
return router;
};