const interfaceModel = require('../models/interface.js'); const interfaceCatModel = require('../models/interfaceCat.js'); const interfaceCaseModel = require('../models/interfaceCase.js'); const followModel = require('../models/follow.js'); const groupModel = require('../models/group.js'); const _ = require('underscore'); const url = require('url'); const baseController = require('./base.js'); const yapi = require('../yapi.js'); const userModel = require('../models/user.js'); const projectModel = require('../models/project.js'); const jsondiffpatch = require('jsondiffpatch'); const formattersHtml = jsondiffpatch.formatters.html; const showDiffMsg = require('../../common/diff-view.js'); const mergeJsonSchema = require('../../common/mergeJsonSchema'); const fs = require('fs-extra'); const path = require('path'); // const annotatedCss = require("jsondiffpatch/public/formatters-styles/annotated.css"); // const htmlCss = require("jsondiffpatch/public/formatters-styles/html.css"); function handleHeaders(values){ let isfile = false, isHaveContentType = false; if (values.req_body_type === 'form') { values.req_body_form.forEach(item => { if (item.type === 'file') { isfile = true; } }); values.req_headers.map(item => { if (item.name === 'Content-Type') { item.value = isfile ? 'multipart/form-data' : 'application/x-www-form-urlencoded'; isHaveContentType = true; } }); if (isHaveContentType === false) { values.req_headers.unshift({ name: 'Content-Type', value: isfile ? 'multipart/form-data' : 'application/x-www-form-urlencoded' }); } } else if (values.req_body_type === 'json') { values.req_headers ? values.req_headers.map(item => { if (item.name === 'Content-Type') { item.value = 'application/json'; isHaveContentType = true; } }) : []; if (isHaveContentType === false) { values.req_headers = values.req_headers || []; values.req_headers.unshift({ name: 'Content-Type', value: 'application/json' }); } } } class interfaceController extends baseController { constructor(ctx) { super(ctx); this.Model = yapi.getInst(interfaceModel); this.catModel = yapi.getInst(interfaceCatModel); this.projectModel = yapi.getInst(projectModel); this.caseModel = yapi.getInst(interfaceCaseModel); this.followModel = yapi.getInst(followModel); this.userModel = yapi.getInst(userModel); this.groupModel = yapi.getInst(groupModel); const minLengthStringField = { type: 'string', minLength: 1 }; const addAndUpCommonField = { desc: 'string', status: 'string', req_query: [ { name: 'string', value: 'string', example: 'string', desc: 'string', required: 'string' } ], req_headers: [ { name: 'string', value: 'string', example: 'string', desc: 'string', required: 'string' } ], req_body_type: 'string', req_params: [ { name: 'string', example: 'string', desc: 'string' } ], req_body_form: [ { name: 'string', type: { type: 'string' }, example: 'string', desc: 'string', required: 'string' } ], req_body_other: 'string', res_body_type: 'string', res_body: 'string', custom_field_value: 'string', api_opened: 'boolean', req_body_is_json_schema: 'string', res_body_is_json_schema: 'string', markdown: 'string', tag: 'array' }; this.schemaMap = { add: Object.assign( { '*project_id': 'number', '*path': minLengthStringField, '*title': minLengthStringField, '*method': minLengthStringField, '*catid': 'number' }, addAndUpCommonField ), up: Object.assign( { '*id': 'number', project_id: 'number', path: minLengthStringField, title: minLengthStringField, method: minLengthStringField, catid: 'number', switch_notice: 'boolean', message: minLengthStringField }, addAndUpCommonField ), save: Object.assign( { project_id: 'number', catid: 'number', title: minLengthStringField, path: minLengthStringField, method: minLengthStringField, message: minLengthStringField, switch_notice: 'boolean', dataSync: 'string' }, addAndUpCommonField ) }; } /** * 添加项目分组 * @interface /interface/add * @method POST * @category interface * @foldnumber 10 * @param {Number} project_id 项目id,不能为空 * @param {String} title 接口标题,不能为空 * @param {String} path 接口请求路径,不能为空 * @param {String} method 请求方式 * @param {Array} [req_headers] 请求的header信息 * @param {String} [req_headers[].name] 请求的header信息名 * @param {String} [req_headers[].value] 请求的header信息值 * @param {Boolean} [req_headers[].required] 是否是必须,默认为否 * @param {String} [req_headers[].desc] header描述 * @param {String} [req_body_type] 请求参数方式,有["form", "json", "text", "xml"]四种 * @param {Array} [req_params] name, desc两个参数 * @param {Mixed} [req_body_form] 请求参数,如果请求方式是form,参数是Array数组,其他格式请求参数是字符串 * @param {String} [req_body_form[].name] 请求参数名 * @param {String} [req_body_form[].value] 请求参数值,可填写生成规则(mock)。如@email,随机生成一条email * @param {String} [req_body_form[].type] 请求参数类型,有["text", "file"]两种 * @param {String} [req_body_other] 非form类型的请求参数可保存到此字段 * @param {String} [res_body_type] 相应信息的数据格式,有["json", "text", "xml"]三种 * @param {String} [res_body] 响应信息,可填写任意字符串,如果res_body_type是json,则会调用mock功能 * @param {String} [desc] 接口描述 * @returns {Object} * @example ./api/interface/add.json */ async add(ctx) { let params = ctx.params; if (!this.$tokenAuth) { let auth = await this.checkAuth(params.project_id, 'project', 'edit'); if (!auth) { return (ctx.body = yapi.commons.resReturn(null, 40033, '没有权限')); } } params.method = params.method || 'GET'; params.res_body_is_json_schema = _.isUndefined(params.res_body_is_json_schema) ? false : params.res_body_is_json_schema; params.req_body_is_json_schema = _.isUndefined(params.req_body_is_json_schema) ? false : params.req_body_is_json_schema; params.method = params.method.toUpperCase(); params.req_params = params.req_params || []; params.res_body_type = params.res_body_type ? params.res_body_type.toLowerCase() : 'json'; let http_path = url.parse(params.path, true); if (!yapi.commons.verifyPath(http_path.pathname)) { return (ctx.body = yapi.commons.resReturn( null, 400, 'path第一位必需为 /, 只允许由 字母数字-/_:.! 组成' )); } handleHeaders(params) params.query_path = {}; params.query_path.path = http_path.pathname; params.query_path.params = []; Object.keys(http_path.query).forEach(item => { params.query_path.params.push({ name: item, value: http_path.query[item] }); }); let checkRepeat = await this.Model.checkRepeat(params.project_id, params.path, params.method); if (checkRepeat > 0) { return (ctx.body = yapi.commons.resReturn( null, 40022, '已存在的接口:' + params.path + '[' + params.method + ']' )); } let data = Object.assign(params, { uid: this.getUid(), add_time: yapi.commons.time(), up_time: yapi.commons.time() }); yapi.commons.handleVarPath(params.path, params.req_params); if (params.req_params.length > 0) { data.type = 'var'; data.req_params = params.req_params; } else { data.type = 'static'; } // 新建接口的人成为项目dev 如果不存在的话 // 命令行导入时无法获知导入接口人的信息,其uid 为 999999 let uid = this.getUid(); if (this.getRole() !== 'admin' && uid !== 999999) { let userdata = await yapi.commons.getUserdata(uid, 'dev'); // 检查一下是否有这个人 let check = await this.projectModel.checkMemberRepeat(params.project_id, uid); if (check === 0 && userdata) { await this.projectModel.addMember(params.project_id, [userdata]); } } let result = await this.Model.save(data); yapi.emitHook('interface_add', result).then(); this.catModel.get(params.catid).then(cate => { let username = this.getUsername(); let title = `${username} 为分类 ${cate.name} 添加了接口 ${data.title} `; yapi.commons.saveLog({ content: title, type: 'project', uid: this.getUid(), username: username, typeid: params.project_id }); this.projectModel.up(params.project_id, { up_time: new Date().getTime() }).then(); }); await this.autoAddTag(params); ctx.body = yapi.commons.resReturn(result); } /** * 保存接口数据,如果接口存在则更新数据,如果接口不存在则添加数据 * @interface /interface/save * @method post * @category interface * @foldnumber 10 * @param {Number} project_id 项目id,不能为空 * @param {String} title 接口标题,不能为空 * @param {String} path 接口请求路径,不能为空 * @param {String} method 请求方式 * @param {Array} [req_headers] 请求的header信息 * @param {String} [req_headers[].name] 请求的header信息名 * @param {String} [req_headers[].value] 请求的header信息值 * @param {Boolean} [req_headers[].required] 是否是必须,默认为否 * @param {String} [req_headers[].desc] header描述 * @param {String} [req_body_type] 请求参数方式,有["form", "json", "text", "xml"]四种 * @param {Array} [req_params] name, desc两个参数 * @param {Mixed} [req_body_form] 请求参数,如果请求方式是form,参数是Array数组,其他格式请求参数是字符串 * @param {String} [req_body_form[].name] 请求参数名 * @param {String} [req_body_form[].value] 请求参数值,可填写生成规则(mock)。如@email,随机生成一条email * @param {String} [req_body_form[].type] 请求参数类型,有["text", "file"]两种 * @param {String} [req_body_other] 非form类型的请求参数可保存到此字段 * @param {String} [res_body_type] 相应信息的数据格式,有["json", "text", "xml"]三种 * @param {String} [res_body] 响应信息,可填写任意字符串,如果res_body_type是json,则会调用mock功能 * @param {String} [desc] 接口描述 * @returns {Object} */ async save(ctx) { let params = ctx.params; if (!this.$tokenAuth) { let auth = await this.checkAuth(params.project_id, 'project', 'edit'); if (!auth) { return (ctx.body = yapi.commons.resReturn(null, 40033, '没有权限')); } } params.method = params.method || 'GET'; params.method = params.method.toUpperCase(); let http_path = url.parse(params.path, true); if (!yapi.commons.verifyPath(http_path.pathname)) { return (ctx.body = yapi.commons.resReturn( null, 400, 'path第一位必需为 /, 只允许由 字母数字-/_:.! 组成' )); } let result = await this.Model.getByPath(params.project_id, params.path, params.method, '_id res_body'); if (result.length > 0) { result.forEach(async item => { params.id = item._id; // console.log(this.schemaMap['up']) let validParams = Object.assign({}, params) let validResult = yapi.commons.validateParams(this.schemaMap['up'], validParams); if (validResult.valid) { let data = Object.assign({}, ctx); data.params = validParams; if(params.res_body_is_json_schema && params.dataSync === 'good'){ try{ let new_res_body = yapi.commons.json_parse(params.res_body) let old_res_body = yapi.commons.json_parse(item.res_body) data.params.res_body = JSON.stringify(mergeJsonSchema(old_res_body, new_res_body),null,2); }catch(err){} } await this.up(data); } else { return (ctx.body = yapi.commons.resReturn(null, 400, validResult.message)); } }); } else { let validResult = yapi.commons.validateParams(this.schemaMap['add'], params); if (validResult.valid) { let data = {}; data.params = params; await this.add(data); } else { return (ctx.body = yapi.commons.resReturn(null, 400, validResult.message)); } } ctx.body = yapi.commons.resReturn(result); // return ctx.body = yapi.commons.resReturn(null, 400, 'path第一位必需为 /, 只允许由 字母数字-/_:.! 组成'); } async autoAddTag(params) { //检查是否提交了目前不存在的tag let tags = params.tag; if (tags && Array.isArray(tags) && tags.length > 0) { let projectData = await this.projectModel.get(params.project_id); let tagsInProject = projectData.tag; let needUpdate = false; if (tagsInProject && Array.isArray(tagsInProject) && tagsInProject.length > 0) { tags.forEach(tag => { if (!_.find(tagsInProject, item => { return item.name === tag; })) {//tag不存在 needUpdate = true; tagsInProject.push({ name: tag, desc: tag }); } }); } else { needUpdate = true tagsInProject = [] tags.forEach(tag => { tagsInProject.push({ name: tag, desc: tag }); }); } if (needUpdate) {//需要更新tag let data = { tag: tagsInProject, up_time: yapi.commons.time() }; await this.projectModel.up(params.project_id, data); } } } /** * 获取项目分组 * @interface /interface/get * @method GET * @category interface * @foldnumber 10 * @param {Number} id 接口id,不能为空 * @returns {Object} * @example ./api/interface/get.json */ async get(ctx) { let params = ctx.params; if (!params.id) { return (ctx.body = yapi.commons.resReturn(null, 400, '接口id不能为空')); } try { let result = await this.Model.get(params.id); if(this.$tokenAuth){ if(params.project_id !== result.project_id){ ctx.body = yapi.commons.resReturn(null, 400, 'token有误') return; } } // console.log('result', result); if (!result) { return (ctx.body = yapi.commons.resReturn(null, 490, '不存在的')); } let userinfo = await this.userModel.findById(result.uid); let project = await this.projectModel.getBaseInfo(result.project_id); if (project.project_type === 'private') { if ((await this.checkAuth(project._id, 'project', 'view')) !== true) { return (ctx.body = yapi.commons.resReturn(null, 406, '没有权限')); } } yapi.emitHook('interface_get', result).then(); result = result.toObject(); if (userinfo) { result.username = userinfo.username; } ctx.body = yapi.commons.resReturn(result); } catch (e) { ctx.body = yapi.commons.resReturn(null, 402, e.message); } } /** * 接口列表 * @interface /interface/list * @method GET * @category interface * @foldnumber 10 * @param {Number} project_id 项目id,不能为空 * @param {Number} page 当前页 * @param {Number} limit 每一页限制条数 * @returns {Object} * @example ./api/interface/list.json */ async list(ctx) { let project_id = ctx.params.project_id; let page = ctx.request.query.page || 1, limit = ctx.request.query.limit || 10; let status = ctx.request.query.status, tag = ctx.request.query.tag; let project = await this.projectModel.getBaseInfo(project_id); if (!project) { return (ctx.body = yapi.commons.resReturn(null, 407, '不存在的项目')); } if (project.project_type === 'private') { if ((await this.checkAuth(project._id, 'project', 'view')) !== true) { return (ctx.body = yapi.commons.resReturn(null, 406, '没有权限')); } } if (!project_id) { return (ctx.body = yapi.commons.resReturn(null, 400, '项目id不能为空')); } try { let result, count; if (limit === 'all') { result = await this.Model.list(project_id); count = await this.Model.listCount({project_id}); } else { let option = {project_id}; if (status) { if (Array.isArray(status)) { option.status = {"$in": status}; } else { option.status = status; } } if (tag) { if (Array.isArray(tag)) { option.tag = {"$in": tag}; } else { option.tag = tag; } } result = await this.Model.listByOptionWithPage(option, page, limit); count = await this.Model.listCount(option); } ctx.body = yapi.commons.resReturn({ count: count, total: Math.ceil(count / limit), list: result }); yapi.emitHook('interface_list', result).then(); } catch (err) { ctx.body = yapi.commons.resReturn(null, 402, err.message); } } async downloadCrx(ctx) { let filename = 'crossRequest.zip'; let dataBuffer = yapi.fs.readFileSync( yapi.path.join(yapi.WEBROOT, 'static/attachment/cross-request.zip') ); ctx.set('Content-disposition', 'attachment; filename=' + filename); ctx.set('Content-Type', 'application/zip'); ctx.body = dataBuffer; } async listByCat(ctx) { let catid = ctx.request.query.catid; let page = ctx.request.query.page || 1, limit = ctx.request.query.limit || 10; let status = ctx.request.query.status, tag = ctx.request.query.tag; if (!catid) { return (ctx.body = yapi.commons.resReturn(null, 400, 'catid不能为空')); } try { let catdata = await this.catModel.get(catid); let project = await this.projectModel.getBaseInfo(catdata.project_id); if (project.project_type === 'private') { if ((await this.checkAuth(project._id, 'project', 'view')) !== true) { return (ctx.body = yapi.commons.resReturn(null, 406, '没有权限')); } } let option = {catid} if (status) { if (Array.isArray(status)) { option.status = {"$in": status}; } else { option.status = status; } } if (tag) { if (Array.isArray(tag)) { option.tag = {"$in": tag}; } else { option.tag = tag; } } let result = await this.Model.listByOptionWithPage(option, page, limit); let count = await this.Model.listCount(option); ctx.body = yapi.commons.resReturn({ count: count, total: Math.ceil(count / limit), list: result }); } catch (err) { ctx.body = yapi.commons.resReturn(null, 402, err.message + '1'); } } async listByMenu(ctx) { let project_id = ctx.params.project_id; if (!project_id) { return (ctx.body = yapi.commons.resReturn(null, 400, '项目id不能为空')); } let project = await this.projectModel.getBaseInfo(project_id); if (!project) { return (ctx.body = yapi.commons.resReturn(null, 406, '不存在的项目')); } if (project.project_type === 'private') { if ((await this.checkAuth(project._id, 'project', 'view')) !== true) { return (ctx.body = yapi.commons.resReturn(null, 406, '没有权限')); } } try { let result = await this.catModel.list(project_id), newResult = []; for (let i = 0, item, list; i < result.length; i++) { item = result[i].toObject(); list = await this.Model.listByCatid(item._id); for (let j = 0; j < list.length; j++) { list[j] = list[j].toObject(); } item.list = list; newResult[i] = item; } ctx.body = yapi.commons.resReturn(newResult); } catch (err) { ctx.body = yapi.commons.resReturn(null, 402, err.message); } } /** * 编辑接口 * @interface /interface/up * @method POST * @category interface * @foldnumber 10 * @param {Number} id 接口id,不能为空 * @param {String} [path] 接口请求路径 * @param {String} [method] 请求方式 * @param {Array} [req_headers] 请求的header信息 * @param {String} [req_headers[].name] 请求的header信息名 * @param {String} [req_headers[].value] 请求的header信息值 * @param {Boolean} [req_headers[].required] 是否是必须,默认为否 * @param {String} [req_headers[].desc] header描述 * @param {String} [req_body_type] 请求参数方式,有["form", "json", "text", "xml"]四种 * @param {Mixed} [req_body_form] 请求参数,如果请求方式是form,参数是Array数组,其他格式请求参数是字符串 * @param {String} [req_body_form[].name] 请求参数名 * @param {String} [req_body_form[].value] 请求参数值,可填写生成规则(mock)。如@email,随机生成一条email * @param {String} [req_body_form[].type] 请求参数类型,有["text", "file"]两种 * @param {String} [req_body_other] 非form类型的请求参数可保存到此字段 * @param {String} [res_body_type] 相应信息的数据格式,有["json", "text", "xml"]三种 * @param {String} [res_body] 响应信息,可填写任意字符串,如果res_body_type是json,则会调用mock功能 * @param {String} [desc] 接口描述 * @returns {Object} * @example ./api/interface/up.json */ async up(ctx) { let params = ctx.params; if (!_.isUndefined(params.method)) { params.method = params.method || 'GET'; params.method = params.method.toUpperCase(); } let id = params.id; params.message = params.message || ''; params.message = params.message.replace(/\n/g, '
'); // params.res_body_is_json_schema = _.isUndefined (params.res_body_is_json_schema) ? true : params.res_body_is_json_schema; // params.req_body_is_json_schema = _.isUndefined(params.req_body_is_json_schema) ? true : params.req_body_is_json_schema; handleHeaders(params) let interfaceData = await this.Model.get(id); if (!interfaceData) { return (ctx.body = yapi.commons.resReturn(null, 400, '不存在的接口')); } if (!this.$tokenAuth) { let auth = await this.checkAuth(interfaceData.project_id, 'project', 'edit'); if (!auth) { return (ctx.body = yapi.commons.resReturn(null, 400, '没有权限')); } } let data = Object.assign( { up_time: yapi.commons.time() }, params ); if (params.path) { let http_path; http_path = url.parse(params.path, true); if (!yapi.commons.verifyPath(http_path.pathname)) { return (ctx.body = yapi.commons.resReturn( null, 400, 'path第一位必需为 /, 只允许由 字母数字-/_:.! 组成' )); } params.query_path = {}; params.query_path.path = http_path.pathname; params.query_path.params = []; Object.keys(http_path.query).forEach(item => { params.query_path.params.push({ name: item, value: http_path.query[item] }); }); data.query_path = params.query_path; } if ( params.path && (params.path !== interfaceData.path || params.method !== interfaceData.method) ) { let checkRepeat = await this.Model.checkRepeat( interfaceData.project_id, params.path, params.method ); if (checkRepeat > 0) { return (ctx.body = yapi.commons.resReturn( null, 401, '已存在的接口:' + params.path + '[' + params.method + ']' )); } } if (!_.isUndefined(data.req_params)) { if (Array.isArray(data.req_params) && data.req_params.length > 0) { data.type = 'var'; } else { data.type = 'static'; data.req_params = []; } } let result = await this.Model.up(id, data); let username = this.getUsername(); let CurrentInterfaceData = await this.Model.get(id); let logData = { interface_id: id, cat_id: data.catid, current: CurrentInterfaceData.toObject(), old: interfaceData.toObject() }; this.catModel.get(interfaceData.catid).then(cate => { let diffView2 = showDiffMsg(jsondiffpatch, formattersHtml, logData); if (diffView2.length <= 0) { return; // 没有变化时,不写日志 } yapi.commons.saveLog({ content: `${username} 更新了分类 ${cate.name} 下的接口 ${ interfaceData.title }

${params.message}

`, type: 'project', uid: this.getUid(), username: username, typeid: cate.project_id, data: logData }); }); this.projectModel.up(interfaceData.project_id, { up_time: new Date().getTime() }).then(); if (params.switch_notice === true) { let diffView = showDiffMsg(jsondiffpatch, formattersHtml, logData); let annotatedCss = fs.readFileSync( path.resolve( yapi.WEBROOT, 'node_modules/jsondiffpatch/dist/formatters-styles/annotated.css' ), 'utf8' ); let htmlCss = fs.readFileSync( path.resolve(yapi.WEBROOT, 'node_modules/jsondiffpatch/dist/formatters-styles/html.css'), 'utf8' ); let project = await this.projectModel.getBaseInfo(interfaceData.project_id); let interfaceUrl = `${ctx.request.origin}/project/${ interfaceData.project_id }/interface/api/${id}`; yapi.commons.sendNotice(interfaceData.project_id, { title: `${username} 更新了接口`, content: `

${username}更新了接口(${data.title})

项目名:${project.name}

修改用户: ${username}

接口名: ${data.title}

接口路径: [${data.method}]${data.path}

详细改动日志: ${this.diffHTML(diffView)}

` }); } yapi.emitHook('interface_update', id).then(); await this.autoAddTag(params); ctx.body = yapi.commons.resReturn(result); return 1; } diffHTML(html) { if (html.length === 0) { return `没有改动,该操作未改动Api数据`; } return html.map(item => { return `

${item.title}

${item.content}
`; }); } /** * 删除接口 * @interface /interface/del * @method GET * @category interface * @foldnumber 10 * @param {Number} id 接口id,不能为空 * @returns {Object} * @example ./api/interface/del.json */ async del(ctx) { try { let id = ctx.request.body.id; if (!id) { return (ctx.body = yapi.commons.resReturn(null, 400, '接口id不能为空')); } let data = await this.Model.get(id); if (data.uid != this.getUid()) { let auth = await this.checkAuth(data.project_id, 'project', 'danger'); if (!auth) { return (ctx.body = yapi.commons.resReturn(null, 400, '没有权限')); } } // let inter = await this.Model.get(id); let result = await this.Model.del(id); yapi.emitHook('interface_del', id).then(); await this.caseModel.delByInterfaceId(id); let username = this.getUsername(); this.catModel.get(data.catid).then(cate => { yapi.commons.saveLog({ content: `${username} 删除了分类 ${cate.name} 下的接口 "${data.title}"`, type: 'project', uid: this.getUid(), username: username, typeid: cate.project_id }); }); this.projectModel.up(data.project_id, { up_time: new Date().getTime() }).then(); ctx.body = yapi.commons.resReturn(result); } catch (err) { ctx.body = yapi.commons.resReturn(null, 402, err.message); } } // 处理编辑冲突 async solveConflict(ctx) { try { let id = parseInt(ctx.query.id, 10), result, userInst, userinfo, data; if (!id) { return ctx.websocket.send('id 参数有误'); } result = await this.Model.get(id); if (result.edit_uid !== 0 && result.edit_uid !== this.getUid()) { userInst = yapi.getInst(userModel); userinfo = await userInst.findById(result.edit_uid); data = { errno: result.edit_uid, data: { uid: result.edit_uid, username: userinfo.username } }; } else { this.Model.upEditUid(id, this.getUid()).then(); data = { errno: 0, data: result }; } ctx.websocket.send(JSON.stringify(data)); ctx.websocket.on('close', () => { this.Model.upEditUid(id, 0).then(); }); } catch (err) { yapi.commons.log(err, 'error'); } } async addCat(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 (!this.$tokenAuth) { let auth = await this.checkAuth(params.project_id, 'project', 'edit'); if (!auth) { return (ctx.body = yapi.commons.resReturn(null, 400, '没有权限')); } } if (!params.name) { return (ctx.body = yapi.commons.resReturn(null, 400, '名称不能为空')); } let result = await this.catModel.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: `${username} 添加了分类 ${params.name}`, type: 'project', uid: this.getUid(), username: username, typeid: params.project_id }); ctx.body = yapi.commons.resReturn(result); } catch (e) { ctx.body = yapi.commons.resReturn(null, 402, e.message); } } async upCat(ctx) { try { let params = ctx.request.body; let username = this.getUsername(); let cate = await this.catModel.get(params.catid); let auth = await this.checkAuth(cate.project_id, 'project', 'edit'); if (!auth) { return (ctx.body = yapi.commons.resReturn(null, 400, '没有权限')); } let result = await this.catModel.up(params.catid, { name: params.name, desc: params.desc, up_time: yapi.commons.time() }); yapi.commons.saveLog({ content: `${username} 更新了分类 ${cate.name}`, type: 'project', uid: this.getUid(), username: username, typeid: cate.project_id }); ctx.body = yapi.commons.resReturn(result); } catch (e) { ctx.body = yapi.commons.resReturn(null, 400, e.message); } } async delCat(ctx) { try { let id = ctx.request.body.catid; let catData = await this.catModel.get(id); if (!catData) { ctx.body = yapi.commons.resReturn(null, 400, '不存在的分类'); } if (catData.uid !== this.getUid()) { let auth = await this.checkAuth(catData.project_id, 'project', 'danger'); if (!auth) { return (ctx.body = yapi.commons.resReturn(null, 400, '没有权限')); } } let username = this.getUsername(); yapi.commons.saveLog({ content: `${username} 删除了分类 "${ catData.name }" 及该分类下的接口`, type: 'project', uid: this.getUid(), username: username, typeid: catData.project_id }); let interfaceData = await this.Model.listByCatid(id); interfaceData.forEach(async item => { try { yapi.emitHook('interface_del', item._id).then(); await this.caseModel.delByInterfaceId(item._id); } catch (e) { yapi.commons.log(e.message, 'error'); } }); await this.catModel.del(id); let r = await this.Model.delByCatid(id); return (ctx.body = yapi.commons.resReturn(r)); } catch (e) { yapi.commons.resReturn(null, 400, e.message); } } /** * 获取分类列表 * @interface /interface/getCatMenu * @method GET * @category interface * @foldnumber 10 * @param {Number} project_id 项目id,不能为空 * @returns {Object} * @example ./api/interface/getCatMenu */ async getCatMenu(ctx) { let project_id = ctx.params.project_id; if (!project_id || isNaN(project_id)) { return (ctx.body = yapi.commons.resReturn(null, 400, '项目id不能为空')); } try { let project = await this.projectModel.getBaseInfo(project_id); if (project.project_type === 'private') { if ((await this.checkAuth(project._id, 'project', 'edit')) !== true) { return (ctx.body = yapi.commons.resReturn(null, 406, '没有权限')); } } let res = await this.catModel.list(project_id); return (ctx.body = yapi.commons.resReturn(res)); } catch (e) { yapi.commons.resReturn(null, 400, e.message); } } /** * 获取自定义接口字段数据 * @interface /interface/get_custom_field * @method GET * @category interface * @foldnumber 10 * @param {String} app_code = '111' * @returns {Object} * */ async getCustomField(ctx) { let params = ctx.request.query; if (Object.keys(params).length !== 1) { return (ctx.body = yapi.commons.resReturn(null, 400, '参数数量错误')); } let customFieldName = Object.keys(params)[0]; let customFieldValue = params[customFieldName]; try { // 查找有customFieldName的分组(group) let groups = await this.groupModel.getcustomFieldName(customFieldName); if (groups.length === 0) { return (ctx.body = yapi.commons.resReturn(null, 404, '没有找到对应自定义接口')); } // 在每个分组(group)下查找对应project的id值 let interfaces = []; for (let i = 0; i < groups.length; i++) { let projects = await this.projectModel.list(groups[i]._id); // 在每个项目(project)中查找interface下的custom_field_value for (let j = 0; j < projects.length; j++) { let data = {}; let inter = await this.Model.getcustomFieldValue(projects[j]._id, customFieldValue); if (inter.length > 0) { data.project_name = projects[j].name; data.project_id = projects[j]._id; inter = inter.map((item, i) => { item = inter[i] = inter[i].toObject(); item.res_body = yapi.commons.json_parse(item.res_body); item.req_body_other = yapi.commons.json_parse(item.req_body_other); return item; }); data.list = inter; interfaces.push(data); } } } return (ctx.body = yapi.commons.resReturn(interfaces)); } catch (e) { yapi.commons.resReturn(null, 400, e.message); } } requiredSort(params) { return params.sort((item1, item2) => { return item2.required - item1.required; }); } /** * 更新多个接口case index * @interface /interface/up_index * @method POST * @category col * @foldnumber 10 * @param {Array} [id, index] * @returns {Object} * @example */ async upIndex(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.Model.upIndex(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); } } /** * 更新多个接口cat index * @interface /interface/up_cat_index * @method POST * @category col * @foldnumber 10 * @param {Array} [id, index] * @returns {Object} * @example */ async upCatIndex(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.catModel.upCatIndex(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); } } async schema2json(ctx) { let schema = ctx.request.body.schema; let required = ctx.request.body.required; let res = yapi.commons.schemaToJson(schema, { alwaysFakeOptionals: _.isUndefined(required) ? true : required }); // console.log('res',res) return (ctx.body = res); } // 获取开放接口数据 async listByOpen(ctx) { let project_id = ctx.request.query.project_id; if (!project_id) { return (ctx.body = yapi.commons.resReturn(null, 400, '项目id不能为空')); } let project = await this.projectModel.getBaseInfo(project_id); if (!project) { return (ctx.body = yapi.commons.resReturn(null, 406, '不存在的项目')); } if (project.project_type === 'private') { if ((await this.checkAuth(project._id, 'project', 'view')) !== true) { return (ctx.body = yapi.commons.resReturn(null, 406, '没有权限')); } } let basepath = project.basepath; try { let result = await this.catModel.list(project_id), newResult = []; for (let i = 0, item, list; i < result.length; i++) { item = result[i].toObject(); list = await this.Model.listByInterStatus(item._id, 'open'); for (let j = 0; j < list.length; j++) { list[j] = list[j].toObject(); list[j].basepath = basepath; } newResult = [].concat(newResult, list); } ctx.body = yapi.commons.resReturn(newResult); } catch (err) { ctx.body = yapi.commons.resReturn(null, 402, err.message); } } } module.exports = interfaceController;