fork from bc4552c5a8
This commit is contained in:
69
server/app.js
Normal file
69
server/app.js
Normal file
@@ -0,0 +1,69 @@
|
||||
process.env.NODE_PATH = __dirname;
|
||||
require('module').Module._initPaths();
|
||||
|
||||
const yapi = require('./yapi.js');
|
||||
const commons = require('./utils/commons');
|
||||
yapi.commons = commons;
|
||||
const dbModule = require('./utils/db.js');
|
||||
yapi.connect = dbModule.connect();
|
||||
const mockServer = require('./middleware/mockServer.js');
|
||||
require('./plugin.js');
|
||||
const websockify = require('koa-websocket');
|
||||
const websocket = require('./websocket.js');
|
||||
const storageCreator = require('./utils/storage')
|
||||
require('./utils/notice')
|
||||
|
||||
const Koa = require('koa');
|
||||
const koaStatic = require('koa-static');
|
||||
// const bodyParser = require('koa-bodyparser');
|
||||
const koaBody = require('koa-body');
|
||||
const router = require('./router.js');
|
||||
|
||||
global.storageCreator = storageCreator;
|
||||
let indexFile = process.argv[2] === 'dev' ? 'dev.html' : 'index.html';
|
||||
|
||||
const app = websockify(new Koa());
|
||||
app.proxy = true;
|
||||
yapi.app = app;
|
||||
|
||||
// app.use(bodyParser({multipart: true}));
|
||||
app.use(koaBody({strict: false, multipart: true, jsonLimit: '2mb', formLimit: '1mb', textLimit: '1mb' }));
|
||||
app.use(mockServer);
|
||||
app.use(router.routes());
|
||||
app.use(router.allowedMethods());
|
||||
|
||||
websocket(app);
|
||||
|
||||
app.use(async (ctx, next) => {
|
||||
if (/^\/(?!api)[a-zA-Z0-9\/\-_]*$/.test(ctx.path)) {
|
||||
ctx.path = '/';
|
||||
await next();
|
||||
} else {
|
||||
await next();
|
||||
}
|
||||
});
|
||||
|
||||
app.use(async (ctx, next) => {
|
||||
if (ctx.path.indexOf('/prd') === 0) {
|
||||
ctx.set('Cache-Control', 'max-age=8640000000');
|
||||
if (yapi.commons.fileExist(yapi.path.join(yapi.WEBROOT, 'static', ctx.path + '.gz'))) {
|
||||
ctx.set('Content-Encoding', 'gzip');
|
||||
ctx.path = ctx.path + '.gz';
|
||||
}
|
||||
}
|
||||
await next();
|
||||
});
|
||||
|
||||
|
||||
app.use(koaStatic(yapi.path.join(yapi.WEBROOT, 'static'), { index: indexFile, gzip: true }));
|
||||
|
||||
|
||||
const server = app.listen(yapi.WEBCONFIG.port);
|
||||
|
||||
server.setTimeout(yapi.WEBCONFIG.timeout);
|
||||
|
||||
commons.log(
|
||||
`服务已启动,请打开下面链接访问: \nhttp://127.0.0.1${
|
||||
yapi.WEBCONFIG.port == '80' ? '' : ':' + yapi.WEBCONFIG.port
|
||||
}/`
|
||||
);
|
||||
317
server/controllers/base.js
Normal file
317
server/controllers/base.js
Normal file
@@ -0,0 +1,317 @@
|
||||
const yapi = require('../yapi.js');
|
||||
const projectModel = require('../models/project.js');
|
||||
const userModel = require('../models/user.js');
|
||||
const interfaceModel = require('../models/interface.js');
|
||||
const groupModel = require('../models/group.js');
|
||||
const tokenModel = require('../models/token.js');
|
||||
const _ = require('underscore');
|
||||
const jwt = require('jsonwebtoken');
|
||||
const {parseToken} = require('../utils/token')
|
||||
|
||||
class baseController {
|
||||
constructor(ctx) {
|
||||
this.ctx = ctx;
|
||||
//网站上线后,role对象key是不能修改的,value可以修改
|
||||
this.roles = {
|
||||
admin: 'Admin',
|
||||
member: '网站会员'
|
||||
};
|
||||
}
|
||||
|
||||
async init(ctx) {
|
||||
this.$user = null;
|
||||
this.tokenModel = yapi.getInst(tokenModel);
|
||||
this.projectModel = yapi.getInst(projectModel);
|
||||
let ignoreRouter = [
|
||||
'/api/user/login_by_token',
|
||||
'/api/user/login',
|
||||
'/api/user/reg',
|
||||
'/api/user/status',
|
||||
'/api/user/logout',
|
||||
'/api/user/avatar',
|
||||
'/api/user/login_by_ldap'
|
||||
];
|
||||
if (ignoreRouter.indexOf(ctx.path) > -1) {
|
||||
this.$auth = true;
|
||||
} else {
|
||||
await this.checkLogin(ctx);
|
||||
}
|
||||
|
||||
let openApiRouter = [
|
||||
'/api/open/run_auto_test',
|
||||
'/api/open/import_data',
|
||||
'/api/interface/add',
|
||||
'/api/interface/save',
|
||||
'/api/interface/up',
|
||||
'/api/interface/get',
|
||||
'/api/interface/list',
|
||||
'/api/interface/list_menu',
|
||||
'/api/interface/add_cat',
|
||||
'/api/interface/getCatMenu',
|
||||
'/api/interface/list_cat',
|
||||
'/api/project/get',
|
||||
'/api/plugin/export',
|
||||
'/api/project/up',
|
||||
'/api/plugin/exportSwagger'
|
||||
];
|
||||
|
||||
let params = Object.assign({}, ctx.query, ctx.request.body);
|
||||
let token = params.token;
|
||||
|
||||
// 如果前缀是 /api/open,执行 parse token 逻辑
|
||||
if (token && typeof token === 'string' && (openApiRouter.indexOf(ctx.path) > -1 || ctx.path.indexOf('/api/open/') === 0 )) {
|
||||
|
||||
let tokens = parseToken(token)
|
||||
|
||||
const oldTokenUid = '999999'
|
||||
|
||||
let tokenUid = oldTokenUid;
|
||||
|
||||
if(!tokens){
|
||||
let checkId = await this.getProjectIdByToken(token);
|
||||
if(!checkId)return;
|
||||
}else{
|
||||
token = tokens.projectToken;
|
||||
tokenUid = tokens.uid;
|
||||
}
|
||||
|
||||
// if (this.$auth) {
|
||||
// ctx.params.project_id = await this.getProjectIdByToken(token);
|
||||
|
||||
// if (!ctx.params.project_id) {
|
||||
// return (this.$tokenAuth = false);
|
||||
// }
|
||||
// return (this.$tokenAuth = true);
|
||||
// }
|
||||
|
||||
let checkId = await this.getProjectIdByToken(token);
|
||||
if(!checkId){
|
||||
ctx.body = yapi.commons.resReturn(null, 42014, 'token 无效');
|
||||
}
|
||||
let projectData = await this.projectModel.get(checkId);
|
||||
if (projectData) {
|
||||
ctx.query.pid = checkId; // 兼容:/api/plugin/export
|
||||
ctx.params.project_id = checkId;
|
||||
this.$tokenAuth = true;
|
||||
this.$uid = tokenUid;
|
||||
let result;
|
||||
if(tokenUid === oldTokenUid){
|
||||
result = {
|
||||
_id: tokenUid,
|
||||
role: 'member',
|
||||
username: 'system'
|
||||
}
|
||||
}else{
|
||||
let userInst = yapi.getInst(userModel); //创建user实体
|
||||
result = await userInst.findById(tokenUid);
|
||||
}
|
||||
|
||||
this.$user = result;
|
||||
this.$auth = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async getProjectIdByToken(token) {
|
||||
let projectId = await this.tokenModel.findId(token);
|
||||
if (projectId) {
|
||||
return projectId.toObject().project_id;
|
||||
}
|
||||
}
|
||||
|
||||
getUid() {
|
||||
return parseInt(this.$uid, 10);
|
||||
}
|
||||
|
||||
async checkLogin(ctx) {
|
||||
let token = ctx.cookies.get('_yapi_token');
|
||||
let uid = ctx.cookies.get('_yapi_uid');
|
||||
try {
|
||||
if (!token || !uid) {
|
||||
return false;
|
||||
}
|
||||
let userInst = yapi.getInst(userModel); //创建user实体
|
||||
let result = await userInst.findById(uid);
|
||||
if (!result) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let decoded;
|
||||
try {
|
||||
decoded = jwt.verify(token, result.passsalt);
|
||||
} catch (err) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (decoded.uid == uid) {
|
||||
this.$uid = uid;
|
||||
this.$auth = true;
|
||||
this.$user = result;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
} catch (e) {
|
||||
yapi.commons.log(e, 'error');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async checkRegister() {
|
||||
// console.log('config', yapi.WEBCONFIG);
|
||||
if (yapi.WEBCONFIG.closeRegister) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
async checkLDAP() {
|
||||
// console.log('config', yapi.WEBCONFIG);
|
||||
if (!yapi.WEBCONFIG.ldapLogin) {
|
||||
return false;
|
||||
} else {
|
||||
return yapi.WEBCONFIG.ldapLogin.enable || false;
|
||||
}
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param {*} ctx
|
||||
*/
|
||||
|
||||
async getLoginStatus(ctx) {
|
||||
let body;
|
||||
if ((await this.checkLogin(ctx)) === true) {
|
||||
let result = yapi.commons.fieldSelect(this.$user, [
|
||||
'_id',
|
||||
'username',
|
||||
'email',
|
||||
'up_time',
|
||||
'add_time',
|
||||
'role',
|
||||
'type',
|
||||
'study'
|
||||
]);
|
||||
body = yapi.commons.resReturn(result);
|
||||
} else {
|
||||
body = yapi.commons.resReturn(null, 40011, '请登录...');
|
||||
}
|
||||
|
||||
body.ladp = await this.checkLDAP();
|
||||
body.canRegister = await this.checkRegister();
|
||||
ctx.body = body;
|
||||
}
|
||||
|
||||
getRole() {
|
||||
return this.$user.role;
|
||||
}
|
||||
|
||||
getUsername() {
|
||||
return this.$user.username;
|
||||
}
|
||||
|
||||
getEmail() {
|
||||
return this.$user.email;
|
||||
}
|
||||
|
||||
async getProjectRole(id, type) {
|
||||
let result = {};
|
||||
try {
|
||||
if (this.getRole() === 'admin') {
|
||||
return 'admin';
|
||||
}
|
||||
if (type === 'interface') {
|
||||
let interfaceInst = yapi.getInst(interfaceModel);
|
||||
let interfaceData = await interfaceInst.get(id);
|
||||
result.interfaceData = interfaceData;
|
||||
// 项目创建者相当于 owner
|
||||
if (interfaceData.uid === this.getUid()) {
|
||||
return 'owner';
|
||||
}
|
||||
type = 'project';
|
||||
id = interfaceData.project_id;
|
||||
}
|
||||
|
||||
if (type === 'project') {
|
||||
let projectInst = yapi.getInst(projectModel);
|
||||
let projectData = await projectInst.get(id);
|
||||
if (projectData.uid === this.getUid()) {
|
||||
// 建立项目的人
|
||||
return 'owner';
|
||||
}
|
||||
let memberData = _.find(projectData.members, m => {
|
||||
if (m && m.uid === this.getUid()) {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
if (memberData && memberData.role) {
|
||||
if (memberData.role === 'owner') {
|
||||
return 'owner';
|
||||
} else if (memberData.role === 'dev') {
|
||||
return 'dev';
|
||||
} else {
|
||||
return 'guest';
|
||||
}
|
||||
}
|
||||
type = 'group';
|
||||
id = projectData.group_id;
|
||||
}
|
||||
|
||||
if (type === 'group') {
|
||||
let groupInst = yapi.getInst(groupModel);
|
||||
let groupData = await groupInst.get(id);
|
||||
// 建立分组的人
|
||||
if (groupData.uid === this.getUid()) {
|
||||
return 'owner';
|
||||
}
|
||||
|
||||
let groupMemberData = _.find(groupData.members, m => {
|
||||
if (m.uid === this.getUid()) {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
if (groupMemberData && groupMemberData.role) {
|
||||
if (groupMemberData.role === 'owner') {
|
||||
return 'owner';
|
||||
} else if (groupMemberData.role === 'dev') {
|
||||
return 'dev';
|
||||
} else {
|
||||
return 'guest';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 'member';
|
||||
} catch (e) {
|
||||
yapi.commons.log(e, 'error');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 身份验证
|
||||
* @param {*} id type对应的id
|
||||
* @param {*} type enum[interface, project, group]
|
||||
* @param {*} action enum[ danger, edit, view ] danger只有owner或管理员才能操作,edit只要是dev或以上就能执行
|
||||
*/
|
||||
async checkAuth(id, type, action) {
|
||||
let role = await this.getProjectRole(id, type);
|
||||
|
||||
if (action === 'danger') {
|
||||
if (role === 'admin' || role === 'owner') {
|
||||
return true;
|
||||
}
|
||||
} else if (action === 'edit') {
|
||||
if (role === 'admin' || role === 'owner' || role === 'dev') {
|
||||
return true;
|
||||
}
|
||||
} else if (action === 'view') {
|
||||
if (role === 'admin' || role === 'owner' || role === 'dev' || role === 'guest') {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = baseController;
|
||||
135
server/controllers/follow.js
Normal file
135
server/controllers/follow.js
Normal file
@@ -0,0 +1,135 @@
|
||||
const yapi = require('../yapi.js');
|
||||
const baseController = require('./base.js');
|
||||
const followModel = require('../models/follow');
|
||||
const projectModel = require('../models/project');
|
||||
|
||||
class followController extends baseController {
|
||||
constructor(ctx) {
|
||||
super(ctx);
|
||||
this.Model = yapi.getInst(followModel);
|
||||
this.projectModel = yapi.getInst(projectModel);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取关注项目列表
|
||||
* @interface /follow/list
|
||||
* @method GET
|
||||
* @category follow
|
||||
* @foldnumber 10
|
||||
* @param {Number} [page] 分页页码
|
||||
* @param {Number} [limit] 分页大小
|
||||
* @returns {Object}
|
||||
* @example /follow/list
|
||||
*/
|
||||
|
||||
async list(ctx) {
|
||||
let uid = this.getUid();
|
||||
// 关注列表暂时不分页 page & limit 为分页配置
|
||||
// page = ctx.request.query.page || 1,
|
||||
// limit = ctx.request.query.limit || 10;
|
||||
|
||||
if (!uid) {
|
||||
return (ctx.body = yapi.commons.resReturn(null, 400, '用户id不能为空'));
|
||||
}
|
||||
|
||||
try {
|
||||
let result = await this.Model.list(uid);
|
||||
|
||||
ctx.body = yapi.commons.resReturn({
|
||||
list: result
|
||||
});
|
||||
} catch (err) {
|
||||
ctx.body = yapi.commons.resReturn(null, 402, err.message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消关注
|
||||
* @interface /follow/del
|
||||
* @method POST
|
||||
* @category follow
|
||||
* @foldnumber 10
|
||||
* @param {Number} projectid
|
||||
* @returns {Object}
|
||||
* @example /follow/del
|
||||
*/
|
||||
|
||||
async del(ctx) {
|
||||
let params = ctx.request.body,
|
||||
uid = this.getUid();
|
||||
|
||||
if (!params.projectid) {
|
||||
return (ctx.body = yapi.commons.resReturn(null, 400, '项目id不能为空'));
|
||||
}
|
||||
|
||||
let checkRepeat = await this.Model.checkProjectRepeat(uid, params.projectid);
|
||||
|
||||
if (checkRepeat == 0) {
|
||||
return (ctx.body = yapi.commons.resReturn(null, 401, '项目未关注'));
|
||||
}
|
||||
|
||||
try {
|
||||
let result = await this.Model.del(params.projectid, this.getUid());
|
||||
ctx.body = yapi.commons.resReturn(result);
|
||||
} catch (e) {
|
||||
ctx.body = yapi.commons.resReturn(null, 402, e.message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加关注
|
||||
* @interface /follow/add
|
||||
* @method GET
|
||||
* @category follow
|
||||
* @foldnumber 10
|
||||
* @param {Number} projectid 项目id
|
||||
* @param {String} projectname 项目名
|
||||
* @param {String} icon 项目icon
|
||||
* @returns {Object}
|
||||
* @example /follow/add
|
||||
*/
|
||||
|
||||
async add(ctx) {
|
||||
let params = ctx.request.body;
|
||||
params = yapi.commons.handleParams(params, {
|
||||
projectid: 'number'
|
||||
});
|
||||
|
||||
let uid = this.getUid();
|
||||
|
||||
if (!params.projectid) {
|
||||
return (ctx.body = yapi.commons.resReturn(null, 400, '项目id不能为空'));
|
||||
}
|
||||
|
||||
let checkRepeat = await this.Model.checkProjectRepeat(uid, params.projectid);
|
||||
|
||||
if (checkRepeat) {
|
||||
return (ctx.body = yapi.commons.resReturn(null, 401, '项目已关注'));
|
||||
}
|
||||
|
||||
try {
|
||||
let project = await this.projectModel.get(params.projectid);
|
||||
let data = {
|
||||
uid: uid,
|
||||
projectid: params.projectid,
|
||||
projectname: project.name,
|
||||
icon: project.icon,
|
||||
color: project.color
|
||||
};
|
||||
let result = await this.Model.save(data);
|
||||
result = yapi.commons.fieldSelect(result, [
|
||||
'_id',
|
||||
'uid',
|
||||
'projectid',
|
||||
'projectname',
|
||||
'icon',
|
||||
'color'
|
||||
]);
|
||||
ctx.body = yapi.commons.resReturn(result);
|
||||
} catch (e) {
|
||||
ctx.body = yapi.commons.resReturn(null, 402, e.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = followController;
|
||||
534
server/controllers/group.js
Normal file
534
server/controllers/group.js
Normal file
@@ -0,0 +1,534 @@
|
||||
const groupModel = require('../models/group.js');
|
||||
const yapi = require('../yapi.js');
|
||||
const baseController = require('./base.js');
|
||||
const projectModel = require('../models/project.js');
|
||||
const userModel = require('../models/user.js');
|
||||
const interfaceModel = require('../models/interface.js');
|
||||
const interfaceColModel = require('../models/interfaceCol.js');
|
||||
const interfaceCaseModel = require('../models/interfaceCase.js');
|
||||
const _ = require('underscore')
|
||||
|
||||
const rolename = {
|
||||
owner: '组长',
|
||||
dev: '开发者',
|
||||
guest: '访客'
|
||||
};
|
||||
|
||||
class groupController extends baseController {
|
||||
constructor(ctx) {
|
||||
super(ctx);
|
||||
|
||||
const id = 'number';
|
||||
const group_name = {
|
||||
type: 'string',
|
||||
minLength: 1
|
||||
};
|
||||
|
||||
const group_desc = 'string';
|
||||
const role = {
|
||||
type: 'string',
|
||||
enum: ['owner', 'dev', 'guest']
|
||||
};
|
||||
|
||||
const member_uids = {
|
||||
type: 'array',
|
||||
items: 'number',
|
||||
minItems: 1
|
||||
};
|
||||
|
||||
this.schemaMap = {
|
||||
get: {
|
||||
'*id': id
|
||||
},
|
||||
add: {
|
||||
'*group_name': group_name,
|
||||
group_desc: group_desc,
|
||||
owner_uids: ['number']
|
||||
},
|
||||
addMember: {
|
||||
'*id': id,
|
||||
role: role,
|
||||
'*member_uids': member_uids
|
||||
},
|
||||
changeMemberRole: {
|
||||
'*member_uid': 'number',
|
||||
'*id': id,
|
||||
role: role
|
||||
},
|
||||
getMemberList: {
|
||||
'*id': id
|
||||
},
|
||||
delMember: {
|
||||
'*id': id,
|
||||
'*member_uid': 'number'
|
||||
},
|
||||
del: {
|
||||
'*id': id
|
||||
},
|
||||
up: {
|
||||
'*id': id,
|
||||
'*group_name': group_name,
|
||||
group_desc: group_desc,
|
||||
custom_field1: {
|
||||
name: 'string',
|
||||
enable: 'boolen'
|
||||
},
|
||||
custom_field2: {
|
||||
name: 'string',
|
||||
enable: 'boolen'
|
||||
},
|
||||
custom_field3: {
|
||||
name: 'string',
|
||||
enable: 'boolen'
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询项目分组
|
||||
* @interface /group/get
|
||||
* @method GET
|
||||
* @category group
|
||||
* @foldnumber 10
|
||||
* @param {String} id 项目分组ID
|
||||
* @returns {Object}
|
||||
* @example
|
||||
*/
|
||||
async get(ctx) {
|
||||
let params = ctx.params;
|
||||
|
||||
let groupInst = yapi.getInst(groupModel);
|
||||
let result = await groupInst.getGroupById(params.id);
|
||||
if (result) {
|
||||
result = result.toObject();
|
||||
let role = await this.getProjectRole(params.id, 'group');
|
||||
result.role = role;
|
||||
if (result.type === 'private') {
|
||||
result.group_name = '个人空间';
|
||||
}
|
||||
ctx.body = yapi.commons.resReturn(result);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加项目分组
|
||||
* @interface /group/add
|
||||
* @method POST
|
||||
* @category group
|
||||
* @foldnumber 10
|
||||
* @param {String} group_name 项目分组名称,不能为空
|
||||
* @param {String} [group_desc] 项目分组描述
|
||||
* @param {String} [owner_uids] 组长[uid]
|
||||
* @returns {Object}
|
||||
* @example ./api/group/add.json
|
||||
*/
|
||||
async add(ctx) {
|
||||
let params = ctx.params;
|
||||
|
||||
// 新版每个人都有权限添加分组
|
||||
|
||||
// if (this.getRole() !== 'admin') {
|
||||
// return (ctx.body = yapi.commons.resReturn(null, 401, '没有权限'));
|
||||
// }
|
||||
|
||||
let owners = [];
|
||||
|
||||
if(params.owner_uids.length === 0){
|
||||
params.owner_uids.push(
|
||||
this.getUid()
|
||||
)
|
||||
}
|
||||
|
||||
if (params.owner_uids) {
|
||||
for (let i = 0, len = params.owner_uids.length; i < len; i++) {
|
||||
let id = params.owner_uids[i];
|
||||
let groupUserdata = await this.getUserdata(id, 'owner');
|
||||
if (groupUserdata) {
|
||||
owners.push(groupUserdata);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let groupInst = yapi.getInst(groupModel);
|
||||
|
||||
let checkRepeat = await groupInst.checkRepeat(params.group_name);
|
||||
|
||||
if (checkRepeat > 0) {
|
||||
return (ctx.body = yapi.commons.resReturn(null, 401, '项目分组名已存在'));
|
||||
}
|
||||
|
||||
let data = {
|
||||
group_name: params.group_name,
|
||||
group_desc: params.group_desc,
|
||||
uid: this.getUid(),
|
||||
add_time: yapi.commons.time(),
|
||||
up_time: yapi.commons.time(),
|
||||
members: owners
|
||||
};
|
||||
|
||||
let result = await groupInst.save(data);
|
||||
result = yapi.commons.fieldSelect(result, [
|
||||
'_id',
|
||||
'group_name',
|
||||
'group_desc',
|
||||
'uid',
|
||||
'members',
|
||||
'type'
|
||||
]);
|
||||
let username = this.getUsername();
|
||||
yapi.commons.saveLog({
|
||||
content: `<a href="/user/profile/${this.getUid()}">${username}</a> 新增了分组 <a href="/group/${
|
||||
result._id
|
||||
}">${params.group_name}</a>`,
|
||||
type: 'group',
|
||||
uid: this.getUid(),
|
||||
username: username,
|
||||
typeid: result._id
|
||||
});
|
||||
ctx.body = yapi.commons.resReturn(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户数据
|
||||
* @param uid
|
||||
* @param role
|
||||
* @returns {Promise.<*>}
|
||||
*/
|
||||
|
||||
async getUserdata(uid, role) {
|
||||
role = role || 'dev';
|
||||
let userInst = yapi.getInst(userModel);
|
||||
let userData = await userInst.findById(uid);
|
||||
if (!userData) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
_role: userData.role,
|
||||
role: role,
|
||||
uid: userData._id,
|
||||
username: userData.username,
|
||||
email: userData.email
|
||||
};
|
||||
}
|
||||
|
||||
async getMyGroup(ctx){
|
||||
var groupInst = yapi.getInst(groupModel);
|
||||
let privateGroup = await groupInst.getByPrivateUid(this.getUid());
|
||||
if (!privateGroup) {
|
||||
privateGroup = await groupInst.save({
|
||||
uid: this.getUid(),
|
||||
group_name: 'User-' + this.getUid(),
|
||||
add_time: yapi.commons.time(),
|
||||
up_time: yapi.commons.time(),
|
||||
type: 'private'
|
||||
});
|
||||
}
|
||||
if(privateGroup){
|
||||
ctx.body = yapi.commons.resReturn(privateGroup)
|
||||
}else{
|
||||
ctx.body = yapi.commons.resReturn(null)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加项目分组成员
|
||||
* @interface /group/add_member
|
||||
* @method POST
|
||||
* @category group
|
||||
* @foldnumber 10
|
||||
* @param {String} id 项目分组id
|
||||
* @param {String} member_uids 项目分组成员[uid]
|
||||
* @param {String} role 成员角色,owner or dev or guest
|
||||
* @returns {Object}
|
||||
* @example
|
||||
*/
|
||||
async addMember(ctx) {
|
||||
let params = ctx.params;
|
||||
let groupInst = yapi.getInst(groupModel);
|
||||
|
||||
params.role = ['owner', 'dev', 'guest'].find(v => v === params.role) || 'dev';
|
||||
let add_members = [];
|
||||
let exist_members = [];
|
||||
let no_members = [];
|
||||
for (let i = 0, len = params.member_uids.length; i < len; i++) {
|
||||
let id = params.member_uids[i];
|
||||
let check = await groupInst.checkMemberRepeat(params.id, id);
|
||||
let userdata = await this.getUserdata(id, params.role);
|
||||
if (check > 0) {
|
||||
exist_members.push(userdata);
|
||||
} else if (!userdata) {
|
||||
no_members.push(id);
|
||||
} else {
|
||||
userdata.role !== 'admin' && add_members.push(userdata);
|
||||
delete userdata._role;
|
||||
}
|
||||
}
|
||||
|
||||
let result = await groupInst.addMember(params.id, add_members);
|
||||
let username = this.getUsername();
|
||||
if (add_members.length) {
|
||||
let members = add_members.map(item => {
|
||||
return `<a href = "/user/profile/${item.uid}">${item.username}</a>`;
|
||||
});
|
||||
members = members.join('、');
|
||||
yapi.commons.saveLog({
|
||||
content: `<a href="/user/profile/${this.getUid()}">${username}</a> 新增了分组成员 ${members} 为 ${
|
||||
rolename[params.role]
|
||||
}`,
|
||||
type: 'group',
|
||||
uid: this.getUid(),
|
||||
username: username,
|
||||
typeid: params.id
|
||||
});
|
||||
}
|
||||
ctx.body = yapi.commons.resReturn({
|
||||
result,
|
||||
add_members,
|
||||
exist_members,
|
||||
no_members
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改项目分组成员角色
|
||||
* @interface /group/change_member_role
|
||||
* @method POST
|
||||
* @category group
|
||||
* @foldnumber 10
|
||||
* @param {String} id 项目分组id
|
||||
* @param {String} member_uid 项目分组成员uid
|
||||
* @param {String} role 权限 ['owner'|'dev']
|
||||
* @returns {Object}
|
||||
* @example
|
||||
*/
|
||||
async changeMemberRole(ctx) {
|
||||
let params = ctx.request.body;
|
||||
let groupInst = yapi.getInst(groupModel);
|
||||
|
||||
var check = await groupInst.checkMemberRepeat(params.id, params.member_uid);
|
||||
if (check === 0) {
|
||||
return (ctx.body = yapi.commons.resReturn(null, 400, '分组成员不存在'));
|
||||
}
|
||||
if ((await this.checkAuth(params.id, 'group', 'danger')) !== true) {
|
||||
return (ctx.body = yapi.commons.resReturn(null, 405, '没有权限'));
|
||||
}
|
||||
|
||||
params.role = ['owner', 'dev', 'guest'].find(v => v === params.role) || 'dev';
|
||||
|
||||
let result = await groupInst.changeMemberRole(params.id, params.member_uid, params.role);
|
||||
let username = this.getUsername();
|
||||
|
||||
let groupUserdata = await this.getUserdata(params.member_uid, params.role);
|
||||
yapi.commons.saveLog({
|
||||
content: `<a href="/user/profile/${this.getUid()}">${username}</a> 更改了分组成员 <a href="/user/profile/${
|
||||
params.member_uid
|
||||
}">${groupUserdata ? groupUserdata.username : ''}</a> 的权限为 "${rolename[params.role]}"`,
|
||||
type: 'group',
|
||||
uid: this.getUid(),
|
||||
username: username,
|
||||
typeid: params.id
|
||||
});
|
||||
ctx.body = yapi.commons.resReturn(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有项目成员
|
||||
* @interface /group/get_member_list
|
||||
* @method GET
|
||||
* @category group
|
||||
* @foldnumber 10
|
||||
* @param {String} id 项目分组id
|
||||
* @returns {Object}
|
||||
* @example
|
||||
*/
|
||||
|
||||
async getMemberList(ctx) {
|
||||
let params = ctx.params;
|
||||
let groupInst = yapi.getInst(groupModel);
|
||||
let group = await groupInst.get(params.id);
|
||||
ctx.body = yapi.commons.resReturn(group.members);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除项目成员
|
||||
* @interface /group/del_member
|
||||
* @method POST
|
||||
* @category group
|
||||
* @foldnumber 10
|
||||
* @param {String} id 项目分组id
|
||||
* @param {String} member_uid 项目分组成员uid
|
||||
* @returns {Object}
|
||||
* @example
|
||||
*/
|
||||
|
||||
async delMember(ctx) {
|
||||
let params = ctx.params;
|
||||
let groupInst = yapi.getInst(groupModel);
|
||||
var check = await groupInst.checkMemberRepeat(params.id, params.member_uid);
|
||||
if (check === 0) {
|
||||
return (ctx.body = yapi.commons.resReturn(null, 400, '分组成员不存在'));
|
||||
}
|
||||
if ((await this.checkAuth(params.id, 'group', 'danger')) !== true) {
|
||||
return (ctx.body = yapi.commons.resReturn(null, 405, '没有权限'));
|
||||
}
|
||||
|
||||
let result = await groupInst.delMember(params.id, params.member_uid);
|
||||
let username = this.getUsername();
|
||||
|
||||
let groupUserdata = await this.getUserdata(params.member_uid, params.role);
|
||||
yapi.commons.saveLog({
|
||||
content: `<a href="/user/profile/${this.getUid()}">${username}</a> 删除了分组成员 <a href="/user/profile/${
|
||||
params.member_uid
|
||||
}">${groupUserdata ? groupUserdata.username : ''}</a>`,
|
||||
type: 'group',
|
||||
uid: this.getUid(),
|
||||
username: username,
|
||||
typeid: params.id
|
||||
});
|
||||
ctx.body = yapi.commons.resReturn(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取项目分组列表
|
||||
* @interface /group/list
|
||||
* @method get
|
||||
* @category group
|
||||
* @foldnumber 10
|
||||
* @returns {Object}
|
||||
* @example ./api/group/list.json
|
||||
*/
|
||||
async list(ctx) {
|
||||
var groupInst = yapi.getInst(groupModel);
|
||||
let projectInst = yapi.getInst(projectModel);
|
||||
|
||||
let privateGroup = await groupInst.getByPrivateUid(this.getUid());
|
||||
let newResult = [];
|
||||
|
||||
if (!privateGroup) {
|
||||
privateGroup = await groupInst.save({
|
||||
uid: this.getUid(),
|
||||
group_name: 'User-' + this.getUid(),
|
||||
add_time: yapi.commons.time(),
|
||||
up_time: yapi.commons.time(),
|
||||
type: 'private'
|
||||
});
|
||||
}
|
||||
|
||||
if(this.getRole() === 'admin'){
|
||||
let result = await groupInst.list();
|
||||
if(result && result.length > 0 ){
|
||||
for (let i = 0; i < result.length; i++){
|
||||
result[i] = result[i].toObject();
|
||||
newResult.unshift(result[i])
|
||||
}
|
||||
}
|
||||
}else{
|
||||
let result = await groupInst.getAuthList(this.getUid());
|
||||
if(result && result.length > 0 ){
|
||||
for (let i = 0; i < result.length; i++){
|
||||
result[i] = result[i].toObject();
|
||||
newResult.unshift(result[i])
|
||||
}
|
||||
}
|
||||
|
||||
const groupIds = newResult.map(item=> item._id);
|
||||
const newGroupIds = [];
|
||||
|
||||
let groupByProject = await projectInst.getAuthList(this.getUid());
|
||||
if(groupByProject && groupByProject.length > 0){
|
||||
groupByProject.forEach( _data=>{
|
||||
const _temp = [...groupIds, ...newGroupIds];
|
||||
if(!_.find(_temp, id=> id === _data.group_id)){
|
||||
newGroupIds.push(_data.group_id)
|
||||
}
|
||||
})
|
||||
}
|
||||
let newData = await groupInst.findByGroups(newGroupIds)
|
||||
newData.forEach(_data=>{
|
||||
_data = _data.toObject();
|
||||
newResult.push(_data);
|
||||
})
|
||||
}
|
||||
if (privateGroup) {
|
||||
privateGroup = privateGroup.toObject();
|
||||
privateGroup.group_name = '个人空间';
|
||||
privateGroup.role = 'owner';
|
||||
newResult.unshift(privateGroup);
|
||||
}
|
||||
|
||||
ctx.body = yapi.commons.resReturn(newResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除项目分组
|
||||
* @interface /group/del
|
||||
* @method post
|
||||
* @param {String} id 项目分组id
|
||||
* @category group
|
||||
* @foldnumber 10
|
||||
* @returns {Object}
|
||||
* @example ./api/group/del.json
|
||||
*/
|
||||
async del(ctx) {
|
||||
if (this.getRole() !== 'admin') {
|
||||
return (ctx.body = yapi.commons.resReturn(null, 401, '没有权限'));
|
||||
}
|
||||
|
||||
let groupInst = yapi.getInst(groupModel);
|
||||
let projectInst = yapi.getInst(projectModel);
|
||||
let interfaceInst = yapi.getInst(interfaceModel);
|
||||
let interfaceColInst = yapi.getInst(interfaceColModel);
|
||||
let interfaceCaseInst = yapi.getInst(interfaceCaseModel);
|
||||
let id = ctx.params.id;
|
||||
|
||||
let projectList = await projectInst.list(id, true);
|
||||
projectList.forEach(async p => {
|
||||
await interfaceInst.delByProjectId(p._id);
|
||||
await interfaceCaseInst.delByProjectId(p._id);
|
||||
await interfaceColInst.delByProjectId(p._id);
|
||||
});
|
||||
if (projectList.length > 0) {
|
||||
await projectInst.delByGroupid(id);
|
||||
}
|
||||
|
||||
let result = await groupInst.del(id);
|
||||
ctx.body = yapi.commons.resReturn(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新项目分组
|
||||
* @interface /group/up
|
||||
* @method post
|
||||
* @param {String} id 项目分组id
|
||||
* @param {String} group_name 项目分组名称
|
||||
* @param {String} group_desc 项目分组描述
|
||||
* @category group
|
||||
* @foldnumber 10
|
||||
* @returns {Object}
|
||||
* @example ./api/group/up.json
|
||||
*/
|
||||
async up(ctx) {
|
||||
let groupInst = yapi.getInst(groupModel);
|
||||
let params = ctx.params;
|
||||
|
||||
if ((await this.checkAuth(params.id, 'group', 'danger')) !== true) {
|
||||
return (ctx.body = yapi.commons.resReturn(null, 405, '没有权限'));
|
||||
}
|
||||
|
||||
let result = await groupInst.up(params.id, params);
|
||||
let username = this.getUsername();
|
||||
yapi.commons.saveLog({
|
||||
content: `<a href="/user/profile/${this.getUid()}">${username}</a> 更新了 <a href="/group/${
|
||||
params.id
|
||||
}">${params.group_name}</a> 分组`,
|
||||
type: 'group',
|
||||
uid: this.getUid(),
|
||||
username: username,
|
||||
typeid: params.id
|
||||
});
|
||||
ctx.body = yapi.commons.resReturn(result);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = groupController;
|
||||
1286
server/controllers/interface.js
Normal file
1286
server/controllers/interface.js
Normal file
File diff suppressed because it is too large
Load Diff
876
server/controllers/interfaceCol.js
Normal file
876
server/controllers/interfaceCol.js
Normal file
@@ -0,0 +1,876 @@
|
||||
const interfaceColModel = require('../models/interfaceCol.js');
|
||||
const interfaceCaseModel = require('../models/interfaceCase.js');
|
||||
const interfaceModel = require('../models/interface.js');
|
||||
const projectModel = require('../models/project.js');
|
||||
const baseController = require('./base.js');
|
||||
const yapi = require('../yapi.js');
|
||||
const _ = require('underscore');
|
||||
|
||||
class interfaceColController extends baseController {
|
||||
constructor(ctx) {
|
||||
super(ctx);
|
||||
this.colModel = yapi.getInst(interfaceColModel);
|
||||
this.caseModel = yapi.getInst(interfaceCaseModel);
|
||||
this.interfaceModel = yapi.getInst(interfaceModel);
|
||||
this.projectModel = yapi.getInst(projectModel);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有接口集
|
||||
* @interface /col/list
|
||||
* @method GET
|
||||
* @category col
|
||||
* @foldnumber 10
|
||||
* @param {String} project_id email名称,不能为空
|
||||
* @returns {Object}
|
||||
* @example
|
||||
*/
|
||||
async list(ctx) {
|
||||
try {
|
||||
let id = ctx.query.project_id;
|
||||
let project = await this.projectModel.getBaseInfo(id);
|
||||
if (project.project_type === 'private') {
|
||||
if ((await this.checkAuth(project._id, 'project', 'view')) !== true) {
|
||||
return (ctx.body = yapi.commons.resReturn(null, 406, '没有权限'));
|
||||
}
|
||||
}
|
||||
let result = await this.colModel.list(id);
|
||||
result = result.sort((a, b) => {
|
||||
return a.index - b.index;
|
||||
});
|
||||
|
||||
for (let i = 0; i < result.length; i++) {
|
||||
result[i] = result[i].toObject();
|
||||
let caseList = await this.caseModel.list(result[i]._id);
|
||||
|
||||
for(let j=0; j< caseList.length; j++){
|
||||
let item = caseList[j].toObject();
|
||||
let interfaceData = await this.interfaceModel.getBaseinfo(item.interface_id);
|
||||
item.path = interfaceData.path;
|
||||
caseList[j] = item;
|
||||
}
|
||||
|
||||
caseList = caseList.sort((a, b) => {
|
||||
return a.index - b.index;
|
||||
});
|
||||
result[i].caseList = caseList;
|
||||
|
||||
}
|
||||
ctx.body = yapi.commons.resReturn(result);
|
||||
} catch (e) {
|
||||
ctx.body = yapi.commons.resReturn(null, 402, e.message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 增加接口集
|
||||
* @interface /col/add_col
|
||||
* @method POST
|
||||
* @category col
|
||||
* @foldnumber 10
|
||||
* @param {Number} project_id
|
||||
* @param {String} name
|
||||
* @param {String} desc
|
||||
* @returns {Object}
|
||||
* @example
|
||||
*/
|
||||
|
||||
async addCol(ctx) {
|
||||
try {
|
||||
let params = ctx.request.body;
|
||||
params = yapi.commons.handleParams(params, {
|
||||
name: 'string',
|
||||
project_id: 'number',
|
||||
desc: 'string'
|
||||
});
|
||||
|
||||
if (!params.project_id) {
|
||||
return (ctx.body = yapi.commons.resReturn(null, 400, '项目id不能为空'));
|
||||
}
|
||||
if (!params.name) {
|
||||
return (ctx.body = yapi.commons.resReturn(null, 400, '名称不能为空'));
|
||||
}
|
||||
|
||||
let auth = await this.checkAuth(params.project_id, 'project', 'edit');
|
||||
if (!auth) {
|
||||
return (ctx.body = yapi.commons.resReturn(null, 400, '没有权限'));
|
||||
}
|
||||
|
||||
let result = await this.colModel.save({
|
||||
name: params.name,
|
||||
project_id: params.project_id,
|
||||
desc: params.desc,
|
||||
uid: this.getUid(),
|
||||
add_time: yapi.commons.time(),
|
||||
up_time: yapi.commons.time()
|
||||
});
|
||||
let username = this.getUsername();
|
||||
yapi.commons.saveLog({
|
||||
content: `<a href="/user/profile/${this.getUid()}">${username}</a> 添加了接口集 <a href="/project/${
|
||||
params.project_id
|
||||
}/interface/col/${result._id}">${params.name}</a>`,
|
||||
type: 'project',
|
||||
uid: this.getUid(),
|
||||
username: username,
|
||||
typeid: params.project_id
|
||||
});
|
||||
// this.projectModel.up(params.project_id,{up_time: new Date().getTime()}).then();
|
||||
ctx.body = yapi.commons.resReturn(result);
|
||||
} catch (e) {
|
||||
ctx.body = yapi.commons.resReturn(null, 402, e.message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取一个接口集下的所有的测试用例
|
||||
* @interface /col/case_list
|
||||
* @method GET
|
||||
* @category col
|
||||
* @foldnumber 10
|
||||
* @param {String} col_id 接口集id
|
||||
* @returns {Object}
|
||||
* @example
|
||||
*/
|
||||
async getCaseList(ctx) {
|
||||
try {
|
||||
let id = ctx.query.col_id;
|
||||
if (!id || id == 0) {
|
||||
return (ctx.body = yapi.commons.resReturn(null, 407, 'col_id不能为空'));
|
||||
}
|
||||
|
||||
let colData = await this.colModel.get(id);
|
||||
let project = await this.projectModel.getBaseInfo(colData.project_id);
|
||||
if (project.project_type === 'private') {
|
||||
if ((await this.checkAuth(project._id, 'project', 'view')) !== true) {
|
||||
return (ctx.body = yapi.commons.resReturn(null, 406, '没有权限'));
|
||||
}
|
||||
}
|
||||
|
||||
ctx.body = await yapi.commons.getCaseList(id);
|
||||
} catch (e) {
|
||||
ctx.body = yapi.commons.resReturn(null, 402, e.message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取一个接口集下的所有的测试用例的环境变量
|
||||
* @interface /col/case_env_list
|
||||
* @method GET
|
||||
* @category col
|
||||
* @foldnumber 10
|
||||
* @param {String} col_id 接口集id
|
||||
* @returns {Object}
|
||||
* @example
|
||||
*/
|
||||
async getCaseEnvList(ctx) {
|
||||
try {
|
||||
let id = ctx.query.col_id;
|
||||
if (!id || id == 0) {
|
||||
return (ctx.body = yapi.commons.resReturn(null, 407, 'col_id不能为空'));
|
||||
}
|
||||
|
||||
let colData = await this.colModel.get(id);
|
||||
let project = await this.projectModel.getBaseInfo(colData.project_id);
|
||||
if (project.project_type === 'private') {
|
||||
if ((await this.checkAuth(project._id, 'project', 'view')) !== true) {
|
||||
return (ctx.body = yapi.commons.resReturn(null, 406, '没有权限'));
|
||||
}
|
||||
}
|
||||
|
||||
// 通过col_id 找到 caseList
|
||||
let projectList = await this.caseModel.list(id, 'project_id');
|
||||
// 对projectList 进行去重处理
|
||||
projectList = this.unique(projectList, 'project_id');
|
||||
|
||||
// 遍历projectList 找到项目和env
|
||||
let projectEnvList = [];
|
||||
for (let i = 0; i < projectList.length; i++) {
|
||||
let result = await this.projectModel.getBaseInfo(projectList[i], 'name env');
|
||||
projectEnvList.push(result);
|
||||
}
|
||||
ctx.body = yapi.commons.resReturn(projectEnvList);
|
||||
} catch (e) {
|
||||
ctx.body = yapi.commons.resReturn(null, 402, e.message);
|
||||
}
|
||||
}
|
||||
|
||||
requestParamsToObj(arr) {
|
||||
if (!arr || !Array.isArray(arr) || arr.length === 0) {
|
||||
return {};
|
||||
}
|
||||
let obj = {};
|
||||
arr.forEach(item => {
|
||||
obj[item.name] = '';
|
||||
});
|
||||
return obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取一个接口集下的所有的测试用例
|
||||
* @interface /col/case_list_by_var_params
|
||||
* @method GET
|
||||
* @category col
|
||||
* @foldnumber 10
|
||||
* @param {String} col_id 接口集id
|
||||
* @returns {Object}
|
||||
* @example
|
||||
*/
|
||||
|
||||
async getCaseListByVariableParams(ctx) {
|
||||
try {
|
||||
let id = ctx.query.col_id;
|
||||
if (!id || id == 0) {
|
||||
return (ctx.body = yapi.commons.resReturn(null, 407, 'col_id不能为空'));
|
||||
}
|
||||
let resultList = await this.caseModel.list(id, 'all');
|
||||
if (resultList.length === 0) {
|
||||
return (ctx.body = yapi.commons.resReturn([]));
|
||||
}
|
||||
let project = await this.projectModel.getBaseInfo(resultList[0].project_id);
|
||||
|
||||
if (project.project_type === 'private') {
|
||||
if ((await this.checkAuth(project._id, 'project', 'view')) !== true) {
|
||||
return (ctx.body = yapi.commons.resReturn(null, 406, '没有权限'));
|
||||
}
|
||||
}
|
||||
|
||||
for (let index = 0; index < resultList.length; index++) {
|
||||
let result = resultList[index].toObject();
|
||||
let item = {},
|
||||
body,
|
||||
query,
|
||||
bodyParams,
|
||||
pathParams;
|
||||
let data = await this.interfaceModel.get(result.interface_id);
|
||||
if (!data) {
|
||||
await this.caseModel.del(result._id);
|
||||
continue;
|
||||
}
|
||||
item._id = result._id;
|
||||
item.casename = result.casename;
|
||||
body = yapi.commons.json_parse(data.res_body);
|
||||
body = typeof body === 'object' ? body : {};
|
||||
if (data.res_body_is_json_schema) {
|
||||
body = yapi.commons.schemaToJson(body, {
|
||||
alwaysFakeOptionals: true
|
||||
});
|
||||
}
|
||||
item.body = Object.assign({}, body);
|
||||
query = this.requestParamsToObj(data.req_query);
|
||||
pathParams = this.requestParamsToObj(data.req_params);
|
||||
if (data.req_body_type === 'form') {
|
||||
bodyParams = this.requestParamsToObj(data.req_body_form);
|
||||
} else {
|
||||
bodyParams = yapi.commons.json_parse(data.req_body_other);
|
||||
if (data.req_body_is_json_schema) {
|
||||
bodyParams = yapi.commons.schemaToJson(bodyParams, {
|
||||
alwaysFakeOptionals: true
|
||||
});
|
||||
}
|
||||
bodyParams = typeof bodyParams === 'object' ? bodyParams : {};
|
||||
}
|
||||
item.params = Object.assign(pathParams, query, bodyParams);
|
||||
item.index = result.index;
|
||||
resultList[index] = item;
|
||||
}
|
||||
|
||||
ctx.body = yapi.commons.resReturn(resultList);
|
||||
} catch (e) {
|
||||
ctx.body = yapi.commons.resReturn(null, 402, e.message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 增加一个测试用例
|
||||
* @interface /col/add_case
|
||||
* @method POST
|
||||
* @category col
|
||||
* @foldnumber 10
|
||||
* @param {String} casename
|
||||
* @param {Number} col_id
|
||||
* @param {Number} project_id
|
||||
* @param {String} domain
|
||||
* @param {String} path
|
||||
* @param {String} method
|
||||
* @param {Object} req_query
|
||||
* @param {Object} req_headers
|
||||
* @param {String} req_body_type
|
||||
* @param {Array} req_body_form
|
||||
* @param {String} req_body_other
|
||||
* @returns {Object}
|
||||
* @example
|
||||
*/
|
||||
|
||||
async addCase(ctx) {
|
||||
try {
|
||||
let params = ctx.request.body;
|
||||
params = yapi.commons.handleParams(params, {
|
||||
casename: 'string',
|
||||
project_id: 'number',
|
||||
col_id: 'number',
|
||||
interface_id: 'number',
|
||||
case_env: 'string'
|
||||
});
|
||||
|
||||
if (!params.project_id) {
|
||||
return (ctx.body = yapi.commons.resReturn(null, 400, '项目id不能为空'));
|
||||
}
|
||||
|
||||
if (!params.interface_id) {
|
||||
return (ctx.body = yapi.commons.resReturn(null, 400, '接口id不能为空'));
|
||||
}
|
||||
|
||||
let auth = await this.checkAuth(params.project_id, 'project', 'edit');
|
||||
if (!auth) {
|
||||
return (ctx.body = yapi.commons.resReturn(null, 400, '没有权限'));
|
||||
}
|
||||
|
||||
if (!params.col_id) {
|
||||
return (ctx.body = yapi.commons.resReturn(null, 400, '接口集id不能为空'));
|
||||
}
|
||||
|
||||
if (!params.casename) {
|
||||
return (ctx.body = yapi.commons.resReturn(null, 400, '用例名称不能为空'));
|
||||
}
|
||||
|
||||
params.uid = this.getUid();
|
||||
params.index = 0;
|
||||
params.add_time = yapi.commons.time();
|
||||
params.up_time = yapi.commons.time();
|
||||
let result = await this.caseModel.save(params);
|
||||
let username = this.getUsername();
|
||||
|
||||
this.colModel.get(params.col_id).then(col => {
|
||||
yapi.commons.saveLog({
|
||||
content: `<a href="/user/profile/${this.getUid()}">${username}</a> 在接口集 <a href="/project/${
|
||||
params.project_id
|
||||
}/interface/col/${params.col_id}">${col.name}</a> 下添加了测试用例 <a href="/project/${
|
||||
params.project_id
|
||||
}/interface/case/${result._id}">${params.casename}</a>`,
|
||||
type: 'project',
|
||||
uid: this.getUid(),
|
||||
username: username,
|
||||
typeid: params.project_id
|
||||
});
|
||||
});
|
||||
this.projectModel.up(params.project_id, { up_time: new Date().getTime() }).then();
|
||||
|
||||
ctx.body = yapi.commons.resReturn(result);
|
||||
} catch (e) {
|
||||
ctx.body = yapi.commons.resReturn(null, 402, e.message);
|
||||
}
|
||||
}
|
||||
|
||||
async addCaseList(ctx) {
|
||||
try {
|
||||
let params = ctx.request.body;
|
||||
params = yapi.commons.handleParams(params, {
|
||||
project_id: 'number',
|
||||
col_id: 'number'
|
||||
});
|
||||
if (!params.interface_list || !Array.isArray(params.interface_list)) {
|
||||
return (ctx.body = yapi.commons.resReturn(null, 400, 'interface_list 参数有误'));
|
||||
}
|
||||
|
||||
if (!params.project_id) {
|
||||
return (ctx.body = yapi.commons.resReturn(null, 400, '项目id不能为空'));
|
||||
}
|
||||
|
||||
let auth = await this.checkAuth(params.project_id, 'project', 'edit');
|
||||
if (!auth) {
|
||||
return (ctx.body = yapi.commons.resReturn(null, 400, '没有权限'));
|
||||
}
|
||||
|
||||
if (!params.col_id) {
|
||||
return (ctx.body = yapi.commons.resReturn(null, 400, '接口集id不能为空'));
|
||||
}
|
||||
|
||||
let data = {
|
||||
uid: this.getUid(),
|
||||
index: 0,
|
||||
add_time: yapi.commons.time(),
|
||||
up_time: yapi.commons.time(),
|
||||
project_id: params.project_id,
|
||||
col_id: params.col_id
|
||||
};
|
||||
|
||||
for (let i = 0; i < params.interface_list.length; i++) {
|
||||
let interfaceData = await this.interfaceModel.get(params.interface_list[i]);
|
||||
data.interface_id = params.interface_list[i];
|
||||
data.casename = interfaceData.title;
|
||||
|
||||
// 处理json schema 解析
|
||||
if (
|
||||
interfaceData.req_body_type === 'json' &&
|
||||
interfaceData.req_body_other &&
|
||||
interfaceData.req_body_is_json_schema
|
||||
) {
|
||||
let req_body_other = yapi.commons.json_parse(interfaceData.req_body_other);
|
||||
req_body_other = yapi.commons.schemaToJson(req_body_other, {
|
||||
alwaysFakeOptionals: true
|
||||
});
|
||||
|
||||
data.req_body_other = JSON.stringify(req_body_other);
|
||||
} else {
|
||||
data.req_body_other = interfaceData.req_body_other;
|
||||
}
|
||||
|
||||
data.req_body_type = interfaceData.req_body_type;
|
||||
let caseResultData = await this.caseModel.save(data);
|
||||
let username = this.getUsername();
|
||||
this.colModel.get(params.col_id).then(col => {
|
||||
yapi.commons.saveLog({
|
||||
content: `<a href="/user/profile/${this.getUid()}">${username}</a> 在接口集 <a href="/project/${
|
||||
params.project_id
|
||||
}/interface/col/${params.col_id}">${col.name}</a> 下导入了测试用例 <a href="/project/${
|
||||
params.project_id
|
||||
}/interface/case/${caseResultData._id}">${data.casename}</a>`,
|
||||
type: 'project',
|
||||
uid: this.getUid(),
|
||||
username: username,
|
||||
typeid: params.project_id
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
this.projectModel.up(params.project_id, { up_time: new Date().getTime() }).then();
|
||||
|
||||
ctx.body = yapi.commons.resReturn('ok');
|
||||
} catch (e) {
|
||||
ctx.body = yapi.commons.resReturn(null, 402, e.message);
|
||||
}
|
||||
}
|
||||
|
||||
async cloneCaseList(ctx) {
|
||||
try {
|
||||
let params = ctx.request.body;
|
||||
params = yapi.commons.handleParams(params, {
|
||||
project_id: 'number',
|
||||
col_id: 'number',
|
||||
new_col_id: 'number'
|
||||
});
|
||||
|
||||
const { project_id, col_id, new_col_id } = params;
|
||||
|
||||
if (!project_id) {
|
||||
return (ctx.body = yapi.commons.resReturn(null, 400, '项目id不能为空'));
|
||||
}
|
||||
|
||||
let auth = await this.checkAuth(params.project_id, 'project', 'edit');
|
||||
|
||||
if (!auth) {
|
||||
return (ctx.body = yapi.commons.resReturn(null, 400, '没有权限'));
|
||||
}
|
||||
|
||||
if (!col_id) {
|
||||
return (ctx.body = yapi.commons.resReturn(null, 400, '被克隆的接口集id不能为空'));
|
||||
}
|
||||
|
||||
if (!new_col_id) {
|
||||
return (ctx.body = yapi.commons.resReturn(null, 400, '克隆的接口集id不能为空'));
|
||||
}
|
||||
|
||||
let oldColCaselistData = await this.caseModel.list(col_id, 'all');
|
||||
|
||||
oldColCaselistData = oldColCaselistData.sort((a, b) => {
|
||||
return a.index - b.index;
|
||||
});
|
||||
|
||||
const newCaseList = [];
|
||||
const oldCaseObj = {};
|
||||
let obj = {};
|
||||
|
||||
const handleTypeParams = (data, name) => {
|
||||
let res = data[name];
|
||||
const type = Object.prototype.toString.call(res);
|
||||
if (type === '[object Array]' && res.length) {
|
||||
res = JSON.stringify(res);
|
||||
try {
|
||||
res = JSON.parse(handleReplaceStr(res));
|
||||
} catch (e) {
|
||||
console.log('e ->', e);
|
||||
}
|
||||
} else if (type === '[object String]' && data[name]) {
|
||||
res = handleReplaceStr(res);
|
||||
}
|
||||
return res;
|
||||
};
|
||||
|
||||
const handleReplaceStr = str => {
|
||||
if (str.indexOf('$') !== -1) {
|
||||
str = str.replace(/\$\.([0-9]+)\./g, function(match, p1) {
|
||||
p1 = p1.toString();
|
||||
return `$.${newCaseList[oldCaseObj[p1]]}.` || '';
|
||||
});
|
||||
}
|
||||
return str;
|
||||
};
|
||||
|
||||
// 处理数据里面的$id;
|
||||
const handleParams = data => {
|
||||
data.col_id = new_col_id;
|
||||
delete data._id;
|
||||
delete data.add_time;
|
||||
delete data.up_time;
|
||||
delete data.__v;
|
||||
data.req_body_other = handleTypeParams(data, 'req_body_other');
|
||||
data.req_query = handleTypeParams(data, 'req_query');
|
||||
data.req_params = handleTypeParams(data, 'req_params');
|
||||
data.req_body_form = handleTypeParams(data, 'req_body_form');
|
||||
return data;
|
||||
};
|
||||
|
||||
for (let i = 0; i < oldColCaselistData.length; i++) {
|
||||
obj = oldColCaselistData[i].toObject();
|
||||
// 将被克隆的id和位置绑定
|
||||
oldCaseObj[obj._id] = i;
|
||||
let caseData = handleParams(obj);
|
||||
let newCase = await this.caseModel.save(caseData);
|
||||
newCaseList.push(newCase._id);
|
||||
}
|
||||
|
||||
this.projectModel.up(params.project_id, { up_time: new Date().getTime() }).then();
|
||||
ctx.body = yapi.commons.resReturn('ok');
|
||||
} catch (e) {
|
||||
ctx.body = yapi.commons.resReturn(null, 402, e.message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新一个测试用例
|
||||
* @interface /col/up_case
|
||||
* @method POST
|
||||
* @category col
|
||||
* @foldnumber 10
|
||||
* @param {number} id
|
||||
* @param {String} casename
|
||||
* @param {String} domain
|
||||
* @param {String} path
|
||||
* @param {String} method
|
||||
* @param {Object} req_query
|
||||
* @param {Object} req_headers
|
||||
* @param {String} req_body_type
|
||||
* @param {Array} req_body_form
|
||||
* @param {String} req_body_other
|
||||
* @returns {Object}
|
||||
* @example
|
||||
*/
|
||||
|
||||
async upCase(ctx) {
|
||||
try {
|
||||
let params = ctx.request.body;
|
||||
params = yapi.commons.handleParams(params, {
|
||||
id: 'number',
|
||||
casename: 'string'
|
||||
});
|
||||
|
||||
if (!params.id) {
|
||||
return (ctx.body = yapi.commons.resReturn(null, 400, '用例id不能为空'));
|
||||
}
|
||||
|
||||
// if (!params.casename) {
|
||||
// return (ctx.body = yapi.commons.resReturn(null, 400, '用例名称不能为空'));
|
||||
// }
|
||||
|
||||
let caseData = await this.caseModel.get(params.id);
|
||||
let auth = await this.checkAuth(caseData.project_id, 'project', 'edit');
|
||||
if (!auth) {
|
||||
return (ctx.body = yapi.commons.resReturn(null, 400, '没有权限'));
|
||||
}
|
||||
|
||||
params.uid = this.getUid();
|
||||
|
||||
//不允许修改接口id和项目id
|
||||
delete params.interface_id;
|
||||
delete params.project_id;
|
||||
let result = await this.caseModel.up(params.id, params);
|
||||
let username = this.getUsername();
|
||||
this.colModel.get(caseData.col_id).then(col => {
|
||||
yapi.commons.saveLog({
|
||||
content: `<a href="/user/profile/${this.getUid()}">${username}</a> 在接口集 <a href="/project/${
|
||||
caseData.project_id
|
||||
}/interface/col/${caseData.col_id}">${col.name}</a> 更新了测试用例 <a href="/project/${
|
||||
caseData.project_id
|
||||
}/interface/case/${params.id}">${params.casename || caseData.casename}</a>`,
|
||||
type: 'project',
|
||||
uid: this.getUid(),
|
||||
username: username,
|
||||
typeid: caseData.project_id
|
||||
});
|
||||
});
|
||||
|
||||
this.projectModel.up(caseData.project_id, { up_time: new Date().getTime() }).then();
|
||||
|
||||
ctx.body = yapi.commons.resReturn(result);
|
||||
} catch (e) {
|
||||
ctx.body = yapi.commons.resReturn(null, 402, e.message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取一个测试用例详情
|
||||
* @interface /col/case
|
||||
* @method GET
|
||||
* @category col
|
||||
* @foldnumber 10
|
||||
* @param {String} caseid
|
||||
* @returns {Object}
|
||||
* @example
|
||||
*/
|
||||
|
||||
async getCase(ctx) {
|
||||
try {
|
||||
let id = ctx.query.caseid;
|
||||
let result = await this.caseModel.get(id);
|
||||
if (!result) {
|
||||
return (ctx.body = yapi.commons.resReturn(null, 400, '不存在的case'));
|
||||
}
|
||||
result = result.toObject();
|
||||
let data = await this.interfaceModel.get(result.interface_id);
|
||||
if (!data) {
|
||||
return (ctx.body = yapi.commons.resReturn(null, 400, '找不到对应的接口,请联系管理员'));
|
||||
}
|
||||
data = data.toObject();
|
||||
|
||||
let projectData = await this.projectModel.getBaseInfo(data.project_id);
|
||||
result.path = projectData.basepath + data.path;
|
||||
result.method = data.method;
|
||||
result.req_body_type = data.req_body_type;
|
||||
result.req_headers = yapi.commons.handleParamsValue(data.req_headers, result.req_headers);
|
||||
result.res_body = data.res_body;
|
||||
result.res_body_type = data.res_body_type;
|
||||
result.req_body_form = yapi.commons.handleParamsValue(
|
||||
data.req_body_form,
|
||||
result.req_body_form
|
||||
);
|
||||
result.req_query = yapi.commons.handleParamsValue(data.req_query, result.req_query);
|
||||
result.req_params = yapi.commons.handleParamsValue(data.req_params, result.req_params);
|
||||
result.interface_up_time = data.up_time;
|
||||
result.req_body_is_json_schema = data.req_body_is_json_schema;
|
||||
result.res_body_is_json_schema = data.res_body_is_json_schema;
|
||||
ctx.body = yapi.commons.resReturn(result);
|
||||
} catch (e) {
|
||||
ctx.body = yapi.commons.resReturn(null, 400, e.message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新一个接口集name或描述
|
||||
* @interface /col/up_col
|
||||
* @method POST
|
||||
* @category col
|
||||
* @foldnumber 10
|
||||
* @param {String} name
|
||||
* @param {String} desc
|
||||
* @returns {Object}
|
||||
* @example
|
||||
*/
|
||||
|
||||
async upCol(ctx) {
|
||||
try {
|
||||
let params = ctx.request.body;
|
||||
let id = params.col_id;
|
||||
if (!id) {
|
||||
return (ctx.body = yapi.commons.resReturn(null, 400, '缺少 col_id 参数'));
|
||||
}
|
||||
let colData = await this.colModel.get(id);
|
||||
if (!colData) {
|
||||
return (ctx.body = yapi.commons.resReturn(null, 400, '不存在'));
|
||||
}
|
||||
let auth = await this.checkAuth(colData.project_id, 'project', 'edit');
|
||||
if (!auth) {
|
||||
return (ctx.body = yapi.commons.resReturn(null, 400, '没有权限'));
|
||||
}
|
||||
delete params.col_id;
|
||||
let result = await this.colModel.up(id, params);
|
||||
let username = this.getUsername();
|
||||
yapi.commons.saveLog({
|
||||
content: `<a href="/user/profile/${this.getUid()}">${username}</a> 更新了测试集合 <a href="/project/${
|
||||
colData.project_id
|
||||
}/interface/col/${id}">${colData.name}</a> 的信息`,
|
||||
type: 'project',
|
||||
uid: this.getUid(),
|
||||
username: username,
|
||||
typeid: colData.project_id
|
||||
});
|
||||
|
||||
ctx.body = yapi.commons.resReturn(result);
|
||||
} catch (e) {
|
||||
ctx.body = yapi.commons.resReturn(null, 400, e.message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新多个接口case index
|
||||
* @interface /col/up_case_index
|
||||
* @method POST
|
||||
* @category col
|
||||
* @foldnumber 10
|
||||
* @param {Array} [id, index]
|
||||
* @returns {Object}
|
||||
* @example
|
||||
*/
|
||||
|
||||
async upCaseIndex(ctx) {
|
||||
try {
|
||||
let params = ctx.request.body;
|
||||
if (!params || !Array.isArray(params)) {
|
||||
ctx.body = yapi.commons.resReturn(null, 400, '请求参数必须是数组');
|
||||
}
|
||||
params.forEach(item => {
|
||||
if (item.id) {
|
||||
this.caseModel.upCaseIndex(item.id, item.index).then(
|
||||
res => {},
|
||||
err => {
|
||||
yapi.commons.log(err.message, 'error');
|
||||
}
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
return (ctx.body = yapi.commons.resReturn('成功!'));
|
||||
} catch (e) {
|
||||
ctx.body = yapi.commons.resReturn(null, 400, e.message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新多个测试集合 index
|
||||
* @interface /col/up_col_index
|
||||
* @method POST
|
||||
* @category col
|
||||
* @foldnumber 10
|
||||
* @param {Array} [id, index]
|
||||
* @returns {Object}
|
||||
* @example
|
||||
*/
|
||||
|
||||
async upColIndex(ctx) {
|
||||
try {
|
||||
let params = ctx.request.body;
|
||||
if (!params || !Array.isArray(params)) {
|
||||
ctx.body = yapi.commons.resReturn(null, 400, '请求参数必须是数组');
|
||||
}
|
||||
params.forEach(item => {
|
||||
if (item.id) {
|
||||
this.colModel.upColIndex(item.id, item.index).then(
|
||||
res => {},
|
||||
err => {
|
||||
yapi.commons.log(err.message, 'error');
|
||||
}
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
return (ctx.body = yapi.commons.resReturn('成功!'));
|
||||
} catch (e) {
|
||||
ctx.body = yapi.commons.resReturn(null, 400, e.message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除一个接口集
|
||||
* @interface /col/del_col
|
||||
* @method GET
|
||||
* @category col
|
||||
* @foldnumber 10
|
||||
* @param {String}
|
||||
* @returns {Object}
|
||||
* @example
|
||||
*/
|
||||
|
||||
async delCol(ctx) {
|
||||
try {
|
||||
let id = ctx.query.col_id;
|
||||
let colData = await this.colModel.get(id);
|
||||
if (!colData) {
|
||||
ctx.body = yapi.commons.resReturn(null, 400, '不存在的id');
|
||||
}
|
||||
|
||||
if (colData.uid !== this.getUid()) {
|
||||
let auth = await this.checkAuth(colData.project_id, 'project', 'danger');
|
||||
if (!auth) {
|
||||
return (ctx.body = yapi.commons.resReturn(null, 400, '没有权限'));
|
||||
}
|
||||
}
|
||||
let result = await this.colModel.del(id);
|
||||
await this.caseModel.delByCol(id);
|
||||
let username = this.getUsername();
|
||||
yapi.commons.saveLog({
|
||||
content: `<a href="/user/profile/${this.getUid()}">${username}</a> 删除了接口集 ${
|
||||
colData.name
|
||||
} 及其下面的接口`,
|
||||
type: 'project',
|
||||
uid: this.getUid(),
|
||||
username: username,
|
||||
typeid: colData.project_id
|
||||
});
|
||||
return (ctx.body = yapi.commons.resReturn(result));
|
||||
} catch (e) {
|
||||
yapi.commons.resReturn(null, 400, e.message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {*} ctx
|
||||
*/
|
||||
|
||||
async delCase(ctx) {
|
||||
try {
|
||||
let caseid = ctx.query.caseid;
|
||||
let caseData = await this.caseModel.get(caseid);
|
||||
if (!caseData) {
|
||||
ctx.body = yapi.commons.resReturn(null, 400, '不存在的caseid');
|
||||
}
|
||||
|
||||
if (caseData.uid !== this.getUid()) {
|
||||
let auth = await this.checkAuth(caseData.project_id, 'project', 'danger');
|
||||
if (!auth) {
|
||||
return (ctx.body = yapi.commons.resReturn(null, 400, '没有权限'));
|
||||
}
|
||||
}
|
||||
|
||||
let result = await this.caseModel.del(caseid);
|
||||
|
||||
let username = this.getUsername();
|
||||
this.colModel.get(caseData.col_id).then(col => {
|
||||
yapi.commons.saveLog({
|
||||
content: `<a href="/user/profile/${this.getUid()}">${username}</a> 删除了接口集 <a href="/project/${
|
||||
caseData.project_id
|
||||
}/interface/col/${caseData.col_id}">${col.name}</a> 下的接口 ${caseData.casename}`,
|
||||
type: 'project',
|
||||
uid: this.getUid(),
|
||||
username: username,
|
||||
typeid: caseData.project_id
|
||||
});
|
||||
});
|
||||
|
||||
this.projectModel.up(caseData.project_id, { up_time: new Date().getTime() }).then();
|
||||
return (ctx.body = yapi.commons.resReturn(result));
|
||||
} catch (e) {
|
||||
yapi.commons.resReturn(null, 400, e.message);
|
||||
}
|
||||
}
|
||||
|
||||
async runCaseScript(ctx) {
|
||||
let params = ctx.request.body;
|
||||
ctx.body = await yapi.commons.runCaseScript(params, params.col_id, params.interface_id, this.getUid());
|
||||
}
|
||||
|
||||
// 数组去重
|
||||
unique(array, compare) {
|
||||
let hash = {};
|
||||
let arr = array.reduce(function(item, next) {
|
||||
hash[next[compare]] ? '' : (hash[next[compare]] = true && item.push(next));
|
||||
// console.log('item',item.project_id)
|
||||
return item;
|
||||
}, []);
|
||||
// 输出去重以后的project_id
|
||||
return arr.map(item => {
|
||||
return item[compare];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = interfaceColController;
|
||||
145
server/controllers/log.js
Normal file
145
server/controllers/log.js
Normal file
@@ -0,0 +1,145 @@
|
||||
const logModel = require('../models/log.js');
|
||||
const yapi = require('../yapi.js');
|
||||
const baseController = require('./base.js');
|
||||
const groupModel = require('../models/group');
|
||||
const projectModel = require('../models/project');
|
||||
const interfaceModel = require('../models/interface');
|
||||
|
||||
class logController extends baseController {
|
||||
constructor(ctx) {
|
||||
super(ctx);
|
||||
this.Model = yapi.getInst(logModel);
|
||||
this.groupModel = yapi.getInst(groupModel);
|
||||
this.projectModel = yapi.getInst(projectModel);
|
||||
this.interfaceModel = yapi.getInst(interfaceModel);
|
||||
this.schemaMap = {
|
||||
listByUpdate: {
|
||||
'*type': 'string',
|
||||
'*typeid': 'number',
|
||||
apis: [
|
||||
{
|
||||
method: 'string',
|
||||
path: 'string'
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取动态列表
|
||||
* @interface /log/list
|
||||
* @method GET
|
||||
* @category log
|
||||
* @foldnumber 10
|
||||
* @param {Number} typeid 动态类型id, 不能为空
|
||||
* @param {Number} [page] 分页页码
|
||||
* @param {Number} [limit] 分页大小
|
||||
* @returns {Object}
|
||||
* @example /log/list
|
||||
*/
|
||||
|
||||
async list(ctx) {
|
||||
let typeid = ctx.request.query.typeid,
|
||||
page = ctx.request.query.page || 1,
|
||||
limit = ctx.request.query.limit || 10,
|
||||
type = ctx.request.query.type,
|
||||
selectValue = ctx.request.query.selectValue;
|
||||
if (!typeid) {
|
||||
return (ctx.body = yapi.commons.resReturn(null, 400, 'typeid不能为空'));
|
||||
}
|
||||
if (!type) {
|
||||
return (ctx.body = yapi.commons.resReturn(null, 400, 'type不能为空'));
|
||||
}
|
||||
try {
|
||||
if (type === 'group') {
|
||||
let projectList = await this.projectModel.list(typeid);
|
||||
let projectIds = [],
|
||||
projectDatas = {};
|
||||
for (let i in projectList) {
|
||||
projectDatas[projectList[i]._id] = projectList[i];
|
||||
projectIds[i] = projectList[i]._id;
|
||||
}
|
||||
let projectLogList = await this.Model.listWithPagingByGroup(
|
||||
typeid,
|
||||
projectIds,
|
||||
page,
|
||||
limit
|
||||
);
|
||||
projectLogList.forEach((item, index) => {
|
||||
item = item.toObject();
|
||||
if (item.type === 'project') {
|
||||
item.content =
|
||||
`在 <a href="/project/${item.typeid}">${projectDatas[item.typeid].name}</a> 项目: ` +
|
||||
item.content;
|
||||
}
|
||||
projectLogList[index] = item;
|
||||
});
|
||||
let total = await this.Model.listCountByGroup(typeid, projectIds);
|
||||
ctx.body = yapi.commons.resReturn({
|
||||
list: projectLogList,
|
||||
total: Math.ceil(total / limit)
|
||||
});
|
||||
} else if (type === "project") {
|
||||
let result = await this.Model.listWithPaging(typeid, type, page, limit, selectValue);
|
||||
let count = await this.Model.listCount(typeid, type, selectValue);
|
||||
|
||||
ctx.body = yapi.commons.resReturn({
|
||||
total: Math.ceil(count / limit),
|
||||
list: result
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
ctx.body = yapi.commons.resReturn(null, 402, err.message);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 获取特定cat_id下最新修改的动态信息
|
||||
* @interface /log/list_by_update
|
||||
* @method post
|
||||
* @category log
|
||||
* @foldnumber 10
|
||||
* @param {Number} typeid 动态类型id, 不能为空
|
||||
* @returns {Object}
|
||||
* @example /log/list
|
||||
*/
|
||||
|
||||
async listByUpdate(ctx) {
|
||||
let params = ctx.params;
|
||||
|
||||
try {
|
||||
let { typeid, type, apis } = params;
|
||||
let list = [];
|
||||
let projectDatas = await this.projectModel.getBaseInfo(typeid, 'basepath');
|
||||
let basePath = projectDatas.toObject().basepath;
|
||||
|
||||
for (let i = 0; i < apis.length; i++) {
|
||||
let api = apis[i];
|
||||
if (basePath) {
|
||||
api.path = api.path.indexOf(basePath) === 0 ? api.path.substr(basePath.length) : api.path;
|
||||
}
|
||||
let interfaceIdList = await this.interfaceModel.getByPath(
|
||||
typeid,
|
||||
api.path,
|
||||
api.method,
|
||||
'_id'
|
||||
);
|
||||
|
||||
for (let j = 0; j < interfaceIdList.length; j++) {
|
||||
let interfaceId = interfaceIdList[j];
|
||||
let id = interfaceId.id;
|
||||
let result = await this.Model.listWithCatid(typeid, type, id);
|
||||
|
||||
list = list.concat(result);
|
||||
}
|
||||
}
|
||||
|
||||
// let result = await this.Model.listWithCatid(typeid, type, catId);
|
||||
ctx.body = yapi.commons.resReturn(list);
|
||||
} catch (err) {
|
||||
ctx.body = yapi.commons.resReturn(null, 402, err.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = logController;
|
||||
422
server/controllers/open.js
Normal file
422
server/controllers/open.js
Normal file
@@ -0,0 +1,422 @@
|
||||
const projectModel = require('../models/project.js');
|
||||
const interfaceColModel = require('../models/interfaceCol.js');
|
||||
const interfaceCaseModel = require('../models/interfaceCase.js');
|
||||
const interfaceModel = require('../models/interface.js');
|
||||
const interfaceCatModel = require('../models/interfaceCat.js');
|
||||
const followModel = require('../models/follow.js');
|
||||
const userModel = require('../models/user.js');
|
||||
const yapi = require('../yapi.js');
|
||||
const baseController = require('./base.js');
|
||||
const {
|
||||
handleParams,
|
||||
crossRequest,
|
||||
handleCurrDomain,
|
||||
checkNameIsExistInArray
|
||||
} = require('../../common/postmanLib');
|
||||
const { handleParamsValue, ArrayToObject } = require('../../common/utils.js');
|
||||
const renderToHtml = require('../utils/reportHtml');
|
||||
const HanldeImportData = require('../../common/HandleImportData');
|
||||
const _ = require('underscore');
|
||||
const createContex = require('../../common/createContext')
|
||||
|
||||
/**
|
||||
* {
|
||||
* postman: require('./m')
|
||||
* }
|
||||
*/
|
||||
const importDataModule = {};
|
||||
|
||||
class openController extends baseController {
|
||||
constructor(ctx) {
|
||||
super(ctx);
|
||||
this.projectModel = yapi.getInst(projectModel);
|
||||
this.interfaceColModel = yapi.getInst(interfaceColModel);
|
||||
this.interfaceCaseModel = yapi.getInst(interfaceCaseModel);
|
||||
this.interfaceModel = yapi.getInst(interfaceModel);
|
||||
this.interfaceCatModel = yapi.getInst(interfaceCatModel);
|
||||
this.followModel = yapi.getInst(followModel);
|
||||
this.userModel = yapi.getInst(userModel);
|
||||
this.handleValue = this.handleValue.bind(this);
|
||||
this.schemaMap = {
|
||||
runAutoTest: {
|
||||
'*id': 'number',
|
||||
project_id: 'string',
|
||||
token: 'string',
|
||||
mode: {
|
||||
type: 'string',
|
||||
default: 'html'
|
||||
},
|
||||
email: {
|
||||
type: 'boolean',
|
||||
default: false
|
||||
},
|
||||
download: {
|
||||
type: 'boolean',
|
||||
default: false
|
||||
},
|
||||
closeRemoveAdditional: true
|
||||
},
|
||||
importData: {
|
||||
'*type': 'string',
|
||||
url: 'string',
|
||||
'*token': 'string',
|
||||
json: 'string',
|
||||
project_id: 'string',
|
||||
merge: {
|
||||
type: 'string',
|
||||
default: 'normal'
|
||||
}
|
||||
}
|
||||
};
|
||||
yapi.emitHook('import_data', importDataModule);
|
||||
}
|
||||
|
||||
async importData(ctx) {
|
||||
let type = ctx.params.type;
|
||||
let content = ctx.params.json;
|
||||
let project_id = ctx.params.project_id;
|
||||
let dataSync = ctx.params.merge;
|
||||
|
||||
let warnMessage = ''
|
||||
|
||||
/**
|
||||
* 因为以前接口文档写错了,做下兼容
|
||||
*/
|
||||
try{
|
||||
if(!dataSync &&ctx.params.dataSync){
|
||||
warnMessage = 'importData Api 已废弃 dataSync 传参,请联系管理员将 dataSync 改为 merge.'
|
||||
dataSync = ctx.params.dataSync
|
||||
}
|
||||
}catch(e){}
|
||||
|
||||
let token = ctx.params.token;
|
||||
if (!type || !importDataModule[type]) {
|
||||
return (ctx.body = yapi.commons.resReturn(null, 40022, '不存在的导入方式'));
|
||||
}
|
||||
|
||||
if (!content && !ctx.params.url) {
|
||||
return (ctx.body = yapi.commons.resReturn(null, 40022, 'json 或者 url 参数,不能都为空'));
|
||||
}
|
||||
try {
|
||||
let request = require("request");// let Promise = require('Promise');
|
||||
let syncGet = function (url){
|
||||
return new Promise(function(resolve, reject){
|
||||
request.get({url : url}, function(error, response, body){
|
||||
if(error){
|
||||
reject(error);
|
||||
}else{
|
||||
resolve(body);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
if(ctx.params.url){
|
||||
content = await syncGet(ctx.params.url);
|
||||
}else if(content.indexOf('http://') === 0 || content.indexOf('https://') === 0){
|
||||
content = await syncGet(content);
|
||||
}
|
||||
content = JSON.parse(content);
|
||||
} catch (e) {
|
||||
return (ctx.body = yapi.commons.resReturn(null, 40022, 'json 格式有误:' + e));
|
||||
}
|
||||
|
||||
let menuList = await this.interfaceCatModel.list(project_id);
|
||||
/**
|
||||
* 防止分类被都被删除时取不到 selectCatid
|
||||
* 如果没有分类,增加一个默认分类
|
||||
*/
|
||||
if (menuList.length === 0) {
|
||||
const catInst = yapi.getInst(interfaceCatModel);
|
||||
const menu = await catInst.save({
|
||||
name: '默认分类',
|
||||
project_id: project_id,
|
||||
desc: '默认分类',
|
||||
uid: this.getUid(),
|
||||
add_time: yapi.commons.time(),
|
||||
up_time: yapi.commons.time()
|
||||
});
|
||||
menuList.push(menu);
|
||||
}
|
||||
let selectCatid = menuList[0]._id;
|
||||
let projectData = await this.projectModel.get(project_id);
|
||||
let res = await importDataModule[type](content);
|
||||
|
||||
let successMessage;
|
||||
let errorMessage = [];
|
||||
await HanldeImportData(
|
||||
res,
|
||||
project_id,
|
||||
selectCatid,
|
||||
menuList,
|
||||
projectData.basePath,
|
||||
dataSync,
|
||||
err => {
|
||||
errorMessage.push(err);
|
||||
},
|
||||
msg => {
|
||||
successMessage = msg;
|
||||
},
|
||||
() => {},
|
||||
token,
|
||||
yapi.WEBCONFIG.port
|
||||
);
|
||||
|
||||
if (errorMessage.length > 0) {
|
||||
return (ctx.body = yapi.commons.resReturn(null, 404, errorMessage.join('\n')));
|
||||
}
|
||||
ctx.body = yapi.commons.resReturn(null, 0, successMessage + warnMessage);
|
||||
}
|
||||
|
||||
async projectInterfaceData(ctx) {
|
||||
ctx.body = 'projectInterfaceData';
|
||||
}
|
||||
|
||||
handleValue(val, global) {
|
||||
let globalValue = ArrayToObject(global);
|
||||
let context = Object.assign({}, {global: globalValue}, this.records);
|
||||
return handleParamsValue(val, context);
|
||||
}
|
||||
|
||||
handleEvnParams(params) {
|
||||
let result = [];
|
||||
Object.keys(params).map(item => {
|
||||
if (/env_/gi.test(item)) {
|
||||
let curEnv = yapi.commons.trim(params[item]);
|
||||
let value = { curEnv, project_id: item.split('_')[1] };
|
||||
result.push(value);
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
async runAutoTest(ctx) {
|
||||
if (!this.$tokenAuth) {
|
||||
return (ctx.body = yapi.commons.resReturn(null, 40022, 'token 验证失败'));
|
||||
}
|
||||
// console.log(1231312)
|
||||
const token = ctx.query.token;
|
||||
|
||||
const projectId = ctx.params.project_id;
|
||||
const startTime = new Date().getTime();
|
||||
const records = (this.records = {});
|
||||
const reports = (this.reports = {});
|
||||
const testList = [];
|
||||
let id = ctx.params.id;
|
||||
let curEnvList = this.handleEvnParams(ctx.params);
|
||||
|
||||
let colData = await this.interfaceColModel.get(id);
|
||||
if (!colData) {
|
||||
return (ctx.body = yapi.commons.resReturn(null, 40022, 'id值不存在'));
|
||||
}
|
||||
|
||||
let projectData = await this.projectModel.get(projectId);
|
||||
|
||||
let caseList = await yapi.commons.getCaseList(id);
|
||||
if (caseList.errcode !== 0) {
|
||||
ctx.body = caseList;
|
||||
}
|
||||
caseList = caseList.data;
|
||||
for (let i = 0, l = caseList.length; i < l; i++) {
|
||||
let item = caseList[i];
|
||||
let projectEvn = await this.projectModel.getByEnv(item.project_id);
|
||||
|
||||
item.id = item._id;
|
||||
let curEnvItem = _.find(curEnvList, key => {
|
||||
return key.project_id == item.project_id;
|
||||
});
|
||||
|
||||
item.case_env = curEnvItem ? curEnvItem.curEnv || item.case_env : item.case_env;
|
||||
item.req_headers = this.handleReqHeader(item.req_headers, projectEvn.env, item.case_env);
|
||||
item.pre_script = projectData.pre_script;
|
||||
item.after_script = projectData.after_script;
|
||||
item.env = projectEvn.env;
|
||||
let result;
|
||||
// console.log('item',item.case_env)
|
||||
try {
|
||||
result = await this.handleTest(item);
|
||||
} catch (err) {
|
||||
result = err;
|
||||
}
|
||||
|
||||
reports[item.id] = result;
|
||||
records[item.id] = {
|
||||
params: result.params,
|
||||
body: result.res_body
|
||||
};
|
||||
testList.push(result);
|
||||
}
|
||||
|
||||
function getMessage(testList) {
|
||||
let successNum = 0,
|
||||
failedNum = 0,
|
||||
len = 0,
|
||||
msg = '';
|
||||
testList.forEach(item => {
|
||||
len++;
|
||||
if (item.code === 0) {
|
||||
successNum++;
|
||||
}
|
||||
else {
|
||||
failedNum++;
|
||||
}
|
||||
});
|
||||
if (failedNum === 0) {
|
||||
msg = `一共 ${len} 测试用例,全部验证通过`;
|
||||
} else {
|
||||
msg = `一共 ${len} 测试用例,${successNum} 个验证通过, ${failedNum} 个未通过。`;
|
||||
}
|
||||
|
||||
return { msg, len, successNum, failedNum };
|
||||
}
|
||||
|
||||
const endTime = new Date().getTime();
|
||||
const executionTime = (endTime - startTime) / 1000;
|
||||
|
||||
let reportsResult = {
|
||||
message: getMessage(testList),
|
||||
runTime: executionTime + 's',
|
||||
numbs: testList.length,
|
||||
list: testList
|
||||
};
|
||||
|
||||
if (ctx.params.email === true && reportsResult.message.failedNum !== 0) {
|
||||
let autoTestUrl = `${
|
||||
ctx.request.origin
|
||||
}/api/open/run_auto_test?id=${id}&token=${token}&mode=${ctx.params.mode}`;
|
||||
yapi.commons.sendNotice(projectId, {
|
||||
title: `YApi自动化测试报告`,
|
||||
content: `
|
||||
<html>
|
||||
<head>
|
||||
<title>测试报告</title>
|
||||
<meta charset="utf-8" />
|
||||
<body>
|
||||
<div>
|
||||
<h3>测试结果:</h3>
|
||||
<p>${reportsResult.message.msg}</p>
|
||||
<h3>测试结果详情如下:</h3>
|
||||
<p>${autoTestUrl}</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>`
|
||||
});
|
||||
}
|
||||
let mode = ctx.params.mode || 'html';
|
||||
if(ctx.params.download === true) {
|
||||
ctx.set('Content-Disposition', `attachment; filename=test.${mode}`);
|
||||
}
|
||||
if (ctx.params.mode === 'json') {
|
||||
return (ctx.body = reportsResult);
|
||||
} else {
|
||||
return (ctx.body = renderToHtml(reportsResult));
|
||||
}
|
||||
}
|
||||
|
||||
async handleTest(interfaceData) {
|
||||
let requestParams = {};
|
||||
let options;
|
||||
options = handleParams(interfaceData, this.handleValue, requestParams);
|
||||
let result = {
|
||||
id: interfaceData.id,
|
||||
name: interfaceData.casename,
|
||||
path: interfaceData.path,
|
||||
code: 400,
|
||||
validRes: []
|
||||
};
|
||||
try {
|
||||
options.taskId = this.getUid();
|
||||
let data = await crossRequest(options, interfaceData.pre_script, interfaceData.after_script,createContex(
|
||||
this.getUid(),
|
||||
interfaceData.project_id,
|
||||
interfaceData.interface_id
|
||||
));
|
||||
let res = data.res;
|
||||
|
||||
result = Object.assign(result, {
|
||||
status: res.status,
|
||||
statusText: res.statusText,
|
||||
url: data.req.url,
|
||||
method: data.req.method,
|
||||
data: data.req.data,
|
||||
headers: data.req.headers,
|
||||
res_header: res.header,
|
||||
res_body: res.body
|
||||
});
|
||||
if (options.data && typeof options.data === 'object') {
|
||||
requestParams = Object.assign(requestParams, options.data);
|
||||
}
|
||||
|
||||
let validRes = [];
|
||||
|
||||
let responseData = Object.assign(
|
||||
{},
|
||||
{
|
||||
status: res.status,
|
||||
body: res.body,
|
||||
header: res.header,
|
||||
statusText: res.statusText
|
||||
}
|
||||
);
|
||||
|
||||
await this.handleScriptTest(interfaceData, responseData, validRes, requestParams);
|
||||
result.params = requestParams;
|
||||
if (validRes.length === 0) {
|
||||
result.code = 0;
|
||||
result.validRes = [{ message: '验证通过' }];
|
||||
} else if (validRes.length > 0) {
|
||||
result.code = 1;
|
||||
result.validRes = validRes;
|
||||
}
|
||||
} catch (data) {
|
||||
result = Object.assign(options, result, {
|
||||
res_header: data.header,
|
||||
res_body: data.body || data.message,
|
||||
status: null,
|
||||
statusText: data.message,
|
||||
code: 400
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
async handleScriptTest(interfaceData, response, validRes, requestParams) {
|
||||
|
||||
try {
|
||||
let test = await yapi.commons.runCaseScript({
|
||||
response: response,
|
||||
records: this.records,
|
||||
script: interfaceData.test_script,
|
||||
params: requestParams
|
||||
}, interfaceData.col_id, interfaceData.interface_id, this.getUid());
|
||||
if (test.errcode !== 0) {
|
||||
test.data.logs.forEach(item => {
|
||||
validRes.push({
|
||||
message: item
|
||||
});
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
validRes.push({
|
||||
message: 'Error: ' + err.message
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
handleReqHeader(req_header, envData, curEnvName) {
|
||||
let currDomain = handleCurrDomain(envData, curEnvName);
|
||||
|
||||
let header = currDomain.header;
|
||||
header.forEach(item => {
|
||||
if (!checkNameIsExistInArray(item.name, req_header)) {
|
||||
item.abled = true;
|
||||
req_header.push(item);
|
||||
}
|
||||
});
|
||||
req_header = req_header.filter(item => {
|
||||
return item && typeof item === 'object';
|
||||
});
|
||||
return req_header;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = openController;
|
||||
1139
server/controllers/project.js
Normal file
1139
server/controllers/project.js
Normal file
File diff suppressed because it is too large
Load Diff
242
server/controllers/test.js
Normal file
242
server/controllers/test.js
Normal file
@@ -0,0 +1,242 @@
|
||||
const yapi = require('../yapi.js');
|
||||
const baseController = require('./base.js');
|
||||
const fs = require('fs'); //引入文件模块
|
||||
const path = require('path');
|
||||
|
||||
class interfaceColController extends baseController {
|
||||
constructor(ctx) {
|
||||
super(ctx);
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试 get
|
||||
* @interface /test/get
|
||||
* @method GET
|
||||
* @returns {Object}
|
||||
* @example
|
||||
*/
|
||||
async testGet(ctx) {
|
||||
try {
|
||||
let query = ctx.query;
|
||||
// cookie 检测
|
||||
ctx.cookies.set('_uid', 12, {
|
||||
expires: yapi.commons.expireDate(7),
|
||||
httpOnly: true
|
||||
});
|
||||
ctx.body = yapi.commons.resReturn(query);
|
||||
} catch (e) {
|
||||
ctx.body = yapi.commons.resReturn(null, 402, e.message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试 code
|
||||
* @interface /http/code
|
||||
* @method GET
|
||||
* @returns {Object}
|
||||
* @example
|
||||
*/
|
||||
|
||||
async testHttpCode(ctx) {
|
||||
try {
|
||||
let params = ctx.request.body;
|
||||
ctx.status = +ctx.query.code || 200;
|
||||
ctx.body = yapi.commons.resReturn(params);
|
||||
} catch(e) {
|
||||
ctx.body = yapi.commons.resReturn(null, 402, e.message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试 post
|
||||
* @interface /test/post
|
||||
* @method POST
|
||||
* @returns {Object}
|
||||
* @example
|
||||
*/
|
||||
async testPost(ctx) {
|
||||
try {
|
||||
let params = ctx.request.body;
|
||||
ctx.body = yapi.commons.resReturn(params);
|
||||
} catch (e) {
|
||||
ctx.body = yapi.commons.resReturn(null, 402, e.message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试 单文件上传
|
||||
* @interface /test/single/upload
|
||||
* @method POST
|
||||
* @returns {Object}
|
||||
* @example
|
||||
*/
|
||||
async testSingleUpload(ctx) {
|
||||
try {
|
||||
// let params = ctx.request.body;
|
||||
let req = ctx.req;
|
||||
|
||||
let chunks = [],
|
||||
size = 0;
|
||||
req.on('data', function(chunk) {
|
||||
chunks.push(chunk);
|
||||
size += chunk.length;
|
||||
});
|
||||
|
||||
req.on('finish', function() {
|
||||
console.log(34343);
|
||||
});
|
||||
|
||||
req.on('end', function() {
|
||||
let data = new Buffer(size);
|
||||
for (let i = 0, pos = 0, l = chunks.length; i < l; i++) {
|
||||
let chunk = chunks[i];
|
||||
chunk.copy(data, pos);
|
||||
pos += chunk.length;
|
||||
}
|
||||
fs.writeFileSync(path.join(yapi.WEBROOT_RUNTIME, 'test.text'), data, function(err) {
|
||||
return (ctx.body = yapi.commons.resReturn(null, 402, '写入失败'));
|
||||
});
|
||||
});
|
||||
|
||||
ctx.body = yapi.commons.resReturn({ res: '上传成功' });
|
||||
} catch (e) {
|
||||
ctx.body = yapi.commons.resReturn(null, 402, e.message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试 文件上传
|
||||
* @interface /test/files/upload
|
||||
* @method POST
|
||||
* @returns {Object}
|
||||
* @example
|
||||
*/
|
||||
async testFilesUpload(ctx) {
|
||||
try {
|
||||
let file = ctx.request.body.files.file;
|
||||
let newPath = path.join(yapi.WEBROOT_RUNTIME, 'test.text');
|
||||
fs.renameSync(file.path, newPath);
|
||||
ctx.body = yapi.commons.resReturn({ res: '上传成功' });
|
||||
} catch (e) {
|
||||
ctx.body = yapi.commons.resReturn(null, 402, e.message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试 put
|
||||
* @interface /test/put
|
||||
* @method PUT
|
||||
* @returns {Object}
|
||||
* @example
|
||||
*/
|
||||
async testPut(ctx) {
|
||||
try {
|
||||
let params = ctx.request.body;
|
||||
ctx.body = yapi.commons.resReturn(params);
|
||||
} catch (e) {
|
||||
ctx.body = yapi.commons.resReturn(null, 402, e.message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试 delete
|
||||
* @interface /test/delete
|
||||
* @method DELETE
|
||||
* @returns {Object}
|
||||
* @example
|
||||
*/
|
||||
async testDelete(ctx) {
|
||||
try {
|
||||
let body = ctx.request.body;
|
||||
ctx.body = yapi.commons.resReturn(body);
|
||||
} catch (e) {
|
||||
ctx.body = yapi.commons.resReturn(null, 402, e.message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试 head
|
||||
* @interface /test/head
|
||||
* @method HEAD
|
||||
* @returns {Object}
|
||||
* @example
|
||||
*/
|
||||
async testHead(ctx) {
|
||||
try {
|
||||
let query = ctx.query;
|
||||
ctx.body = yapi.commons.resReturn(query);
|
||||
} catch (e) {
|
||||
ctx.body = yapi.commons.resReturn(null, 402, e.message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试 options
|
||||
* @interface /test/options
|
||||
* @method OPTIONS
|
||||
* @returns {Object}
|
||||
* @example
|
||||
*/
|
||||
async testOptions(ctx) {
|
||||
try {
|
||||
let query = ctx.query;
|
||||
ctx.body = yapi.commons.resReturn(query);
|
||||
} catch (e) {
|
||||
ctx.body = yapi.commons.resReturn(null, 402, e.message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试 patch
|
||||
* @interface /test/patch
|
||||
* @method PATCH
|
||||
* @returns {Object}
|
||||
* @example
|
||||
*/
|
||||
async testPatch(ctx) {
|
||||
try {
|
||||
let params = ctx.request.body;
|
||||
ctx.body = yapi.commons.resReturn(params);
|
||||
} catch (e) {
|
||||
ctx.body = yapi.commons.resReturn(null, 402, e.message);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 测试 raw
|
||||
* @interface /test/raw
|
||||
* @method POST
|
||||
* @return {Object}
|
||||
* @example
|
||||
*/
|
||||
async testRaw(ctx) {
|
||||
try {
|
||||
let params = ctx.request.body;
|
||||
ctx.body = yapi.commons.resReturn(params);
|
||||
} catch (e) {
|
||||
ctx.body = yapi.commons.resReturn(null, 402, e.message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试返回值
|
||||
* @interface /test/response
|
||||
* @method get
|
||||
* @return {Object}
|
||||
* @example
|
||||
*/
|
||||
async testResponse(ctx) {
|
||||
try {
|
||||
// let result = `<div><h2>12222222</h2></div>`;
|
||||
// let result = `wieieieieiieieie`
|
||||
let result = { b: '12', c: '23' };
|
||||
ctx.set('Access-Control-Allow-Origin', '*');
|
||||
ctx.set('Content-Type', 'text');
|
||||
console.log(ctx.response);
|
||||
ctx.body = result;
|
||||
} catch (e) {
|
||||
ctx.body = yapi.commons.resReturn(null, 402, e.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = interfaceColController;
|
||||
731
server/controllers/user.js
Normal file
731
server/controllers/user.js
Normal file
@@ -0,0 +1,731 @@
|
||||
const userModel = require('../models/user.js');
|
||||
const yapi = require('../yapi.js');
|
||||
const baseController = require('./base.js');
|
||||
const common = require('../utils/commons.js');
|
||||
const ldap = require('../utils/ldap.js');
|
||||
|
||||
const interfaceModel = require('../models/interface.js');
|
||||
const groupModel = require('../models/group.js');
|
||||
const projectModel = require('../models/project.js');
|
||||
const avatarModel = require('../models/avatar.js');
|
||||
|
||||
const jwt = require('jsonwebtoken');
|
||||
|
||||
class userController extends baseController {
|
||||
constructor(ctx) {
|
||||
super(ctx);
|
||||
this.Model = yapi.getInst(userModel);
|
||||
}
|
||||
/**
|
||||
* 用户登录接口
|
||||
* @interface /user/login
|
||||
* @method POST
|
||||
* @category user
|
||||
* @foldnumber 10
|
||||
* @param {String} email email名称,不能为空
|
||||
* @param {String} password 密码,不能为空
|
||||
* @returns {Object}
|
||||
* @example ./api/user/login.json
|
||||
*/
|
||||
async login(ctx) {
|
||||
//登录
|
||||
let userInst = yapi.getInst(userModel); //创建user实体
|
||||
let email = ctx.request.body.email;
|
||||
email = (email || '').trim();
|
||||
let password = ctx.request.body.password;
|
||||
|
||||
if (!email) {
|
||||
return (ctx.body = yapi.commons.resReturn(null, 400, 'email不能为空'));
|
||||
}
|
||||
if (!password) {
|
||||
return (ctx.body = yapi.commons.resReturn(null, 400, '密码不能为空'));
|
||||
}
|
||||
|
||||
let result = await userInst.findByEmail(email);
|
||||
|
||||
if (!result) {
|
||||
return (ctx.body = yapi.commons.resReturn(null, 404, '该用户不存在'));
|
||||
} else if (yapi.commons.generatePassword(password, result.passsalt) === result.password) {
|
||||
this.setLoginCookie(result._id, result.passsalt);
|
||||
|
||||
return (ctx.body = yapi.commons.resReturn(
|
||||
{
|
||||
username: result.username,
|
||||
role: result.role,
|
||||
uid: result._id,
|
||||
email: result.email,
|
||||
add_time: result.add_time,
|
||||
up_time: result.up_time,
|
||||
type: 'site',
|
||||
study: result.study
|
||||
},
|
||||
0,
|
||||
'logout success...'
|
||||
));
|
||||
} else {
|
||||
return (ctx.body = yapi.commons.resReturn(null, 405, '密码错误'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 退出登录接口
|
||||
* @interface /user/logout
|
||||
* @method GET
|
||||
* @category user
|
||||
* @foldnumber 10
|
||||
* @returns {Object}
|
||||
* @example ./api/user/logout.json
|
||||
*/
|
||||
|
||||
async logout(ctx) {
|
||||
ctx.cookies.set('_yapi_token', null);
|
||||
ctx.cookies.set('_yapi_uid', null);
|
||||
ctx.body = yapi.commons.resReturn('ok');
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新
|
||||
* @interface /user/up_study
|
||||
* @method GET
|
||||
* @category user
|
||||
* @foldnumber 10
|
||||
* @returns {Object}
|
||||
* @example
|
||||
*/
|
||||
|
||||
async upStudy(ctx) {
|
||||
let userInst = yapi.getInst(userModel); //创建user实体
|
||||
let data = {
|
||||
up_time: yapi.commons.time(),
|
||||
study: true
|
||||
};
|
||||
try {
|
||||
let result = await userInst.update(this.getUid(), data);
|
||||
ctx.body = yapi.commons.resReturn(result);
|
||||
} catch (e) {
|
||||
ctx.body = yapi.commons.resReturn(null, 401, e.message);
|
||||
}
|
||||
}
|
||||
|
||||
async loginByToken(ctx) {
|
||||
try {
|
||||
let ret = await yapi.emitHook('third_login', ctx);
|
||||
let login = await this.handleThirdLogin(ret.email, ret.username);
|
||||
if (login === true) {
|
||||
yapi.commons.log('login success');
|
||||
ctx.redirect('/group');
|
||||
}
|
||||
} catch (e) {
|
||||
yapi.commons.log(e.message, 'error');
|
||||
ctx.redirect('/');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ldap登录
|
||||
* @interface /user/login_by_ldap
|
||||
* @method
|
||||
* @category user
|
||||
* @foldnumber 10
|
||||
* @param {String} email email名称,不能为空
|
||||
* @param {String} password 密码,不能为空
|
||||
* @returns {Object}
|
||||
*
|
||||
*/
|
||||
async getLdapAuth(ctx) {
|
||||
try {
|
||||
const { email, password } = ctx.request.body;
|
||||
// const username = email.split(/\@/g)[0];
|
||||
const { info: ldapInfo } = await ldap.ldapQuery(email, password);
|
||||
const emailPrefix = email.split(/\@/g)[0];
|
||||
const emailPostfix = yapi.WEBCONFIG.ldapLogin.emailPostfix;
|
||||
|
||||
const emailParams =
|
||||
ldapInfo[yapi.WEBCONFIG.ldapLogin.emailKey || 'mail'] ||
|
||||
(emailPostfix ? emailPrefix + emailPostfix : email);
|
||||
const username = ldapInfo[yapi.WEBCONFIG.ldapLogin.usernameKey] || emailPrefix;
|
||||
|
||||
let login = await this.handleThirdLogin(emailParams, username);
|
||||
|
||||
if (login === true) {
|
||||
let userInst = yapi.getInst(userModel); //创建user实体
|
||||
let result = await userInst.findByEmail(emailParams);
|
||||
return (ctx.body = yapi.commons.resReturn(
|
||||
{
|
||||
username: result.username,
|
||||
role: result.role,
|
||||
uid: result._id,
|
||||
email: result.email,
|
||||
add_time: result.add_time,
|
||||
up_time: result.up_time,
|
||||
type: result.type || 'third',
|
||||
study: result.study
|
||||
},
|
||||
0,
|
||||
'logout success...'
|
||||
));
|
||||
}
|
||||
} catch (e) {
|
||||
yapi.commons.log(e.message, 'error');
|
||||
return (ctx.body = yapi.commons.resReturn(null, 401, e.message));
|
||||
}
|
||||
}
|
||||
|
||||
// 处理第三方登录
|
||||
async handleThirdLogin(email, username) {
|
||||
let user, data, passsalt;
|
||||
let userInst = yapi.getInst(userModel);
|
||||
|
||||
try {
|
||||
user = await userInst.findByEmail(email);
|
||||
|
||||
// 新建用户信息
|
||||
if (!user || !user._id) {
|
||||
passsalt = yapi.commons.randStr();
|
||||
data = {
|
||||
username: username,
|
||||
password: yapi.commons.generatePassword(passsalt, passsalt),
|
||||
email: email,
|
||||
passsalt: passsalt,
|
||||
role: 'member',
|
||||
add_time: yapi.commons.time(),
|
||||
up_time: yapi.commons.time(),
|
||||
type: 'third'
|
||||
};
|
||||
user = await userInst.save(data);
|
||||
await this.handlePrivateGroup(user._id, username, email);
|
||||
yapi.commons.sendMail({
|
||||
to: email,
|
||||
contents: `<h3>亲爱的用户:</h3><p>您好,感谢使用YApi平台,你的邮箱账号是:${email}</p>`
|
||||
});
|
||||
}
|
||||
|
||||
this.setLoginCookie(user._id, user.passsalt);
|
||||
return true;
|
||||
} catch (e) {
|
||||
console.error('third_login:', e.message); // eslint-disable-line
|
||||
throw new Error(`third_login: ${e.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改用户密码
|
||||
* @interface /user/change_password
|
||||
* @method POST
|
||||
* @category user
|
||||
* @param {Number} uid 用户ID
|
||||
* @param {Number} [old_password] 旧密码, 非admin用户必须传
|
||||
* @param {Number} password 新密码
|
||||
* @return {Object}
|
||||
* @example ./api/user/change_password.json
|
||||
*/
|
||||
async changePassword(ctx) {
|
||||
let params = ctx.request.body;
|
||||
let userInst = yapi.getInst(userModel);
|
||||
|
||||
if (!params.uid) {
|
||||
return (ctx.body = yapi.commons.resReturn(null, 400, 'uid不能为空'));
|
||||
}
|
||||
|
||||
if (!params.password) {
|
||||
return (ctx.body = yapi.commons.resReturn(null, 400, '密码不能为空'));
|
||||
}
|
||||
|
||||
let user = await userInst.findById(params.uid);
|
||||
if (this.getRole() !== 'admin' && params.uid != this.getUid()) {
|
||||
return (ctx.body = yapi.commons.resReturn(null, 402, '没有权限'));
|
||||
}
|
||||
|
||||
if (this.getRole() !== 'admin' || user.role === 'admin') {
|
||||
if (!params.old_password) {
|
||||
return (ctx.body = yapi.commons.resReturn(null, 400, '旧密码不能为空'));
|
||||
}
|
||||
|
||||
if (yapi.commons.generatePassword(params.old_password, user.passsalt) !== user.password) {
|
||||
return (ctx.body = yapi.commons.resReturn(null, 402, '旧密码错误'));
|
||||
}
|
||||
}
|
||||
|
||||
let passsalt = yapi.commons.randStr();
|
||||
let data = {
|
||||
up_time: yapi.commons.time(),
|
||||
password: yapi.commons.generatePassword(params.password, passsalt),
|
||||
passsalt: passsalt
|
||||
};
|
||||
try {
|
||||
let result = await userInst.update(params.uid, data);
|
||||
ctx.body = yapi.commons.resReturn(result);
|
||||
} catch (e) {
|
||||
ctx.body = yapi.commons.resReturn(null, 401, e.message);
|
||||
}
|
||||
}
|
||||
|
||||
async handlePrivateGroup(uid) {
|
||||
var groupInst = yapi.getInst(groupModel);
|
||||
await groupInst.save({
|
||||
uid: uid,
|
||||
group_name: 'User-' + uid,
|
||||
add_time: yapi.commons.time(),
|
||||
up_time: yapi.commons.time(),
|
||||
type: 'private'
|
||||
});
|
||||
}
|
||||
|
||||
setLoginCookie(uid, passsalt) {
|
||||
let token = jwt.sign({ uid: uid }, passsalt, { expiresIn: '7 days' });
|
||||
|
||||
this.ctx.cookies.set('_yapi_token', token, {
|
||||
expires: yapi.commons.expireDate(7),
|
||||
httpOnly: true
|
||||
});
|
||||
this.ctx.cookies.set('_yapi_uid', uid, {
|
||||
expires: yapi.commons.expireDate(7),
|
||||
httpOnly: true
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户注册接口
|
||||
* @interface /user/reg
|
||||
* @method POST
|
||||
* @category user
|
||||
* @foldnumber 10
|
||||
* @param {String} email email名称,不能为空
|
||||
* @param {String} password 密码,不能为空
|
||||
* @param {String} [username] 用户名
|
||||
* @returns {Object}
|
||||
* @example ./api/user/login.json
|
||||
*/
|
||||
async reg(ctx) {
|
||||
//注册
|
||||
if (yapi.WEBCONFIG.closeRegister) {
|
||||
return (ctx.body = yapi.commons.resReturn(null, 400, '禁止注册,请联系管理员'));
|
||||
}
|
||||
let userInst = yapi.getInst(userModel);
|
||||
let params = ctx.request.body; //获取请求的参数,检查是否存在用户名和密码
|
||||
|
||||
params = yapi.commons.handleParams(params, {
|
||||
username: 'string',
|
||||
password: 'string',
|
||||
email: 'string'
|
||||
});
|
||||
|
||||
if (!params.email) {
|
||||
return (ctx.body = yapi.commons.resReturn(null, 400, '邮箱不能为空'));
|
||||
}
|
||||
|
||||
if (!params.password) {
|
||||
return (ctx.body = yapi.commons.resReturn(null, 400, '密码不能为空'));
|
||||
}
|
||||
|
||||
let checkRepeat = await userInst.checkRepeat(params.email); //然后检查是否已经存在该用户
|
||||
|
||||
if (checkRepeat > 0) {
|
||||
return (ctx.body = yapi.commons.resReturn(null, 401, '该email已经注册'));
|
||||
}
|
||||
|
||||
let passsalt = yapi.commons.randStr();
|
||||
let data = {
|
||||
username: params.username,
|
||||
password: yapi.commons.generatePassword(params.password, passsalt), //加密
|
||||
email: params.email,
|
||||
passsalt: passsalt,
|
||||
role: 'member',
|
||||
add_time: yapi.commons.time(),
|
||||
up_time: yapi.commons.time(),
|
||||
type: 'site'
|
||||
};
|
||||
|
||||
if (!data.username) {
|
||||
data.username = data.email.substr(0, data.email.indexOf('@'));
|
||||
}
|
||||
|
||||
try {
|
||||
let user = await userInst.save(data);
|
||||
|
||||
this.setLoginCookie(user._id, user.passsalt);
|
||||
await this.handlePrivateGroup(user._id, user.username, user.email);
|
||||
ctx.body = yapi.commons.resReturn({
|
||||
uid: user._id,
|
||||
email: user.email,
|
||||
username: user.username,
|
||||
add_time: user.add_time,
|
||||
up_time: user.up_time,
|
||||
role: 'member',
|
||||
type: user.type,
|
||||
study: false
|
||||
});
|
||||
yapi.commons.sendMail({
|
||||
to: user.email,
|
||||
contents: `<h3>亲爱的用户:</h3><p>您好,感谢使用YApi可视化接口平台,您的账号 ${
|
||||
params.email
|
||||
} 已经注册成功</p>`
|
||||
});
|
||||
} catch (e) {
|
||||
ctx.body = yapi.commons.resReturn(null, 401, e.message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户列表
|
||||
* @interface /user/list
|
||||
* @method GET
|
||||
* @category user
|
||||
* @foldnumber 10
|
||||
* @param {Number} [page] 分页页码
|
||||
* @param {Number} [limit] 分页大小,默认为10条
|
||||
* @returns {Object}
|
||||
* @example
|
||||
*/
|
||||
async list(ctx) {
|
||||
let page = ctx.request.query.page || 1,
|
||||
limit = ctx.request.query.limit || 10;
|
||||
|
||||
const userInst = yapi.getInst(userModel);
|
||||
try {
|
||||
let user = await userInst.listWithPaging(page, limit);
|
||||
let count = await userInst.listCount();
|
||||
return (ctx.body = yapi.commons.resReturn({
|
||||
count: count,
|
||||
total: Math.ceil(count / limit),
|
||||
list: user
|
||||
}));
|
||||
} catch (e) {
|
||||
return (ctx.body = yapi.commons.resReturn(null, 402, e.message));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户个人信息
|
||||
* @interface /user/find
|
||||
* @method GET
|
||||
* @param id 用户uid
|
||||
* @category user
|
||||
* @foldnumber 10
|
||||
* @returns {Object}
|
||||
* @example
|
||||
*/
|
||||
async findById(ctx) {
|
||||
//根据id获取用户信息
|
||||
try {
|
||||
let userInst = yapi.getInst(userModel);
|
||||
let id = ctx.request.query.id;
|
||||
|
||||
if (this.getRole() !== 'admin' && id != this.getUid()) {
|
||||
return (ctx.body = yapi.commons.resReturn(null, 401, '没有权限'));
|
||||
}
|
||||
|
||||
if (!id) {
|
||||
return (ctx.body = yapi.commons.resReturn(null, 400, 'uid不能为空'));
|
||||
}
|
||||
|
||||
let result = await userInst.findById(id);
|
||||
|
||||
if (!result) {
|
||||
return (ctx.body = yapi.commons.resReturn(null, 402, '不存在的用户'));
|
||||
}
|
||||
|
||||
return (ctx.body = yapi.commons.resReturn({
|
||||
uid: result._id,
|
||||
username: result.username,
|
||||
email: result.email,
|
||||
role: result.role,
|
||||
type: result.type,
|
||||
add_time: result.add_time,
|
||||
up_time: result.up_time
|
||||
}));
|
||||
} catch (e) {
|
||||
return (ctx.body = yapi.commons.resReturn(null, 402, e.message));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除用户,只有admin用户才有此权限
|
||||
* @interface /user/del
|
||||
* @method POST
|
||||
* @param id 用户uid
|
||||
* @category user
|
||||
* @foldnumber 10
|
||||
* @returns {Object}
|
||||
* @example
|
||||
*/
|
||||
async del(ctx) {
|
||||
//根据id删除一个用户
|
||||
try {
|
||||
if (this.getRole() !== 'admin') {
|
||||
return (ctx.body = yapi.commons.resReturn(null, 402, 'Without permission.'));
|
||||
}
|
||||
|
||||
let userInst = yapi.getInst(userModel);
|
||||
let id = ctx.request.body.id;
|
||||
if (id == this.getUid()) {
|
||||
return (ctx.body = yapi.commons.resReturn(null, 403, '禁止删除管理员'));
|
||||
}
|
||||
if (!id) {
|
||||
return (ctx.body = yapi.commons.resReturn(null, 400, 'uid不能为空'));
|
||||
}
|
||||
|
||||
let result = await userInst.del(id);
|
||||
|
||||
ctx.body = yapi.commons.resReturn(result);
|
||||
} catch (e) {
|
||||
ctx.body = yapi.commons.resReturn(null, 402, e.message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新用户个人信息
|
||||
* @interface /user/update
|
||||
* @method POST
|
||||
* @param uid 用户uid
|
||||
* @param [role] 用户角色,只有管理员有权限修改
|
||||
* @param [username] String
|
||||
* @param [email] String
|
||||
* @category user
|
||||
* @foldnumber 10
|
||||
* @returns {Object}
|
||||
* @example
|
||||
*/
|
||||
async update(ctx) {
|
||||
//更新用户信息
|
||||
try {
|
||||
let params = ctx.request.body;
|
||||
|
||||
params = yapi.commons.handleParams(params, {
|
||||
username: 'string',
|
||||
email: 'string'
|
||||
});
|
||||
|
||||
if (this.getRole() !== 'admin' && params.uid != this.getUid()) {
|
||||
return (ctx.body = yapi.commons.resReturn(null, 401, '没有权限'));
|
||||
}
|
||||
|
||||
let userInst = yapi.getInst(userModel);
|
||||
let id = params.uid;
|
||||
|
||||
if (!id) {
|
||||
return (ctx.body = yapi.commons.resReturn(null, 400, 'uid不能为空'));
|
||||
}
|
||||
|
||||
let userData = await userInst.findById(id);
|
||||
if (!userData) {
|
||||
return (ctx.body = yapi.commons.resReturn(null, 400, 'uid不存在'));
|
||||
}
|
||||
|
||||
let data = {
|
||||
up_time: yapi.commons.time()
|
||||
};
|
||||
|
||||
params.username && (data.username = params.username);
|
||||
params.email && (data.email = params.email);
|
||||
|
||||
if (data.email) {
|
||||
var checkRepeat = await userInst.checkRepeat(data.email); //然后检查是否已经存在该用户
|
||||
if (checkRepeat > 0) {
|
||||
return (ctx.body = yapi.commons.resReturn(null, 401, '该email已经注册'));
|
||||
}
|
||||
}
|
||||
|
||||
let member = {
|
||||
uid: id,
|
||||
username: data.username || userData.username,
|
||||
email: data.email || userData.email
|
||||
};
|
||||
let groupInst = yapi.getInst(groupModel);
|
||||
await groupInst.updateMember(member);
|
||||
let projectInst = yapi.getInst(projectModel);
|
||||
await projectInst.updateMember(member);
|
||||
|
||||
let result = await userInst.update(id, data);
|
||||
ctx.body = yapi.commons.resReturn(result);
|
||||
} catch (e) {
|
||||
ctx.body = yapi.commons.resReturn(null, 402, e.message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传用户头像
|
||||
* @interface /user/upload_avatar
|
||||
* @method POST
|
||||
* @param {*} basecode base64编码,通过h5 api传给后端
|
||||
* @category user
|
||||
* @returns {Object}
|
||||
* @example
|
||||
*/
|
||||
|
||||
async uploadAvatar(ctx) {
|
||||
try {
|
||||
let basecode = ctx.request.body.basecode;
|
||||
if (!basecode) {
|
||||
return (ctx.body = yapi.commons.resReturn(null, 400, 'basecode不能为空'));
|
||||
}
|
||||
let pngPrefix = 'data:image/png;base64,';
|
||||
let jpegPrefix = 'data:image/jpeg;base64,';
|
||||
let type;
|
||||
if (basecode.substr(0, pngPrefix.length) === pngPrefix) {
|
||||
basecode = basecode.substr(pngPrefix.length);
|
||||
type = 'image/png';
|
||||
} else if (basecode.substr(0, jpegPrefix.length) === jpegPrefix) {
|
||||
basecode = basecode.substr(jpegPrefix.length);
|
||||
type = 'image/jpeg';
|
||||
} else {
|
||||
return (ctx.body = yapi.commons.resReturn(null, 400, '仅支持jpeg和png格式的图片'));
|
||||
}
|
||||
let strLength = basecode.length;
|
||||
if (parseInt(strLength - (strLength / 8) * 2) > 200000) {
|
||||
return (ctx.body = yapi.commons.resReturn(null, 400, '图片大小不能超过200kb'));
|
||||
}
|
||||
|
||||
let avatarInst = yapi.getInst(avatarModel);
|
||||
let result = await avatarInst.up(this.getUid(), basecode, type);
|
||||
ctx.body = yapi.commons.resReturn(result);
|
||||
} catch (e) {
|
||||
ctx.body = yapi.commons.resReturn(null, 401, e.message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据用户uid头像
|
||||
* @interface /user/avatar
|
||||
* @method GET
|
||||
* @param {*} uid
|
||||
* @category user
|
||||
* @returns {Object}
|
||||
* @example
|
||||
*/
|
||||
|
||||
async avatar(ctx) {
|
||||
try {
|
||||
let uid = ctx.query.uid ? ctx.query.uid : this.getUid();
|
||||
let avatarInst = yapi.getInst(avatarModel);
|
||||
let data = await avatarInst.get(uid);
|
||||
let dataBuffer, type;
|
||||
if (!data || !data.basecode) {
|
||||
dataBuffer = yapi.fs.readFileSync(yapi.path.join(yapi.WEBROOT, 'static/image/avatar.png'));
|
||||
type = 'image/png';
|
||||
} else {
|
||||
type = data.type;
|
||||
dataBuffer = new Buffer(data.basecode, 'base64');
|
||||
}
|
||||
|
||||
ctx.set('Content-type', type);
|
||||
ctx.body = dataBuffer;
|
||||
} catch (err) {
|
||||
ctx.body = 'error:' + err.message;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 模糊搜索用户名或者email
|
||||
* @interface /user/search
|
||||
* @method GET
|
||||
* @category user
|
||||
* @foldnumber 10
|
||||
* @param {String} q
|
||||
* @return {Object}
|
||||
* @example ./api/user/search.json
|
||||
*/
|
||||
async search(ctx) {
|
||||
const { q } = ctx.request.query;
|
||||
|
||||
if (!q) {
|
||||
return (ctx.body = yapi.commons.resReturn(void 0, 400, 'No keyword.'));
|
||||
}
|
||||
|
||||
if (!yapi.commons.validateSearchKeyword(q)) {
|
||||
return (ctx.body = yapi.commons.resReturn(void 0, 400, 'Bad query.'));
|
||||
}
|
||||
|
||||
let queryList = await this.Model.search(q);
|
||||
let rules = [
|
||||
{
|
||||
key: '_id',
|
||||
alias: 'uid'
|
||||
},
|
||||
'username',
|
||||
'email',
|
||||
'role',
|
||||
{
|
||||
key: 'add_time',
|
||||
alias: 'addTime'
|
||||
},
|
||||
{
|
||||
key: 'up_time',
|
||||
alias: 'upTime'
|
||||
}
|
||||
];
|
||||
|
||||
let filteredRes = common.filterRes(queryList, rules);
|
||||
|
||||
return (ctx.body = yapi.commons.resReturn(filteredRes, 0, 'ok'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据路由id初始化项目数据
|
||||
* @interface /user/project
|
||||
* @method GET
|
||||
* @category user
|
||||
* @foldnumber 10
|
||||
* @param {String} type 可选group|interface|project
|
||||
* @param {Number} id
|
||||
* @return {Object}
|
||||
* @example
|
||||
*/
|
||||
async project(ctx) {
|
||||
let { id, type } = ctx.request.query;
|
||||
let result = {};
|
||||
try {
|
||||
if (type === 'interface') {
|
||||
let interfaceInst = yapi.getInst(interfaceModel);
|
||||
let interfaceData = await interfaceInst.get(id);
|
||||
result.interface = interfaceData;
|
||||
type = 'project';
|
||||
id = interfaceData.project_id;
|
||||
}
|
||||
|
||||
if (type === 'project') {
|
||||
let projectInst = yapi.getInst(projectModel);
|
||||
let projectData = await projectInst.get(id);
|
||||
result.project = projectData.toObject();
|
||||
let ownerAuth = await this.checkAuth(id, 'project', 'danger'),
|
||||
devAuth;
|
||||
if (ownerAuth) {
|
||||
result.project.role = 'owner';
|
||||
} else {
|
||||
devAuth = await this.checkAuth(id, 'project', 'site');
|
||||
if (devAuth) {
|
||||
result.project.role = 'dev';
|
||||
} else {
|
||||
result.project.role = 'member';
|
||||
}
|
||||
}
|
||||
type = 'group';
|
||||
id = projectData.group_id;
|
||||
}
|
||||
|
||||
if (type === 'group') {
|
||||
let groupInst = yapi.getInst(groupModel);
|
||||
let groupData = await groupInst.get(id);
|
||||
result.group = groupData.toObject();
|
||||
let ownerAuth = await this.checkAuth(id, 'group', 'danger'),
|
||||
devAuth;
|
||||
if (ownerAuth) {
|
||||
result.group.role = 'owner';
|
||||
} else {
|
||||
devAuth = await this.checkAuth(id, 'group', 'site');
|
||||
if (devAuth) {
|
||||
result.group.role = 'dev';
|
||||
} else {
|
||||
result.group.role = 'member';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (ctx.body = yapi.commons.resReturn(result));
|
||||
} catch (e) {
|
||||
return (ctx.body = yapi.commons.resReturn(result, 422, e.message));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = userController;
|
||||
155
server/install.js
Normal file
155
server/install.js
Normal file
@@ -0,0 +1,155 @@
|
||||
const fs = require('fs-extra');
|
||||
const yapi = require('./yapi.js');
|
||||
const commons = require('./utils/commons');
|
||||
const dbModule = require('./utils/db.js');
|
||||
const userModel = require('./models/user.js');
|
||||
const mongoose = require('mongoose');
|
||||
|
||||
yapi.commons = commons;
|
||||
yapi.connect = dbModule.connect();
|
||||
|
||||
function install() {
|
||||
let exist = yapi.commons.fileExist(yapi.path.join(yapi.WEBROOT_RUNTIME, 'init.lock'));
|
||||
|
||||
if (exist) {
|
||||
throw new Error(
|
||||
'init.lock文件已存在,请确认您是否已安装。如果需要重新安装,请删掉init.lock文件'
|
||||
);
|
||||
}
|
||||
|
||||
setupSql();
|
||||
}
|
||||
|
||||
function setupSql() {
|
||||
let userInst = yapi.getInst(userModel);
|
||||
let passsalt = yapi.commons.randStr();
|
||||
let result = userInst.save({
|
||||
username: yapi.WEBCONFIG.adminAccount.substr(0, yapi.WEBCONFIG.adminAccount.indexOf('@')),
|
||||
email: yapi.WEBCONFIG.adminAccount,
|
||||
password: yapi.commons.generatePassword(yapi.WEBCONFIG.adminPassword || "ymfe.org", passsalt),
|
||||
passsalt: passsalt,
|
||||
role: 'admin',
|
||||
add_time: yapi.commons.time(),
|
||||
up_time: yapi.commons.time()
|
||||
});
|
||||
|
||||
yapi.connect
|
||||
.then(function() {
|
||||
let userCol = mongoose.connection.db.collection('user');
|
||||
userCol.createIndex({
|
||||
username: 1
|
||||
});
|
||||
userCol.createIndex(
|
||||
{
|
||||
email: 1
|
||||
},
|
||||
{
|
||||
unique: true
|
||||
}
|
||||
);
|
||||
|
||||
let projectCol = mongoose.connection.db.collection('project');
|
||||
projectCol.createIndex({
|
||||
uid: 1
|
||||
});
|
||||
projectCol.createIndex({
|
||||
name: 1
|
||||
});
|
||||
projectCol.createIndex({
|
||||
group_id: 1
|
||||
});
|
||||
|
||||
let logCol = mongoose.connection.db.collection('log');
|
||||
logCol.createIndex({
|
||||
uid: 1
|
||||
});
|
||||
|
||||
logCol.createIndex({
|
||||
typeid: 1,
|
||||
type: 1
|
||||
});
|
||||
|
||||
let interfaceColCol = mongoose.connection.db.collection('interface_col');
|
||||
interfaceColCol.createIndex({
|
||||
uid: 1
|
||||
});
|
||||
interfaceColCol.createIndex({
|
||||
project_id: 1
|
||||
});
|
||||
|
||||
let interfaceCatCol = mongoose.connection.db.collection('interface_cat');
|
||||
interfaceCatCol.createIndex({
|
||||
uid: 1
|
||||
});
|
||||
interfaceCatCol.createIndex({
|
||||
project_id: 1
|
||||
});
|
||||
|
||||
let interfaceCaseCol = mongoose.connection.db.collection('interface_case');
|
||||
interfaceCaseCol.createIndex({
|
||||
uid: 1
|
||||
});
|
||||
interfaceCaseCol.createIndex({
|
||||
col_id: 1
|
||||
});
|
||||
interfaceCaseCol.createIndex({
|
||||
project_id: 1
|
||||
});
|
||||
|
||||
let interfaceCol = mongoose.connection.db.collection('interface');
|
||||
interfaceCol.createIndex({
|
||||
uid: 1
|
||||
});
|
||||
interfaceCol.createIndex({
|
||||
path: 1,
|
||||
method: 1
|
||||
});
|
||||
interfaceCol.createIndex({
|
||||
project_id: 1
|
||||
});
|
||||
|
||||
let groupCol = mongoose.connection.db.collection('group');
|
||||
groupCol.createIndex({
|
||||
uid: 1
|
||||
});
|
||||
groupCol.createIndex({
|
||||
group_name: 1
|
||||
});
|
||||
|
||||
let avatarCol = mongoose.connection.db.collection('avatar');
|
||||
avatarCol.createIndex({
|
||||
uid: 1
|
||||
});
|
||||
|
||||
let tokenCol = mongoose.connection.db.collection('token');
|
||||
tokenCol.createIndex({
|
||||
project_id: 1
|
||||
});
|
||||
|
||||
let followCol = mongoose.connection.db.collection('follow');
|
||||
followCol.createIndex({
|
||||
uid: 1
|
||||
});
|
||||
followCol.createIndex({
|
||||
project_id: 1
|
||||
});
|
||||
|
||||
result.then(
|
||||
function() {
|
||||
fs.ensureFileSync(yapi.path.join(yapi.WEBROOT_RUNTIME, 'init.lock'));
|
||||
console.log(
|
||||
`初始化管理员账号成功,账号名:"${yapi.WEBCONFIG.adminAccount}",密码:${yapi.WEBCONFIG.adminPassword || "ymfe.org"}"`
|
||||
); // eslint-disable-line
|
||||
process.exit(0);
|
||||
},
|
||||
function(err) {
|
||||
throw new Error(`初始化管理员账号 "${yapi.WEBCONFIG.adminAccount}" 失败, ${err.message}`);
|
||||
}
|
||||
);
|
||||
})
|
||||
.catch(function(err) {
|
||||
throw new Error(err.message);
|
||||
});
|
||||
}
|
||||
|
||||
install();
|
||||
386
server/middleware/mockServer.js
Normal file
386
server/middleware/mockServer.js
Normal file
@@ -0,0 +1,386 @@
|
||||
const yapi = require('../yapi.js');
|
||||
const projectModel = require('../models/project.js');
|
||||
const interfaceModel = require('../models/interface.js');
|
||||
const mockExtra = require('../../common/mock-extra.js');
|
||||
const { schemaValidator } = require('../../common/utils.js');
|
||||
const _ = require('underscore');
|
||||
const Mock = require('mockjs');
|
||||
const variable = require('../../client/constants/variable.js')
|
||||
/**
|
||||
*
|
||||
* @param {*} apiPath /user/tom
|
||||
* @param {*} apiRule /user/:username
|
||||
*/
|
||||
function matchApi(apiPath, apiRule) {
|
||||
let apiRules = apiRule.split('/');
|
||||
let apiPaths = apiPath.split('/');
|
||||
let pathParams = {
|
||||
__weight: 0
|
||||
};
|
||||
|
||||
if (apiPaths.length !== apiRules.length) {
|
||||
return false;
|
||||
}
|
||||
for (let i = 0; i < apiRules.length; i++) {
|
||||
if (apiRules[i]) {
|
||||
apiRules[i] = apiRules[i].trim();
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
if (
|
||||
apiRules[i].length > 2 &&
|
||||
apiRules[i][0] === '{' &&
|
||||
apiRules[i][apiRules[i].length - 1] === '}'
|
||||
) {
|
||||
pathParams[apiRules[i].substr(1, apiRules[i].length - 2)] = apiPaths[i];
|
||||
} else if (apiRules[i].indexOf(':') === 0) {
|
||||
pathParams[apiRules[i].substr(1)] = apiPaths[i];
|
||||
} else if (
|
||||
apiRules[i].length > 2 &&
|
||||
apiRules[i].indexOf('{') > -1 &&
|
||||
apiRules[i].indexOf('}') > -1
|
||||
) {
|
||||
let params = [];
|
||||
apiRules[i] = apiRules[i].replace(/\{(.+?)\}/g, function(src, match) {
|
||||
params.push(match);
|
||||
return '([^\\/\\s]+)';
|
||||
});
|
||||
apiRules[i] = new RegExp(apiRules[i]);
|
||||
if (!apiRules[i].test(apiPaths[i])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let matchs = apiPaths[i].match(apiRules[i]);
|
||||
|
||||
params.forEach((item, index) => {
|
||||
pathParams[item] = matchs[index + 1];
|
||||
});
|
||||
} else {
|
||||
if (apiRules[i] !== apiPaths[i]) {
|
||||
return false;
|
||||
}else{
|
||||
pathParams.__weight++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return pathParams;
|
||||
}
|
||||
|
||||
function parseCookie(str) {
|
||||
if (!str || typeof str !== 'string') {
|
||||
return str;
|
||||
}
|
||||
if (str.split(';')[0]) {
|
||||
let c = str.split(';')[0].split('=');
|
||||
return { name: c[0], value: c[1] || '' };
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function handleCorsRequest(ctx) {
|
||||
let header = ctx.request.header;
|
||||
ctx.set('Access-Control-Allow-Origin', header.origin);
|
||||
ctx.set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, HEADER, PATCH, OPTIONS');
|
||||
ctx.set('Access-Control-Allow-Headers', header['access-control-request-headers']);
|
||||
ctx.set('Access-Control-Allow-Credentials', true);
|
||||
ctx.set('Access-Control-Max-Age', 1728000);
|
||||
ctx.body = 'ok';
|
||||
}
|
||||
// 必填字段是否填写好
|
||||
function mockValidator(interfaceData, ctx) {
|
||||
let i,
|
||||
j,
|
||||
l,
|
||||
len,
|
||||
noRequiredArr = [];
|
||||
let method = interfaceData.method.toUpperCase() || 'GET';
|
||||
// query 判断
|
||||
for (i = 0, l = interfaceData.req_query.length; i < l; i++) {
|
||||
let curQuery = interfaceData.req_query[i];
|
||||
if (curQuery && typeof curQuery === 'object' && curQuery.required === '1') {
|
||||
if (!ctx.query[curQuery.name]) {
|
||||
noRequiredArr.push(curQuery.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
// form 表单判断
|
||||
if (variable.HTTP_METHOD[method].request_body && interfaceData.req_body_type === 'form') {
|
||||
for (j = 0, len = interfaceData.req_body_form.length; j < len; j++) {
|
||||
let curForm = interfaceData.req_body_form[j];
|
||||
if (curForm && typeof curForm === 'object' && curForm.required === '1') {
|
||||
if (
|
||||
ctx.request.body[curForm.name] ||
|
||||
(ctx.request.body.fields && ctx.request.body.fields[curForm.name]) ||
|
||||
(ctx.request.body.files && ctx.request.body.files[curForm.name])
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
noRequiredArr.push(curForm.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
let validResult;
|
||||
// json schema 判断
|
||||
if (variable.HTTP_METHOD[method].request_body && interfaceData.req_body_type === 'json' && interfaceData.req_body_is_json_schema === true) {
|
||||
const schema = yapi.commons.json_parse(interfaceData.req_body_other);
|
||||
const params = yapi.commons.json_parse(ctx.request.body);
|
||||
validResult = schemaValidator(schema, params);
|
||||
}
|
||||
if (noRequiredArr.length > 0 || (validResult && !validResult.valid)) {
|
||||
let message = `错误信息:`;
|
||||
message += noRequiredArr.length > 0 ? `缺少必须字段 ${noRequiredArr.join(',')} ` : '';
|
||||
message += validResult && !validResult.valid ? `schema 验证请求参数 ${validResult.message}` : '';
|
||||
|
||||
return {
|
||||
valid: false,
|
||||
message
|
||||
};
|
||||
}
|
||||
|
||||
return { valid: true };
|
||||
}
|
||||
|
||||
module.exports = async (ctx, next) => {
|
||||
// no used variable 'hostname' & 'config'
|
||||
// let hostname = ctx.hostname;
|
||||
// let config = yapi.WEBCONFIG;
|
||||
let path = ctx.path;
|
||||
let header = ctx.request.header;
|
||||
|
||||
if (path.indexOf('/mock/') !== 0) {
|
||||
if (next) await next();
|
||||
return true;
|
||||
}
|
||||
|
||||
let paths = path.split('/');
|
||||
let projectId = paths[2];
|
||||
paths.splice(0, 3);
|
||||
path = '/' + paths.join('/');
|
||||
|
||||
ctx.set('Access-Control-Allow-Origin', header.origin);
|
||||
ctx.set('Access-Control-Allow-Credentials', true);
|
||||
|
||||
// ctx.set('Access-Control-Allow-Origin', '*');
|
||||
|
||||
if (!projectId) {
|
||||
return (ctx.body = yapi.commons.resReturn(null, 400, 'projectId不能为空'));
|
||||
}
|
||||
|
||||
let projectInst = yapi.getInst(projectModel),
|
||||
project;
|
||||
try {
|
||||
project = await projectInst.get(projectId);
|
||||
} catch (e) {
|
||||
return (ctx.body = yapi.commons.resReturn(null, 403, e.message));
|
||||
}
|
||||
|
||||
if (!project) {
|
||||
return (ctx.body = yapi.commons.resReturn(null, 400, '不存在的项目'));
|
||||
}
|
||||
|
||||
let interfaceData, newpath;
|
||||
let interfaceInst = yapi.getInst(interfaceModel);
|
||||
|
||||
try {
|
||||
newpath = path.substr(project.basepath.length);
|
||||
interfaceData = await interfaceInst.getByPath(project._id, newpath, ctx.method);
|
||||
let queryPathInterfaceData = await interfaceInst.getByQueryPath(project._id, newpath, ctx.method);
|
||||
//处理query_path情况 url 中有 ?params=xxx
|
||||
if (!interfaceData || interfaceData.length != queryPathInterfaceData.length) {
|
||||
|
||||
let i,
|
||||
l,
|
||||
j,
|
||||
len,
|
||||
curQuery,
|
||||
match = false;
|
||||
for (i = 0, l = queryPathInterfaceData.length; i < l; i++) {
|
||||
match = false;
|
||||
let currentInterfaceData = queryPathInterfaceData[i];
|
||||
curQuery = currentInterfaceData.query_path;
|
||||
if (!curQuery || typeof curQuery !== 'object' || !curQuery.path) {
|
||||
continue;
|
||||
}
|
||||
for (j = 0, len = curQuery.params.length; j < len; j++) {
|
||||
if (ctx.query[curQuery.params[j].name] !== curQuery.params[j].value) {
|
||||
continue;
|
||||
}
|
||||
if (j === len - 1) {
|
||||
match = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (match) {
|
||||
interfaceData = [currentInterfaceData];
|
||||
break;
|
||||
}
|
||||
// if (i === l - 1) {
|
||||
// interfaceData = [];
|
||||
// }
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
//处理动态路由
|
||||
if (!interfaceData || interfaceData.length === 0) {
|
||||
let newData = await interfaceInst.getVar(project._id, ctx.method);
|
||||
|
||||
let findInterface;
|
||||
let weight = 0;
|
||||
_.each(newData, item => {
|
||||
let m = matchApi(newpath, item.path);
|
||||
if (m !== false) {
|
||||
if(m.__weight >= weight){
|
||||
findInterface = item;
|
||||
}
|
||||
delete m.__weight;
|
||||
ctx.request.query = Object.assign(m, ctx.request.query);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
if (!findInterface) {
|
||||
//非正常跨域预检请求回应
|
||||
if (ctx.method === 'OPTIONS' && ctx.request.header['access-control-request-method']) {
|
||||
return handleCorsRequest(ctx);
|
||||
}
|
||||
|
||||
return (ctx.body = yapi.commons.resReturn(
|
||||
null,
|
||||
404,
|
||||
`不存在的api, 当前请求path为 ${newpath}, 请求方法为 ${
|
||||
ctx.method
|
||||
} ,请确认是否定义此请求。`
|
||||
));
|
||||
}
|
||||
interfaceData = [await interfaceInst.get(findInterface._id)];
|
||||
}
|
||||
|
||||
if (interfaceData.length > 1) {
|
||||
return (ctx.body = yapi.commons.resReturn(null, 405, '存在多个api,请检查数据库'));
|
||||
} else {
|
||||
interfaceData = interfaceData[0];
|
||||
}
|
||||
|
||||
// 必填字段是否填写好
|
||||
if (project.strice) {
|
||||
const validResult = mockValidator(interfaceData, ctx);
|
||||
if (!validResult.valid) {
|
||||
return (ctx.body = yapi.commons.resReturn(
|
||||
null,
|
||||
404,
|
||||
`接口字段验证不通过, ${validResult.message}`
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
let res;
|
||||
// mock 返回值处理
|
||||
res = interfaceData.res_body;
|
||||
try {
|
||||
if (interfaceData.res_body_type === 'json') {
|
||||
if (interfaceData.res_body_is_json_schema === true) {
|
||||
//json-schema
|
||||
const schema = yapi.commons.json_parse(interfaceData.res_body);
|
||||
res = yapi.commons.schemaToJson(schema, {
|
||||
alwaysFakeOptionals: true
|
||||
});
|
||||
} else {
|
||||
// console.log('header', ctx.request.header['content-type'].indexOf('multipart/form-data'))
|
||||
// 处理 format-data
|
||||
|
||||
if (
|
||||
_.isString(ctx.request.header['content-type']) &&
|
||||
ctx.request.header['content-type'].indexOf('multipart/form-data') > -1
|
||||
) {
|
||||
ctx.request.body = ctx.request.body.fields;
|
||||
}
|
||||
// console.log('body', ctx.request.body)
|
||||
|
||||
res = mockExtra(yapi.commons.json_parse(interfaceData.res_body), {
|
||||
query: ctx.request.query,
|
||||
body: ctx.request.body,
|
||||
params: Object.assign({}, ctx.request.query, ctx.request.body)
|
||||
});
|
||||
// console.log('res',res)
|
||||
}
|
||||
|
||||
try {
|
||||
res = Mock.mock(res);
|
||||
} catch (e) {
|
||||
console.log('err', e.message);
|
||||
yapi.commons.log(e, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
let context = {
|
||||
projectData: project,
|
||||
interfaceData: interfaceData,
|
||||
ctx: ctx,
|
||||
mockJson: res,
|
||||
resHeader: {},
|
||||
httpCode: 200,
|
||||
delay: 0
|
||||
};
|
||||
|
||||
if (project.is_mock_open && project.project_mock_script) {
|
||||
// 项目层面的mock脚本解析
|
||||
let script = project.project_mock_script;
|
||||
await yapi.commons.handleMockScript(script, context);
|
||||
}
|
||||
|
||||
await yapi.emitHook('mock_after', context);
|
||||
|
||||
let handleMock = new Promise(resolve => {
|
||||
setTimeout(() => {
|
||||
resolve(true);
|
||||
}, context.delay);
|
||||
});
|
||||
await handleMock;
|
||||
if (context.resHeader && typeof context.resHeader === 'object') {
|
||||
for (let i in context.resHeader) {
|
||||
let cookie;
|
||||
if (i === 'Set-Cookie') {
|
||||
if (context.resHeader[i] && typeof context.resHeader[i] === 'string') {
|
||||
cookie = parseCookie(context.resHeader[i]);
|
||||
if (cookie && typeof cookie === 'object') {
|
||||
ctx.cookies.set(cookie.name, cookie.value, {
|
||||
maxAge: 864000000,
|
||||
httpOnly: false
|
||||
});
|
||||
}
|
||||
} else if (context.resHeader[i] && Array.isArray(context.resHeader[i])) {
|
||||
context.resHeader[i].forEach(item => {
|
||||
cookie = parseCookie(item);
|
||||
if (cookie && typeof cookie === 'object') {
|
||||
ctx.cookies.set(cookie.name, cookie.value, {
|
||||
maxAge: 864000000,
|
||||
httpOnly: false
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
ctx.set(i, context.resHeader[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ctx.status = context.httpCode;
|
||||
ctx.body = context.mockJson;
|
||||
return;
|
||||
} catch (e) {
|
||||
yapi.commons.log(e, 'error');
|
||||
return (ctx.body = {
|
||||
errcode: 400,
|
||||
errmsg: '解析出错,请检查。Error: ' + e.message,
|
||||
data: null
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
yapi.commons.log(e, 'error');
|
||||
return (ctx.body = yapi.commons.resReturn(null, 409, e.message));
|
||||
}
|
||||
};
|
||||
39
server/models/avatar.js
Normal file
39
server/models/avatar.js
Normal file
@@ -0,0 +1,39 @@
|
||||
const yapi = require('../yapi.js');
|
||||
const baseModel = require('./base.js');
|
||||
|
||||
class avatarModel extends baseModel {
|
||||
getName() {
|
||||
return 'avatar';
|
||||
}
|
||||
|
||||
getSchema() {
|
||||
return {
|
||||
uid: { type: Number, required: true },
|
||||
basecode: String,
|
||||
type: String
|
||||
};
|
||||
}
|
||||
|
||||
get(uid) {
|
||||
return this.model.findOne({
|
||||
uid: uid
|
||||
});
|
||||
}
|
||||
|
||||
up(uid, basecode, type) {
|
||||
return this.model.update(
|
||||
{
|
||||
uid: uid
|
||||
},
|
||||
{
|
||||
type: type,
|
||||
basecode: basecode
|
||||
},
|
||||
{
|
||||
upsert: true
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = avatarModel;
|
||||
49
server/models/base.js
Normal file
49
server/models/base.js
Normal file
@@ -0,0 +1,49 @@
|
||||
const yapi = require('../yapi.js');
|
||||
const mongoose = require('mongoose');
|
||||
const autoIncrement = require('../utils/mongoose-auto-increment');
|
||||
|
||||
/**
|
||||
* 所有的model都需要继承baseModel, 且需要 getSchema和getName方法,不然会报错
|
||||
*/
|
||||
|
||||
class baseModel {
|
||||
constructor() {
|
||||
this.schema = new mongoose.Schema(this.getSchema());
|
||||
this.name = this.getName();
|
||||
|
||||
if (this.isNeedAutoIncrement() === true) {
|
||||
this.schema.plugin(autoIncrement.plugin, {
|
||||
model: this.name,
|
||||
field: this.getPrimaryKey(),
|
||||
startAt: 11,
|
||||
incrementBy: yapi.commons.rand(1, 10)
|
||||
});
|
||||
}
|
||||
|
||||
this.model = yapi.db(this.name, this.schema);
|
||||
}
|
||||
|
||||
isNeedAutoIncrement() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 可通过覆盖此方法生成其他自增字段
|
||||
*/
|
||||
getPrimaryKey() {
|
||||
return '_id';
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取collection的schema结构
|
||||
*/
|
||||
getSchema() {
|
||||
yapi.commons.log('Model Class need getSchema function', 'error');
|
||||
}
|
||||
|
||||
getName() {
|
||||
yapi.commons.log('Model Class need name', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = baseModel;
|
||||
84
server/models/follow.js
Normal file
84
server/models/follow.js
Normal file
@@ -0,0 +1,84 @@
|
||||
const baseModel = require('./base.js');
|
||||
|
||||
class followModel extends baseModel {
|
||||
getName() {
|
||||
return 'follow';
|
||||
}
|
||||
|
||||
getSchema() {
|
||||
return {
|
||||
uid: { type: Number, required: true },
|
||||
projectid: { type: Number, required: true },
|
||||
projectname: { type: String, required: true },
|
||||
icon: String,
|
||||
color: String
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Number} uid 用户id
|
||||
* @param {Number} projectid 项目id
|
||||
* @param {String} projectname 项目名
|
||||
* @param {String} icon 项目图标
|
||||
*/
|
||||
|
||||
save(data) {
|
||||
//关注
|
||||
let saveData = {
|
||||
uid: data.uid,
|
||||
projectid: data.projectid,
|
||||
projectname: data.projectname,
|
||||
icon: data.icon,
|
||||
color: data.color
|
||||
};
|
||||
let follow = new this.model(saveData);
|
||||
return follow.save();
|
||||
}
|
||||
|
||||
del(projectid, uid) {
|
||||
return this.model.remove({
|
||||
projectid: projectid,
|
||||
uid: uid
|
||||
});
|
||||
}
|
||||
|
||||
delByProjectId(projectid){
|
||||
return this.model.remove({
|
||||
projectid: projectid
|
||||
})
|
||||
}
|
||||
|
||||
list(uid) {
|
||||
return this.model
|
||||
.find({
|
||||
uid: uid
|
||||
})
|
||||
.exec();
|
||||
}
|
||||
|
||||
listByProjectId(projectid) {
|
||||
return this.model.find({
|
||||
projectid: projectid
|
||||
});
|
||||
}
|
||||
|
||||
checkProjectRepeat(uid, projectid) {
|
||||
return this.model.countDocuments({
|
||||
uid: uid,
|
||||
projectid: projectid
|
||||
});
|
||||
}
|
||||
|
||||
updateById(id, typeid, data) {
|
||||
return this.model.update(
|
||||
{
|
||||
uid: id,
|
||||
projectid: typeid
|
||||
},
|
||||
data,
|
||||
{ runValidators: true }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = followModel;
|
||||
211
server/models/group.js
Normal file
211
server/models/group.js
Normal file
@@ -0,0 +1,211 @@
|
||||
const yapi = require('../yapi.js');
|
||||
const baseModel = require('./base.js');
|
||||
|
||||
class groupModel extends baseModel {
|
||||
getName() {
|
||||
return 'group';
|
||||
}
|
||||
|
||||
getSchema() {
|
||||
return {
|
||||
uid: Number,
|
||||
group_name: String,
|
||||
group_desc: String,
|
||||
add_time: Number,
|
||||
up_time: Number,
|
||||
type: { type: String, default: 'public', enum: ['public', 'private'] },
|
||||
members: [
|
||||
{
|
||||
uid: Number,
|
||||
role: { type: String, enum: ['owner', 'dev'] },
|
||||
username: String,
|
||||
email: String
|
||||
}
|
||||
],
|
||||
|
||||
custom_field1: {
|
||||
name: String,
|
||||
enable: { type: Boolean, default: false }
|
||||
}
|
||||
// custom_field2: {
|
||||
// name: String,
|
||||
// enable: { type: Boolean, default: false }
|
||||
// },
|
||||
// custom_field3: {
|
||||
// name: String,
|
||||
// enable: { type: Boolean, default: false }
|
||||
// }
|
||||
};
|
||||
}
|
||||
|
||||
save(data) {
|
||||
let m = new this.model(data);
|
||||
return m.save();
|
||||
}
|
||||
|
||||
get(id) {
|
||||
return this.model
|
||||
.findOne({
|
||||
_id: id
|
||||
})
|
||||
.exec();
|
||||
}
|
||||
|
||||
updateMember(data) {
|
||||
return this.model.update(
|
||||
{
|
||||
'members.uid': data.uid
|
||||
},
|
||||
{
|
||||
$set: {
|
||||
'members.$.username': data.username,
|
||||
'members.$.email': data.email
|
||||
}
|
||||
},
|
||||
{ multi: true }
|
||||
);
|
||||
}
|
||||
|
||||
getByPrivateUid(uid) {
|
||||
return this.model
|
||||
.findOne({
|
||||
uid: uid,
|
||||
type: 'private'
|
||||
})
|
||||
.select('group_name _id group_desc add_time up_time type custom_field1')
|
||||
.exec();
|
||||
}
|
||||
|
||||
getGroupById(id) {
|
||||
return this.model
|
||||
.findOne({
|
||||
_id: id
|
||||
})
|
||||
.select('uid group_name group_desc add_time up_time type custom_field1')
|
||||
.exec();
|
||||
}
|
||||
|
||||
checkRepeat(name) {
|
||||
return this.model.countDocuments({
|
||||
group_name: name
|
||||
});
|
||||
}
|
||||
// 分组数量统计
|
||||
getGroupListCount() {
|
||||
return this.model.countDocuments({ type: 'public' });
|
||||
}
|
||||
|
||||
addMember(id, data) {
|
||||
return this.model.update(
|
||||
{
|
||||
_id: id
|
||||
},
|
||||
{
|
||||
// $push: { members: data },
|
||||
$push: { members: { $each: data } }
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
delMember(id, uid) {
|
||||
return this.model.update(
|
||||
{
|
||||
_id: id
|
||||
},
|
||||
{
|
||||
$pull: { members: { uid: uid } }
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
changeMemberRole(id, uid, role) {
|
||||
return this.model.update(
|
||||
{
|
||||
_id: id,
|
||||
'members.uid': uid
|
||||
},
|
||||
{
|
||||
$set: { 'members.$.role': role }
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
checkMemberRepeat(id, uid) {
|
||||
return this.model.countDocuments({
|
||||
_id: id,
|
||||
'members.uid': uid
|
||||
});
|
||||
}
|
||||
|
||||
list() {
|
||||
return this.model
|
||||
.find({
|
||||
type: 'public'
|
||||
})
|
||||
.select('group_name _id group_desc add_time up_time type uid custom_field1')
|
||||
.exec();
|
||||
}
|
||||
|
||||
getAuthList(uid){
|
||||
return this.model.find({
|
||||
$or: [{
|
||||
'members.uid': uid,
|
||||
'type': 'public'
|
||||
}, {
|
||||
'type': 'public',
|
||||
uid
|
||||
}]
|
||||
}).select(' _id group_name group_desc add_time up_time type uid custom_field1')
|
||||
.exec();
|
||||
|
||||
}
|
||||
|
||||
findByGroups(ids = []){
|
||||
return this.model.find({
|
||||
_id: {
|
||||
$in: ids
|
||||
},
|
||||
type: 'public'
|
||||
})
|
||||
}
|
||||
|
||||
del(id) {
|
||||
return this.model.remove({
|
||||
_id: id
|
||||
});
|
||||
}
|
||||
|
||||
up(id, data) {
|
||||
return this.model.update(
|
||||
{
|
||||
_id: id
|
||||
},
|
||||
{
|
||||
custom_field1: data.custom_field1,
|
||||
group_name: data.group_name,
|
||||
group_desc: data.group_desc,
|
||||
up_time: yapi.commons.time()
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
getcustomFieldName(name) {
|
||||
return this.model
|
||||
.find({
|
||||
'custom_field1.name': name,
|
||||
'custom_field1.enable': true
|
||||
})
|
||||
.select('_id')
|
||||
.exec();
|
||||
}
|
||||
|
||||
search(keyword) {
|
||||
return this.model
|
||||
.find({
|
||||
group_name: new RegExp(keyword, 'i')
|
||||
})
|
||||
.limit(10);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = groupModel;
|
||||
360
server/models/interface.js
Normal file
360
server/models/interface.js
Normal file
@@ -0,0 +1,360 @@
|
||||
const yapi = require('../yapi.js')
|
||||
const baseModel = require('./base.js')
|
||||
|
||||
class interfaceModel extends baseModel {
|
||||
getName() {
|
||||
return 'interface'
|
||||
}
|
||||
|
||||
getSchema() {
|
||||
return {
|
||||
title: { type: String, required: true },
|
||||
uid: { type: Number, required: true },
|
||||
path: { type: String, required: true },
|
||||
method: { type: String, required: true },
|
||||
project_id: { type: Number, required: true },
|
||||
catid: { type: Number, required: true },
|
||||
edit_uid: { type: Number, default: 0 },
|
||||
status: { type: String, enum: ['undone', 'done'], default: 'undone' },
|
||||
desc: String,
|
||||
markdown: String,
|
||||
add_time: Number,
|
||||
up_time: Number,
|
||||
type: { type: String, enum: ['static', 'var'], default: 'static' },
|
||||
query_path: {
|
||||
path: String,
|
||||
params: [
|
||||
{
|
||||
name: String,
|
||||
value: String
|
||||
}
|
||||
]
|
||||
},
|
||||
req_query: [
|
||||
{
|
||||
name: String,
|
||||
value: String,
|
||||
example: String,
|
||||
desc: String,
|
||||
required: {
|
||||
type: String,
|
||||
enum: ['1', '0'],
|
||||
default: '1'
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
enum: ['string', 'number'],
|
||||
default: 'string'
|
||||
}
|
||||
}
|
||||
],
|
||||
req_headers: [
|
||||
{
|
||||
name: String,
|
||||
value: String,
|
||||
example: String,
|
||||
desc: String,
|
||||
required: {
|
||||
type: String,
|
||||
enum: ['1', '0'],
|
||||
default: '1'
|
||||
}
|
||||
}
|
||||
],
|
||||
req_params: [
|
||||
{
|
||||
name: String,
|
||||
desc: String,
|
||||
example: String,
|
||||
type: {
|
||||
type: String,
|
||||
enum: ['string', 'number'],
|
||||
default: 'string'
|
||||
}
|
||||
}
|
||||
],
|
||||
req_body_type: {
|
||||
type: String,
|
||||
enum: ['form', 'json', 'text', 'file', 'raw']
|
||||
},
|
||||
req_body_is_json_schema: { type: Boolean, default: false },
|
||||
req_body_form: [
|
||||
{
|
||||
name: String,
|
||||
type: { type: String, enum: ['text', 'file'] },
|
||||
example: String,
|
||||
value: String,
|
||||
desc: String,
|
||||
required: {
|
||||
type: String,
|
||||
enum: ['1', '0'],
|
||||
default: '1'
|
||||
}
|
||||
}
|
||||
],
|
||||
req_body_other: String,
|
||||
res_body_type: {
|
||||
type: String,
|
||||
enum: ['json', 'text', 'xml', 'raw', 'json-schema']
|
||||
},
|
||||
res_body: String,
|
||||
res_body_is_json_schema: { type: Boolean, default: false },
|
||||
custom_field_value: String,
|
||||
field2: String,
|
||||
field3: String,
|
||||
api_opened: { type: Boolean, default: false },
|
||||
index: { type: Number, default: 0 },
|
||||
tag: Array
|
||||
}
|
||||
}
|
||||
|
||||
save(data) {
|
||||
let m = new this.model(data)
|
||||
return m.save()
|
||||
}
|
||||
|
||||
get(id) {
|
||||
return this.model
|
||||
.findOne({
|
||||
_id: id
|
||||
})
|
||||
.exec()
|
||||
}
|
||||
|
||||
getBaseinfo(id) {
|
||||
return this.model
|
||||
.findOne({
|
||||
_id: id
|
||||
})
|
||||
.select('path method uid title project_id cat_id status ')
|
||||
.exec()
|
||||
}
|
||||
|
||||
getVar(project_id, method) {
|
||||
return this.model
|
||||
.find({
|
||||
project_id: project_id,
|
||||
type: 'var',
|
||||
method: method
|
||||
})
|
||||
.select('_id path')
|
||||
.exec()
|
||||
}
|
||||
|
||||
getByQueryPath(project_id, path, method) {
|
||||
return this.model
|
||||
.find({
|
||||
'project_id': project_id,
|
||||
'query_path.path': path,
|
||||
'method': method
|
||||
})
|
||||
.exec()
|
||||
}
|
||||
|
||||
getByPath(project_id, path, method, select) {
|
||||
select =
|
||||
select ||
|
||||
'_id title uid path method project_id catid edit_uid status add_time up_time type query_path req_query req_headers req_params req_body_type req_body_form req_body_other res_body_type custom_field_value res_body res_body_is_json_schema req_body_is_json_schema'
|
||||
return this.model
|
||||
.find({
|
||||
project_id: project_id,
|
||||
path: path,
|
||||
method: method
|
||||
})
|
||||
.select(select)
|
||||
.exec()
|
||||
}
|
||||
|
||||
checkRepeat(id, path, method) {
|
||||
return this.model.countDocuments({
|
||||
project_id: id,
|
||||
path: path,
|
||||
method: method
|
||||
})
|
||||
}
|
||||
|
||||
countByProjectId(id) {
|
||||
return this.model.countDocuments({
|
||||
project_id: id
|
||||
})
|
||||
}
|
||||
|
||||
list(project_id, select) {
|
||||
select =
|
||||
select ||
|
||||
'_id title uid path method project_id catid edit_uid status add_time up_time'
|
||||
return this.model
|
||||
.find({
|
||||
project_id: project_id
|
||||
})
|
||||
.select(select)
|
||||
.sort({ title: 1 })
|
||||
.exec()
|
||||
}
|
||||
|
||||
listWithPage(project_id, page, limit) {
|
||||
page = parseInt(page)
|
||||
limit = parseInt(limit)
|
||||
return this.model
|
||||
.find({
|
||||
project_id: project_id
|
||||
})
|
||||
.sort({ title: 1 })
|
||||
.skip((page - 1) * limit)
|
||||
.limit(limit)
|
||||
.select(
|
||||
'_id title uid path method project_id catid api_opened edit_uid status add_time up_time tag',
|
||||
)
|
||||
.exec()
|
||||
}
|
||||
|
||||
listByPid(project_id) {
|
||||
return this.model
|
||||
.find({
|
||||
project_id: project_id
|
||||
})
|
||||
.sort({ title: 1 })
|
||||
.exec()
|
||||
}
|
||||
|
||||
//获取全部接口信息
|
||||
getInterfaceListCount() {
|
||||
return this.model.countDocuments({})
|
||||
}
|
||||
|
||||
listByCatid(catid, select) {
|
||||
select =
|
||||
select ||
|
||||
'_id title uid path method project_id catid edit_uid status add_time up_time index tag'
|
||||
return this.model
|
||||
.find({
|
||||
catid: catid
|
||||
})
|
||||
.select(select)
|
||||
.sort({ index: 1 })
|
||||
.exec()
|
||||
}
|
||||
|
||||
listByCatidWithPage(catid, page, limit) {
|
||||
page = parseInt(page)
|
||||
limit = parseInt(limit)
|
||||
return this.model
|
||||
.find({
|
||||
catid: catid
|
||||
})
|
||||
.sort({ index: 1 })
|
||||
.skip((page - 1) * limit)
|
||||
.limit(limit)
|
||||
.select(
|
||||
'_id title uid path method project_id catid edit_uid api_opened status add_time up_time, index, tag',
|
||||
)
|
||||
.exec()
|
||||
}
|
||||
|
||||
listByOptionWithPage(option, page, limit) {
|
||||
page = parseInt(page)
|
||||
limit = parseInt(limit)
|
||||
return this.model
|
||||
.find(option)
|
||||
.sort({ index: 1 })
|
||||
.skip((page - 1) * limit)
|
||||
.limit(limit)
|
||||
.select(
|
||||
'_id title uid path method project_id catid edit_uid api_opened status add_time up_time, index, tag',
|
||||
)
|
||||
.exec()
|
||||
}
|
||||
|
||||
listByInterStatus(catid, status) {
|
||||
let option = {}
|
||||
if (status === 'open') {
|
||||
option = {
|
||||
catid: catid,
|
||||
api_opened: true
|
||||
}
|
||||
} else {
|
||||
option = {
|
||||
catid: catid
|
||||
}
|
||||
}
|
||||
return this.model.find(option).select().sort({ title: 1 }).exec()
|
||||
}
|
||||
|
||||
del(id) {
|
||||
return this.model.remove({
|
||||
_id: id
|
||||
})
|
||||
}
|
||||
|
||||
delByCatid(id) {
|
||||
return this.model.remove({
|
||||
catid: id
|
||||
})
|
||||
}
|
||||
|
||||
delByProjectId(id) {
|
||||
return this.model.remove({
|
||||
project_id: id
|
||||
})
|
||||
}
|
||||
|
||||
up(id, data) {
|
||||
data.up_time = yapi.commons.time()
|
||||
return this.model.update(
|
||||
{
|
||||
_id: id
|
||||
},
|
||||
data,
|
||||
{ runValidators: true },
|
||||
)
|
||||
}
|
||||
|
||||
upEditUid(id, uid) {
|
||||
return this.model.update(
|
||||
{
|
||||
_id: id
|
||||
},
|
||||
{ edit_uid: uid },
|
||||
{ runValidators: true },
|
||||
)
|
||||
}
|
||||
getcustomFieldValue(id, value) {
|
||||
return this.model
|
||||
.find({
|
||||
project_id: id,
|
||||
custom_field_value: value
|
||||
})
|
||||
.select(
|
||||
'title uid path method edit_uid status desc add_time up_time type query_path req_query req_headers req_params req_body_type req_body_form req_body_other res_body_type custom_field_value',
|
||||
)
|
||||
.exec()
|
||||
}
|
||||
|
||||
listCount(option) {
|
||||
return this.model.countDocuments(option)
|
||||
}
|
||||
|
||||
upIndex(id, index) {
|
||||
return this.model.update(
|
||||
{
|
||||
_id: id
|
||||
},
|
||||
{
|
||||
index: index
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
search(keyword) {
|
||||
return this.model
|
||||
.find({
|
||||
$or: [
|
||||
{ title: new RegExp(keyword, 'ig') },
|
||||
{ path: new RegExp(keyword, 'ig') }
|
||||
]
|
||||
})
|
||||
.limit(10)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = interfaceModel
|
||||
135
server/models/interfaceCase.js
Normal file
135
server/models/interfaceCase.js
Normal file
@@ -0,0 +1,135 @@
|
||||
const yapi = require('../yapi.js');
|
||||
const baseModel = require('./base.js');
|
||||
var mongoose = require('mongoose');
|
||||
var Schema = mongoose.Schema;
|
||||
|
||||
class interfaceCase extends baseModel {
|
||||
getName() {
|
||||
return 'interface_case';
|
||||
}
|
||||
|
||||
getSchema() {
|
||||
return {
|
||||
casename: { type: String, required: true },
|
||||
uid: { type: Number, required: true },
|
||||
col_id: { type: Number, required: true },
|
||||
index: { type: Number, default: 0 },
|
||||
project_id: { type: Number, required: true },
|
||||
interface_id: { type: Number, required: true },
|
||||
add_time: Number,
|
||||
up_time: Number,
|
||||
case_env: { type: String },
|
||||
req_params: [
|
||||
{
|
||||
name: String,
|
||||
value: String
|
||||
}
|
||||
],
|
||||
req_headers: [
|
||||
{
|
||||
name: String,
|
||||
value: String
|
||||
}
|
||||
],
|
||||
req_query: [
|
||||
{
|
||||
name: String,
|
||||
value: String,
|
||||
enable: { type: Boolean, default: true }
|
||||
}
|
||||
],
|
||||
|
||||
req_body_form: [
|
||||
{
|
||||
name: String,
|
||||
value: String,
|
||||
enable: { type: Boolean, default: true }
|
||||
}
|
||||
],
|
||||
req_body_other: String,
|
||||
test_res_body: String,
|
||||
test_status: { type: String, enum: ['ok', 'invalid', 'error', ''] },
|
||||
test_res_header: Schema.Types.Mixed,
|
||||
mock_verify: { type: Boolean, default: false },
|
||||
enable_script: { type: Boolean, default: false },
|
||||
test_script: String
|
||||
};
|
||||
}
|
||||
|
||||
save(data) {
|
||||
let m = new this.model(data);
|
||||
return m.save();
|
||||
}
|
||||
|
||||
//获取全部测试接口信息
|
||||
getInterfaceCaseListCount() {
|
||||
return this.model.countDocuments({});
|
||||
}
|
||||
|
||||
get(id) {
|
||||
return this.model
|
||||
.findOne({
|
||||
_id: id
|
||||
})
|
||||
.exec();
|
||||
}
|
||||
|
||||
list(col_id, select) {
|
||||
select = select || 'casename uid col_id _id index interface_id project_id';
|
||||
if (select === 'all') {
|
||||
return this.model
|
||||
.find({
|
||||
col_id: col_id
|
||||
})
|
||||
.exec();
|
||||
}
|
||||
return this.model
|
||||
.find({
|
||||
col_id: col_id
|
||||
})
|
||||
.select(select)
|
||||
.exec();
|
||||
}
|
||||
|
||||
del(id) {
|
||||
return this.model.remove({
|
||||
_id: id
|
||||
});
|
||||
}
|
||||
|
||||
delByProjectId(id) {
|
||||
return this.model.remove({
|
||||
project_id: id
|
||||
});
|
||||
}
|
||||
|
||||
delByInterfaceId(id) {
|
||||
return this.model.remove({
|
||||
interface_id: id
|
||||
});
|
||||
}
|
||||
|
||||
delByCol(id) {
|
||||
return this.model.remove({
|
||||
col_id: id
|
||||
});
|
||||
}
|
||||
|
||||
up(id, data) {
|
||||
data.up_time = yapi.commons.time();
|
||||
return this.model.update({ _id: id }, data);
|
||||
}
|
||||
|
||||
upCaseIndex(id, index) {
|
||||
return this.model.update(
|
||||
{
|
||||
_id: id
|
||||
},
|
||||
{
|
||||
index: index
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = interfaceCase;
|
||||
86
server/models/interfaceCat.js
Normal file
86
server/models/interfaceCat.js
Normal file
@@ -0,0 +1,86 @@
|
||||
const yapi = require('../yapi.js');
|
||||
const baseModel = require('./base.js');
|
||||
|
||||
/**
|
||||
* 接口分类
|
||||
*/
|
||||
class interfaceCat extends baseModel {
|
||||
getName() {
|
||||
return 'interface_cat';
|
||||
}
|
||||
|
||||
getSchema() {
|
||||
return {
|
||||
name: { type: String, required: true },
|
||||
uid: { type: Number, required: true },
|
||||
project_id: { type: Number, required: true },
|
||||
desc: String,
|
||||
add_time: Number,
|
||||
up_time: Number,
|
||||
index: { type: Number, default: 0 }
|
||||
};
|
||||
}
|
||||
|
||||
save(data) {
|
||||
let m = new this.model(data);
|
||||
return m.save();
|
||||
}
|
||||
|
||||
get(id) {
|
||||
return this.model
|
||||
.findOne({
|
||||
_id: id
|
||||
})
|
||||
.exec();
|
||||
}
|
||||
|
||||
checkRepeat(name) {
|
||||
return this.model.countDocuments({
|
||||
name: name
|
||||
});
|
||||
}
|
||||
|
||||
list(project_id) {
|
||||
return this.model
|
||||
.find({
|
||||
project_id: project_id
|
||||
})
|
||||
.sort({ index: 1 })
|
||||
.exec();
|
||||
}
|
||||
|
||||
del(id) {
|
||||
return this.model.remove({
|
||||
_id: id
|
||||
});
|
||||
}
|
||||
|
||||
delByProjectId(id) {
|
||||
return this.model.remove({
|
||||
project_id: id
|
||||
});
|
||||
}
|
||||
|
||||
up(id, data) {
|
||||
data.up_time = yapi.commons.time();
|
||||
return this.model.update(
|
||||
{
|
||||
_id: id
|
||||
},
|
||||
data
|
||||
);
|
||||
}
|
||||
|
||||
upCatIndex(id, index) {
|
||||
return this.model.update(
|
||||
{
|
||||
_id: id
|
||||
},
|
||||
{
|
||||
index: index
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = interfaceCat;
|
||||
117
server/models/interfaceCol.js
Normal file
117
server/models/interfaceCol.js
Normal file
@@ -0,0 +1,117 @@
|
||||
const yapi = require('../yapi.js');
|
||||
const baseModel = require('./base.js');
|
||||
|
||||
class interfaceCol extends baseModel {
|
||||
getName() {
|
||||
return 'interface_col';
|
||||
}
|
||||
|
||||
getSchema() {
|
||||
return {
|
||||
name: { type: String, required: true },
|
||||
uid: { type: Number, required: true },
|
||||
project_id: { type: Number, required: true },
|
||||
desc: String,
|
||||
add_time: Number,
|
||||
up_time: Number,
|
||||
index: { type: Number, default: 0 },
|
||||
test_report: { type: String, default: '{}' },
|
||||
checkHttpCodeIs200: {
|
||||
type:Boolean,
|
||||
default: false
|
||||
},
|
||||
checkResponseSchema: {
|
||||
type:Boolean,
|
||||
default: false
|
||||
},
|
||||
checkResponseField: {
|
||||
name: {
|
||||
type: String,
|
||||
required: true,
|
||||
default: "code"
|
||||
},
|
||||
value: {
|
||||
type: String,
|
||||
required: true,
|
||||
default: "0"
|
||||
},
|
||||
enable: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
checkScript: {
|
||||
content: {
|
||||
type: String
|
||||
},
|
||||
enable: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
save(data) {
|
||||
let m = new this.model(data);
|
||||
return m.save();
|
||||
}
|
||||
|
||||
get(id) {
|
||||
return this.model
|
||||
.findOne({
|
||||
_id: id
|
||||
})
|
||||
.exec();
|
||||
}
|
||||
|
||||
checkRepeat(name) {
|
||||
return this.model.countDocuments({
|
||||
name: name
|
||||
});
|
||||
}
|
||||
|
||||
list(project_id) {
|
||||
return this.model
|
||||
.find({
|
||||
project_id: project_id
|
||||
})
|
||||
.select('name uid project_id desc add_time up_time, index')
|
||||
.exec();
|
||||
}
|
||||
|
||||
del(id) {
|
||||
return this.model.remove({
|
||||
_id: id
|
||||
});
|
||||
}
|
||||
|
||||
delByProjectId(id) {
|
||||
return this.model.remove({
|
||||
project_id: id
|
||||
});
|
||||
}
|
||||
|
||||
up(id, data) {
|
||||
data.up_time = yapi.commons.time();
|
||||
return this.model.update(
|
||||
{
|
||||
_id: id
|
||||
},
|
||||
data
|
||||
);
|
||||
}
|
||||
|
||||
upColIndex(id, index) {
|
||||
return this.model.update(
|
||||
{
|
||||
_id: id
|
||||
},
|
||||
{
|
||||
index: index
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = interfaceCol;
|
||||
155
server/models/log.js
Normal file
155
server/models/log.js
Normal file
@@ -0,0 +1,155 @@
|
||||
const yapi = require('../yapi.js');
|
||||
const baseModel = require('./base.js');
|
||||
var mongoose = require('mongoose');
|
||||
var Schema = mongoose.Schema;
|
||||
|
||||
class logModel extends baseModel {
|
||||
getName() {
|
||||
return 'log';
|
||||
}
|
||||
|
||||
getSchema() {
|
||||
return {
|
||||
uid: { type: Number, required: true },
|
||||
typeid: { type: Number, required: true },
|
||||
type: {
|
||||
type: String,
|
||||
enum: ['user', 'group', 'interface', 'project', 'other', 'interface_col'],
|
||||
required: true
|
||||
},
|
||||
content: { type: String, required: true },
|
||||
username: { type: String, required: true },
|
||||
add_time: Number,
|
||||
data: Schema.Types.Mixed //用于原始数据存储
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {String} content log内容
|
||||
* @param {Enum} type log类型, ['user', 'group', 'interface', 'project', 'other']
|
||||
* @param {Number} uid 用户id
|
||||
* @param {String} username 用户名
|
||||
* @param {Number} typeid 类型id
|
||||
* @param {Number} add_time 时间
|
||||
*/
|
||||
save(data) {
|
||||
let saveData = {
|
||||
content: data.content,
|
||||
type: data.type,
|
||||
uid: data.uid,
|
||||
username: data.username,
|
||||
typeid: data.typeid,
|
||||
add_time: yapi.commons.time(),
|
||||
data: data.data
|
||||
};
|
||||
|
||||
let log = new this.model(saveData);
|
||||
|
||||
return log.save();
|
||||
}
|
||||
|
||||
del(id) {
|
||||
return this.model.remove({
|
||||
_id: id
|
||||
});
|
||||
}
|
||||
|
||||
list(typeid, type) {
|
||||
return this.model
|
||||
.find({
|
||||
typeid: typeid,
|
||||
type: type
|
||||
})
|
||||
.exec();
|
||||
}
|
||||
|
||||
listWithPaging(typeid, type, page, limit, selectValue) {
|
||||
page = parseInt(page);
|
||||
limit = parseInt(limit);
|
||||
const params = {
|
||||
type: type,
|
||||
typeid: typeid
|
||||
};
|
||||
|
||||
if (selectValue === 'wiki') {
|
||||
params['data.type'] = selectValue;
|
||||
}
|
||||
if (selectValue && !isNaN(selectValue)) {
|
||||
params['data.interface_id'] = +selectValue;
|
||||
}
|
||||
return this.model
|
||||
.find(params)
|
||||
.sort({ add_time: -1 })
|
||||
.skip((page - 1) * limit)
|
||||
.limit(limit)
|
||||
.exec();
|
||||
}
|
||||
listWithPagingByGroup(typeid, pidList, page, limit) {
|
||||
page = parseInt(page);
|
||||
limit = parseInt(limit);
|
||||
return this.model
|
||||
.find({
|
||||
$or: [
|
||||
{
|
||||
type: 'project',
|
||||
typeid: { $in: pidList }
|
||||
},
|
||||
{
|
||||
type: 'group',
|
||||
typeid: typeid
|
||||
}
|
||||
]
|
||||
})
|
||||
.sort({ add_time: -1 })
|
||||
.skip((page - 1) * limit)
|
||||
.limit(limit)
|
||||
.exec();
|
||||
}
|
||||
listCountByGroup(typeid, pidList) {
|
||||
return this.model.countDocuments({
|
||||
$or: [
|
||||
{
|
||||
type: 'project',
|
||||
typeid: { $in: pidList }
|
||||
},
|
||||
{
|
||||
type: 'group',
|
||||
typeid: typeid
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
listCount(typeid, type, selectValue) {
|
||||
const params = {
|
||||
type: type,
|
||||
typeid: typeid
|
||||
};
|
||||
|
||||
if (selectValue === 'wiki') {
|
||||
params['data.type'] = selectValue;
|
||||
}
|
||||
|
||||
if (selectValue && !isNaN(selectValue)) {
|
||||
params['data.interface_id'] = +selectValue;
|
||||
}
|
||||
return this.model.countDocuments(params);
|
||||
}
|
||||
|
||||
listWithCatid(typeid, type, interfaceId) {
|
||||
const params = {
|
||||
type: type,
|
||||
typeid: typeid
|
||||
};
|
||||
if (interfaceId && !isNaN(interfaceId)) {
|
||||
params['data.interface_id'] = +interfaceId;
|
||||
}
|
||||
return this.model
|
||||
.find(params)
|
||||
.sort({ add_time: -1 })
|
||||
.limit(1)
|
||||
.select('uid content type username typeid add_time')
|
||||
.exec();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = logModel;
|
||||
309
server/models/project.js
Normal file
309
server/models/project.js
Normal file
@@ -0,0 +1,309 @@
|
||||
const yapi = require('../yapi.js');
|
||||
const baseModel = require('./base.js');
|
||||
|
||||
class projectModel extends baseModel {
|
||||
getName() {
|
||||
return 'project';
|
||||
}
|
||||
|
||||
constructor(){
|
||||
super()
|
||||
this.handleEnvNullData = this.handleEnvNullData.bind(this)
|
||||
}
|
||||
|
||||
getAuthList(uid){
|
||||
return this.model.find({
|
||||
$or: [{
|
||||
'members.uid': uid,
|
||||
project_type: 'private'
|
||||
}, {
|
||||
uid,
|
||||
project_type: 'private'
|
||||
}, {
|
||||
project_type: 'public'
|
||||
}]
|
||||
}).select('group_id')
|
||||
.exec();
|
||||
}
|
||||
|
||||
getSchema() {
|
||||
return {
|
||||
uid: { type: Number, required: true },
|
||||
name: { type: String, required: true },
|
||||
basepath: { type: String },
|
||||
switch_notice: { type: Boolean, default: true },
|
||||
desc: String,
|
||||
group_id: { type: Number, required: true },
|
||||
project_type: { type: String, required: true, enum: ['public', 'private'] },
|
||||
members: [
|
||||
{
|
||||
uid: Number,
|
||||
role: { type: String, enum: ['owner', 'dev'] },
|
||||
username: String,
|
||||
email: String,
|
||||
email_notice: { type: Boolean, default: true }
|
||||
}
|
||||
],
|
||||
env: [{ name: String, domain: String, header: Array, global: [{
|
||||
name: String,
|
||||
value: String
|
||||
}] }],
|
||||
icon: String,
|
||||
color: String,
|
||||
add_time: Number,
|
||||
up_time: Number,
|
||||
pre_script: String,
|
||||
after_script: String,
|
||||
project_mock_script: String,
|
||||
is_mock_open: { type: Boolean, default: false },
|
||||
strice: { type: Boolean, default: false },
|
||||
is_json5: { type: Boolean, default: true },
|
||||
tag: [{name: String, desc: String}]
|
||||
};
|
||||
}
|
||||
|
||||
updateMember(data) {
|
||||
return this.model.update(
|
||||
{
|
||||
'members.uid': data.uid
|
||||
},
|
||||
{
|
||||
$set: {
|
||||
'members.$.username': data.username,
|
||||
'members.$.email': data.email
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
save(data) {
|
||||
let m = new this.model(data);
|
||||
return m.save();
|
||||
}
|
||||
|
||||
handleEnvNullData(data){
|
||||
if (data == null) {
|
||||
return false;
|
||||
}
|
||||
data = data.toObject();
|
||||
data.toObject = ()=> data;
|
||||
let isFix = false;
|
||||
if(Array.isArray(data.env)){
|
||||
data.env = data.env.map(item=>{
|
||||
item.global = item.global.filter(g=>{
|
||||
if(!g || typeof g !== 'object'){
|
||||
isFix = true;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
})
|
||||
return item;
|
||||
})
|
||||
}
|
||||
|
||||
if(isFix){
|
||||
this.model.update(
|
||||
{
|
||||
_id: data._id
|
||||
|
||||
},
|
||||
{
|
||||
$set: { env: data.env }
|
||||
},
|
||||
{ runValidators: true }
|
||||
);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
get(id) {
|
||||
return this.model
|
||||
.findOne({
|
||||
_id: id
|
||||
})
|
||||
.exec().then(this.handleEnvNullData)
|
||||
}
|
||||
|
||||
getByEnv(id) {
|
||||
return this.model
|
||||
.findOne({
|
||||
_id: id
|
||||
})
|
||||
.select('env')
|
||||
.exec().then(this.handleEnvNullData);
|
||||
}
|
||||
|
||||
getProjectWithAuth(group_id, uid) {
|
||||
return this.model.countDocuments({
|
||||
group_id: group_id,
|
||||
'members.uid': uid
|
||||
});
|
||||
}
|
||||
|
||||
getBaseInfo(id, select) {
|
||||
select =
|
||||
select ||
|
||||
'_id uid name basepath switch_notice desc group_id project_type env icon color add_time up_time pre_script after_script project_mock_script is_mock_open strice is_json5 tag';
|
||||
return this.model
|
||||
.findOne({
|
||||
_id: id
|
||||
})
|
||||
.select(select)
|
||||
.exec().then(this.handleEnvNullData);
|
||||
}
|
||||
|
||||
getByDomain(domain) {
|
||||
return this.model
|
||||
.find({
|
||||
prd_host: domain
|
||||
})
|
||||
.exec().then(this.handleEnvNullData);
|
||||
}
|
||||
|
||||
checkNameRepeat(name, groupid) {
|
||||
return this.model.countDocuments({
|
||||
name: name,
|
||||
group_id: groupid
|
||||
});
|
||||
}
|
||||
|
||||
checkDomainRepeat(domain, basepath) {
|
||||
return this.model.countDocuments({
|
||||
prd_host: domain,
|
||||
basepath: basepath
|
||||
});
|
||||
}
|
||||
|
||||
list(group_id) {
|
||||
let params = { group_id: group_id };
|
||||
return this.model
|
||||
.find(params)
|
||||
.select(
|
||||
'_id uid name basepath switch_notice desc group_id project_type color icon env add_time up_time'
|
||||
)
|
||||
.sort({ _id: -1 })
|
||||
.exec();
|
||||
}
|
||||
|
||||
// 获取项目数量统计
|
||||
getProjectListCount() {
|
||||
return this.model.countDocuments();
|
||||
}
|
||||
|
||||
countWithPublic(group_id) {
|
||||
let params = { group_id: group_id, project_type: 'public' };
|
||||
return this.model.countDocuments(params);
|
||||
}
|
||||
|
||||
listWithPaging(group_id, page, limit) {
|
||||
page = parseInt(page);
|
||||
limit = parseInt(limit);
|
||||
return this.model
|
||||
.find({
|
||||
group_id: group_id
|
||||
})
|
||||
.sort({ _id: -1 })
|
||||
.skip((page - 1) * limit)
|
||||
.limit(limit)
|
||||
.exec();
|
||||
}
|
||||
|
||||
listCount(group_id) {
|
||||
return this.model.countDocuments({
|
||||
group_id: group_id
|
||||
});
|
||||
}
|
||||
|
||||
countByGroupId(group_id) {
|
||||
return this.model.countDocuments({
|
||||
group_id: group_id
|
||||
});
|
||||
}
|
||||
|
||||
del(id) {
|
||||
return this.model.remove({
|
||||
_id: id
|
||||
});
|
||||
}
|
||||
|
||||
delByGroupid(groupId) {
|
||||
return this.model.remove({
|
||||
group_id: groupId
|
||||
});
|
||||
}
|
||||
|
||||
up(id, data) {
|
||||
data.up_time = yapi.commons.time();
|
||||
return this.model.update(
|
||||
{
|
||||
_id: id
|
||||
},
|
||||
data,
|
||||
{ runValidators: true }
|
||||
);
|
||||
}
|
||||
|
||||
addMember(id, data) {
|
||||
return this.model.update(
|
||||
{
|
||||
_id: id
|
||||
},
|
||||
{
|
||||
// $push: { members: data }
|
||||
$push: { members: { $each: data } }
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
delMember(id, uid) {
|
||||
return this.model.update(
|
||||
{
|
||||
_id: id
|
||||
},
|
||||
{
|
||||
$pull: { members: { uid: uid } }
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
checkMemberRepeat(id, uid) {
|
||||
return this.model.countDocuments({
|
||||
_id: id,
|
||||
'members.uid': uid
|
||||
});
|
||||
}
|
||||
|
||||
changeMemberRole(id, uid, role) {
|
||||
return this.model.update(
|
||||
{
|
||||
_id: id,
|
||||
'members.uid': uid
|
||||
},
|
||||
{
|
||||
$set: { 'members.$.role': role }
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
changeMemberEmailNotice(id, uid, notice) {
|
||||
return this.model.update(
|
||||
{
|
||||
_id: id,
|
||||
'members.uid': uid
|
||||
},
|
||||
{
|
||||
$set: { 'members.$.email_notice': notice }
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
search(keyword) {
|
||||
return this.model
|
||||
.find({
|
||||
name: new RegExp(keyword, 'ig')
|
||||
})
|
||||
.limit(10);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = projectModel;
|
||||
70
server/models/storage.js
Normal file
70
server/models/storage.js
Normal file
@@ -0,0 +1,70 @@
|
||||
const baseModel = require('./base.js');
|
||||
const mongoose = require('mongoose');
|
||||
|
||||
class stroageModel extends baseModel {
|
||||
constructor() {
|
||||
super()
|
||||
let storageCol = mongoose.connection.db.collection('storage');
|
||||
storageCol.createIndex(
|
||||
{
|
||||
key: 1
|
||||
},
|
||||
{
|
||||
unique: true
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
getName() {
|
||||
return 'storage';
|
||||
}
|
||||
|
||||
getSchema() {
|
||||
return {
|
||||
key: { type: Number, required: true },
|
||||
data: {
|
||||
type: String,
|
||||
default: ''
|
||||
} //用于原始数据存储
|
||||
};
|
||||
}
|
||||
save(key, data = {}, isInsert = false) {
|
||||
|
||||
let saveData = {
|
||||
key,
|
||||
data: JSON.stringify(data, null, 2)
|
||||
};
|
||||
if(isInsert){
|
||||
let r = new this.model(saveData);
|
||||
return r.save();
|
||||
}
|
||||
return this.model.updateOne({
|
||||
key
|
||||
}, saveData)
|
||||
}
|
||||
|
||||
del(key) {
|
||||
return this.model.remove({
|
||||
key
|
||||
});
|
||||
}
|
||||
|
||||
get(key) {
|
||||
return this.model
|
||||
.findOne({
|
||||
key
|
||||
})
|
||||
.exec().then(data => {
|
||||
this.save(key, {})
|
||||
if (!data) return null;
|
||||
data = data.toObject().data;
|
||||
try {
|
||||
return JSON.parse(data)
|
||||
} catch (e) {
|
||||
return {}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = stroageModel;
|
||||
48
server/models/token.js
Normal file
48
server/models/token.js
Normal file
@@ -0,0 +1,48 @@
|
||||
const yapi = require('../yapi.js');
|
||||
const baseModel = require('./base.js');
|
||||
|
||||
class tokenModel extends baseModel {
|
||||
getName() {
|
||||
return 'token';
|
||||
}
|
||||
|
||||
getSchema() {
|
||||
return {
|
||||
project_id: { type: Number, required: true },
|
||||
token: String
|
||||
};
|
||||
}
|
||||
|
||||
save(data) {
|
||||
let m = new this.model(data);
|
||||
return m.save();
|
||||
}
|
||||
|
||||
get(project_id) {
|
||||
return this.model.findOne({
|
||||
project_id: project_id
|
||||
});
|
||||
}
|
||||
|
||||
findId(token) {
|
||||
return this.model
|
||||
.findOne({
|
||||
token: token
|
||||
})
|
||||
.select('project_id')
|
||||
.exec();
|
||||
}
|
||||
|
||||
up(project_id, token) {
|
||||
return this.model.update(
|
||||
{
|
||||
project_id: project_id
|
||||
},
|
||||
{
|
||||
token: token
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = tokenModel;
|
||||
114
server/models/user.js
Normal file
114
server/models/user.js
Normal file
@@ -0,0 +1,114 @@
|
||||
const baseModel = require('./base.js');
|
||||
|
||||
class userModel extends baseModel {
|
||||
getName() {
|
||||
return 'user';
|
||||
}
|
||||
|
||||
getSchema() {
|
||||
return {
|
||||
username: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
password: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
email: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
passsalt: String,
|
||||
study: { type: Boolean, default: false },
|
||||
role: String,
|
||||
add_time: Number,
|
||||
up_time: Number,
|
||||
type: { type: String, enum: ['site', 'third'], default: 'site' } //site用户是网站注册用户, third是第三方登录过来的用户
|
||||
};
|
||||
}
|
||||
|
||||
save(data) {
|
||||
let user = new this.model(data);
|
||||
return user.save();
|
||||
}
|
||||
|
||||
checkRepeat(email) {
|
||||
return this.model.countDocuments({
|
||||
email: email
|
||||
});
|
||||
}
|
||||
|
||||
list() {
|
||||
return this.model
|
||||
.find()
|
||||
.select('_id username email role type add_time up_time study')
|
||||
.exec(); //显示id name email role
|
||||
}
|
||||
|
||||
findByUids(uids) {
|
||||
return this.model
|
||||
.find({
|
||||
_id: { $in: uids }
|
||||
})
|
||||
.select('_id username email role type add_time up_time study')
|
||||
.exec();
|
||||
}
|
||||
|
||||
listWithPaging(page, limit) {
|
||||
page = parseInt(page);
|
||||
limit = parseInt(limit);
|
||||
return this.model
|
||||
.find()
|
||||
.sort({ _id: -1 })
|
||||
.skip((page - 1) * limit)
|
||||
.limit(limit)
|
||||
.select('_id username email role type add_time up_time study')
|
||||
.exec();
|
||||
}
|
||||
|
||||
listCount() {
|
||||
return this.model.countDocuments();
|
||||
}
|
||||
|
||||
findByEmail(email) {
|
||||
return this.model.findOne({ email: email });
|
||||
}
|
||||
|
||||
findById(id) {
|
||||
return this.model.findOne({
|
||||
_id: id
|
||||
});
|
||||
}
|
||||
|
||||
del(id) {
|
||||
return this.model.remove({
|
||||
_id: id
|
||||
});
|
||||
}
|
||||
|
||||
update(id, data) {
|
||||
return this.model.update(
|
||||
{
|
||||
_id: id
|
||||
},
|
||||
data
|
||||
);
|
||||
}
|
||||
|
||||
search(keyword) {
|
||||
return this.model
|
||||
.find(
|
||||
{
|
||||
$or: [{ email: new RegExp(keyword, 'i') }, { username: new RegExp(keyword, 'i') }]
|
||||
},
|
||||
{
|
||||
passsalt: 0,
|
||||
password: 0
|
||||
}
|
||||
)
|
||||
.limit(10);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = userModel;
|
||||
273
server/plugin.js
Normal file
273
server/plugin.js
Normal file
@@ -0,0 +1,273 @@
|
||||
const yapi = require('./yapi.js');
|
||||
const plugin_path = yapi.path.join(yapi.WEBROOT, 'node_modules');
|
||||
const plugin_system_path = yapi.path.join(yapi.WEBROOT, 'exts');
|
||||
const initPlugins = require('../common/plugin.js').initPlugins;
|
||||
var extConfig = require('../common/config.js').exts;
|
||||
|
||||
/**
|
||||
* 钩子配置
|
||||
*/
|
||||
var hooks = {
|
||||
/**
|
||||
* 第三方sso登录钩子,暂只支持设置一个
|
||||
* @param ctx
|
||||
* @return 必需返回一个 promise 对象,resolve({username: '', email: ''})
|
||||
*/
|
||||
third_login: {
|
||||
type: 'single',
|
||||
listener: null
|
||||
},
|
||||
/**
|
||||
* 客户端增加接口成功后触发
|
||||
* @param data 接口的详细信息
|
||||
*/
|
||||
interface_add: {
|
||||
type: 'multi',
|
||||
listener: []
|
||||
},
|
||||
/**
|
||||
* 客户端删除接口成功后触发
|
||||
* @param data 接口id
|
||||
*/
|
||||
interface_del: {
|
||||
type: 'multi',
|
||||
listener: []
|
||||
},
|
||||
/**
|
||||
* 客户端更新接口成功后触发
|
||||
* @param id 接口id
|
||||
*/
|
||||
interface_update: {
|
||||
type: 'multi',
|
||||
listener: []
|
||||
},
|
||||
/**
|
||||
* 客户端获取接口数据列表
|
||||
* @param list 返回接口的数据列表
|
||||
*/
|
||||
interface_list: {
|
||||
type: 'multi',
|
||||
listener: []
|
||||
},
|
||||
/**
|
||||
* 客户端获取一条接口信息触发
|
||||
* @param data 接口的详细信息
|
||||
*/
|
||||
interface_get: {
|
||||
type: 'multi',
|
||||
listener: []
|
||||
},
|
||||
/**
|
||||
* 客户端增加一个新项目
|
||||
* @param id 项目id
|
||||
*/
|
||||
project_add: {
|
||||
type: 'multi',
|
||||
listener: []
|
||||
},
|
||||
/**
|
||||
* 客户端更新一个新项目
|
||||
* @param id 项目id
|
||||
*/
|
||||
project_up: {
|
||||
type: 'multi',
|
||||
listener: []
|
||||
},
|
||||
/**
|
||||
* 客户端获取一个项目
|
||||
* @param id 项目id
|
||||
*/
|
||||
project_get: {
|
||||
type: 'multi',
|
||||
listener: []
|
||||
},
|
||||
/**
|
||||
* 客户端删除删除一个项目
|
||||
* @param id 项目id
|
||||
*/
|
||||
project_del: {
|
||||
type: 'multi',
|
||||
listener: []
|
||||
},
|
||||
/**
|
||||
* 导出 markdown 数据
|
||||
* @param context Object
|
||||
* {
|
||||
* projectData: project,
|
||||
interfaceData: interfaceData,
|
||||
ctx: ctx,
|
||||
mockJson: res
|
||||
* }
|
||||
*
|
||||
*/
|
||||
export_markdown: {
|
||||
type: 'multi',
|
||||
listener: []
|
||||
},
|
||||
/**
|
||||
* MockServer生成mock数据后触发
|
||||
* @param context Object
|
||||
* {
|
||||
* projectData: project,
|
||||
interfaceData: interfaceData,
|
||||
ctx: ctx,
|
||||
mockJson: res
|
||||
* }
|
||||
*
|
||||
*/
|
||||
mock_after: {
|
||||
type: 'multi',
|
||||
listener: []
|
||||
},
|
||||
/**
|
||||
* 增加路由的钩子
|
||||
* type Sync
|
||||
* @param addPluginRouter Function
|
||||
* @info
|
||||
* addPLuginPLugin(config)
|
||||
*
|
||||
* config = {
|
||||
* path, // String 路由名称
|
||||
* method, // String 请求方法 get post ...
|
||||
* controller // Class 继承baseController的class
|
||||
* action // String controller的Action
|
||||
* }
|
||||
*
|
||||
* 示例:
|
||||
* config = {
|
||||
* path: "export/pdf",
|
||||
* method: "get",
|
||||
* controller: controller,
|
||||
* action: "exportPdf"
|
||||
* }
|
||||
*/
|
||||
add_router: {
|
||||
type: 'multi',
|
||||
listener: []
|
||||
},
|
||||
/**
|
||||
* 增加websocket路由的钩子
|
||||
* type Sync
|
||||
* @param addPluginRouter Function
|
||||
* @info
|
||||
* addPLuginPLugin(config)
|
||||
*
|
||||
* config = {
|
||||
* path, // String 路由名称
|
||||
* method, // String 请求方法 get post ...
|
||||
* controller // Class 继承baseController的class
|
||||
* action // String controller的Action
|
||||
* }
|
||||
*
|
||||
* 示例:
|
||||
* config = {
|
||||
* path: "export/pdf",
|
||||
* method: "get",
|
||||
* controller: controller,
|
||||
* action: "exportPdf"
|
||||
* }
|
||||
*/
|
||||
add_ws_router: {
|
||||
type: 'multi',
|
||||
listener: []
|
||||
},
|
||||
|
||||
import_data: {
|
||||
type: 'multi',
|
||||
listener: []
|
||||
},
|
||||
|
||||
/**
|
||||
* addNoticePlugin(config)
|
||||
*
|
||||
* config.weixin = {
|
||||
* title: 'wechat',
|
||||
* hander: (emails, title, content)=> {...}
|
||||
* }
|
||||
*/
|
||||
addNotice:{
|
||||
type: 'multi',
|
||||
listener: []
|
||||
}
|
||||
};
|
||||
|
||||
function bindHook(name, listener) {
|
||||
if (!name) throw new Error('缺少hookname');
|
||||
if (name in hooks === false) {
|
||||
throw new Error('不存在的hookname');
|
||||
}
|
||||
if (hooks[name].type === 'multi') {
|
||||
hooks[name].listener.push(listener);
|
||||
} else {
|
||||
if (typeof hooks[name].listener === 'function') {
|
||||
throw new Error('重复绑定singleHook(' + name + '), 请检查');
|
||||
}
|
||||
hooks[name].listener = listener;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {*} hookname
|
||||
* @return promise
|
||||
*/
|
||||
function emitHook(name) {
|
||||
if (hooks[name] && typeof hooks[name] === 'object') {
|
||||
let args = Array.prototype.slice.call(arguments, 1);
|
||||
if (hooks[name].type === 'single' && typeof hooks[name].listener === 'function') {
|
||||
return Promise.resolve(hooks[name].listener.apply(yapi, args));
|
||||
}
|
||||
let promiseAll = [];
|
||||
if (Array.isArray(hooks[name].listener)) {
|
||||
let listenerList = hooks[name].listener;
|
||||
for (let i = 0, l = listenerList.length; i < l; i++) {
|
||||
promiseAll.push(Promise.resolve(listenerList[i].apply(yapi, args)));
|
||||
}
|
||||
}
|
||||
return Promise.all(promiseAll);
|
||||
}
|
||||
}
|
||||
|
||||
yapi.bindHook = bindHook;
|
||||
yapi.emitHook = emitHook;
|
||||
yapi.emitHookSync = emitHook;
|
||||
|
||||
let pluginsConfig = initPlugins(yapi.WEBCONFIG.plugins, 'plugin');
|
||||
pluginsConfig.forEach(plugin => {
|
||||
if (!plugin || plugin.enable === false || plugin.server === false) return null;
|
||||
|
||||
if (
|
||||
!yapi.commons.fileExist(
|
||||
yapi.path.join(plugin_path, 'yapi-plugin-' + plugin.name + '/server.js')
|
||||
)
|
||||
) {
|
||||
throw new Error(`config.json配置了插件${plugin},但plugins目录没有找到此插件,请安装此插件`);
|
||||
}
|
||||
let pluginModule = require(yapi.path.join(
|
||||
plugin_path,
|
||||
'yapi-plugin-' + plugin.name + '/server.js'
|
||||
));
|
||||
pluginModule.call(yapi, plugin.options);
|
||||
});
|
||||
|
||||
extConfig = initPlugins(extConfig, 'ext');
|
||||
|
||||
extConfig.forEach(plugin => {
|
||||
if (!plugin || plugin.enable === false || plugin.server === false) return null;
|
||||
|
||||
if (
|
||||
!yapi.commons.fileExist(
|
||||
yapi.path.join(plugin_system_path, 'yapi-plugin-' + plugin.name + '/server.js')
|
||||
)
|
||||
) {
|
||||
throw new Error(`config.json配置了插件${plugin},但plugins目录没有找到此插件,请安装此插件`);
|
||||
}
|
||||
let pluginModule = require(yapi.path.join(
|
||||
plugin_system_path,
|
||||
'yapi-plugin-' + plugin.name + '/server.js'
|
||||
));
|
||||
pluginModule.call(yapi, plugin.options);
|
||||
});
|
||||
|
||||
//delete bindHook方法,避免误操作
|
||||
delete yapi.bindHook;
|
||||
612
server/router.js
Normal file
612
server/router.js
Normal file
@@ -0,0 +1,612 @@
|
||||
const koaRouter = require('koa-router');
|
||||
const interfaceController = require('./controllers/interface.js');
|
||||
const groupController = require('./controllers/group.js');
|
||||
const userController = require('./controllers/user.js');
|
||||
const interfaceColController = require('./controllers/interfaceCol.js');
|
||||
const testController = require('./controllers/test.js');
|
||||
|
||||
const yapi = require('./yapi.js');
|
||||
const projectController = require('./controllers/project.js');
|
||||
const logController = require('./controllers/log.js');
|
||||
const followController = require('./controllers/follow.js');
|
||||
const openController = require('./controllers/open.js');
|
||||
const { createAction } = require('./utils/commons.js');
|
||||
|
||||
const router = koaRouter();
|
||||
|
||||
let INTERFACE_CONFIG = {
|
||||
interface: {
|
||||
prefix: '/interface/',
|
||||
controller: interfaceController
|
||||
},
|
||||
user: {
|
||||
prefix: '/user/',
|
||||
controller: userController
|
||||
},
|
||||
group: {
|
||||
prefix: '/group/',
|
||||
controller: groupController
|
||||
},
|
||||
project: {
|
||||
prefix: '/project/',
|
||||
controller: projectController
|
||||
},
|
||||
log: {
|
||||
prefix: '/log/',
|
||||
controller: logController
|
||||
},
|
||||
follow: {
|
||||
prefix: '/follow/',
|
||||
controller: followController
|
||||
},
|
||||
col: {
|
||||
prefix: '/col/',
|
||||
controller: interfaceColController
|
||||
},
|
||||
test: {
|
||||
prefix: '/test/',
|
||||
controller: testController
|
||||
},
|
||||
open: {
|
||||
prefix: '/open/',
|
||||
controller: openController
|
||||
}
|
||||
};
|
||||
|
||||
let routerConfig = {
|
||||
group: [
|
||||
{
|
||||
action: 'getMyGroup',
|
||||
path: 'get_mygroup',
|
||||
method: 'get'
|
||||
},
|
||||
|
||||
|
||||
{
|
||||
action: 'list',
|
||||
path: 'list',
|
||||
method: 'get'
|
||||
},
|
||||
{
|
||||
action: 'add',
|
||||
path: 'add',
|
||||
method: 'post'
|
||||
},
|
||||
{
|
||||
action: 'up',
|
||||
path: 'up',
|
||||
method: 'post'
|
||||
},
|
||||
{
|
||||
action: 'del',
|
||||
path: 'del',
|
||||
method: 'post'
|
||||
},
|
||||
{
|
||||
action: 'addMember',
|
||||
path: 'add_member',
|
||||
method: 'post'
|
||||
},
|
||||
{
|
||||
action: 'changeMemberRole',
|
||||
path: 'change_member_role',
|
||||
method: 'post'
|
||||
},
|
||||
{
|
||||
action: 'delMember',
|
||||
path: 'del_member',
|
||||
method: 'post'
|
||||
},
|
||||
{
|
||||
action: 'getMemberList',
|
||||
path: 'get_member_list',
|
||||
method: 'get'
|
||||
},
|
||||
{
|
||||
action: 'get',
|
||||
path: 'get',
|
||||
method: 'get'
|
||||
}
|
||||
],
|
||||
user: [
|
||||
{
|
||||
action: 'login',
|
||||
path: 'login',
|
||||
method: 'post'
|
||||
},
|
||||
{
|
||||
action: 'reg',
|
||||
path: 'reg',
|
||||
method: 'post'
|
||||
},
|
||||
{
|
||||
action: 'list',
|
||||
path: 'list',
|
||||
method: 'get'
|
||||
},
|
||||
{
|
||||
action: 'findById',
|
||||
path: 'find',
|
||||
method: 'get'
|
||||
},
|
||||
{
|
||||
action: 'update',
|
||||
path: 'update',
|
||||
method: 'post'
|
||||
},
|
||||
{
|
||||
action: 'del',
|
||||
path: 'del',
|
||||
method: 'post'
|
||||
},
|
||||
{
|
||||
action: 'getLoginStatus',
|
||||
path: 'status',
|
||||
method: 'get'
|
||||
},
|
||||
{
|
||||
action: 'logout',
|
||||
path: 'logout',
|
||||
method: 'get'
|
||||
},
|
||||
{
|
||||
action: 'loginByToken',
|
||||
path: 'login_by_token',
|
||||
method: 'all'
|
||||
},
|
||||
{
|
||||
action: 'getLdapAuth',
|
||||
path: 'login_by_ldap',
|
||||
method: 'all'
|
||||
},
|
||||
{
|
||||
action: 'upStudy',
|
||||
path: 'up_study',
|
||||
method: 'get'
|
||||
},
|
||||
{
|
||||
action: 'changePassword',
|
||||
path: 'change_password',
|
||||
method: 'post'
|
||||
},
|
||||
{
|
||||
action: 'search',
|
||||
path: 'search',
|
||||
method: 'get'
|
||||
},
|
||||
{
|
||||
action: 'project',
|
||||
path: 'project',
|
||||
method: 'get'
|
||||
},
|
||||
{
|
||||
action: 'avatar',
|
||||
path: 'avatar',
|
||||
method: 'get'
|
||||
},
|
||||
{
|
||||
action: 'uploadAvatar',
|
||||
path: 'upload_avatar',
|
||||
method: 'post'
|
||||
}
|
||||
],
|
||||
project: [
|
||||
{
|
||||
action: 'upSet',
|
||||
path: 'upset',
|
||||
method: 'post'
|
||||
},
|
||||
{
|
||||
action: 'getEnv',
|
||||
path: 'get_env',
|
||||
method: 'get'
|
||||
},
|
||||
{
|
||||
action: 'add',
|
||||
path: 'add',
|
||||
method: 'post'
|
||||
},
|
||||
{
|
||||
action: 'list',
|
||||
path: 'list',
|
||||
method: 'get'
|
||||
},
|
||||
{
|
||||
action: 'get',
|
||||
path: 'get',
|
||||
method: 'get'
|
||||
},
|
||||
{
|
||||
action: 'up',
|
||||
path: 'up',
|
||||
method: 'post'
|
||||
},
|
||||
{
|
||||
action: 'del',
|
||||
path: 'del',
|
||||
method: 'post'
|
||||
},
|
||||
{
|
||||
action: 'addMember',
|
||||
path: 'add_member',
|
||||
method: 'post'
|
||||
},
|
||||
{
|
||||
action: 'delMember',
|
||||
path: 'del_member',
|
||||
method: 'post'
|
||||
},
|
||||
{
|
||||
action: 'changeMemberRole',
|
||||
path: 'change_member_role',
|
||||
method: 'post'
|
||||
},
|
||||
{
|
||||
action: 'changeMemberEmailNotice',
|
||||
path: 'change_member_email_notice',
|
||||
method: 'post'
|
||||
},
|
||||
{
|
||||
action: 'getMemberList',
|
||||
path: 'get_member_list',
|
||||
method: 'get'
|
||||
},
|
||||
{
|
||||
action: 'search',
|
||||
path: 'search',
|
||||
method: 'get'
|
||||
},
|
||||
{
|
||||
action: 'upEnv',
|
||||
path: 'up_env',
|
||||
method: 'post'
|
||||
},
|
||||
{
|
||||
action: 'upTag',
|
||||
path: 'up_tag',
|
||||
method: 'post'
|
||||
},
|
||||
{
|
||||
action: 'token',
|
||||
path: 'token',
|
||||
method: 'get'
|
||||
},
|
||||
{
|
||||
action: 'updateToken',
|
||||
path: 'update_token',
|
||||
method: 'get'
|
||||
},
|
||||
{
|
||||
action: 'checkProjectName',
|
||||
path: 'check_project_name',
|
||||
method: 'get'
|
||||
},
|
||||
{
|
||||
action: 'copy',
|
||||
path: 'copy',
|
||||
method: 'post'
|
||||
},
|
||||
{
|
||||
action: 'swaggerUrl',
|
||||
path: 'swagger_url',
|
||||
method: 'get'
|
||||
}
|
||||
],
|
||||
interface: [
|
||||
{
|
||||
action: 'add',
|
||||
path: 'add',
|
||||
method: 'post'
|
||||
},
|
||||
{
|
||||
action: 'downloadCrx',
|
||||
path: 'download_crx',
|
||||
method: 'get'
|
||||
},
|
||||
{
|
||||
action: 'getCatMenu',
|
||||
path: 'getCatMenu',
|
||||
method: 'get'
|
||||
},
|
||||
{
|
||||
action: 'list',
|
||||
path: 'list',
|
||||
method: 'get'
|
||||
},
|
||||
{
|
||||
action: 'get',
|
||||
path: 'get',
|
||||
method: 'get'
|
||||
},
|
||||
{
|
||||
action: 'up',
|
||||
path: 'up',
|
||||
method: 'post'
|
||||
},
|
||||
{
|
||||
action: 'del',
|
||||
path: 'del',
|
||||
method: 'post'
|
||||
},
|
||||
{
|
||||
action: 'interUpload',
|
||||
path: 'interUpload',
|
||||
method: 'post'
|
||||
},
|
||||
{
|
||||
action: 'listByCat',
|
||||
path: 'list_cat',
|
||||
method: 'get'
|
||||
},
|
||||
{
|
||||
action: 'listByMenu',
|
||||
path: 'list_menu',
|
||||
method: 'get'
|
||||
},
|
||||
{
|
||||
action: 'listByOpen',
|
||||
path: 'list_open',
|
||||
method: 'get'
|
||||
},
|
||||
{
|
||||
action: 'addCat',
|
||||
path: 'add_cat',
|
||||
method: 'post'
|
||||
},
|
||||
{
|
||||
action: 'upCat',
|
||||
path: 'up_cat',
|
||||
method: 'post'
|
||||
},
|
||||
{
|
||||
action: 'delCat',
|
||||
path: 'del_cat',
|
||||
method: 'post'
|
||||
},
|
||||
{
|
||||
action: 'getCustomField',
|
||||
path: 'get_custom_field',
|
||||
method: 'get'
|
||||
},
|
||||
{
|
||||
action: 'save',
|
||||
path: 'save',
|
||||
method: 'post'
|
||||
},
|
||||
{
|
||||
action: 'upIndex',
|
||||
path: 'up_index',
|
||||
method: 'post'
|
||||
},
|
||||
{
|
||||
action: 'upCatIndex',
|
||||
path: 'up_cat_index',
|
||||
method: 'post'
|
||||
},
|
||||
{
|
||||
action: 'schema2json',
|
||||
path: 'schema2json',
|
||||
method: 'post'
|
||||
}
|
||||
],
|
||||
log: [
|
||||
{
|
||||
action: 'list',
|
||||
path: 'list',
|
||||
method: 'get'
|
||||
},
|
||||
{
|
||||
action: 'listByUpdate',
|
||||
path: 'list_by_update',
|
||||
method: 'post'
|
||||
}
|
||||
],
|
||||
follow: [
|
||||
{
|
||||
action: 'list',
|
||||
path: 'list',
|
||||
method: 'get'
|
||||
},
|
||||
{
|
||||
action: 'add',
|
||||
path: 'add',
|
||||
method: 'post'
|
||||
},
|
||||
{
|
||||
action: 'del',
|
||||
path: 'del',
|
||||
method: 'post'
|
||||
}
|
||||
],
|
||||
col: [
|
||||
{
|
||||
action: 'addCol',
|
||||
path: 'add_col',
|
||||
method: 'post'
|
||||
},
|
||||
{
|
||||
action: 'addCaseList',
|
||||
path: 'add_case_list',
|
||||
method: 'post'
|
||||
},
|
||||
{
|
||||
action: 'cloneCaseList',
|
||||
path: 'clone_case_list',
|
||||
method: 'post'
|
||||
},
|
||||
{
|
||||
action: 'list',
|
||||
path: 'list',
|
||||
method: 'get'
|
||||
},
|
||||
{
|
||||
action: 'getCaseList',
|
||||
path: 'case_list',
|
||||
method: 'get'
|
||||
},
|
||||
{
|
||||
action: 'getCaseListByVariableParams',
|
||||
path: 'case_list_by_var_params',
|
||||
method: 'get'
|
||||
},
|
||||
{
|
||||
action: 'addCase',
|
||||
path: 'add_case',
|
||||
method: 'post'
|
||||
},
|
||||
{
|
||||
action: 'upCase',
|
||||
path: 'up_case',
|
||||
method: 'post'
|
||||
},
|
||||
{
|
||||
action: 'getCase',
|
||||
path: 'case',
|
||||
method: 'get'
|
||||
},
|
||||
{
|
||||
action: 'upCol',
|
||||
path: 'up_col',
|
||||
method: 'post'
|
||||
},
|
||||
{
|
||||
action: 'upCaseIndex',
|
||||
path: 'up_case_index',
|
||||
method: 'post'
|
||||
},
|
||||
{
|
||||
action: 'upColIndex',
|
||||
path: 'up_col_index',
|
||||
method: 'post'
|
||||
},
|
||||
{
|
||||
action: 'delCol',
|
||||
path: 'del_col',
|
||||
method: 'get'
|
||||
},
|
||||
{
|
||||
action: 'delCase',
|
||||
path: 'del_case',
|
||||
method: 'get'
|
||||
},
|
||||
{
|
||||
action: 'runCaseScript',
|
||||
path: 'run_script',
|
||||
method: 'post'
|
||||
},
|
||||
{
|
||||
action: 'getCaseEnvList',
|
||||
path: 'case_env_list',
|
||||
method: 'get'
|
||||
}
|
||||
],
|
||||
test: [
|
||||
{
|
||||
action: 'testPost',
|
||||
path: 'post',
|
||||
method: 'post'
|
||||
},
|
||||
{
|
||||
action: 'testGet',
|
||||
path: 'get',
|
||||
method: 'get'
|
||||
},
|
||||
{
|
||||
action: 'testPut',
|
||||
path: 'put',
|
||||
method: 'put'
|
||||
},
|
||||
{
|
||||
action: 'testDelete',
|
||||
path: 'delete',
|
||||
method: 'del'
|
||||
},
|
||||
{
|
||||
action: 'testHead',
|
||||
path: 'head',
|
||||
method: 'head'
|
||||
},
|
||||
{
|
||||
action: 'testOptions',
|
||||
path: 'options',
|
||||
method: 'options'
|
||||
},
|
||||
{
|
||||
action: 'testPatch',
|
||||
path: 'patch',
|
||||
method: 'patch'
|
||||
},
|
||||
{
|
||||
action: 'testFilesUpload',
|
||||
path: 'files/upload',
|
||||
method: 'post'
|
||||
},
|
||||
{
|
||||
action: 'testSingleUpload',
|
||||
path: 'single/upload',
|
||||
method: 'post'
|
||||
},
|
||||
{
|
||||
action: 'testHttpCode',
|
||||
path: 'http/code',
|
||||
method: 'post'
|
||||
},
|
||||
{
|
||||
action: 'testRaw',
|
||||
path: 'raw',
|
||||
method: 'post'
|
||||
},
|
||||
{
|
||||
action: 'testResponse',
|
||||
path: 'response',
|
||||
method: 'get'
|
||||
}
|
||||
],
|
||||
open: [
|
||||
{
|
||||
action: 'projectInterfaceData',
|
||||
path: 'project_interface_data',
|
||||
method: 'get'
|
||||
},
|
||||
{
|
||||
action: 'runAutoTest',
|
||||
path: 'run_auto_test',
|
||||
method: 'get'
|
||||
},
|
||||
{
|
||||
action: 'importData',
|
||||
path: 'import_data',
|
||||
method: 'post'
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
let pluginsRouterPath = [];
|
||||
|
||||
function addPluginRouter(config) {
|
||||
if (!config.path || !config.controller || !config.action) {
|
||||
throw new Error('Plugin Route config Error');
|
||||
}
|
||||
let method = config.method || 'GET';
|
||||
// let routerPath = '/plugin/' + config.path;
|
||||
// 支持 /api/open/plugin 前缀的 openApi
|
||||
let routerPath = (config.prefix || '') + '/plugin/' + config.path;
|
||||
if (pluginsRouterPath.indexOf(routerPath) > -1) {
|
||||
throw new Error('Plugin Route path conflict, please try rename the path');
|
||||
}
|
||||
pluginsRouterPath.push(routerPath);
|
||||
createAction(router, '/api', config.controller, config.action, routerPath, method, false);
|
||||
}
|
||||
|
||||
yapi.emitHookSync('add_router', addPluginRouter);
|
||||
|
||||
for (let ctrl in routerConfig) {
|
||||
let actions = routerConfig[ctrl];
|
||||
actions.forEach(item => {
|
||||
let routerController = INTERFACE_CONFIG[ctrl].controller;
|
||||
let routerPath = INTERFACE_CONFIG[ctrl].prefix + item.path;
|
||||
createAction(router, '/api', routerController, item.action, routerPath, item.method);
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = router;
|
||||
683
server/utils/commons.js
Normal file
683
server/utils/commons.js
Normal file
@@ -0,0 +1,683 @@
|
||||
const fs = require('fs-extra');
|
||||
const path = require('path');
|
||||
const yapi = require('../yapi.js');
|
||||
const sha1 = require('sha1');
|
||||
const logModel = require('../models/log.js');
|
||||
const projectModel = require('../models/project.js');
|
||||
const interfaceColModel = require('../models/interfaceCol.js');
|
||||
const interfaceCaseModel = require('../models/interfaceCase.js');
|
||||
const interfaceModel = require('../models/interface.js');
|
||||
const userModel = require('../models/user.js');
|
||||
const json5 = require('json5');
|
||||
const _ = require('underscore');
|
||||
const Ajv = require('ajv');
|
||||
const Mock = require('mockjs');
|
||||
const sandboxFn = require('./sandbox')
|
||||
|
||||
|
||||
|
||||
const ejs = require('easy-json-schema');
|
||||
|
||||
const jsf = require('json-schema-faker');
|
||||
const { schemaValidator } = require('../../common/utils');
|
||||
const http = require('http');
|
||||
|
||||
jsf.extend('mock', function () {
|
||||
return {
|
||||
mock: function (xx) {
|
||||
return Mock.mock(xx);
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
const defaultOptions = {
|
||||
failOnInvalidTypes: false,
|
||||
failOnInvalidFormat: false
|
||||
};
|
||||
|
||||
// formats.forEach(item => {
|
||||
// item = item.name;
|
||||
// jsf.format(item, () => {
|
||||
// if (item === 'mobile') {
|
||||
// return jsf.random.randexp('^[1][34578][0-9]{9}$');
|
||||
// }
|
||||
// return Mock.mock('@' + item);
|
||||
// });
|
||||
// });
|
||||
|
||||
exports.schemaToJson = function (schema, options = {}) {
|
||||
Object.assign(options, defaultOptions);
|
||||
|
||||
jsf.option(options);
|
||||
let result;
|
||||
try {
|
||||
result = jsf(schema);
|
||||
} catch (err) {
|
||||
result = err.message;
|
||||
}
|
||||
jsf.option(defaultOptions);
|
||||
return result;
|
||||
};
|
||||
|
||||
exports.resReturn = (data, num, errmsg) => {
|
||||
num = num || 0;
|
||||
|
||||
return {
|
||||
errcode: num,
|
||||
errmsg: errmsg || '成功!',
|
||||
data: data
|
||||
};
|
||||
};
|
||||
|
||||
exports.log = (msg, type) => {
|
||||
if (!msg) {
|
||||
return;
|
||||
}
|
||||
|
||||
type = type || 'log';
|
||||
|
||||
let f;
|
||||
|
||||
switch (type) {
|
||||
case 'log':
|
||||
f = console.log; // eslint-disable-line
|
||||
break;
|
||||
case 'warn':
|
||||
f = console.warn; // eslint-disable-line
|
||||
break;
|
||||
case 'error':
|
||||
f = console.error; // eslint-disable-line
|
||||
break;
|
||||
default:
|
||||
f = console.log; // eslint-disable-line
|
||||
break;
|
||||
}
|
||||
|
||||
f(type + ':', msg);
|
||||
|
||||
let date = new Date();
|
||||
let year = date.getFullYear();
|
||||
let month = date.getMonth() + 1;
|
||||
|
||||
let logfile = path.join(yapi.WEBROOT_LOG, year + '-' + month + '.log');
|
||||
|
||||
if (typeof msg === 'object') {
|
||||
if (msg instanceof Error) msg = msg.message;
|
||||
else msg = JSON.stringify(msg);
|
||||
}
|
||||
|
||||
// let data = (new Date).toLocaleString() + '\t|\t' + type + '\t|\t' + msg + '\n';
|
||||
let data = `[ ${new Date().toLocaleString()} ] [ ${type} ] ${msg}\n`;
|
||||
|
||||
fs.writeFileSync(logfile, data, {
|
||||
flag: 'a'
|
||||
});
|
||||
};
|
||||
|
||||
exports.fileExist = filePath => {
|
||||
try {
|
||||
return fs.statSync(filePath).isFile();
|
||||
} catch (err) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
exports.time = () => {
|
||||
return Date.parse(new Date()) / 1000;
|
||||
};
|
||||
|
||||
exports.fieldSelect = (data, field) => {
|
||||
if (!data || !field || !Array.isArray(field)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var arr = {};
|
||||
|
||||
field.forEach(f => {
|
||||
typeof data[f] !== 'undefined' && (arr[f] = data[f]);
|
||||
});
|
||||
|
||||
return arr;
|
||||
};
|
||||
|
||||
exports.rand = (min, max) => {
|
||||
return Math.floor(Math.random() * (max - min) + min);
|
||||
};
|
||||
|
||||
exports.json_parse = json => {
|
||||
try {
|
||||
return json5.parse(json);
|
||||
} catch (e) {
|
||||
return json;
|
||||
}
|
||||
};
|
||||
|
||||
exports.randStr = () => {
|
||||
return Math.random()
|
||||
.toString(36)
|
||||
.substr(2);
|
||||
};
|
||||
exports.getIp = ctx => {
|
||||
let ip;
|
||||
try {
|
||||
ip = ctx.ip.match(/\d+.\d+.\d+.\d+/) ? ctx.ip.match(/\d+.\d+.\d+.\d+/)[0] : 'localhost';
|
||||
} catch (e) {
|
||||
ip = null;
|
||||
}
|
||||
return ip;
|
||||
};
|
||||
|
||||
exports.generatePassword = (password, passsalt) => {
|
||||
return sha1(password + sha1(passsalt));
|
||||
};
|
||||
|
||||
exports.expireDate = day => {
|
||||
let date = new Date();
|
||||
date.setTime(date.getTime() + day * 86400000);
|
||||
return date;
|
||||
};
|
||||
|
||||
exports.sendMail = (options, cb) => {
|
||||
if (!yapi.mail) return false;
|
||||
options.subject = options.subject ? options.subject + '-YApi 平台' : 'YApi 平台';
|
||||
|
||||
cb =
|
||||
cb ||
|
||||
function (err) {
|
||||
if (err) {
|
||||
yapi.commons.log('send mail ' + options.to + ' error,' + err.message, 'error');
|
||||
} else {
|
||||
yapi.commons.log('send mail ' + options.to + ' success');
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
yapi.mail.sendMail(
|
||||
{
|
||||
from: yapi.WEBCONFIG.mail.from,
|
||||
to: options.to,
|
||||
subject: options.subject,
|
||||
html: options.contents
|
||||
},
|
||||
cb
|
||||
);
|
||||
} catch (e) {
|
||||
yapi.commons.log(e.message, 'error');
|
||||
console.error(e.message); // eslint-disable-line
|
||||
}
|
||||
};
|
||||
|
||||
exports.validateSearchKeyword = keyword => {
|
||||
if (/^\*|\?|\+|\$|\^|\\|\.$/.test(keyword)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
exports.filterRes = (list, rules) => {
|
||||
return list.map(item => {
|
||||
let filteredRes = {};
|
||||
|
||||
rules.forEach(rule => {
|
||||
if (typeof rule == 'string') {
|
||||
filteredRes[rule] = item[rule];
|
||||
} else if (typeof rule == 'object') {
|
||||
filteredRes[rule.alias] = item[rule.key];
|
||||
}
|
||||
});
|
||||
|
||||
return filteredRes;
|
||||
});
|
||||
};
|
||||
|
||||
exports.handleVarPath = (pathname, params) => {
|
||||
function insertParams(name) {
|
||||
if (!_.find(params, { name: name })) {
|
||||
params.push({
|
||||
name: name,
|
||||
desc: ''
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (!pathname) return;
|
||||
if (pathname.indexOf(':') !== -1) {
|
||||
let paths = pathname.split('/'),
|
||||
name,
|
||||
i;
|
||||
for (i = 1; i < paths.length; i++) {
|
||||
if (paths[i] && paths[i][0] === ':') {
|
||||
name = paths[i].substr(1);
|
||||
insertParams(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
pathname.replace(/\{(.+?)\}/g, function (str, match) {
|
||||
insertParams(match);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 验证一个 path 是否合法
|
||||
* path第一位必需为 /, path 只允许由 字母数字-/_:.{}= 组成
|
||||
*/
|
||||
exports.verifyPath = path => {
|
||||
// if (/^\/[a-zA-Z0-9\-\/_:!\.\{\}\=]*$/.test(path)) {
|
||||
// return true;
|
||||
// } else {
|
||||
// return false;
|
||||
// }
|
||||
return /^\/[a-zA-Z0-9\-\/_:!\.\{\}\=]*$/.test(path);
|
||||
};
|
||||
|
||||
/**
|
||||
* 沙盒执行 js 代码
|
||||
* @sandbox Object context
|
||||
* @script String script
|
||||
* @return sandbox
|
||||
*
|
||||
* @example let a = sandbox({a: 1}, 'a=2')
|
||||
* a = {a: 2}
|
||||
*/
|
||||
exports.sandbox = (sandbox, script) => {
|
||||
let filter = '^(process|exec|require)$';
|
||||
let reg = new RegExp(filter, "g");
|
||||
if (reg.test(script)) {
|
||||
throw new Error("执行失败,脚本中含敏感操作....");
|
||||
}
|
||||
try {
|
||||
const vm = require('vm');
|
||||
sandbox = sandbox || {};
|
||||
script = new vm.Script(script);
|
||||
const context = new vm.createContext(sandbox);
|
||||
script.runInContext(context, {
|
||||
timeout: 3000
|
||||
});
|
||||
return sandbox
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
};
|
||||
|
||||
function trim(str) {
|
||||
if (!str) {
|
||||
return str;
|
||||
}
|
||||
|
||||
str = str + '';
|
||||
|
||||
return str.replace(/(^\s*)|(\s*$)/g, '');
|
||||
}
|
||||
|
||||
function ltrim(str) {
|
||||
if (!str) {
|
||||
return str;
|
||||
}
|
||||
|
||||
str = str + '';
|
||||
|
||||
return str.replace(/(^\s*)/g, '');
|
||||
}
|
||||
|
||||
function rtrim(str) {
|
||||
if (!str) {
|
||||
return str;
|
||||
}
|
||||
|
||||
str = str + '';
|
||||
|
||||
return str.replace(/(\s*$)/g, '');
|
||||
}
|
||||
|
||||
exports.trim = trim;
|
||||
exports.ltrim = ltrim;
|
||||
exports.rtrim = rtrim;
|
||||
|
||||
/**
|
||||
* 处理请求参数类型,String 字符串去除两边空格,Number 使用parseInt 转换为数字
|
||||
* @params Object {a: ' ab ', b: ' 123 '}
|
||||
* @keys Object {a: 'string', b: 'number'}
|
||||
* @return Object {a: 'ab', b: 123}
|
||||
*/
|
||||
exports.handleParams = (params, keys) => {
|
||||
if (!params || typeof params !== 'object' || !keys || typeof keys !== 'object') {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (var key in keys) {
|
||||
var filter = keys[key];
|
||||
if (params[key]) {
|
||||
switch (filter) {
|
||||
case 'string':
|
||||
params[key] = trim(params[key] + '');
|
||||
break;
|
||||
case 'number':
|
||||
params[key] = !isNaN(params[key]) ? parseInt(params[key], 10) : 0;
|
||||
break;
|
||||
default:
|
||||
params[key] = trim(params + '');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return params;
|
||||
};
|
||||
|
||||
exports.validateParams = (schema2, params) => {
|
||||
const flag = schema2.closeRemoveAdditional;
|
||||
const ajv = new Ajv({
|
||||
allErrors: true,
|
||||
coerceTypes: true,
|
||||
useDefaults: true,
|
||||
removeAdditional: flag ? false : true
|
||||
});
|
||||
|
||||
var localize = require('ajv-i18n');
|
||||
delete schema2.closeRemoveAdditional;
|
||||
|
||||
const schema = ejs(schema2);
|
||||
|
||||
schema.additionalProperties = flag ? true : false;
|
||||
const validate = ajv.compile(schema);
|
||||
let valid = validate(params);
|
||||
|
||||
let message = '请求参数 ';
|
||||
if (!valid) {
|
||||
localize.zh(validate.errors);
|
||||
message += ajv.errorsText(validate.errors, { separator: '\n' });
|
||||
}
|
||||
|
||||
return {
|
||||
valid: valid,
|
||||
message: message
|
||||
};
|
||||
};
|
||||
|
||||
exports.saveLog = logData => {
|
||||
try {
|
||||
let logInst = yapi.getInst(logModel);
|
||||
let data = {
|
||||
content: logData.content,
|
||||
type: logData.type,
|
||||
uid: logData.uid,
|
||||
username: logData.username,
|
||||
typeid: logData.typeid,
|
||||
data: logData.data
|
||||
};
|
||||
|
||||
logInst.save(data).then();
|
||||
} catch (e) {
|
||||
yapi.commons.log(e, 'error'); // eslint-disable-line
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {*} router router
|
||||
* @param {*} baseurl base_url_path
|
||||
* @param {*} routerController controller
|
||||
* @param {*} path routerPath
|
||||
* @param {*} method request_method , post get put delete ...
|
||||
* @param {*} action controller action_name
|
||||
* @param {*} ws enable ws
|
||||
*/
|
||||
exports.createAction = (router, baseurl, routerController, action, path, method, ws) => {
|
||||
router[method](baseurl + path, async ctx => {
|
||||
let inst = new routerController(ctx);
|
||||
try {
|
||||
await inst.init(ctx);
|
||||
ctx.params = Object.assign({}, ctx.request.query, ctx.request.body, ctx.params);
|
||||
if (inst.schemaMap && typeof inst.schemaMap === 'object' && inst.schemaMap[action]) {
|
||||
|
||||
let validResult = yapi.commons.validateParams(inst.schemaMap[action], ctx.params);
|
||||
|
||||
if (!validResult.valid) {
|
||||
return (ctx.body = yapi.commons.resReturn(null, 400, validResult.message));
|
||||
}
|
||||
}
|
||||
if (inst.$auth === true) {
|
||||
await inst[action].call(inst, ctx);
|
||||
} else {
|
||||
if (ws === true) {
|
||||
ctx.ws.send('请登录...');
|
||||
} else {
|
||||
ctx.body = yapi.commons.resReturn(null, 40011, '请登录...');
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
ctx.body = yapi.commons.resReturn(null, 40011, '服务器出错...');
|
||||
yapi.commons.log(err, 'error');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {*} params 接口定义的参数
|
||||
* @param {*} val 接口case 定义的参数值
|
||||
*/
|
||||
function handleParamsValue(params, val) {
|
||||
let value = {};
|
||||
try {
|
||||
params = params.toObject();
|
||||
} catch (e) { }
|
||||
if (params.length === 0 || val.length === 0) {
|
||||
return params;
|
||||
}
|
||||
val.forEach(item => {
|
||||
value[item.name] = item;
|
||||
});
|
||||
params.forEach((item, index) => {
|
||||
if (!value[item.name] || typeof value[item.name] !== 'object') return null;
|
||||
params[index].value = value[item.name].value;
|
||||
if (!_.isUndefined(value[item.name].enable)) {
|
||||
params[index].enable = value[item.name].enable;
|
||||
}
|
||||
});
|
||||
return params;
|
||||
}
|
||||
|
||||
exports.handleParamsValue = handleParamsValue;
|
||||
|
||||
exports.getCaseList = async function getCaseList(id) {
|
||||
const caseInst = yapi.getInst(interfaceCaseModel);
|
||||
const colInst = yapi.getInst(interfaceColModel);
|
||||
const projectInst = yapi.getInst(projectModel);
|
||||
const interfaceInst = yapi.getInst(interfaceModel);
|
||||
|
||||
let resultList = await caseInst.list(id, 'all');
|
||||
let colData = await colInst.get(id);
|
||||
for (let index = 0; index < resultList.length; index++) {
|
||||
let result = resultList[index].toObject();
|
||||
let data = await interfaceInst.get(result.interface_id);
|
||||
if (!data) {
|
||||
await caseInst.del(result._id);
|
||||
continue;
|
||||
}
|
||||
let projectData = await projectInst.getBaseInfo(data.project_id);
|
||||
result.path = projectData.basepath + data.path;
|
||||
result.method = data.method;
|
||||
result.title = data.title;
|
||||
result.req_body_type = data.req_body_type;
|
||||
result.req_headers = handleParamsValue(data.req_headers, result.req_headers);
|
||||
result.res_body_type = data.res_body_type;
|
||||
result.req_body_form = handleParamsValue(data.req_body_form, result.req_body_form);
|
||||
result.req_query = handleParamsValue(data.req_query, result.req_query);
|
||||
result.req_params = handleParamsValue(data.req_params, result.req_params);
|
||||
resultList[index] = result;
|
||||
}
|
||||
resultList = resultList.sort((a, b) => {
|
||||
return a.index - b.index;
|
||||
});
|
||||
let ctxBody = yapi.commons.resReturn(resultList);
|
||||
ctxBody.colData = colData;
|
||||
return ctxBody;
|
||||
};
|
||||
|
||||
function convertString(variable) {
|
||||
if (variable instanceof Error) {
|
||||
return variable.name + ': ' + variable.message;
|
||||
}
|
||||
try {
|
||||
if (variable && typeof variable === 'string') {
|
||||
return variable;
|
||||
}
|
||||
return JSON.stringify(variable, null, ' ');
|
||||
} catch (err) {
|
||||
return variable || '';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
exports.runCaseScript = async function runCaseScript(params, colId, interfaceId) {
|
||||
const colInst = yapi.getInst(interfaceColModel);
|
||||
let colData = await colInst.get(colId);
|
||||
const logs = [];
|
||||
const context = {
|
||||
assert: require('assert'),
|
||||
status: params.response.status,
|
||||
body: params.response.body,
|
||||
header: params.response.header,
|
||||
records: params.records,
|
||||
params: params.params,
|
||||
log: msg => {
|
||||
logs.push('log: ' + convertString(msg));
|
||||
}
|
||||
};
|
||||
|
||||
let result = {};
|
||||
try {
|
||||
|
||||
if (colData.checkHttpCodeIs200) {
|
||||
let status = +params.response.status;
|
||||
if (status !== 200) {
|
||||
throw ('Http status code 不是 200,请检查(该规则来源于于 [测试集->通用规则配置] )')
|
||||
}
|
||||
}
|
||||
|
||||
if (colData.checkResponseField.enable) {
|
||||
if (params.response.body[colData.checkResponseField.name] != colData.checkResponseField.value) {
|
||||
throw (`返回json ${colData.checkResponseField.name} 值不是${colData.checkResponseField.value},请检查(该规则来源于于 [测试集->通用规则配置] )`)
|
||||
}
|
||||
}
|
||||
|
||||
if (colData.checkResponseSchema) {
|
||||
const interfaceInst = yapi.getInst(interfaceModel);
|
||||
let interfaceData = await interfaceInst.get(interfaceId);
|
||||
if (interfaceData.res_body_is_json_schema && interfaceData.res_body) {
|
||||
let schema = JSON.parse(interfaceData.res_body);
|
||||
let result = schemaValidator(schema, context.body)
|
||||
if (!result.valid) {
|
||||
throw (`返回Json 不符合 response 定义的数据结构,原因: ${result.message}
|
||||
数据结构如下:
|
||||
${JSON.stringify(schema, null, 2)}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (colData.checkScript.enable) {
|
||||
let globalScript = colData.checkScript.content;
|
||||
// script 是断言
|
||||
if (globalScript) {
|
||||
logs.push('执行脚本:' + globalScript)
|
||||
result = await sandboxFn(context, globalScript, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
let script = params.script;
|
||||
// script 是断言
|
||||
if (script) {
|
||||
logs.push('执行脚本:' + script)
|
||||
result = await sandboxFn(context, script, true);
|
||||
}
|
||||
result.logs = logs;
|
||||
return yapi.commons.resReturn(result);
|
||||
} catch (err) {
|
||||
logs.push(convertString(err));
|
||||
result.logs = logs;
|
||||
return yapi.commons.resReturn(result, 400, err.name + ': ' + err.message);
|
||||
}
|
||||
};
|
||||
|
||||
exports.getUserdata = async function getUserdata(uid, role) {
|
||||
role = role || 'dev';
|
||||
let userInst = yapi.getInst(userModel);
|
||||
let userData = await userInst.findById(uid);
|
||||
if (!userData) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
role: role,
|
||||
uid: userData._id,
|
||||
username: userData.username,
|
||||
email: userData.email
|
||||
};
|
||||
};
|
||||
|
||||
// 处理mockJs脚本
|
||||
exports.handleMockScript = async function (script, context) {
|
||||
let sandbox = {
|
||||
header: context.ctx.header,
|
||||
query: context.ctx.query,
|
||||
body: context.ctx.request.body,
|
||||
mockJson: context.mockJson,
|
||||
params: Object.assign({}, context.ctx.query, context.ctx.request.body),
|
||||
resHeader: context.resHeader,
|
||||
httpCode: context.httpCode,
|
||||
delay: context.httpCode,
|
||||
Random: Mock.Random
|
||||
};
|
||||
sandbox.cookie = {};
|
||||
|
||||
context.ctx.header.cookie &&
|
||||
context.ctx.header.cookie.split(';').forEach(function (Cookie) {
|
||||
var parts = Cookie.split('=');
|
||||
sandbox.cookie[parts[0].trim()] = (parts[1] || '').trim();
|
||||
});
|
||||
sandbox = await sandboxFn(sandbox, script, false);
|
||||
sandbox.delay = isNaN(sandbox.delay) ? 0 : +sandbox.delay;
|
||||
|
||||
context.mockJson = sandbox.mockJson;
|
||||
context.resHeader = sandbox.resHeader;
|
||||
context.httpCode = sandbox.httpCode;
|
||||
context.delay = sandbox.delay;
|
||||
};
|
||||
|
||||
|
||||
|
||||
exports.createWebAPIRequest = function (ops) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
let req = '';
|
||||
let http_client = http.request(
|
||||
{
|
||||
host: ops.hostname,
|
||||
method: 'GET',
|
||||
port: ops.port,
|
||||
path: ops.path
|
||||
},
|
||||
function (res) {
|
||||
res.on('error', function (err) {
|
||||
reject(err);
|
||||
});
|
||||
res.setEncoding('utf8');
|
||||
if (res.statusCode != 200) {
|
||||
reject({ message: 'statusCode != 200' });
|
||||
} else {
|
||||
res.on('data', function (chunk) {
|
||||
req += chunk;
|
||||
});
|
||||
res.on('end', function () {
|
||||
resolve(req);
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
http_client.on('error', (e) => {
|
||||
reject({ message: `request error: ${e.message}` });
|
||||
});
|
||||
http_client.end();
|
||||
});
|
||||
}
|
||||
|
||||
83
server/utils/db.js
Normal file
83
server/utils/db.js
Normal file
@@ -0,0 +1,83 @@
|
||||
const mongoose = require('mongoose');
|
||||
const yapi = require('../yapi.js');
|
||||
const autoIncrement = require('./mongoose-auto-increment');
|
||||
|
||||
function model(model, schema) {
|
||||
if (schema instanceof mongoose.Schema === false) {
|
||||
schema = new mongoose.Schema(schema);
|
||||
}
|
||||
|
||||
schema.set('autoIndex', false);
|
||||
|
||||
return mongoose.model(model, schema, model);
|
||||
}
|
||||
|
||||
function connect(callback) {
|
||||
mongoose.Promise = global.Promise;
|
||||
mongoose.set('useNewUrlParser', true);
|
||||
mongoose.set('useFindAndModify', false);
|
||||
mongoose.set('useCreateIndex', true);
|
||||
|
||||
let config = yapi.WEBCONFIG;
|
||||
let options = {useNewUrlParser: true, useCreateIndex: true, useUnifiedTopology: true};
|
||||
|
||||
if (config.db.user) {
|
||||
options.user = config.db.user;
|
||||
options.pass = config.db.pass;
|
||||
}
|
||||
|
||||
if (config.db.reconnectTries) {
|
||||
options.reconnectTries = config.db.reconnectTries;
|
||||
}
|
||||
|
||||
if (config.db.reconnectInterval) {
|
||||
options.reconnectInterval = config.db.reconnectInterval;
|
||||
}
|
||||
|
||||
|
||||
options = Object.assign({}, options, config.db.options)
|
||||
|
||||
var connectString = '';
|
||||
|
||||
if(config.db.connectString){
|
||||
connectString = config.db.connectString;
|
||||
}else{
|
||||
connectString = `mongodb://${config.db.servername}:${config.db.port}/${config.db.DATABASE}`;
|
||||
if (config.db.authSource) {
|
||||
connectString = connectString + `?authSource=${config.db.authSource}`;
|
||||
}
|
||||
}
|
||||
|
||||
let db = mongoose.connect(
|
||||
connectString,
|
||||
options,
|
||||
function(err) {
|
||||
if (err) {
|
||||
yapi.commons.log(err + ', mongodb Authentication failed', 'error');
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
db.then(
|
||||
function() {
|
||||
yapi.commons.log('mongodb load success...');
|
||||
|
||||
if (typeof callback === 'function') {
|
||||
callback.call(db);
|
||||
}
|
||||
},
|
||||
function(err) {
|
||||
yapi.commons.log(err + 'mongodb connect error', 'error');
|
||||
}
|
||||
);
|
||||
|
||||
autoIncrement.initialize(db);
|
||||
return db;
|
||||
}
|
||||
|
||||
yapi.db = model;
|
||||
|
||||
module.exports = {
|
||||
model: model,
|
||||
connect: connect
|
||||
};
|
||||
13
server/utils/initConfig.js
Normal file
13
server/utils/initConfig.js
Normal file
@@ -0,0 +1,13 @@
|
||||
const path = require('path');
|
||||
const fs = require('fs-extra');
|
||||
const config = require('../config.js');
|
||||
|
||||
let runtimePath = config.runtime_path;
|
||||
fs.ensureDirSync(runtimePath);
|
||||
fs.ensureDirSync(path.join(runtimePath, 'log'));
|
||||
let configPath = path.join(runtimePath, 'config.json');
|
||||
|
||||
fs.writeFileSync(configPath,
|
||||
JSON.stringify(config, null, '\t'),
|
||||
{ encoding: 'utf8' }
|
||||
);
|
||||
134
server/utils/ldap.js
Normal file
134
server/utils/ldap.js
Normal file
@@ -0,0 +1,134 @@
|
||||
const ldap = require('ldapjs');
|
||||
const yapi = require('../yapi.js');
|
||||
const util = require('util');
|
||||
|
||||
exports.ldapQuery = (username, password) => {
|
||||
// const deferred = Q.defer();
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const { ldapLogin } = yapi.WEBCONFIG;
|
||||
|
||||
// 使用ldapjs库创建一个LDAP客户端
|
||||
const client = ldap.createClient({
|
||||
url: ldapLogin.server
|
||||
});
|
||||
|
||||
client.once('error', err => {
|
||||
if (err) {
|
||||
let msg = {
|
||||
type: false,
|
||||
message: `once: ${err}`
|
||||
};
|
||||
reject(msg);
|
||||
}
|
||||
});
|
||||
// 注册事件处理函数
|
||||
const ldapSearch = (err, search) => {
|
||||
const users = [];
|
||||
if (err) {
|
||||
let msg = {
|
||||
type: false,
|
||||
message: `ldapSearch: ${err}`
|
||||
};
|
||||
reject(msg);
|
||||
}
|
||||
// 查询结果事件响应
|
||||
search.on('searchEntry', entry => {
|
||||
if (entry) {
|
||||
// 获取查询对象
|
||||
users.push(entry.object);
|
||||
}
|
||||
});
|
||||
// 查询错误事件
|
||||
search.on('error', e => {
|
||||
if (e) {
|
||||
let msg = {
|
||||
type: false,
|
||||
message: `searchErr: ${e}`
|
||||
};
|
||||
reject(msg);
|
||||
}
|
||||
});
|
||||
|
||||
search.on('searchReference', referral => {
|
||||
// if (referral) {
|
||||
// let msg = {
|
||||
// type: false,
|
||||
// message: `searchReference: ${referral}`
|
||||
// };
|
||||
// reject(msg);
|
||||
// }
|
||||
console.log('referral: ' + referral.uris.join());
|
||||
});
|
||||
// 查询结束
|
||||
search.on('end', () => {
|
||||
if (users.length > 0) {
|
||||
client.bind(users[0].dn, password, e => {
|
||||
if (e) {
|
||||
let msg = {
|
||||
type: false,
|
||||
message: `用户名或密码不正确: ${e}`
|
||||
};
|
||||
reject(msg);
|
||||
} else {
|
||||
let msg = {
|
||||
type: true,
|
||||
message: `验证成功`,
|
||||
info: users[0]
|
||||
};
|
||||
resolve(msg);
|
||||
}
|
||||
client.unbind();
|
||||
});
|
||||
} else {
|
||||
let msg = {
|
||||
type: false,
|
||||
message: `用户名不存在`
|
||||
};
|
||||
reject(msg);
|
||||
client.unbind();
|
||||
}
|
||||
});
|
||||
};
|
||||
// 将client绑定LDAP Server
|
||||
// 第一个参数: 是用户,必须是从根结点到用户节点的全路径
|
||||
// 第二个参数: 用户密码
|
||||
return new Promise((resolve, reject) => {
|
||||
if (ldapLogin.bindPassword) {
|
||||
client.bind(ldapLogin.baseDn, ldapLogin.bindPassword, err => {
|
||||
if (err) {
|
||||
let msg = {
|
||||
type: false,
|
||||
message: `LDAP server绑定失败: ${err}`
|
||||
};
|
||||
reject(msg);
|
||||
}
|
||||
|
||||
resolve();
|
||||
});
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
}).then(() => {
|
||||
const searchDn = ldapLogin.searchDn;
|
||||
const searchStandard = ldapLogin.searchStandard;
|
||||
// 处理可以自定义filter
|
||||
let customFilter;
|
||||
if (/^(&|\|)/gi.test(searchStandard)) {
|
||||
customFilter = searchStandard.replace(/%s/g,username);
|
||||
} else {
|
||||
customFilter = `${searchStandard}=${username}`;
|
||||
}
|
||||
const opts = {
|
||||
// filter: `(${searchStandard}=${username})`,
|
||||
filter: `(${customFilter})`,
|
||||
scope: 'sub'
|
||||
};
|
||||
|
||||
// 开始查询
|
||||
// 第一个参数: 查询基础路径,代表在查询用户信息将在这个路径下进行,该路径由根结点开始
|
||||
// 第二个参数: 查询选项
|
||||
client.search(searchDn, opts, ldapSearch);
|
||||
});
|
||||
});
|
||||
};
|
||||
178
server/utils/mongoose-auto-increment.js
Normal file
178
server/utils/mongoose-auto-increment.js
Normal file
@@ -0,0 +1,178 @@
|
||||
// Module Scope
|
||||
var mongoose = require('mongoose'),
|
||||
extend = require('extend'),
|
||||
counterSchema,
|
||||
IdentityCounter;
|
||||
|
||||
// Initialize plugin by creating counter collection in database.
|
||||
exports.initialize = function (connection) {
|
||||
try {
|
||||
IdentityCounter = mongoose.model('IdentityCounter');
|
||||
} catch (ex) {
|
||||
if (ex.name === 'MissingSchemaError') {
|
||||
// Create new counter schema.
|
||||
counterSchema = new mongoose.Schema({
|
||||
model: { type: String, require: true },
|
||||
field: { type: String, require: true },
|
||||
count: { type: Number, default: 0 }
|
||||
});
|
||||
|
||||
// Create a unique index using the "field" and "model" fields.
|
||||
counterSchema.index({ field: 1, model: 1 }, { unique: true, required: true, index: -1 });
|
||||
|
||||
// Create model using new schema.
|
||||
IdentityCounter = mongoose.model('IdentityCounter', counterSchema);
|
||||
}
|
||||
else
|
||||
throw ex;
|
||||
}
|
||||
};
|
||||
|
||||
// The function to use when invoking the plugin on a custom schema.
|
||||
exports.plugin = function (schema, options) {
|
||||
|
||||
// If we don't have reference to the counterSchema or the IdentityCounter model then the plugin was most likely not
|
||||
// initialized properly so throw an error.
|
||||
if (!counterSchema || !IdentityCounter) throw new Error("mongoose-auto-increment has not been initialized");
|
||||
|
||||
// Default settings and plugin scope variables.
|
||||
var settings = {
|
||||
model: null, // The model to configure the plugin for.
|
||||
field: '_id', // The field the plugin should track.
|
||||
startAt: 0, // The number the count should start at.
|
||||
incrementBy: 1, // The number by which to increment the count each time.
|
||||
unique: true // Should we create a unique index for the field
|
||||
},
|
||||
fields = {}, // A hash of fields to add properties to in Mongoose.
|
||||
ready = false; // True if the counter collection has been updated and the document is ready to be saved.
|
||||
|
||||
switch (typeof(options)) {
|
||||
// If string, the user chose to pass in just the model name.
|
||||
case 'string':
|
||||
settings.model = options;
|
||||
break;
|
||||
// If object, the user passed in a hash of options.
|
||||
case 'object':
|
||||
extend(settings, options);
|
||||
break;
|
||||
}
|
||||
|
||||
if (settings.model == null)
|
||||
throw new Error("model must be set");
|
||||
|
||||
// Add properties for field in schema.
|
||||
fields[settings.field] = {
|
||||
type: Number,
|
||||
require: true
|
||||
};
|
||||
if (settings.field !== '_id')
|
||||
fields[settings.field].unique = settings.unique
|
||||
schema.add(fields);
|
||||
|
||||
// Find the counter for this model and the relevant field.
|
||||
IdentityCounter.findOne(
|
||||
{ model: settings.model, field: settings.field },
|
||||
function (err, counter) {
|
||||
if (!counter) {
|
||||
// If no counter exists then create one and save it.
|
||||
counter = new IdentityCounter({ model: settings.model, field: settings.field, count: settings.startAt - settings.incrementBy });
|
||||
counter.save(function () {
|
||||
ready = true;
|
||||
});
|
||||
}
|
||||
else {
|
||||
ready = true;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// Declare a function to get the next counter for the model/schema.
|
||||
var nextCount = function (callback) {
|
||||
IdentityCounter.findOne({
|
||||
model: settings.model,
|
||||
field: settings.field
|
||||
}, function (err, counter) {
|
||||
if (err) return callback(err);
|
||||
callback(null, counter === null ? settings.startAt : counter.count + settings.incrementBy);
|
||||
});
|
||||
};
|
||||
// Add nextCount as both a method on documents and a static on the schema for convenience.
|
||||
schema.method('nextCount', nextCount);
|
||||
schema.static('nextCount', nextCount);
|
||||
|
||||
// Declare a function to reset counter at the start value - increment value.
|
||||
var resetCount = function (callback) {
|
||||
IdentityCounter.findOneAndUpdate(
|
||||
{ model: settings.model, field: settings.field },
|
||||
{ count: settings.startAt - settings.incrementBy },
|
||||
{ new: true }, // new: true specifies that the callback should get the updated counter.
|
||||
function (err) {
|
||||
if (err) return callback(err);
|
||||
callback(null, settings.startAt);
|
||||
}
|
||||
);
|
||||
};
|
||||
// Add resetCount as both a method on documents and a static on the schema for convenience.
|
||||
schema.method('resetCount', resetCount);
|
||||
schema.static('resetCount', resetCount);
|
||||
|
||||
// Every time documents in this schema are saved, run this logic.
|
||||
schema.pre('save', function (next) {
|
||||
// Get reference to the document being saved.
|
||||
var doc = this;
|
||||
|
||||
// Only do this if it is a new document (see http://mongoosejs.com/docs/api.html#document_Document-isNew)
|
||||
if (doc.isNew) {
|
||||
// Declare self-invoking save function.
|
||||
(function save() {
|
||||
// If ready, run increment logic.
|
||||
// Note: ready is true when an existing counter collection is found or after it is created for the
|
||||
// first time.
|
||||
if (ready) {
|
||||
// check that a number has already been provided, and update the counter to that number if it is
|
||||
// greater than the current count
|
||||
if (typeof doc[settings.field] === 'number') {
|
||||
IdentityCounter.findOneAndUpdate(
|
||||
// IdentityCounter documents are identified by the model and field that the plugin was invoked for.
|
||||
// Check also that count is less than field value.
|
||||
{ model: settings.model, field: settings.field, count: { $lt: doc[settings.field] } },
|
||||
// Change the count of the value found to the new field value.
|
||||
{ count: doc[settings.field] },
|
||||
function (err) {
|
||||
if (err) return next(err);
|
||||
// Continue with default document save functionality.
|
||||
next();
|
||||
}
|
||||
);
|
||||
} else {
|
||||
// Find the counter collection entry for this model and field and update it.
|
||||
IdentityCounter.findOneAndUpdate(
|
||||
// IdentityCounter documents are identified by the model and field that the plugin was invoked for.
|
||||
{ model: settings.model, field: settings.field },
|
||||
// Increment the count by `incrementBy`.
|
||||
{ $inc: { count: settings.incrementBy } },
|
||||
// new:true specifies that the callback should get the counter AFTER it is updated (incremented).
|
||||
{ new: true },
|
||||
// Receive the updated counter.
|
||||
function (err, updatedIdentityCounter) {
|
||||
if (err) return next(err);
|
||||
// If there are no errors then go ahead and set the document's field to the current count.
|
||||
doc[settings.field] = updatedIdentityCounter.count;
|
||||
// Continue with default document save functionality.
|
||||
next();
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
// If not ready then set a 5 millisecond timer and try to save again. It will keep doing this until
|
||||
// the counter collection is ready.
|
||||
else
|
||||
setTimeout(save, 5);
|
||||
})();
|
||||
}
|
||||
// If the document does not have the field we're interested in or that field isn't a number AND the user did
|
||||
// not specify that we should increment on updates, then just continue the save without any increment logic.
|
||||
else
|
||||
next();
|
||||
});
|
||||
};
|
||||
63
server/utils/notice.js
Normal file
63
server/utils/notice.js
Normal file
@@ -0,0 +1,63 @@
|
||||
const yapi = require('../yapi.js');
|
||||
|
||||
function arrUnique(arr1, arr2) {
|
||||
let arr = arr1.concat(arr2);
|
||||
let res = arr.filter(function(item, index, arr) {
|
||||
return arr.indexOf(item) === index;
|
||||
});
|
||||
return res;
|
||||
}
|
||||
|
||||
const noticeObj = {
|
||||
mail: {
|
||||
title: '邮件',
|
||||
hander: (emails, title, content)=>{
|
||||
yapi.commons.sendMail({
|
||||
to: emails,
|
||||
contents: content,
|
||||
subject: title
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
yapi.emitHook('addNotice', noticeObj)
|
||||
|
||||
yapi.commons.sendNotice = async function(projectId, data) {
|
||||
const projectModel = require('../models/project.js');
|
||||
const userModel = require('../models/user.js');
|
||||
const followModel = require('../models/follow.js');
|
||||
|
||||
const followInst = yapi.getInst(followModel);
|
||||
const userInst = yapi.getInst(userModel);
|
||||
const projectInst = yapi.getInst(projectModel);
|
||||
const list = await followInst.listByProjectId(projectId);
|
||||
const starUsers = list.map(item => item.uid);
|
||||
|
||||
const projectList = await projectInst.get(projectId);
|
||||
const projectMenbers = projectList.members
|
||||
.filter(item => item.email_notice)
|
||||
.map(item => item.uid);
|
||||
|
||||
const users = arrUnique(projectMenbers, starUsers);
|
||||
const usersInfo = await userInst.findByUids(users);
|
||||
const emails = usersInfo.map(item => item.email).join(',');
|
||||
|
||||
try {
|
||||
Object.keys(noticeObj).forEach(key=>{
|
||||
let noticeItem = noticeObj[key];
|
||||
try{
|
||||
noticeItem.hander(emails, data.title, data.content)
|
||||
}catch(err){
|
||||
yapi.commons.log('发送' + (noticeItem.title || key) + '失败' + err.message, 'error')
|
||||
}
|
||||
})
|
||||
// yapi.commons.sendMail({
|
||||
// to: emails,
|
||||
// contents: data.content,
|
||||
// subject: data.title
|
||||
// });
|
||||
} catch (e) {
|
||||
yapi.commons.log('发送失败:' + e, 'error');
|
||||
}
|
||||
};
|
||||
442
server/utils/reportHtml/defaultTheme.css
Normal file
442
server/utils/reportHtml/defaultTheme.css
Normal file
@@ -0,0 +1,442 @@
|
||||
@charset "UTF-8";
|
||||
html,
|
||||
body,
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6,
|
||||
p,
|
||||
blockquote {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-weight: normal;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
/* 设置滚动条的样式 */
|
||||
::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
/* 外层轨道 */
|
||||
::-webkit-scrollbar-track {
|
||||
-webkit-box-shadow: inset006pxrgba(255, 0, 0, 0.3);
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
/* 滚动条滑块 */
|
||||
::-webkit-scrollbar-thumb {
|
||||
border-radius: 4px;
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
-webkit-box-shadow: inset006pxrgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:window-inactive {
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.yapi-run-auto-test {
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", SimSun, sans-serif;
|
||||
font-size: 13px;
|
||||
line-height: 25px;
|
||||
color: #393838;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.yapi-run-auto-test table {
|
||||
margin: 10px 0 15px 0;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.yapi-run-auto-test td,
|
||||
th {
|
||||
border: 1px solid #ddd;
|
||||
padding: 3px 10px;
|
||||
}
|
||||
|
||||
.yapi-run-auto-test th {
|
||||
padding: 5px 10px;
|
||||
}
|
||||
|
||||
.yapi-run-auto-test a, a:link, a:visited {
|
||||
color: #34495e;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.yapi-run-auto-test a:hover, a:focus {
|
||||
color: #59d69d;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.yapi-run-auto-test a img {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.yapi-run-auto-test p {
|
||||
padding-left: 10px;
|
||||
margin-bottom: 9px;
|
||||
}
|
||||
|
||||
.yapi-run-auto-test h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
color: #404040;
|
||||
line-height: 36px;
|
||||
}
|
||||
|
||||
.yapi-run-auto-test h1 {
|
||||
color: #2c3e50;
|
||||
font-weight: 600;
|
||||
font-size: 32px;
|
||||
padding-bottom: 16px;
|
||||
line-height: 50px;
|
||||
}
|
||||
|
||||
.yapi-run-auto-test h2 {
|
||||
font-size: 28px;
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.yapi-run-auto-test h3 {
|
||||
clear: both;
|
||||
font-weight: 400;
|
||||
margin-top: 20px;
|
||||
margin-bottom: 20px;
|
||||
border-left: 3px solid #59d69d;
|
||||
padding-left: 8px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.yapi-run-auto-test h4 {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.yapi-run-auto-test h5 {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.yapi-run-auto-test h6 {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.yapi-run-auto-test hr {
|
||||
margin: 0 0 19px;
|
||||
border: 0;
|
||||
border-bottom: 1px solid #ccc;
|
||||
}
|
||||
|
||||
.yapi-run-auto-test blockquote {
|
||||
padding: 13px 13px 21px 15px;
|
||||
margin-bottom: 18px;
|
||||
font-family: georgia, serif;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.yapi-run-auto-test blockquote:before {
|
||||
font-size: 40px;
|
||||
margin-left: -10px;
|
||||
font-family: georgia, serif;
|
||||
color: #eee;
|
||||
}
|
||||
|
||||
.yapi-run-auto-test blockquote p {
|
||||
font-size: 14px;
|
||||
font-weight: 300;
|
||||
line-height: 18px;
|
||||
margin-bottom: 0;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.yapi-run-auto-test code,
|
||||
pre {
|
||||
font-family: Monaco, Andale Mono, Courier New, monospace;
|
||||
}
|
||||
|
||||
.yapi-run-auto-test code {
|
||||
background-color: #fee9cc;
|
||||
color: rgba(0, 0, 0, 0.75);
|
||||
padding: 1px 3px;
|
||||
font-size: 12px;
|
||||
-webkit-border-radius: 3px;
|
||||
-moz-border-radius: 3px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.yapi-run-auto-test pre {
|
||||
display: block;
|
||||
padding: 14px;
|
||||
margin: 0 0 18px;
|
||||
line-height: 16px;
|
||||
font-size: 11px;
|
||||
border: 1px solid #d9d9d9;
|
||||
white-space: pre-wrap;
|
||||
background: #f6f6f6;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.yapi-run-auto-test pre code {
|
||||
background-color: #f6f6f6;
|
||||
color: #737373;
|
||||
font-size: 11px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.yapi-run-auto-test sup {
|
||||
font-size: 0.83em;
|
||||
vertical-align: super;
|
||||
line-height: 0;
|
||||
}
|
||||
|
||||
* {
|
||||
-webkit-print-color-adjust: exact;
|
||||
}
|
||||
|
||||
@media print {
|
||||
body,
|
||||
code,
|
||||
pre code,
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
color: black;
|
||||
}
|
||||
|
||||
table,
|
||||
pre {
|
||||
page-break-inside: avoid;
|
||||
}
|
||||
}
|
||||
html,
|
||||
body {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.yapi-run-auto-test .menu-left {
|
||||
position: fixed;
|
||||
top: 61px;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
width: 260px;
|
||||
}
|
||||
|
||||
.yapi-run-auto-test .menu-left > .list-content {
|
||||
overflow: auto;
|
||||
margin: 0px;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 16px 8px 0 20px;
|
||||
}
|
||||
|
||||
.yapi-run-auto-test .list {
|
||||
padding: 2px 0px;
|
||||
text-decoration: none;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
padding-right: 8px;
|
||||
}
|
||||
|
||||
|
||||
.yapi-run-auto-test .content-right {
|
||||
max-width: 700px;
|
||||
margin-left: 290px;
|
||||
padding-left: 70px;
|
||||
flex-grow: 1;
|
||||
}
|
||||
.yapi-run-auto-test .content-right h2:target {
|
||||
padding-top: 80px;
|
||||
}
|
||||
|
||||
.yapi-run-auto-test > p {
|
||||
margin-left: 30px;
|
||||
}
|
||||
|
||||
.yapi-run-auto-test > table {
|
||||
margin-left: 30px;
|
||||
}
|
||||
|
||||
.yapi-run-auto-test > pre {
|
||||
margin-left: 30px;
|
||||
}
|
||||
|
||||
.yapi-run-auto-test .curProject {
|
||||
position: fixed;
|
||||
top: 20px;
|
||||
font-size: 25px;
|
||||
color: black;
|
||||
margin-left: -240px;
|
||||
width: 240px;
|
||||
padding: 5px;
|
||||
line-height: 25px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.yapi-run-auto-test .g-doc {
|
||||
margin-top: 56px;
|
||||
padding-top: 24px;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.yapi-run-auto-test .curproject-name {
|
||||
font-size: 42px;
|
||||
}
|
||||
|
||||
.yapi-run-auto-test .m-header {
|
||||
background: #32363a;
|
||||
height: 56px;
|
||||
line-height: 56px;
|
||||
padding-left: 60px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
position: fixed;
|
||||
z-index: 9;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
}
|
||||
.yapi-run-auto-test .m-header .title {
|
||||
font-size: 22px;
|
||||
color: #fff;
|
||||
font-weight: normal;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
margin: 0;
|
||||
margin-left: 16px;
|
||||
padding: 0;
|
||||
line-height: 56px;
|
||||
border: none;
|
||||
}
|
||||
.yapi-run-auto-test .m-header .nav {
|
||||
color: #fff;
|
||||
font-size: 16px;
|
||||
position: absolute;
|
||||
right: 32px;
|
||||
top: 0;
|
||||
}
|
||||
.yapi-run-auto-test .m-header .nav a {
|
||||
color: #fff;
|
||||
margin-left: 16px;
|
||||
padding: 8px;
|
||||
transition: color .2s;
|
||||
}
|
||||
.yapi-run-auto-test .m-header .nav a:hover {
|
||||
color: #59d69d;
|
||||
}
|
||||
|
||||
.yapi-run-auto-test .m-footer {
|
||||
border-top: 1px solid #ddd;
|
||||
padding-top: 16px;
|
||||
padding-bottom: 16px;
|
||||
}
|
||||
.yapi-run-auto-test .row{
|
||||
position: relative;
|
||||
height: auto;
|
||||
zoom: 1;
|
||||
display: block;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.yapi-run-auto-test .case-report {
|
||||
margin: 10px;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.yapi-run-auto-test .case-report .case-report-title {
|
||||
font-size: 14px;
|
||||
text-align: right;
|
||||
padding-right: 20px;
|
||||
}
|
||||
|
||||
.yapi-run-auto-test .col-3 {
|
||||
display: block;
|
||||
box-sizing: border-box;
|
||||
width: 12.5%;
|
||||
}
|
||||
|
||||
.yapi-run-auto-test .col-21 {
|
||||
display: block;
|
||||
box-sizing: border-box;
|
||||
width: 87.5%;
|
||||
}
|
||||
|
||||
.yapi-run-auto-test .icon {
|
||||
display: inline-block;
|
||||
font-style: normal;
|
||||
vertical-align: baseline;
|
||||
text-align: center;
|
||||
text-transform: none;
|
||||
line-height: 1;
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.yapi-run-auto-test .icon-check-circle:before {
|
||||
content: "\2713";
|
||||
}
|
||||
|
||||
.yapi-run-auto-test .icon-close-circle:before {
|
||||
content: "\2715";
|
||||
}
|
||||
|
||||
.yapi-run-auto-test .icon-warning-circle:before {
|
||||
content: "!";
|
||||
}
|
||||
|
||||
.yapi-run-auto-test .icon:before {
|
||||
display: block;
|
||||
font-family: "anticon" !important;
|
||||
}
|
||||
|
||||
.yapi-run-auto-test .summary {
|
||||
border-bottom: 1px solid #ddd;
|
||||
padding-bottom: 8px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.yapi-run-auto-test .success{
|
||||
color: #208054;
|
||||
font-weight: 700;
|
||||
|
||||
}
|
||||
|
||||
.yapi-run-auto-test .status {
|
||||
flex-shrink: 0;
|
||||
margin: 0 5px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
font-size: 12px;
|
||||
border-radius: 2px;
|
||||
color: #fff;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.yapi-run-auto-test .status-ok {
|
||||
background-color: #17c5a6;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.yapi-run-auto-test .status-ko {
|
||||
background-color: #fd3c3c;
|
||||
}
|
||||
|
||||
.yapi-run-auto-test .status-warning {
|
||||
background-color: #ffb74c;
|
||||
}
|
||||
|
||||
/*# sourceMappingURL=defaultTheme.css.map */
|
||||
4
server/utils/reportHtml/defaultTheme.js
Normal file
4
server/utils/reportHtml/defaultTheme.js
Normal file
@@ -0,0 +1,4 @@
|
||||
const fs = require('fs');
|
||||
const sysPath = require('path');
|
||||
const css = fs.readFileSync(sysPath.join(__dirname, './defaultTheme.css'));
|
||||
module.exports = '<style>' + css + '</style>';
|
||||
207
server/utils/reportHtml/index.js
Normal file
207
server/utils/reportHtml/index.js
Normal file
@@ -0,0 +1,207 @@
|
||||
const defaultTheme = require('./defaultTheme.js');
|
||||
|
||||
function json_format(json) {
|
||||
if (json && typeof json === 'object') {
|
||||
return JSON.stringify(json, null, ' ');
|
||||
}
|
||||
return json;
|
||||
}
|
||||
|
||||
module.exports = function renderToHtml(reports) {
|
||||
let tp = createHtml(reports);
|
||||
return tp;
|
||||
};
|
||||
|
||||
function createHtml(reports) {
|
||||
let mdTemplate = ``;
|
||||
let left = ``;
|
||||
reports.list.map((item, index) => {
|
||||
mdTemplate += baseHtml(index, item.name, item.path, item.status);
|
||||
mdTemplate += validHtml(item.validRes);
|
||||
mdTemplate += requestHtml(item.url, item.headers, item.data);
|
||||
mdTemplate += reponseHtml(item.res_header, item.res_body);
|
||||
left += leftHtml(index, item.name, item.code);
|
||||
// left += codeHtml(item.code);
|
||||
});
|
||||
return createHtml5(left, mdTemplate, reports.message, reports.runTime);
|
||||
}
|
||||
|
||||
function createHtml5(left, tp, msg, runTime) {
|
||||
let message = ``;
|
||||
if (msg.failedNum === 0) {
|
||||
message += `<div>一共 <span class="success">${
|
||||
msg.successNum
|
||||
}</span> 测试用例, 全部验证通过(${runTime})</div>`;
|
||||
} else {
|
||||
message += `<div>一共 ${msg.len} 测试用例,<span class="success"> ${
|
||||
msg.successNum
|
||||
}</span> 个验证通过, ${msg.failedNum} 个未通过(${runTime})</div>`;
|
||||
}
|
||||
|
||||
//html5模板
|
||||
let html = `<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>测试报告</title>
|
||||
<meta charset="utf-8" />
|
||||
${defaultTheme}
|
||||
</head>
|
||||
<body class="yapi-run-auto-test">
|
||||
<div class="m-header">
|
||||
<a href="#" style="display: inherit;"><svg class="svg" width="32px" height="32px" viewBox="0 0 64 64" version="1.1"><title>Icon</title><desc>Created with Sketch.</desc><defs><linearGradient x1="50%" y1="0%" x2="50%" y2="100%" id="linearGradient-1"><stop stop-color="#FFFFFF" offset="0%"></stop><stop stop-color="#F2F2F2" offset="100%"></stop></linearGradient><circle id="path-2" cx="31.9988602" cy="31.9988602" r="2.92886048"></circle><filter x="-85.4%" y="-68.3%" width="270.7%" height="270.7%" filterUnits="objectBoundingBox" id="filter-3"><feOffset dx="0" dy="1" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset><feGaussianBlur stdDeviation="1.5" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur><feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.159703351 0" type="matrix" in="shadowBlurOuter1"></feColorMatrix></filter></defs><g id="首页" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"><g id="大屏幕"><g id="Icon"><circle id="Oval-1" fill="url(#linearGradient-1)" cx="32" cy="32" r="32"></circle><path d="M36.7078009,31.8054514 L36.7078009,51.7110548 C36.7078009,54.2844537 34.6258634,56.3695395 32.0579205,56.3695395 C29.4899777,56.3695395 27.4099998,54.0704461 27.4099998,51.7941246 L27.4099998,31.8061972 C27.4099998,29.528395 29.4909575,27.218453 32.0589004,27.230043 C34.6268432,27.241633 36.7078009,29.528395 36.7078009,31.8054514 Z" id="blue" fill="#2359F1" fill-rule="nonzero"></path><path d="M45.2586091,17.1026914 C45.2586091,17.1026914 45.5657231,34.0524383 45.2345291,37.01141 C44.9033351,39.9703817 43.1767091,41.6667796 40.6088126,41.6667796 C38.040916,41.6667796 35.9609757,39.3676862 35.9609757,37.0913646 L35.9609757,17.1034372 C35.9609757,14.825635 38.0418959,12.515693 40.6097924,12.527283 C43.177689,12.538873 45.2586091,14.825635 45.2586091,17.1026914 Z" id="green" fill="#57CF27" fill-rule="nonzero" transform="translate(40.674608, 27.097010) rotate(60.000000) translate(-40.674608, -27.097010) "></path><path d="M28.0410158,17.0465598 L28.0410158,36.9521632 C28.0410158,39.525562 25.9591158,41.6106479 23.3912193,41.6106479 C20.8233227,41.6106479 18.7433824,39.3115545 18.7433824,37.035233 L18.7433824,17.0473055 C18.7433824,14.7695034 20.8243026,12.4595614 23.3921991,12.4711513 C25.9600956,12.4827413 28.0410158,14.7695034 28.0410158,17.0465598 Z" id="red" fill="#FF561B" fill-rule="nonzero" transform="translate(23.392199, 27.040878) rotate(-60.000000) translate(-23.392199, -27.040878) "></path><g id="inner-round"><use fill="black" fill-opacity="1" filter="url(#filter-3)" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#path-2"></use><use fill="#F7F7F7" fill-rule="evenodd" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#path-2"></use></g></g></g></g></svg></a>
|
||||
<a href="#"><h1 class="title">YAPI 测试结果文档</h1></a>
|
||||
<div class="nav">
|
||||
<a href="https://hellosean1025.github.io/yapi">YApi</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="g-doc">
|
||||
<div class="menu-left">
|
||||
${left}
|
||||
</div>
|
||||
<div id="right" class="content-right">
|
||||
<h1>YApi 测试报告</h1>
|
||||
<div class="summary">${message}</div>
|
||||
${tp}
|
||||
<footer class="m-footer">
|
||||
<p>Build by <a href="https://ymfe.org/">YMFE</a>.</p>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
`;
|
||||
return html;
|
||||
}
|
||||
|
||||
function requestHtml(url, headers, params) {
|
||||
headers = json_format(headers, null, ' ');
|
||||
params = json_format(params);
|
||||
let html = ``;
|
||||
html += `
|
||||
<div>
|
||||
<h3>Request</h3>
|
||||
<div class="row case-report">
|
||||
<div class="col-3 case-report-title">Url</div>
|
||||
<div class="col-21">${url}</div>
|
||||
</div>`;
|
||||
html += headers
|
||||
? `<div class="row case-report">
|
||||
<div class="col-3 case-report-title">Headers</div>
|
||||
<div class="col-21">
|
||||
<pre>${headers}</pre>
|
||||
</div>
|
||||
</div>`
|
||||
: ``;
|
||||
|
||||
html += params
|
||||
? ` <div class="row case-report">
|
||||
<div class="col-3 case-report-title">Body</div>
|
||||
<div class="col-21">
|
||||
<pre>${params}</pre>
|
||||
</div>
|
||||
</div>`
|
||||
: ``;
|
||||
html += `</div>`;
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
function reponseHtml(res_header, res_body) {
|
||||
res_header = json_format(res_header, null, ' ');
|
||||
res_body = json_format(res_body, null, ' ');
|
||||
let html = ``;
|
||||
html += `<div><h3>Reponse</h3>`;
|
||||
|
||||
html += res_header
|
||||
? `
|
||||
<div class="row case-report">
|
||||
<div class="col-3 case-report-title">Headers</div>
|
||||
<div class="col-21">
|
||||
<pre>${res_header}</pre>
|
||||
</div>
|
||||
</div>`
|
||||
: ``;
|
||||
|
||||
html += res_body
|
||||
? ` <div class="row case-report">
|
||||
<div class="col-3 case-report-title">Body</div>
|
||||
<div class="col-21">
|
||||
<pre>${res_body}</pre>
|
||||
</div>
|
||||
</div>`
|
||||
: ``;
|
||||
|
||||
html += `</div>`;
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
function validHtml(validRes) {
|
||||
if (validRes && Array.isArray(validRes)) {
|
||||
validRes = validRes.map((item, index) => {
|
||||
return `<div key=${index}>${item.message}</div>`;
|
||||
});
|
||||
}
|
||||
let html = `
|
||||
<div>
|
||||
<div class="row case-report">
|
||||
<div class="col-3 case-report-title">验证结果</div>
|
||||
<div class="col-21">
|
||||
${validRes}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
`;
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
function baseHtml(index, name, path, status) {
|
||||
let html = `
|
||||
<div>
|
||||
<h2 id=${index}>${name}</h2>
|
||||
<h3>基本信息</h3>
|
||||
<div class="row case-report">
|
||||
<div class="col-3 case-report-title">Path</div>
|
||||
<div class="col-21">${path}</div>
|
||||
</div>
|
||||
<div class="row case-report">
|
||||
<div class="col-3 case-report-title">Status</div>
|
||||
<div class="col-21">${status}</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
function leftHtml(index, name, code) {
|
||||
let html = `
|
||||
<div class="list-content">
|
||||
<a class="list" href="#${index}">${name}</a>
|
||||
${codeHtml(code)}
|
||||
</div>
|
||||
`;
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
function codeHtml(code) {
|
||||
let codeHtml = ``;
|
||||
switch (code) {
|
||||
case 0:
|
||||
codeHtml += `<div title="验证通过" class="status status-ok"><i class="icon icon-check-circle"></i></div>`;
|
||||
break;
|
||||
case 400:
|
||||
codeHtml += `<div title="请求异常" class="status status-ko"><i class="icon icon-close-circle"></i></div>`;
|
||||
break;
|
||||
case 1:
|
||||
codeHtml += `<div title="验证失败" class="status status-warning"><i class="icon icon-warning-circle"></i></div>`;
|
||||
break;
|
||||
default:
|
||||
codeHtml += `<div title="验证通过" class="status status-warning"><i class="icon icon-warning-circle"></i></div>`;
|
||||
break;
|
||||
}
|
||||
return codeHtml;
|
||||
}
|
||||
33
server/utils/sandbox.js
Normal file
33
server/utils/sandbox.js
Normal file
@@ -0,0 +1,33 @@
|
||||
const Safeify = require('safeify').default;
|
||||
|
||||
module.exports = async function sandboxFn(context, script, autoTest) {
|
||||
// 创建 safeify 实例
|
||||
const safeVm = new Safeify({
|
||||
timeout: 3000, //超时时间
|
||||
asyncTimeout: 10000, //包含异步操作的超时时间
|
||||
unrestricted: true,
|
||||
quantity: 4, //沙箱进程数量,默认同 CPU 核数
|
||||
memoryQuota: 500, //沙箱最大能使用的内存(单位 m),默认 500m
|
||||
cpuQuota: 0.5, //沙箱的 cpu 资源配额(百分比),默认 50%
|
||||
unsafe: {
|
||||
modules: {
|
||||
assert: 'assert',
|
||||
mockjs: 'mockjs'
|
||||
}
|
||||
}
|
||||
})
|
||||
safeVm.preset('const assert = require("assert");const Mock = require("mockjs"); const Random = Mock.Random;');
|
||||
|
||||
// 执行动态代码·
|
||||
if (autoTest) {
|
||||
script += "\n return {}";
|
||||
} else {
|
||||
script += "\n return {mockJson, resHeader, httpCode, delay}";
|
||||
}
|
||||
|
||||
const result = await safeVm.run(script, context)
|
||||
|
||||
// 释放资源
|
||||
safeVm.destroy()
|
||||
return result
|
||||
}
|
||||
28
server/utils/storage.js
Normal file
28
server/utils/storage.js
Normal file
@@ -0,0 +1,28 @@
|
||||
module.exports = function storageCreator(id) {
|
||||
const storageModel = require('../models/storage.js');
|
||||
const yapi = require('../yapi.js');
|
||||
const defaultData = {}
|
||||
return {
|
||||
getItem: async (name = '') => {
|
||||
let inst = yapi.getInst(storageModel);
|
||||
let data = await inst.get(id);
|
||||
data = data || defaultData;
|
||||
if (name) return data[name];
|
||||
return data;
|
||||
},
|
||||
setItem: async (name, value) => {
|
||||
let inst = yapi.getInst(storageModel);
|
||||
let curData = await inst.get(id);
|
||||
let data = curData || defaultData;
|
||||
let result;
|
||||
data[name] = value;
|
||||
if(!curData){
|
||||
result = await inst.save(id, data, true)
|
||||
}else{
|
||||
result = await inst.save(id, data, false)
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
67
server/utils/token.js
Normal file
67
server/utils/token.js
Normal file
@@ -0,0 +1,67 @@
|
||||
const yapi = require('../yapi')
|
||||
|
||||
const crypto = require('crypto');
|
||||
|
||||
/*
|
||||
下面是使用加密算法
|
||||
*/
|
||||
|
||||
// 创建加密算法
|
||||
const aseEncode = function(data, password) {
|
||||
|
||||
// 如下方法使用指定的算法与密码来创建cipher对象
|
||||
const cipher = crypto.createCipher('aes192', password);
|
||||
|
||||
// 使用该对象的update方法来指定需要被加密的数据
|
||||
let crypted = cipher.update(data, 'utf-8', 'hex');
|
||||
|
||||
crypted += cipher.final('hex');
|
||||
|
||||
return crypted;
|
||||
};
|
||||
|
||||
// 创建解密算法
|
||||
const aseDecode = function(data, password) {
|
||||
/*
|
||||
该方法使用指定的算法与密码来创建 decipher对象, 第一个算法必须与加密数据时所使用的算法保持一致;
|
||||
第二个参数用于指定解密时所使用的密码,其参数值为一个二进制格式的字符串或一个Buffer对象,该密码同样必须与加密该数据时所使用的密码保持一致
|
||||
*/
|
||||
const decipher = crypto.createDecipher('aes192', password);
|
||||
|
||||
/*
|
||||
第一个参数为一个Buffer对象或一个字符串,用于指定需要被解密的数据
|
||||
第二个参数用于指定被解密数据所使用的编码格式,可指定的参数值为 'hex', 'binary', 'base64'等,
|
||||
第三个参数用于指定输出解密数据时使用的编码格式,可选参数值为 'utf-8', 'ascii' 或 'binary';
|
||||
*/
|
||||
let decrypted = decipher.update(data, 'hex', 'utf-8');
|
||||
|
||||
decrypted += decipher.final('utf-8');
|
||||
return decrypted;
|
||||
};
|
||||
|
||||
const defaultSalt = 'abcde';
|
||||
|
||||
exports.getToken = function getToken(token, uid){
|
||||
if(!token)throw new Error('token 不能为空')
|
||||
yapi.WEBCONFIG.passsalt = yapi.WEBCONFIG.passsalt || defaultSalt;
|
||||
return aseEncode(uid + '|' + token, yapi.WEBCONFIG.passsalt)
|
||||
}
|
||||
|
||||
exports.parseToken = function parseToken(token){
|
||||
if(!token)throw new Error('token 不能为空')
|
||||
yapi.WEBCONFIG.passsalt = yapi.WEBCONFIG.passsalt || defaultSalt;
|
||||
let tokens;
|
||||
try{
|
||||
tokens = aseDecode(token, yapi.WEBCONFIG.passsalt)
|
||||
}catch(e){}
|
||||
if(tokens && typeof tokens === 'string' && tokens.indexOf('|') > 0){
|
||||
tokens = tokens.split('|')
|
||||
return {
|
||||
uid: tokens[0],
|
||||
projectToken: tokens[1]
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
41
server/websocket.js
Normal file
41
server/websocket.js
Normal file
@@ -0,0 +1,41 @@
|
||||
const koaRouter = require('koa-router');
|
||||
const interfaceController = require('./controllers/interface.js');
|
||||
const yapi = require('./yapi.js');
|
||||
|
||||
const router = koaRouter();
|
||||
const { createAction } = require("./utils/commons.js")
|
||||
|
||||
let pluginsRouterPath = [];
|
||||
|
||||
|
||||
function addPluginRouter(config) {
|
||||
if (!config.path || !config.controller || !config.action) {
|
||||
throw new Error('Plugin Route config Error');
|
||||
}
|
||||
let method = config.method || 'GET';
|
||||
let routerPath = '/ws_plugin/' + config.path;
|
||||
if (pluginsRouterPath.indexOf(routerPath) > -1) {
|
||||
throw new Error('Plugin Route path conflict, please try rename the path')
|
||||
}
|
||||
pluginsRouterPath.push(routerPath);
|
||||
createAction(router, "/api", config.controller, config.action, routerPath, method, true);
|
||||
}
|
||||
|
||||
|
||||
function websocket(app) {
|
||||
createAction(router, "/api", interfaceController, "solveConflict", "/interface/solve_conflict", "get")
|
||||
|
||||
yapi.emitHookSync('add_ws_router', addPluginRouter);
|
||||
|
||||
|
||||
app.ws.use(router.routes())
|
||||
app.ws.use(router.allowedMethods());
|
||||
app.ws.use(function (ctx, next) {
|
||||
return ctx.websocket.send(JSON.stringify({
|
||||
errcode: 404,
|
||||
errmsg: 'No Fount.'
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = websocket
|
||||
56
server/yapi.js
Normal file
56
server/yapi.js
Normal file
@@ -0,0 +1,56 @@
|
||||
const path = require('path');
|
||||
const fs = require('fs-extra');
|
||||
const nodemailer = require('nodemailer');
|
||||
const config = require('../../config.json');
|
||||
|
||||
let insts = new Map();
|
||||
let mail;
|
||||
|
||||
const WEBROOT = path.resolve(__dirname, '..'); //路径
|
||||
const WEBROOT_SERVER = __dirname;
|
||||
const WEBROOT_RUNTIME = path.resolve(__dirname, '../..');
|
||||
const WEBROOT_LOG = path.join(WEBROOT_RUNTIME, 'log');
|
||||
const WEBCONFIG = config;
|
||||
|
||||
fs.ensureDirSync(WEBROOT_LOG);
|
||||
|
||||
if (WEBCONFIG.mail && WEBCONFIG.mail.enable) {
|
||||
mail = nodemailer.createTransport(WEBCONFIG.mail);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取一个model实例,如果不存在则创建一个新的返回
|
||||
* @param {*} m class
|
||||
* @example
|
||||
* yapi.getInst(groupModel, arg1, arg2)
|
||||
*/
|
||||
function getInst(m, ...args) {
|
||||
if (!insts.get(m)) {
|
||||
insts.set(m, new m(args));
|
||||
}
|
||||
return insts.get(m);
|
||||
}
|
||||
|
||||
function delInst(m) {
|
||||
try {
|
||||
insts.delete(m);
|
||||
} catch (err) {
|
||||
console.error(err); // eslint-disable-line
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
let r = {
|
||||
fs: fs,
|
||||
path: path,
|
||||
WEBROOT: WEBROOT,
|
||||
WEBROOT_SERVER: WEBROOT_SERVER,
|
||||
WEBROOT_RUNTIME: WEBROOT_RUNTIME,
|
||||
WEBROOT_LOG: WEBROOT_LOG,
|
||||
WEBCONFIG: WEBCONFIG,
|
||||
getInst: getInst,
|
||||
delInst: delInst,
|
||||
getInsts: insts
|
||||
};
|
||||
if (mail) r.mail = mail;
|
||||
module.exports = r;
|
||||
Reference in New Issue
Block a user