2
0
mirror of https://github.com/YMFE/yapi.git synced 2025-04-12 15:10:23 +08:00

fix: log保存封装

This commit is contained in:
qitmac000249 2017-08-16 17:13:14 +08:00
parent ed66c3c0fa
commit 6adbdc3516
41 changed files with 2073 additions and 880 deletions

@ -0,0 +1,69 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { AutoComplete } from 'antd';
import axios from 'axios'
class UsernameAutoComplete extends Component {
constructor(props) {
super(props);
this.state = {
dataSource: [],
uid: 0,
username: ''
}
}
static propTypes = {
callbackState: PropTypes.func
}
onSelect = (userName) => {
this.state.dataSource.forEach((item) => {
if (item.username === userName) {
// 设置本组件 state
this.setState({
uid: item.id,
username: item.username
});
// 回调 将当前选中的uid和username回调给父组件
this.props.callbackState({
uid: item.id,
username: item.username
})
}
});
}
handleSearch = (value) => {
const params = { q: value}
axios.get('/api/user/search', { params })
.then(data => {
const userList = []
data = data.data.data
if (data) {
data.forEach( v => userList.push({
username: v.username,
id: v.uid
}));
this.setState({
dataSource: userList
})
}
})
}
render () {
return (
<AutoComplete
dataSource={this.state.dataSource.map(i => i.username)}
style={{ width: '100%' }}
onSelect={this.onSelect}
onSearch={this.handleSearch}
placeholder="请输入用户名"
size="large"
/>
)
}
}
export default UsernameAutoComplete;

@ -1,14 +1,15 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import { Button, Icon, Popconfirm, Modal, Input, message, Menu, Row, Col } from 'antd'
import { Button, Icon, Modal,Alert, Input, message, Menu, Row, Col } from 'antd'
import { autobind } from 'core-decorators';
import axios from 'axios';
import { withRouter } from 'react-router';
const { TextArea } = Input;
const Search = Input.Search;
const TYPE_EDIT = 'edit';
const confirm = Modal.confirm;
import UsernameAutoComplete from '../../../components/UsernameAutoComplete/UsernameAutoComplete.js';
import {
fetchGroupList,
setCurrGroup,
@ -50,7 +51,8 @@ export default class GroupList extends Component {
newGroupDesc: '',
currGroupName: '',
currGroupDesc: '',
groupList: []
groupList: [],
owner_uid: 0
}
constructor(props) {
@ -105,8 +107,8 @@ export default class GroupList extends Component {
}
@autobind
async addGroup() {
const { newGroupName: group_name, newGroupDesc: group_desc } = this.state;
const res = await axios.post('/api/group/add', { group_name, group_desc })
const { newGroupName: group_name, newGroupDesc: group_desc, owner_uid } = this.state;
const res = await axios.post('/api/group/add', { group_name, group_desc, owner_uid })
if (!res.data.errcode) {
this.setState({
addGroupModalVisible: false
@ -155,6 +157,39 @@ export default class GroupList extends Component {
this.props.history.replace(`${currGroup._id}`);
}
@autobind
onUserSelect(childState) {
this.setState({
owner_uid: childState.uid
})
}
showConfirm =()=> {
let that = this;
confirm({
title: "确认删除"+that.props.currGroup.group_name+"分组吗?",
content: <div style={{marginTop:'10px', fontSize: '12px', lineHeight: '25px'}}>
<Alert message="警告:此操作非常危险,会删除该分组下面所有项目和接口,并且无法恢复!" type="warning" />
<div style={{marginTop: '15px'}}><b>请输入分组名称确认此操作:</b><input id="group_name" /></div>
</div>,
onOk() {
let groupName = document.getElementById('group_name').value;
if(that.props.currGroup.group_name !== groupName){
message.error('分组名称有误')
return new Promise((resolve, reject)=>{
reject('error')
})
}else{
that.deleteGroup()
}
},
iconType: 'delete',
onCancel() { }
});
}
@autobind
async deleteGroup() {
const self = this;
@ -163,7 +198,7 @@ export default class GroupList extends Component {
if (res.data.errcode) {
message.error(res.data.errmsg);
} else {
message.success('删除成功');
message.success('删除成功')
await self.props.fetchGroupList()
const currGroup = self.props.groupList[0] || { group_name: '', group_desc: '' };
self.setState({groupList: self.props.groupList});
@ -185,9 +220,8 @@ export default class GroupList extends Component {
render () {
const { currGroup } = this.props;
const delmark = <Icon className="edit-group" type="edit" title="编辑分组" onClick={() => this.showModal(TYPE_EDIT)}/>
const editmark = (<Popconfirm title={`你确定要删除分组 ${currGroup.group_name}`} onConfirm={this.deleteGroup}>
<Icon className="delete-group" type="delete" title="删除分组"/>
</Popconfirm>)
const editmark = <Icon className="delete-group" onClick={()=> {this.showConfirm()}} type="delete" title="删除分组"/>
return (
@ -249,6 +283,12 @@ export default class GroupList extends Component {
<TextArea rows = {3} placeholder="请输入分组描述" onChange={this.inputNewGroupDesc}></TextArea>
</Col>
</Row>
<Row gutter={6} className="modal-input">
<Col span="5"><div className="label">组长</div></Col>
<Col span="15">
<UsernameAutoComplete callbackState={this.onUserSelect} />
</Col>
</Row>
</Modal>
<Modal
title="编辑分组"

@ -1,12 +1,197 @@
import React, { Component } from 'react';
import { Table } from 'antd';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { Select, Button, Modal, Row, Col, message } from 'antd';
import './MemberList.scss';
import { autobind } from 'core-decorators';
import { fetchGroupMemberList, fetchGroupMsg, addMember } from '../../../reducer/modules/group.js'
import UsernameAutoComplete from '../../../components/UsernameAutoComplete/UsernameAutoComplete.js';
const Option = Select.Option;
import './MemberList.scss'
const arrayAddKey = (arr) => {
return arr.map((item, index) => {
return {
...item,
key: index
}
});
}
@connect(
state => {
return {
currGroup: state.group.currGroup,
uid: state.user.uid,
role: state.group.role
}
},
{
fetchGroupMemberList,
fetchGroupMsg,
addMember
}
)
class MemberList extends Component {
constructor(props) {
super(props);
this.state = {
userInfo: [],
role: '',
visible: false,
dataSource: [],
inputUid: 0,
inputRole: 'dev'
}
}
static propTypes = {
currGroup: PropTypes.object,
uid: PropTypes.number,
fetchGroupMemberList: PropTypes.func,
fetchGroupMsg: PropTypes.func,
addMember: PropTypes.func,
role: PropTypes.string
}
@autobind
handleChange(value) {
console.log(`selected ${value}`);
}
showModal = () => {
this.setState({
visible: true
});
}
@autobind
handleOk() {
console.log(this.props.currGroup._id, this.state.inputUid);
this.props.addMember({
id: this.props.currGroup._id,
member_uid: this.state.inputUid
}).then((res) => {
console.log(res);
if (!res.payload.data.errcode) {
message.success('添加成功!');
// 添加成功后重新获取分组成员列表
this.props.fetchGroupMemberList(this.props.currGroup._id).then((res) => {
this.setState({
userInfo: arrayAddKey(res.payload.data.data),
visible: false
});
});
}
});
}
@autobind
handleCancel() {
// 取消模态框的时候重置模态框中的值
this.setState({
visible: false
});
}
@autobind
changeMemberRole(e) {
console.log(e);
}
componentWillReceiveProps(nextProps) {
if (this.props.currGroup !== nextProps.currGroup) {
this.props.fetchGroupMemberList(nextProps.currGroup._id).then((res) => {
this.setState({
userInfo: arrayAddKey(res.payload.data.data)
});
});
this.props.fetchGroupMsg(nextProps.currGroup._id).then((res) => {
this.setState({
role: res.payload.data.data.role
});
})
}
}
componentDidMount() {
const currGroupId = this.props.currGroup._id;
this.props.fetchGroupMsg(currGroupId).then((res) => {
this.setState({
role: res.payload.data.data.role
});
})
this.props.fetchGroupMemberList(currGroupId).then((res) => {
this.setState({
userInfo: arrayAddKey(res.payload.data.data)
});
});
}
@autobind
onUserSelect(childState) {
console.log(childState);
this.setState({
inputUid: childState.uid
})
}
render() {
console.log(this.state);
const columns = [{
title: this.props.currGroup.group_name + ' 分组成员 ('+this.state.userInfo.length + ') 人',
dataIndex: 'username',
key: 'username',
render: (text, record) => {
return (<div className="m-user">
<img src={location.protocol + '//' + location.host + '/api/user/avatar?uid=' + record.uid} className="m-user-img" />
<p className="m-user-name">{text}</p>
</div>);
}
}, {
title: (this.state.role === 'owner' || this.state.role === 'admin') ? <div className="btn-container"><Button className="btn" type="primary" icon="plus" onClick={this.showModal}>添加成员</Button></div> : '',
key: 'action',
className: 'member-opration',
render: (text, record) => {
if (this.state.role === 'owner' || this.state.role === 'admin') {
return (
<div>
<Select defaultValue={record.role} className="select" onChange={this.handleChange}>
<Option value="owner">组长</Option>
<Option value="dev">开发者</Option>
</Select>
<Button type="danger" icon="minus" className="btn-danger" />
</div>
)
} else {
return '';
}
}
}];
return (
<div className="m-panel">
<Modal
title="添加成员"
visible={this.state.visible}
onOk={this.handleOk}
onCancel={this.handleCancel}
>
<Row gutter={6} className="modal-input">
<Col span="5"><div className="label">用户名: </div></Col>
<Col span="15">
<UsernameAutoComplete callbackState={this.onUserSelect} />
</Col>
</Row>
<Row gutter={6} className="modal-input">
<Col span="5"><div className="label">权限: </div></Col>
<Col span="15">
<Select size="large" defaultValue="dev" className="select" onChange={this.changeMemberRole}>
<Option value="owner">组长</Option>
<Option value="dev">开发者</Option>
</Select>
</Col>
</Row>
</Modal>
<Table columns={columns} dataSource={this.state.userInfo} pagination={false} />
</div>
);
}

@ -6,3 +6,42 @@
min-height: 5rem;
margin-top: 0;
}
.btn-container {
text-align: right;
}
.modal-input {
display: flex;
align-items: center;
margin-bottom: .24rem;
.label {
text-align: right;
}
.select {
width: 1.2rem;
}
}
.member-opration {
text-align: right;
.select {
width: 1.2rem;
}
.btn-danger {
margin-left: .08rem;
}
}
.m-user {
display: flex;
align-items: center;
.m-user-img {
width: .32rem;
height: .32rem;
border-radius: .04rem;
}
.m-user-name {
margin-left: 8px;
}
}

@ -42,7 +42,7 @@ class Interface extends Component {
<Tabs.TabPane tab="接口列表" key="list">
<InterfaceMenu projectId={this.props.match.params.id} />
</Tabs.TabPane>
<Tabs.TabPane tab="接口集合" key="col">
<Tabs.TabPane tab="接口集合" key="col" >
<InterfaceColMenu />
</Tabs.TabPane>
</Tabs>

@ -1,7 +1,7 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import { Button, Input, Select, Card, Alert, Spin, Icon, message, Collapse, Radio } from 'antd'
import { Button, Input, Select, Card, Alert, Spin, Icon, Collapse, Radio } from 'antd'
import { autobind } from 'core-decorators';
import crossRequest from 'cross-request';
import { withRouter } from 'react-router';
@ -24,11 +24,6 @@ const RadioGroup = Radio.Group;
state => ({
currInterface: state.inter.curdata,
currProject: state.project.currProject
// reqParams: state.addInterface.reqParams,
// method: state.addInterface.method,
// url: state.addInterface.url,
// seqGroup: state.addInterface.seqGroup,
// interfaceName: state.addInterface.interfaceName,
})
)
@withRouter
@ -38,11 +33,8 @@ export default class Run extends Component {
match: PropTypes.object,
currProject: PropTypes.object,
currInterface: PropTypes.object,
reqParams: PropTypes.string,
// method: PropTypes.string,
// url: PropTypes.string,
reqBody: PropTypes.string,
interfaceName: PropTypes.string
// seqGroup: PropTypes.array,
}
state = {
@ -50,12 +42,12 @@ export default class Run extends Component {
method: 'GET',
domains: [],
pathname: '',
query: {},
params: {},
paramsNotJson: false,
headers: {},
query: [],
bodyForm: [],
headers: [],
currDomain: '',
paramsType: 'from'
bodyType: '',
bodyOther: ''
}
constructor(props) {
@ -76,7 +68,16 @@ export default class Run extends Component {
const { currInterface, currProject } = props;
console.log('currInterface', currInterface)
console.log('currProject', currProject)
const { method, path: url, req_headers } = currInterface;
const {
method,
path: url,
req_headers = [],
req_body_type,
req_query = [],
req_params = [],
req_body_other = '',
req_body_form = []
} = currInterface;
const { prd_host, basepath, protocol, env } = currProject;
const pathname = (basepath + url).replace(/\/+/g, '/');
@ -85,64 +86,36 @@ export default class Run extends Component {
domains[item.name] = item.domain;
})
const query = [];
let params = [];
let reqParams = this.props.reqParams ? this.props.reqParams : '{}';
let paramsNotJson = false;
try {
reqParams = JSON.parse(reqParams);
// paramsNotJson = false;
} catch (e) {
// paramsNotJson = true;
reqParams = {};
message.error('请求参数不是 JSON 格式');
}
if (method === 'GET') {
Object.keys(reqParams).forEach(key => {
const value = typeof reqParams[key] === 'object' ? JSON.stringify(reqParams[key]) : reqParams[key].toString();
query.push({key, value})
})
} else if (method === 'POST') {
// params = reqParams;
Object.keys(reqParams).forEach(key => {
const value = typeof reqParams[key] === 'object' ? JSON.stringify(reqParams[key]) : reqParams[key].toString();
query.push({key, value, type: 'text'})
})
}
const headers = []
let contentTypeIndex = -1;
req_headers.forEach((headerItem, index) => {
if (headerItem.name) {
// TODO 'Content-Type' 排除大小写不同格式影响
if (headerItem.name === 'Content-Type'){
contentTypeIndex = index;
headerItem.value = headerItem.value || 'application/x-www-form-urlencoded';
}
headers.push({name: headerItem.name, value: headerItem.value});
let hasContentType = false;
req_headers.forEach(headerItem => {
// TODO 'Content-Type' 排除大小写不同格式影响
if (headerItem.name === 'Content-Type'){
hasContentType = true;
headerItem.value = headerItem.value || 'application/x-www-form-urlencoded';
}
})
if (contentTypeIndex === -1) {
headers.push({name: 'Content-Type', value: 'application/x-www-form-urlencoded'});
if (!hasContentType) {
req_headers.push({name: 'Content-Type', value: 'application/x-www-form-urlencoded'});
}
this.setState({
method,
domains,
pathParam: req_params.concat(),
pathname,
query,
params,
paramsNotJson,
headers,
query: req_query.concat(),
bodyForm: req_body_form.concat(),
headers: req_headers.concat(),
bodyOther: req_body_other,
currDomain: domains.prd,
loading: false,
paramsType: 'form'
bodyType: req_body_type || 'form',
loading: false
});
}
@autobind
requestInterface() {
const { headers, params, currDomain, method, pathname, query } = this.state;
reqRealInterface() {
const { headers, bodyForm, bodyOther, currDomain, method, pathname, query, bodyType } = this.state;
const urlObj = URL.parse(currDomain);
const href = URL.format({
@ -158,8 +131,8 @@ export default class Run extends Component {
url: href,
method,
headers: this.getHeadersObj(headers),
data: this.arrToObj(params),
files: this.getFiles(params),
data: bodyType === 'form' ? this.arrToObj(bodyForm) : bodyOther,
files: bodyType === 'form' ? this.getFiles(bodyForm) : {},
success: (res) => {
try {
res = JSON.parse(res)
@ -211,7 +184,7 @@ export default class Run extends Component {
setContentType(type) {
const headersObj = this.getHeadersObj(this.state.headers);
headersObj['Content-Type'] = type;
this.setState({headers: this.objToArr(headersObj, 'name')})
this.setState({headers: this.objToArr(headersObj)})
}
@autobind
@ -219,7 +192,7 @@ export default class Run extends Component {
const query = JSON.parse(JSON.stringify(this.state.query));
const v = e.target.value;
if (isKey) {
query[index].key = v;
query[index].name = v;
} else {
query[index].value = v;
}
@ -228,7 +201,7 @@ export default class Run extends Component {
@autobind
addQuery() {
const { query } = this.state;
this.setState({query: query.concat([{key: '', value: ''}])})
this.setState({query: query.concat([{name: '', value: ''}])})
}
@autobind
deleteQuery(index) {
@ -237,20 +210,51 @@ export default class Run extends Component {
}
@autobind
changeParams(e, index, type) {
const params = JSON.parse(JSON.stringify(this.state.params));
changePathParam(e, index, isKey) {
const pathParam = JSON.parse(JSON.stringify(this.state.pathParam));
const v = e.target.value;
const name = pathParam[index].name;
let newPathname = this.state.pathname;
if (isKey) {
if (!name && v) {
newPathname += `/:${v}`;
} else {
newPathname = newPathname.replace(`/:${name}`, v ? `/:${v}` : '')
}
pathParam[index].name = v;
} else {
pathParam[index].value = v;
}
this.setState({ pathParam, pathname: newPathname });
}
@autobind
addPathParam() {
const { pathParam } = this.state;
this.setState({pathParam: pathParam.concat([{name: '', value: ''}])})
}
@autobind
deletePathParam(index) {
const { pathParam } = this.state;
const name = pathParam[index].name;
const newPathname = this.state.pathname.replace(`/:${name}`, '');
this.setState({pathParam: pathParam.filter((item, i) => +index !== +i), pathname: newPathname});
}
@autobind
changeBody(e, index, type) {
const bodyForm = JSON.parse(JSON.stringify(this.state.bodyForm));
switch (type) {
case 'key':
params[index].key = e.target.value
bodyForm[index].name = e.target.value
break;
case 'type':
params[index].type = e
bodyForm[index].type = e
break;
case 'value':
if (params[index].type === 'file') {
params[index].value = e.target.id
if (bodyForm[index].type === 'file') {
bodyForm[index].value = e.target.id
} else {
params[index].value = e.target.value
bodyForm[index].value = e.target.value
}
break;
default:
@ -259,17 +263,17 @@ export default class Run extends Component {
if (type === 'type' && e === 'file') {
this.setContentType('multipart/form-data')
}
this.setState({ params });
this.setState({ bodyForm });
}
@autobind
addParams() {
const { params } = this.state;
this.setState({params: params.concat([{key: '', value: '', type: 'text'}])})
addBody() {
const { bodyForm } = this.state;
this.setState({bodyForm: bodyForm.concat([{name: '', value: '', type: 'text'}])})
}
@autobind
deleteParams(index) {
const { params } = this.state;
this.setState({params: params.filter((item, i) => +index !== +i)});
deleteBody(index) {
const { bodyForm } = this.state;
this.setState({bodyForm: bodyForm.filter((item, i) => +index !== +i)});
}
@autobind
@ -288,8 +292,8 @@ export default class Run extends Component {
}
@autobind
changeParamsType(value) {
this.setState({paramsType: value})
changeBodyType(value) {
this.setState({bodyType: value})
}
hasCrossRequestPlugin() {
@ -298,7 +302,7 @@ export default class Run extends Component {
}
objToArr(obj, key, value) {
const keyName = key || 'key';
const keyName = key || 'name';
const valueName = value || 'value';
const arr = []
Object.keys(obj).forEach((_key) => {
@ -311,17 +315,17 @@ export default class Run extends Component {
arrToObj(arr) {
const obj = {};
arr.forEach(item => {
if (item.key && item.type !== 'file') {
obj[item.key] = item.value || '';
if (item.name && item.type !== 'file') {
obj[item.name] = item.value || '';
}
})
return obj;
}
getFiles(params) {
getFiles(bodyForm) {
const files = {};
params.forEach(item => {
if (item.key && item.type === 'file') {
files[item.key] = item.value
bodyForm.forEach(item => {
if (item.name && item.type === 'file') {
files[item.name] = item.value
}
})
return files;
@ -329,8 +333,8 @@ export default class Run extends Component {
getQueryObj(query) {
const queryObj = {};
query.forEach(item => {
if (item.key) {
queryObj[item.key] = item.value || '';
if (item.name) {
queryObj[item.name] = item.value || '';
}
})
return queryObj;
@ -353,28 +357,31 @@ export default class Run extends Component {
render () {
const { method, domains, pathname, query, headers, params, currDomain, paramsType } = this.state;
const { method, domains, pathParam, pathname, query, headers, bodyForm, bodyOther, currDomain, bodyType } = this.state;
const hasPlugin = this.hasCrossRequestPlugin();
let path = pathname;
pathParam.forEach(item => {
path = path.replace(`:${item.name}`, item.value || `:${item.name}`);
});
const search = decodeURIComponent(URL.format({query: this.getQueryObj(query)}));
return (
<div className="interface-test">
<div className="has-plugin">
{
hasPlugin ? '' : (
<Alert
message={
<div>
温馨提示当前正在使用接口测试服务请安装我们为您免费提供的&nbsp;
<a
target="blank"
href="https://chrome.google.com/webstore/detail/cross-request/cmnlfmgbjmaciiopcgodlhpiklaghbok?hl=en-US"
>测试增强插件 [点击获取]</a>
</div>
}
type="warning"
/>
)
hasPlugin ? '' :
<Alert
message={
<div>
温馨提示当前正在使用接口测试服务请安装我们为您免费提供的&nbsp;
<a
target="blank"
href="https://chrome.google.com/webstore/detail/cross-request/cmnlfmgbjmaciiopcgodlhpiklaghbok?hl=en-US"
>测试增强插件 [点击获取]</a>
</div>
}
type="warning"
/>
}
</div>
@ -390,23 +397,43 @@ export default class Run extends Component {
Object.keys(domains).map((key, index) => (<Option value={domains[key]} key={index}>{key + '' + domains[key]}</Option>))
}
</Select>
<Input value={pathname + search} onChange={this.changePath} spellCheck="false" style={{flexBasis: 180, flexGrow: 1}} />
<Input value={path + search} onChange={this.changePath} spellCheck="false" style={{flexBasis: 180, flexGrow: 1}} />
</InputGroup>
<Button
onClick={this.requestInterface}
onClick={this.reqRealInterface}
type="primary"
style={{marginLeft: 10}}
loading={this.state.loading}
>发送</Button>
<Button
onClick={this.reqRealInterface}
type="primary"
style={{marginLeft: 10}}
>保存</Button>
</div>
<Collapse defaultActiveKey={['1', '2', '3']} bordered={true}>
<Collapse defaultActiveKey={['0', '1', '2', '3']} bordered={true}>
<Panel header="PATH PARAMETERS" key="0" className={pathParam.length === 0 ? 'hidden' : ''}>
{
pathParam.map((item, index) => {
return (
<div key={index} className="key-value-wrap">
<Input value={item.name} onChange={e => this.changePathParam(e, index, true)} className="key" />
<span className="eq-symbol">=</span>
<Input value={item.value} onChange={e => this.changePathParam(e, index)} className="value" />
<Icon type="delete" className="icon-btn" onClick={() => this.deletePathParam(index)} />
</div>
)
})
}
<Button type="primary" icon="plus" onClick={this.addPathParam}>Add path parameter</Button>
</Panel>
<Panel header="QUERY PARAMETERS" key="1">
{
query.map((item, index) => {
return (
<div key={index} className="key-value-wrap">
<Input value={item.key} onChange={e => this.changeQuery(e, index, true)} className="key" />
<Input value={item.name} onChange={e => this.changeQuery(e, index, true)} className="key" />
<span className="eq-symbol">=</span>
<Input value={item.value} onChange={e => this.changeQuery(e, index)} className="value" />
<Icon type="delete" className="icon-btn" onClick={() => this.deleteQuery(index)} />
@ -436,7 +463,7 @@ export default class Run extends Component {
<div style={{display: 'flex', justifyContent: 'space-between'}}>
<div>BODY</div>
<div onClick={e => e.stopPropagation()} style={{marginRight: 5}}>
<Select defaultValue={paramsType} onChange={this.changeParamsType} className={method === 'POST' ? '' : 'hidden'}>
<Select defaultValue={bodyType} onChange={this.changeBodyType} className={method === 'POST' ? '' : 'hidden'}>
<Option value="text">Text</Option>
<Option value="file">File</Option>
<Option value="form">Form</Option>
@ -446,7 +473,7 @@ export default class Run extends Component {
}
key="3"
>
{ method === 'POST' && paramsType !== 'form' && paramsType !== 'file' &&
{ method === 'POST' && bodyType !== 'form' && bodyType !== 'file' &&
<div>
<RadioGroup defaultValue="json">
<RadioButton value="json">JSON</RadioButton>
@ -455,41 +482,41 @@ export default class Run extends Component {
<RadioButton value="html">HTML</RadioButton>
</RadioGroup>
<TextArea
value={params}
value={bodyOther}
style={{marginTop: 10}}
autosize={{ minRows: 2, maxRows: 6 }}
autosize={{ minRows: 2, maxRows: 10 }}
></TextArea>
</div>
}
{
method === 'POST' && paramsType === 'form' &&
method === 'POST' && bodyType === 'form' &&
<div>
{
params.map((item, index) => {
bodyForm.map((item, index) => {
return (
<div key={index} className="key-value-wrap">
<Input value={item.key} onChange={e => this.changeParams(e, index, 'key')} className="key" />
<Input value={item.name} onChange={e => this.changeBody(e, index, 'key')} className="key" />
<span>[</span>
<Select value={item.type} onChange={e => this.changeParams(e, index, 'type')}>
<Select value={item.type} onChange={e => this.changeBody(e, index, 'type')}>
<Option value="file">File</Option>
<Option value="text">Text</Option>
</Select>
<span>]</span>
<span className="eq-symbol">=</span>
{
item.type === 'file' ? <Input type="file" id={'file_' + index} onChange={e => this.changeParams(e, index, 'value')} multiple className="value" /> :
<Input value={item.value} onChange={e => this.changeParams(e, index, 'value')} className="value" />
item.type === 'file' ? <Input type="file" id={'file_' + index} onChange={e => this.changeBody(e, index, 'value')} multiple className="value" /> :
<Input value={item.value} onChange={e => this.changeBody(e, index, 'value')} className="value" />
}
<Icon type="delete" className="icon-btn" onClick={() => this.deleteParams(index)} />
<Icon type="delete" className="icon-btn" onClick={() => this.deleteBody(index)} />
</div>
)
})
}
<Button type="primary" icon="plus" onClick={this.addParams}>Add form parameter</Button>
<Button type="primary" icon="plus" onClick={this.addBody}>Add form parameter</Button>
</div>
}
{
method === 'POST' && paramsType === 'file' &&
method === 'POST' && bodyType === 'file' &&
<div>
<Input type="file"></Input>
</div>

@ -23,6 +23,11 @@
.ant-tabs.ant-tabs-card > .ant-tabs-bar .ant-tabs-tab-active{
background-color: #efefef
}
.ant-tabs.ant-tabs-card > .ant-tabs-bar{
text-align: center;
line-height: 40px;
}
.interface-filter{
padding-left: 10px;

@ -3,11 +3,16 @@ import axios from 'axios';
// Actions
const FETCH_GROUP_LIST = 'yapi/group/FETCH_GROUP_LIST';
const SET_CURR_GROUP = 'yapi/group/SET_CURR_GROUP';
const FETCH_GROUP_MEMBER = 'yapi/group/FETCH_GROUP_MEMBER';
const FETCH_GROUP_MSG = 'yapi/group/FETCH_GROUP_MSG';
const ADD_FROUP_MEMBER = 'yapi/group/ADD_FROUP_MEMBER';
// Reducer
const initialState = {
groupList: [],
currGroup: { group_name: '' }
currGroup: { group_name: '' },
member: [],
role: ''
};
export default (state = initialState, action) => {
@ -24,12 +29,52 @@ export default (state = initialState, action) => {
currGroup: action.payload
};
}
case FETCH_GROUP_MEMBER: {
return {
...state,
member: action.payload.data.data
};
}
case FETCH_GROUP_MSG: {
return {
...state,
role: action.payload.data.data.role
};
}
default:
return state;
}
};
// 获取 group 信息 (权限信息)
export function fetchGroupMsg(id) {
return {
type: FETCH_GROUP_MSG,
payload: axios.get('/api/group/get', {
params: { id }
})
}
}
// 添加项目分组成员
export function addMember(param) {
return {
type: ADD_FROUP_MEMBER,
payload: axios.post('/api/group/add_member', param)
}
}
// 获取分组成员列表
export function fetchGroupMemberList(id) {
return {
type: FETCH_GROUP_MEMBER,
payload: axios.get('/api/group/get_member_list', {
params: { id }
})
}
}
// Action Creators
export function fetchGroupList() {
return {

@ -26,7 +26,7 @@ class baseController {
'/api/user/status',
'/api/user/logout'
];
if (ignoreRouter.indexOf(ctx.path) > -1) {
if (ignoreRouter.indexOf(ctx.path) > -1) {
this.$auth = true;
} else {
await this.checkLogin(ctx);
@ -82,24 +82,18 @@ class baseController {
getUsername() {
return this.$user.username;
}
/**
*
* @param {*} id type对应的id
* @param {*} type enum[interface, project, group]
* @param {*} action enum[ danger , edit ] danger只有owner或管理员才能操作,edit只要是dev或以上就能执行
*/
async checkAuth(id, type, action) {
async getProjectRole(id, type) {
let result = {};
try {
if (this.getRole() === 'admin') {
return true;
return 'admin';
}
if (type === 'interface') {
let interfaceInst = yapi.getInst(interfaceModel);
let interfaceData = await interfaceInst.get(id)
result.interfaceData = interfaceData;
if (interfaceData.uid === this.getUid()) {
return true;
return 'owner';
}
type = 'project';
id = interfaceData.project_id;
@ -107,10 +101,10 @@ class baseController {
if (type === 'project') {
let projectInst = yapi.getInst(projectModel);
let projectData = await projectInst.get(id);
if(projectData.uid === this.getUid()){
return true;
}
let projectData = await projectInst.get(id);
if (projectData.uid === this.getUid()) {
return 'owner';
}
let memberData = _.find(projectData.members, (m) => {
if (m.uid === this.getUid()) {
return true;
@ -118,11 +112,10 @@ class baseController {
})
if (memberData && memberData.role) {
if(action === 'danger' && memberData.role === 'owner'){
return true;
}
if(action === 'edit'){
return true;
if (memberData.role === 'owner') {
return 'owner';
}else {
return 'dev';
}
}
type = 'group';
@ -138,22 +131,40 @@ class baseController {
}
})
if (groupMemberData && groupMemberData.role) {
if(action === 'danger' && groupMemberData.role === 'owner'){
return true;
}
if(action === 'edit'){
return true;
if (groupMemberData.role === 'owner') {
return 'owner';
}else{
return 'dev'
}
}
}
return false;
return 'member';
}
catch (e) {
yapi.commons.log(e.message, 'error')
return false;
}
}
/**
*
* @param {*} id type对应的id
* @param {*} type enum[interface, project, group]
* @param {*} action enum[ danger , edit ] 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;
}
}
return false;
}
}
module.exports = baseController;

@ -3,11 +3,40 @@ import yapi from '../yapi.js';
import baseController from './base.js';
import projectModel from '../models/project.js';
import userModel from '../models/user.js';
import interfaceModel from '../models/interface.js';
import interfaceColModel from '../models/interfaceCol.js';
import interfaceCaseModel from '../models/interfaceCase.js';
class groupController extends baseController {
constructor(ctx) {
super(ctx);
}
/**
* 添加项目分组
* @interface /group/get
* @method GET
* @category group
* @foldnumber 10
* @param {String} id 项目分组ID
* @returns {Object}
* @example
*/
async get(ctx) {
try {
let params = ctx.request.query;
if (!params.id) {
return ctx.body = yapi.commons.resReturn(null, 400, '分组id不能为空');
}
let groupInst = yapi.getInst(groupModel);
let result = await groupInst.getGroupById(params.id);
result = result.toObject();
result.role = await this.getProjectRole(params.id, 'group');
ctx.body = yapi.commons.resReturn(result);
} catch (e) {
ctx.body = yapi.commons.resReturn(null, 400, e.message)
}
}
/**
* 添加项目分组
@ -16,9 +45,9 @@ class groupController extends baseController {
* @category group
* @foldnumber 10
* @param {String} group_name 项目分组名称不能为空
* @param {String} [group_desc] 项目分组描述
* @param {String} [group_desc] 项目分组描述
* @param {String} owner_uid 组长uid
* @returns {Object}
* @returns {Object}
* @example ./api/group/add.json
*/
async add(ctx) {
@ -38,12 +67,12 @@ class groupController extends baseController {
return ctx.body = yapi.commons.resReturn(null, 400, '项目分组名不能为空');
}
if(!params.owner_uid){
if (!params.owner_uid) {
return ctx.body = yapi.commons.resReturn(null, 400, '项目分组必须添加一个组长');
}
let groupUserdata = await this.getUserdata(params.owner_uid, 'owner');
if(groupUserdata === null){
if (groupUserdata === null) {
return ctx.body = yapi.commons.resReturn(null, 400, '组长uid不存在')
}
let groupInst = yapi.getInst(groupModel);
@ -74,11 +103,11 @@ class groupController extends baseController {
}
async getUserdata(uid, role){
async getUserdata(uid, role) {
role = role || 'dev';
let userInst = yapi.getInst(userModel);
let userData = await userInst.findById(uid);
if(!userData){
if (!userData) {
return null;
}
return {
@ -98,12 +127,14 @@ class groupController extends baseController {
* @foldnumber 10
* @param {String} id 项目分组id
* @param {String} member_uid 项目分组成员uid
* @returns {Object}
* @example
* @returns {Object}
* @example
*/
async addMember(ctx){
async addMember(ctx){
let params = ctx.request.body;
let groupInst = yapi.getInst(groupModel);
if (!params.member_uid) {
@ -118,10 +149,10 @@ class groupController extends baseController {
return ctx.body = yapi.commons.resReturn(null, 400, '成员已存在');
}
let groupUserdata = await this.getUserdata(params.member_uid);
if(groupUserdata === null){
if (groupUserdata === null) {
return ctx.body = yapi.commons.resReturn(null, 400, '组长uid不存在')
}
if(groupUserdata._role === 'admin'){
if (groupUserdata._role === 'admin') {
return ctx.body = yapi.commons.resReturn(null, 400, '不能邀请管理员')
}
delete groupUserdata._role;
@ -142,11 +173,11 @@ class groupController extends baseController {
* @foldnumber 10
* @param {String} id 项目分组id
* @param {String} member_uid 项目分组成员uid
* @param {String} role 组长uid
* @returns {Object}
* @example
* @param {String} role 权限 ['owner'|'dev']
* @returns {Object}
* @example
*/
async changeMemberRole(ctx){
async changeMemberRole(ctx) {
let params = ctx.request.body;
let groupInst = yapi.getInst(groupModel);
if (!params.member_uid) {
@ -179,8 +210,8 @@ class groupController extends baseController {
* @category group
* @foldnumber 10
* @param {String} id 项目分组id
* @returns {Object}
* @example
* @returns {Object}
* @example
*/
async getMemberList(ctx) {
@ -206,8 +237,8 @@ class groupController extends baseController {
* @foldnumber 10
* @param {String} id 项目分组id
* @param {String} member_uid 项目分组成员uid
* @returns {Object}
* @example
* @returns {Object}
* @example
*/
async delMember(ctx) {
@ -241,7 +272,7 @@ class groupController extends baseController {
* @method get
* @category group
* @foldnumber 10
* @returns {Object}
* @returns {Object}
* @example ./api/group/list.json
*/
async list(ctx) {
@ -261,7 +292,7 @@ class groupController extends baseController {
* @param {String} id 项目分组id
* @category group
* @foldnumber 10
* @returns {Object}
* @returns {Object}
* @example ./api/group/del.json
*/
async del(ctx) {
@ -272,18 +303,21 @@ class groupController extends baseController {
try {
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.request.body.id;
if (!id) {
return ctx.body = yapi.commons.resReturn(null, 402, 'id不能为空');
}
let count = await projectInst.countByGroupId(id);
if (count > 0) {
return ctx.body = yapi.commons.resReturn(null, 403, '请先删除该分组下的项目');
}
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)
})
await projectInst.delByGroupid(id);
let result = await groupInst.del(id);
ctx.body = yapi.commons.resReturn(result);
} catch (err) {
@ -300,7 +334,7 @@ class groupController extends baseController {
* @param {String} group_desc 项目分组描述
* @category group
* @foldnumber 10
* @returns {Object}
* @returns {Object}
* @example ./api/group/up.json
*/
async up(ctx) {
@ -329,4 +363,4 @@ class groupController extends baseController {
}
}
module.exports = groupController;
module.exports = groupController;

@ -2,6 +2,7 @@ import interfaceModel from '../models/interface.js';
import baseController from './base.js';
import yapi from '../yapi.js';
class interfaceController extends baseController {
constructor(ctx) {
super(ctx);
@ -91,8 +92,11 @@ class interfaceController extends baseController {
if (params.req_body_form) {
data.req_body_form = params.req_body_form;
}
if (params.req_params) {
if (params.req_params && Array.isArray(params.req_params) && params.req_params.length > 0) {
data.type = 'var'
data.req_params = params.req_params;
} else {
data.type = 'static'
}
if (params.req_body_other) {
data.req_body_other = params.req_body_other;
@ -236,9 +240,13 @@ class interfaceController extends baseController {
if (params.req_body_form) {
data.req_body_form = params.req_body_form;
}
if (params.req_params) {
if (params.req_params && Array.isArray(params.req_params) && params.req_params.length > 0) {
data.type = 'var'
data.req_params = params.req_params;
} else {
data.type = 'static'
}
if (params.req_query) {
data.req_query = params.req_query;
}

@ -2,6 +2,24 @@ import yapi from '../yapi.js';
import projectModel from '../models/project.js';
import interfaceModel from '../models/interface.js';
import Mock from 'mockjs';
import _ from 'underscore';
function matchApi(apiPath, apiRule) {
let apiPaths = apiPath.split("/");
let apiRules = apiRule.split("/");
if (apiPaths.length !== apiRules.length) {
return false;
}
for (let i = 0; i < apiRules.length; i++) {
if (apiRules[i] && apiRules[i].indexOf(":") !== 0) {
if (apiRules[i] !== apiPaths[i]) {
return false;
}
}
}
return true;
}
module.exports = async (ctx, next) => {
yapi.commons.log('Server Recevie Request...');
@ -9,7 +27,7 @@ module.exports = async (ctx, next) => {
let hostname = ctx.hostname;
let config = yapi.WEBCONFIG;
let path = ctx.path;
if (path.indexOf('/mock/') !== 0) {
if (next) await next();
@ -20,7 +38,7 @@ module.exports = async (ctx, next) => {
let projectId = paths[2];
paths.splice(0, 3);
path = "/" + paths.join("/");
if(!projectId){
if (!projectId) {
return ctx.body = yapi.commons.resReturn(null, 400, 'projectId不能为空');
}
@ -35,20 +53,33 @@ module.exports = async (ctx, next) => {
if (project === false) {
return ctx.body = yapi.commons.resReturn(null, 400, '不存在的项目');
}
let interfaceData;
let interfaceData, newData, newpath;
let interfaceInst = yapi.getInst(interfaceModel);
try {
interfaceData = await interfaceInst.getByPath(project._id, path.substr(project.basepath.length), ctx.method);
newpath = path.substr(project.basepath.length);
interfaceData = await interfaceInst.getByPath(project._id, newpath, ctx.method);
if (!interfaceData || interfaceData.length === 0) {
//非正常跨域预检请求回应
if(ctx.method === 'OPTIONS'){
if (ctx.method === 'OPTIONS') {
ctx.set("Access-Control-Allow-Origin", "*")
ctx.set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE")
return ctx.body = 'ok'
}
return ctx.body = yapi.commons.resReturn(null, 404, '不存在的api');
let newData = await interfaceInst.getVar(project._id, ctx.method);
let findInterface = _.find(newData, (item) => {
return matchApi(newpath, item.path)
});
if (!findInterface) {
return ctx.body = yapi.commons.resReturn(null, 404, '不存在的api');
}
interfaceData = [
await interfaceInst.get(findInterface._id)
]
}
if (interfaceData.length > 1) {
@ -58,12 +89,12 @@ module.exports = async (ctx, next) => {
interfaceData = interfaceData[0];
ctx.set("Access-Control-Allow-Origin", "*")
if (interfaceData.res_body_type === 'json') {
try{
try {
const res = Mock.mock(
yapi.commons.json_parse(interfaceData.res_body)
);
return ctx.body = res;
}catch(e){
} catch (e) {
return ctx.body = {
errcode: 400,
errmsg: 'mock json数据格式有误',
@ -73,6 +104,7 @@ module.exports = async (ctx, next) => {
}
return ctx.body = interfaceData.res_body;
} catch (e) {
console.error(e)
return ctx.body = yapi.commons.resReturn(null, 409, e.message);
}
};

@ -35,6 +35,12 @@ class groupModel extends baseModel {
}).exec();
}
getGroupById(id) {
return this.model.findOne({
_id: id
}).select("uid group_name group_desc add_time up_time").exec();
}
checkRepeat(name) {
return this.model.count({
group_name: name

@ -17,6 +17,7 @@ class interfaceModel extends baseModel {
desc: String,
add_time: Number,
up_time: Number,
type: {type: String, enum: ['static', 'var'], default:'static'},
req_query:[{
name: String, value: String, desc: String, required: {
type:String,
@ -67,6 +68,13 @@ class interfaceModel extends baseModel {
.exec();
}
getVar(project_id, method){
return this.model.find({
type: 'var',
method: method
}).select('_id path').exec()
}
getByPath(project_id, path, method) {
return this.model.find({
project_id: project_id,
@ -104,6 +112,12 @@ class interfaceModel extends baseModel {
});
}
delByProjectId(id){
return this.model.deleteMany({
project_id: id
})
}
up(id, data) {
data.up_time = yapi.commons.time();
return this.model.update({

@ -60,6 +60,12 @@ class interfaceCase extends baseModel {
});
}
delByProjectId(id){
return this.model.deleteMany({
project_id: id
})
}
up(id, data) {
data.up_time = yapi.commons.time()
return this.model.update(

@ -46,6 +46,12 @@ class interfaceCol extends baseModel {
});
}
delByProjectId(id){
return this.model.deleteMany({
project_id: id
})
}
up(id, data) {
data.up_time = yapi.commons.time()
return this.model.update(

@ -87,6 +87,12 @@ class projectModel extends baseModel {
});
}
delByGroupid(groupId){
return this.model.deleteMany({
group_id: groupId
})
}
up(id, data) {
data.up_time = yapi.commons.time();
return this.model.update({

@ -91,6 +91,10 @@ const routerConfig = {
"action": "getMemberList",
"path": "get_member_list",
"method": "get"
},{
action: 'get',
path: 'get',
method: 'get'
}
],
"user": [

@ -219,17 +219,10 @@ var baseController = function () {
value: function getUsername() {
return this.$user.username;
}
/**
*
* @param {*} id type对应的id
* @param {*} type enum[interface, project, group]
* @param {*} action enum[ danger , edit ] danger只有owner或管理员才能操作,edit只要是dev或以上就能执行
*/
}, {
key: 'checkAuth',
key: 'getProjectRole',
value: function () {
var _ref4 = (0, _asyncToGenerator3.default)(_regenerator2.default.mark(function _callee4(id, type, action) {
var _ref4 = (0, _asyncToGenerator3.default)(_regenerator2.default.mark(function _callee4(id, type) {
var _this = this;
var result, interfaceInst, interfaceData, projectInst, projectData, memberData, groupInst, groupData, groupMemberData;
@ -245,7 +238,7 @@ var baseController = function () {
break;
}
return _context4.abrupt('return', true);
return _context4.abrupt('return', 'admin');
case 4:
if (!(type === 'interface')) {
@ -267,7 +260,7 @@ var baseController = function () {
break;
}
return _context4.abrupt('return', true);
return _context4.abrupt('return', 'owner');
case 12:
type = 'project';
@ -275,7 +268,7 @@ var baseController = function () {
case 14:
if (!(type === 'project')) {
_context4.next = 29;
_context4.next = 30;
break;
}
@ -291,7 +284,7 @@ var baseController = function () {
break;
}
return _context4.abrupt('return', true);
return _context4.abrupt('return', 'owner');
case 21:
memberData = _underscore2.default.find(projectData.members, function (m) {
@ -301,40 +294,35 @@ var baseController = function () {
});
if (!(memberData && memberData.role)) {
_context4.next = 28;
break;
}
if (!(memberData.role === 'owner')) {
_context4.next = 27;
break;
}
if (!(action === 'danger' && memberData.role === 'owner')) {
_context4.next = 25;
break;
}
return _context4.abrupt('return', true);
case 25:
if (!(action === 'edit')) {
_context4.next = 27;
break;
}
return _context4.abrupt('return', true);
return _context4.abrupt('return', 'owner');
case 27:
return _context4.abrupt('return', 'dev');
case 28:
type = 'group';
id = projectData.group_id;
case 29:
case 30:
if (!(type === 'group')) {
_context4.next = 40;
_context4.next = 42;
break;
}
groupInst = _yapi2.default.getInst(_group2.default);
_context4.next = 33;
_context4.next = 34;
return groupInst.get(id);
case 33:
case 34:
groupData = _context4.sent;
groupMemberData = _underscore2.default.find(groupData.members, function (m) {
if (m.uid === _this.getUid()) {
@ -343,47 +331,110 @@ var baseController = function () {
});
if (!(groupMemberData && groupMemberData.role)) {
_context4.next = 40;
_context4.next = 42;
break;
}
if (!(action === 'danger' && groupMemberData.role === 'owner')) {
_context4.next = 38;
if (!(groupMemberData.role === 'owner')) {
_context4.next = 41;
break;
}
return _context4.abrupt('return', true);
return _context4.abrupt('return', 'owner');
case 38:
if (!(action === 'edit')) {
_context4.next = 40;
break;
}
case 41:
return _context4.abrupt('return', 'dev');
return _context4.abrupt('return', true);
case 42:
return _context4.abrupt('return', 'member');
case 40:
return _context4.abrupt('return', false);
case 43:
_context4.prev = 43;
case 45:
_context4.prev = 45;
_context4.t0 = _context4['catch'](1);
_yapi2.default.commons.log(_context4.t0.message, 'error');
return _context4.abrupt('return', false);
case 47:
case 49:
case 'end':
return _context4.stop();
}
}
}, _callee4, this, [[1, 43]]);
}, _callee4, this, [[1, 45]]);
}));
function checkAuth(_x4, _x5, _x6) {
function getProjectRole(_x4, _x5) {
return _ref4.apply(this, arguments);
}
return getProjectRole;
}()
/**
*
* @param {*} id type对应的id
* @param {*} type enum[interface, project, group]
* @param {*} action enum[ danger , edit ] danger只有owner或管理员才能操作,edit只要是dev或以上就能执行
*/
}, {
key: 'checkAuth',
value: function () {
var _ref5 = (0, _asyncToGenerator3.default)(_regenerator2.default.mark(function _callee5(id, type, action) {
var role;
return _regenerator2.default.wrap(function _callee5$(_context5) {
while (1) {
switch (_context5.prev = _context5.next) {
case 0:
_context5.next = 2;
return this.getProjectRole(id, type);
case 2:
role = _context5.sent;
if (!(action === 'danger')) {
_context5.next = 8;
break;
}
if (!(role === 'admin' || role === 'owner')) {
_context5.next = 6;
break;
}
return _context5.abrupt('return', true);
case 6:
_context5.next = 11;
break;
case 8:
if (!(action === 'edit')) {
_context5.next = 11;
break;
}
if (!(role === 'admin' || role === 'owner' || role === 'dev')) {
_context5.next = 11;
break;
}
return _context5.abrupt('return', true);
case 11:
return _context5.abrupt('return', false);
case 12:
case 'end':
return _context5.stop();
}
}
}, _callee5, this);
}));
function checkAuth(_x6, _x7, _x8) {
return _ref5.apply(this, arguments);
}
return checkAuth;
}()
}]);

File diff suppressed because it is too large Load Diff

@ -169,8 +169,11 @@ var interfaceController = function (_baseController) {
if (params.req_body_form) {
data.req_body_form = params.req_body_form;
}
if (params.req_params) {
if (params.req_params && Array.isArray(params.req_params) && params.req_params.length > 0) {
data.type = 'var';
data.req_params = params.req_params;
} else {
data.type = 'static';
}
if (params.req_body_other) {
data.req_body_other = params.req_body_other;
@ -447,9 +450,13 @@ var interfaceController = function (_baseController) {
if (params.req_body_form) {
data.req_body_form = params.req_body_form;
}
if (params.req_params) {
if (params.req_params && Array.isArray(params.req_params) && params.req_params.length > 0) {
data.type = 'var';
data.req_params = params.req_params;
} else {
data.type = 'static';
}
if (params.req_query) {
data.req_query = params.req_query;
}

@ -24,11 +24,32 @@ var _mockjs = require('mockjs');
var _mockjs2 = _interopRequireDefault(_mockjs);
var _underscore = require('underscore');
var _underscore2 = _interopRequireDefault(_underscore);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function matchApi(apiPath, apiRule) {
var apiPaths = apiPath.split("/");
var apiRules = apiRule.split("/");
if (apiPaths.length !== apiRules.length) {
return false;
}
for (var i = 0; i < apiRules.length; i++) {
if (apiRules[i] && apiRules[i].indexOf(":") !== 0) {
if (apiRules[i] !== apiPaths[i]) {
return false;
}
}
}
return true;
}
module.exports = function () {
var _ref = (0, _asyncToGenerator3.default)(_regenerator2.default.mark(function _callee(ctx, next) {
var hostname, config, path, paths, projectId, projectInst, project, interfaceData, interfaceInst, res;
var hostname, config, path, paths, projectId, projectInst, project, interfaceData, newData, newpath, interfaceInst, _newData, findInterface, res;
return _regenerator2.default.wrap(function _callee$(_context) {
while (1) {
switch (_context.prev = _context.next) {
@ -96,22 +117,24 @@ module.exports = function () {
return _context.abrupt('return', ctx.body = _yapi2.default.commons.resReturn(null, 400, '不存在的项目'));
case 28:
interfaceData = void 0;
interfaceData = void 0, newData = void 0, newpath = void 0;
interfaceInst = _yapi2.default.getInst(_interface2.default);
_context.prev = 30;
_context.next = 33;
return interfaceInst.getByPath(project._id, path.substr(project.basepath.length), ctx.method);
case 33:
newpath = path.substr(project.basepath.length);
_context.next = 34;
return interfaceInst.getByPath(project._id, newpath, ctx.method);
case 34:
interfaceData = _context.sent;
if (!(!interfaceData || interfaceData.length === 0)) {
_context.next = 40;
_context.next = 50;
break;
}
if (!(ctx.method === 'OPTIONS')) {
_context.next = 39;
_context.next = 40;
break;
}
@ -119,54 +142,78 @@ module.exports = function () {
ctx.set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE");
return _context.abrupt('return', ctx.body = 'ok');
case 39:
case 40:
_context.next = 42;
return interfaceInst.getVar(project._id, ctx.method);
case 42:
_newData = _context.sent;
findInterface = _underscore2.default.find(_newData, function (item) {
return matchApi(newpath, item.path);
});
if (findInterface) {
_context.next = 46;
break;
}
return _context.abrupt('return', ctx.body = _yapi2.default.commons.resReturn(null, 404, '不存在的api'));
case 40:
case 46:
_context.next = 48;
return interfaceInst.get(findInterface._id);
case 48:
_context.t1 = _context.sent;
interfaceData = [_context.t1];
case 50:
if (!(interfaceData.length > 1)) {
_context.next = 42;
_context.next = 52;
break;
}
return _context.abrupt('return', ctx.body = _yapi2.default.commons.resReturn(null, 405, '存在多个api请检查数据库'));
case 42:
case 52:
interfaceData = interfaceData[0];
ctx.set("Access-Control-Allow-Origin", "*");
if (!(interfaceData.res_body_type === 'json')) {
_context.next = 53;
_context.next = 63;
break;
}
_context.prev = 45;
_context.prev = 55;
res = _mockjs2.default.mock(_yapi2.default.commons.json_parse(interfaceData.res_body));
return _context.abrupt('return', ctx.body = res);
case 50:
_context.prev = 50;
_context.t1 = _context['catch'](45);
case 60:
_context.prev = 60;
_context.t2 = _context['catch'](55);
return _context.abrupt('return', ctx.body = {
errcode: 400,
errmsg: 'mock json数据格式有误',
data: interfaceData.res_body
});
case 53:
case 63:
return _context.abrupt('return', ctx.body = interfaceData.res_body);
case 56:
_context.prev = 56;
_context.t2 = _context['catch'](30);
return _context.abrupt('return', ctx.body = _yapi2.default.commons.resReturn(null, 409, _context.t2.message));
case 66:
_context.prev = 66;
_context.t3 = _context['catch'](30);
case 59:
console.error(_context.t3);
return _context.abrupt('return', ctx.body = _yapi2.default.commons.resReturn(null, 409, _context.t3.message));
case 70:
case 'end':
return _context.stop();
}
}
}, _callee, undefined, [[17, 23], [30, 56], [45, 50]]);
}, _callee, undefined, [[17, 23], [30, 66], [55, 60]]);
}));
return function (_x, _x2) {

@ -73,6 +73,13 @@ var groupModel = function (_baseModel) {
_id: id
}).exec();
}
}, {
key: 'getGroupById',
value: function getGroupById(id) {
return this.model.findOne({
_id: id
}).select("uid group_name group_desc add_time up_time").exec();
}
}, {
key: 'checkRepeat',
value: function checkRepeat(name) {

@ -56,6 +56,7 @@ var interfaceModel = function (_baseModel) {
desc: String,
add_time: Number,
up_time: Number,
type: { type: String, enum: ['static', 'var'], default: 'static' },
req_query: [{
name: String, value: String, desc: String, required: {
type: String,
@ -106,6 +107,14 @@ var interfaceModel = function (_baseModel) {
_id: id
}).exec();
}
}, {
key: 'getVar',
value: function getVar(project_id, method) {
return this.model.find({
type: 'var',
method: method
}).select('_id path').exec();
}
}, {
key: 'getByPath',
value: function getByPath(project_id, path, method) {
@ -145,6 +154,13 @@ var interfaceModel = function (_baseModel) {
_id: id
});
}
}, {
key: 'delByProjectId',
value: function delByProjectId(id) {
return this.model.deleteMany({
project_id: id
});
}
}, {
key: 'up',
value: function up(id, data) {

@ -102,6 +102,13 @@ var interfaceCase = function (_baseModel) {
_id: id
});
}
}, {
key: 'delByProjectId',
value: function delByProjectId(id) {
return this.model.deleteMany({
project_id: id
});
}
}, {
key: 'up',
value: function up(id, data) {

@ -89,6 +89,13 @@ var interfaceCol = function (_baseModel) {
_id: id
});
}
}, {
key: 'delByProjectId',
value: function delByProjectId(id) {
return this.model.deleteMany({
project_id: id
});
}
}, {
key: 'up',
value: function up(id, data) {

@ -131,6 +131,13 @@ var projectModel = function (_baseModel) {
_id: id
});
}
}, {
key: 'delByGroupid',
value: function delByGroupid(groupId) {
return this.model.deleteMany({
group_id: groupId
});
}
}, {
key: 'up',
value: function up(id, data) {

@ -120,6 +120,10 @@ var routerConfig = {
"action": "getMemberList",
"path": "get_member_list",
"method": "get"
}, {
action: 'get',
path: 'get',
method: 'get'
}],
"user": [{
"action": "login",

File diff suppressed because it is too large Load Diff

@ -175,8 +175,8 @@
var $this = $(this);
$this.removeClass('extend').addClass('fold');
$this.html('折叠代码');
$this.parent().children('.js-code').height('auto');
$this.parent().height('auto');
$this.prev().height('auto');
$this.prev().parent().height('auto');
});
$('.ydoc-example').delegate('.fold', 'click', function() {
var $this = $(this);
@ -184,7 +184,7 @@
EXAMPLE_MAX_HEIGHT = lineHeight * (foldnumber || 6);
$this.removeClass('fold').addClass('extend');
$this.parent().height(EXAMPLE_MAX_HEIGHT); // pre
$this.parent().children('.js-code').height(EXAMPLE_MAX_HEIGHT); // code
$this.prev().height(EXAMPLE_MAX_HEIGHT); // code
$this.html("展开更多……");
});
}

@ -68,23 +68,23 @@
<div class="ydoc-container-content " id="readme">
<article class="markdown-body">
<h2 class="subject" id="快速开始">快速开始 <a class="hashlink" href="#快速开始">#</a></h2><h3 class="subject" id="1_接口管理架构">1 接口管理架构 <a class="hashlink" href="#1_接口管理架构">#</a></h3><p>平台以<strong>项目分组</strong> -&gt; <strong>项目</strong> -&gt; <strong>接口</strong>的划分进行接口组织管理。</p>
<h2 class="subject" id="快速开始">快速开始 <a class="hashlink" href="#快速开始">#</a></h2><h3 class="subject" id="1 接口管理架构">1 接口管理架构 <a class="hashlink" href="#1 接口管理架构">#</a></h3><p>平台以<strong>项目分组</strong> -&gt; <strong>项目</strong> -&gt; <strong>接口</strong>的划分进行接口组织管理。</p>
<p><img src="http://upload-images.jianshu.io/upload_images/842107-305ba49a60ee1ff5.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" width = "800" style="margin:0px auto;display:block;" alt="图片名称" align=center /></p>
<h4 class="subject" id="1.1_项目分组">1.1 项目分组 <a class="hashlink" href="#1.1_项目分组">#</a></h4><p>登录之后进到项目首页,左边侧边栏显示的即分组列表。</p>
<h4 class="subject" id="1.1 项目分组">1.1 项目分组 <a class="hashlink" href="#1.1 项目分组">#</a></h4><p>登录之后进到项目首页,左边侧边栏显示的即分组列表。</p>
<p><img src="http://upload-images.jianshu.io/upload_images/842107-d90ca4b3242fa760.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" width = "200" style="margin-left:170px;display:block;" alt="图片名称" align=center /></p>
<p>管理员有权限添加或删除分组。</p>
<p><img src="http://upload-images.jianshu.io/upload_images/842107-a0d4d9a98003896a.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" width = "500" style="margin-left:170px;display:block;" alt="图片名称" align=center /></p>
<blockquote>
<p>分组名称具有唯一性</p>
</blockquote>
<h4 class="subject" id="1.2_项目">1.2 项目 <a class="hashlink" href="#1.2_项目">#</a></h4><p>选中不同的分组,右边会显示该分组下的项目列表。</p>
<h4 class="subject" id="1.2 项目">1.2 项目 <a class="hashlink" href="#1.2 项目">#</a></h4><p>选中不同的分组,右边会显示该分组下的项目列表。</p>
<p><img src="http://upload-images.jianshu.io/upload_images/842107-137bcae58b84715e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" width = "800" style="margin:0px auto;display:block;" alt="图片名称" align=center /></p>
<p>创建项目需要填写项目名称,项目线上域名(添加完成后可配置项目其他环境域名),项目接口基本路径(接口路径前面相同的部分)以及项目描述。</p>
<p><img src="http://upload-images.jianshu.io/upload_images/842107-360a50ddb746f73d.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" width = "800" style="margin:0px auto;display:block;" alt="图片名称" align=center /></p>
<blockquote>
<p>项目『线上域名 + 基本路径』具有唯一性</p>
</blockquote>
<h4 class="subject" id="1.3_接口">1.3 接口 <a class="hashlink" href="#1.3_接口">#</a></h4><p>点击项目名称,进入该项目接口列表。</p>
<h4 class="subject" id="1.3 接口">1.3 接口 <a class="hashlink" href="#1.3 接口">#</a></h4><p>点击项目名称,进入该项目接口列表。</p>
<p><img src="http://upload-images.jianshu.io/upload_images/842107-e858005f714f4889.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" width = "800" style="margin:0px auto;display:block;" alt="图片名称" align=center /></p>
<p>点击编辑,进入接口详情页(之后接口详情页和编辑也会分开),可以编辑接口或者请求测试真实接口。</p>
<p><img src="http://upload-images.jianshu.io/upload_images/842107-78c0ea839619d068.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" width = "800" style="margin:0px auto;display:block;" alt="图片名称" align=center /></p>
@ -134,8 +134,8 @@
var $this = $(this);
$this.removeClass('extend').addClass('fold');
$this.html('折叠代码');
$this.parent().children('.js-code').height('auto');
$this.parent().height('auto');
$this.prev().height('auto');
$this.prev().parent().height('auto');
});
$('.ydoc-example').delegate('.fold', 'click', function() {
var $this = $(this);
@ -143,7 +143,7 @@
EXAMPLE_MAX_HEIGHT = lineHeight * (foldnumber || 6);
$this.removeClass('fold').addClass('extend');
$this.parent().height(EXAMPLE_MAX_HEIGHT); // pre
$this.parent().children('.js-code').height(EXAMPLE_MAX_HEIGHT); // code
$this.prev().height(EXAMPLE_MAX_HEIGHT); // code
$this.html("展开更多……");
});
}

@ -70,7 +70,7 @@
<article class="markdown-body">
<h2 class="subject" id="Mock功能">Mock功能 <a class="hashlink" href="#Mock功能">#</a></h2> <p style='text-indent:2em;line-height:1.8em'>yapi的Mock功能可以根据用户的输入接口信息如协议、URL、接口名、请求头、请求参数、mock规则(<a href="#mock">点击到Mock规则</a>生成Mock接口这些接口会自动生成模拟数据支持复杂的生成逻辑创建者可以自由构造需要的数据。而且与常见的Mock方式如将Mock写在代码里和JS拦截等相比yapi的Mock在使用场景和效率和复杂度上是相差甚远的正是由于yapi的Mock是一个第三方平台那么在 团队开发时任何人都可以权限许可下创建、修改接口信息等操作,这对于团队开发是很有好处的。</p>
<h3 class="subject" id="1_Mock步骤">1 Mock步骤 <a class="hashlink" href="#1_Mock步骤">#</a></h3><h4 class="subject" id="1.1_创建接口">1.1 创建接口 <a class="hashlink" href="#1.1_创建接口">#</a></h4><p>通过点击页面上的&quot;+添加接口&quot;</p>
<h3 class="subject" id="1 Mock步骤">1 Mock步骤 <a class="hashlink" href="#1 Mock步骤">#</a></h3><h4 class="subject" id="1.1 创建接口">1.1 创建接口 <a class="hashlink" href="#1.1 创建接口">#</a></h4><p>通过点击页面上的&quot;+添加接口&quot;</p>
<p><img src="http://note.youdao.com/yws/api/personal/file/WEB613bd4f29db038f2b41c03dcfceda2b6?method=download&shareKey=29bfc2b855f6f26ce0079baf567e54cc" width = "800" style="margin:0px auto;display:block;" alt="图片名称" align=center /></p>
<p> 输入协议、URL、接口名、请求头、请求参数、Mock规则<a href="#mock">点击到Mock规则</a>)等信息。</p>
<p><img src="http://note.youdao.com/yws/api/personal/file/WEB680a37ba304768804b23cf2cf36ed40d?method=download&shareKey=0d750695dce3a4c7abf697fa58d24c57" width = "800" style="margin:0px auto;display:block;" alt="图片名称" align=center /></p>
@ -84,14 +84,14 @@
<p>取到上面的链接在浏览器中请求就可以得到如下结果。</p>
<p><img src="http://note.youdao.com/yws/api/personal/file/WEB1d1f7dc7b83a8cd6f576953cf45e9719?method=download&shareKey=99b4af9baac527b969543dd0a909d2a1" width = "800" style="margin:0px auto;display:block;" alt="图片名称" align=center /></p>
<p><span id = "mock"></span></p>
<h3 class="subject" id="2.1_Mock语法规范">2.1 Mock语法规范 <a class="hashlink" href="#2.1_Mock语法规范">#</a></h3><blockquote>
<h3 class="subject" id="2.1 Mock语法规范">2.1 Mock语法规范 <a class="hashlink" href="#2.1 Mock语法规范">#</a></h3><blockquote>
<p>了解更多Mock详情<a href="https://github.com/nuysoft/Mock/wiki/Syntax-Specification">Mock.js 官方文档</a></p>
</blockquote>
<p>Mock.js 的语法规范包括两部分:</p>
<p><a href="#DTD">1. 数据模板定义规范Data Template DefinitionDTD</a></p>
<p><a href="#DPD">2. 数据占位符定义规范Data Placeholder DefinitionDPD</a></p>
<p><span id = "DTD"></span></p>
<h3 class="subject" id="数据模板定义规范Data_Template_DefinitionDTD">数据模板定义规范Data Template DefinitionDTD <a class="hashlink" href="#数据模板定义规范Data_Template_DefinitionDTD">#</a></h3><p>数据模板中的每个属性由 3 部分构成:属性名、生成规则、属性值:</p>
<h3 class="subject" id="数据模板定义规范Data Template DefinitionDTD">数据模板定义规范Data Template DefinitionDTD <a class="hashlink" href="#数据模板定义规范Data Template DefinitionDTD">#</a></h3><p>数据模板中的每个属性由 3 部分构成:属性名、生成规则、属性值:</p>
<pre><code>// 属性名 name (与生成规则之间用 "|<span class="token string">" 隔开)
// 生成规则 rule生成规则有7种详见下面的生成规则
// 属性值 value可以含有 "</span>@占位符" 同时也指定了最终值的初始值和类型)
@ -107,14 +107,14 @@
'name|count.dcount'<span class="token operator">:</span> value
'name|+step'<span class="token operator">:</span> value
</code></pre><p>下面提供了6种生成规则以及示例包括 String、Number、Boolean、Object、Array</p>
<h4 class="subject" id="1._属性值是字符串_String">1. 属性值是字符串 String <a class="hashlink" href="#1._属性值是字符串_String">#</a></h4><pre><code><span class="token number">1</span>. 'name|min-max'<span class="token operator">:</span> string
<h4 class="subject" id="1. 属性值是字符串 String">1. 属性值是字符串 String <a class="hashlink" href="#1. 属性值是字符串 String">#</a></h4><pre><code><span class="token number">1</span>. 'name|min-max'<span class="token operator">:</span> string
通过重复 string 生成一个字符串,重复次数大于等于 min小于等于 max。
<span class="token number">2</span>. 'name|count'<span class="token operator">:</span> string
通过重复 string 生成一个字符串,重复次数等于 count。
</code></pre><h4 class="subject" id="2._属性值是数字_Number">2. 属性值是数字 Number <a class="hashlink" href="#2._属性值是数字_Number">#</a></h4><pre><code><span class="token number">1</span>. 'name|+<span class="token number">1</span>'<span class="token operator">:</span> number
</code></pre><h4 class="subject" id="2. 属性值是数字 Number">2. 属性值是数字 Number <a class="hashlink" href="#2. 属性值是数字 Number">#</a></h4><pre><code><span class="token number">1</span>. 'name|+<span class="token number">1</span>'<span class="token operator">:</span> number
属性值自动加 <span class="token number">1</span>,初始值为 number。
@ -140,21 +140,21 @@ Mock.mock(<span class="token punctuation">{</span>
<span class="token property">"number3"</span><span class="token operator">:</span> <span class="token number">123.777</span><span class="token punctuation">,</span>
<span class="token property">"number4"</span><span class="token operator">:</span> <span class="token number">123.1231091814</span>
<span class="token punctuation">}</span>
</code></pre><h4 class="subject" id="3._属性值是布尔型_Boolean">3. 属性值是布尔型 Boolean <a class="hashlink" href="#3._属性值是布尔型_Boolean">#</a></h4><pre><code><span class="token number">1</span>. 'name|<span class="token number">1</span>'<span class="token operator">:</span> boolean
</code></pre><h4 class="subject" id="3. 属性值是布尔型 Boolean">3. 属性值是布尔型 Boolean <a class="hashlink" href="#3. 属性值是布尔型 Boolean">#</a></h4><pre><code><span class="token number">1</span>. 'name|<span class="token number">1</span>'<span class="token operator">:</span> boolean
随机生成一个布尔值,值为 <span class="token boolean">true</span> 的概率是 <span class="token number">1</span>/<span class="token number">2</span>,值为 <span class="token boolean">false</span> 的概率同样是 <span class="token number">1</span>/<span class="token number">2</span>
<span class="token number">2</span>. 'name|min-max'<span class="token operator">:</span> value
随机生成一个布尔值,值为 value 的概率是 min / (min + max<span class="token punctuation">)</span>,值为 !value 的概率是 max / (min + max<span class="token punctuation">)</span>
</code></pre><h4 class="subject" id="4._属性值是对象_Object">4. 属性值是对象 Object <a class="hashlink" href="#4._属性值是对象_Object">#</a></h4><pre><code><span class="token number">1</span>. 'name|count'<span class="token operator">:</span> object
</code></pre><h4 class="subject" id="4. 属性值是对象 Object">4. 属性值是对象 Object <a class="hashlink" href="#4. 属性值是对象 Object">#</a></h4><pre><code><span class="token number">1</span>. 'name|count'<span class="token operator">:</span> object
从属性值 object 中随机选取 count 个属性。
<span class="token number">2</span>. 'name|min-max'<span class="token operator">:</span> object
从属性值 object 中随机选取 min 到 max 个属性。
</code></pre><h4 class="subject" id="5._属性值是数组_Array">5. 属性值是数组 Array <a class="hashlink" href="#5._属性值是数组_Array">#</a></h4><pre><code><span class="token number">1</span>. 'name|<span class="token number">1</span>'<span class="token operator">:</span> array
</code></pre><h4 class="subject" id="5. 属性值是数组 Array">5. 属性值是数组 Array <a class="hashlink" href="#5. 属性值是数组 Array">#</a></h4><pre><code><span class="token number">1</span>. 'name|<span class="token number">1</span>'<span class="token operator">:</span> array
从属性值 array 中随机选取 <span class="token number">1</span> 个元素,作为最终值。
@ -170,7 +170,7 @@ Mock.mock(<span class="token punctuation">{</span>
通过重复属性值 array 生成一个新数组,重复次数为 count。
</code></pre><p><span id = "DPD"></span></p>
<h3 class="subject" id="数据占位符定义规范Data_Placeholder_DefinitionDPD">数据占位符定义规范Data Placeholder DefinitionDPD <a class="hashlink" href="#数据占位符定义规范Data_Placeholder_DefinitionDPD">#</a></h3><pre><code>占位符 只是在属性值字符串中占个位置,并不出现在最终的属性值中。
<h3 class="subject" id="数据占位符定义规范Data Placeholder DefinitionDPD">数据占位符定义规范Data Placeholder DefinitionDPD <a class="hashlink" href="#数据占位符定义规范Data Placeholder DefinitionDPD">#</a></h3><pre><code>占位符 只是在属性值字符串中占个位置,并不出现在最终的属性值中。
占位符 的格式为:
@ -240,8 +240,8 @@ name<span class="token operator">:</span> <span class="token punctuation">{</spa
var $this = $(this);
$this.removeClass('extend').addClass('fold');
$this.html('折叠代码');
$this.parent().children('.js-code').height('auto');
$this.parent().height('auto');
$this.prev().height('auto');
$this.prev().parent().height('auto');
});
$('.ydoc-example').delegate('.fold', 'click', function() {
var $this = $(this);
@ -249,7 +249,7 @@ name<span class="token operator">:</span> <span class="token punctuation">{</spa
EXAMPLE_MAX_HEIGHT = lineHeight * (foldnumber || 6);
$this.removeClass('fold').addClass('extend');
$this.parent().height(EXAMPLE_MAX_HEIGHT); // pre
$this.parent().children('.js-code').height(EXAMPLE_MAX_HEIGHT); // code
$this.prev().height(EXAMPLE_MAX_HEIGHT); // code
$this.html("展开更多……");
});
}

@ -129,8 +129,7 @@ $(document).ready(function() {
});
$('.markdown-body pre').map(function(i, item) {
$(item).addClass('ydoc-example').append('<div class="ui-copy js-copy" data-clipboard-action="copy" data-clipboard-target=".js-code-' + i + '" data-copy-number="' + i + '">copy</div><div class="copy-tip copy-tip-' + i + '">已复制</div>');
$(item).children('code').addClass('js-code-'+i);
$(item).addClass('ydoc-example');
});
var winHeight = $(window).height() - 44,
@ -214,17 +213,4 @@ $(document).ready(function() {
return false;
}
}
// 代码复制功能
var clipboard = new Clipboard('.js-copy');
clipboard.on('success', function(e) {
var copyNumber = $(e.trigger).attr('data-copy-number');
$('.copy-tip-'+copyNumber).show();
setTimeout(function() {
$('.copy-tip-'+copyNumber).hide();
}, 1000);
e.clearSelection();
});
});

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -53,7 +53,7 @@ class baseController {
'/api/user/status',
'/api/user/logout'
];
if (ignoreRouter.indexOf(ctx.path) > -1) {
if (ignoreRouter.indexOf(ctx.path) > -1) {
this.$auth = true;
} else {
await this.checkLogin(ctx);
@ -99,7 +99,7 @@ class baseController {
result.server_ip = yapi.WEBCONFIG.server_ip;
return ctx.body = yapi.commons.resReturn(result);
}
return ctx.body = yapi.commons.resReturn(null, 300, 'Please login.');
return ctx.body = yapi.commons.resReturn(null, 40011, '请登录...');
}
getRole() {
@ -109,24 +109,18 @@ class baseController {
getUsername() {
return this.$user.username;
}
/**
*
* @param {*} id type对应的id
* @param {*} type enum[interface, project, group]
* @param {*} action enum[ danger , edit ] danger只有owner或管理员才能操作,edit只要是dev或以上就能执行
*/
async checkAuth(id, type, action) {
async getProjectRole(id, type) {
let result = {};
try {
if (this.getRole() === 'admin') {
return true;
return 'admin';
}
if (type === 'interface') {
let interfaceInst = yapi.getInst(interfaceModel);
let interfaceData = await interfaceInst.get(id)
result.interfaceData = interfaceData;
if (interfaceData.uid === this.getUid()) {
return true;
return 'owner';
}
type = 'project';
id = interfaceData.project_id;
@ -134,10 +128,10 @@ class baseController {
if (type === 'project') {
let projectInst = yapi.getInst(projectModel);
let projectData = await projectInst.get(id);
if(projectData.uid === this.getUid()){
return true;
}
let projectData = await projectInst.get(id);
if (projectData.uid === this.getUid()) {
return 'owner';
}
let memberData = _.find(projectData.members, (m) => {
if (m.uid === this.getUid()) {
return true;
@ -145,11 +139,10 @@ class baseController {
})
if (memberData && memberData.role) {
if(action === 'danger' && memberData.role === 'owner'){
return true;
}
if(action === 'edit'){
return true;
if (memberData.role === 'owner') {
return 'owner';
}else {
return 'dev';
}
}
type = 'group';
@ -165,22 +158,40 @@ class baseController {
}
})
if (groupMemberData && groupMemberData.role) {
if(action === 'danger' && groupMemberData.role === 'owner'){
return true;
}
if(action === 'edit'){
return true;
if (groupMemberData.role === 'owner') {
return 'owner';
}else{
return 'dev'
}
}
}
return false;
return 'member';
}
catch (e) {
yapi.commons.log(e.message, 'error')
return false;
}
}
/**
*
* @param {*} id type对应的id
* @param {*} type enum[interface, project, group]
* @param {*} action enum[ danger , edit ] 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;
}
}
return false;
}
}
module.exports = baseController;

@ -36,6 +36,33 @@ class groupController extends baseController {
super(ctx);
}
/**
* 添加项目分组
* @interface /group/get
* @method GET
* @category group
* @foldnumber 10
* @param {String} id 项目分组ID
* @returns {Object}
* @example
*/
async get(ctx) {
try {
let params = ctx.request.query;
if (!params.id) {
return ctx.body = yapi.commons.resReturn(null, 400, '分组id不能为空');
}
let groupInst = yapi.getInst(groupModel);
let result = await groupInst.getGroupById(params.id);
result = result.toObject();
result.role = await this.getProjectRole(params.id, 'group');
ctx.body = yapi.commons.resReturn(result);
} catch (e) {
ctx.body = yapi.commons.resReturn(null, 400, e.message)
}
}
/**
* 添加项目分组
* @interface /group/add
@ -43,9 +70,9 @@ class groupController extends baseController {
* @category group
* @foldnumber 10
* @param {String} group_name 项目分组名称,不能为空
* @param {String} [group_desc] 项目分组描述
* @param {String} [group_desc] 项目分组描述
* @param {String} owner_uid 组长uid
* @returns {Object}
* @returns {Object}
* @example ./api/group/add.json
*/
async add(ctx) {
@ -65,12 +92,12 @@ class groupController extends baseController {
return ctx.body = yapi.commons.resReturn(null, 400, '项目分组名不能为空');
}
if(!params.owner_uid){
if (!params.owner_uid) {
return ctx.body = yapi.commons.resReturn(null, 400, '项目分组必须添加一个组长');
}
let groupUserdata = await this.getUserdata(params.owner_uid, 'owner');
if(groupUserdata === null){
if (groupUserdata === null) {
return ctx.body = yapi.commons.resReturn(null, 400, '组长uid不存在')
}
let groupInst = yapi.getInst(groupModel);
@ -101,14 +128,15 @@ class groupController extends baseController {
}
async getUserdata(uid, role){
async getUserdata(uid, role) {
role = role || 'dev';
let userInst = yapi.getInst(userModel);
let userData = await userInst.findById(uid);
if(!userData){
if (!userData) {
return null;
}
return {
_role: userData.role,
role: role,
uid: userData._id,
username: userData.username,
@ -116,7 +144,22 @@ class groupController extends baseController {
}
}
async addMember(ctx){
/**
* 添加项目分组成员
* @interface /group/add_member
* @method POST
* @category group
* @foldnumber 10
* @param {String} id 项目分组id
* @param {String} member_uid 项目分组成员uid
* @returns {Object}
* @example
*/
async addMember(ctx){
let params = ctx.request.body;
let groupInst = yapi.getInst(groupModel);
if (!params.member_uid) {
@ -131,9 +174,13 @@ class groupController extends baseController {
return ctx.body = yapi.commons.resReturn(null, 400, '成员已存在');
}
let groupUserdata = await this.getUserdata(params.member_uid);
if(groupUserdata === null){
if (groupUserdata === null) {
return ctx.body = yapi.commons.resReturn(null, 400, '组长uid不存在')
}
if (groupUserdata._role === 'admin') {
return ctx.body = yapi.commons.resReturn(null, 400, '不能邀请管理员')
}
delete groupUserdata._role;
try {
let result = await groupInst.addMember(params.id, groupUserdata);
ctx.body = yapi.commons.resReturn(result);
@ -142,7 +189,20 @@ class groupController extends baseController {
}
}
async changeMemberRole(ctx){
/**
* 修改项目分组成员角色
* @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);
if (!params.member_uid) {
@ -155,7 +215,7 @@ class groupController extends baseController {
if (check === 0) {
return ctx.body = yapi.commons.resReturn(null, 400, '分组成员不存在');
}
if (await this.checkAuth(id, 'group', 'danger') !== true) {
if (await this.checkAuth(params.id, 'group', 'danger') !== true) {
return ctx.body = yapi.commons.resReturn(null, 405, '没有权限');
}
@ -168,6 +228,16 @@ class groupController extends baseController {
ctx.body = yapi.commons.resReturn(null, 402, e.message);
}
}
/**
* 获取所有项目成员
* @interface /group/get_member_list
* @method GET
* @category group
* @foldnumber 10
* @param {String} id 项目分组id
* @returns {Object}
* @example
*/
async getMemberList(ctx) {
let params = ctx.request.query;
@ -184,6 +254,18 @@ class groupController extends baseController {
}
}
/**
* 删除项目成员
* @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.request.body;
let groupInst = yapi.getInst(groupModel);
@ -197,7 +279,7 @@ class groupController extends baseController {
if (check === 0) {
return ctx.body = yapi.commons.resReturn(null, 400, '分组成员不存在');
}
if (await this.checkAuth(id, 'group', 'danger') !== true) {
if (await this.checkAuth(params.id, 'group', 'danger') !== true) {
return ctx.body = yapi.commons.resReturn(null, 405, '没有权限');
}
@ -215,7 +297,7 @@ class groupController extends baseController {
* @method get
* @category group
* @foldnumber 10
* @returns {Object}
* @returns {Object}
* @example ./api/group/list.json
*/
async list(ctx) {
@ -235,7 +317,7 @@ class groupController extends baseController {
* @param {String} id 项目分组id
* @category group
* @foldnumber 10
* @returns {Object}
* @returns {Object}
* @example ./api/group/del.json
*/
async del(ctx) {
@ -274,7 +356,7 @@ class groupController extends baseController {
* @param {String} group_desc 项目分组描述
* @category group
* @foldnumber 10
* @returns {Object}
* @returns {Object}
* @example ./api/group/up.json
*/
async up(ctx) {
@ -304,6 +386,7 @@ class groupController extends baseController {
}
module.exports = groupController;
</pre>
</div>
</div>

@ -50,12 +50,13 @@ class interfaceController extends baseController {
* @param {String} [req_headers[].value] 请求的header信息值
* @param {Boolean} [req_headers[].required] 是否是必须,默认为否
* @param {String} [req_headers[].desc] header描述
* @param {String} [req_params_type] 请求参数方式,有["form", "json", "text", "xml"]四种
* @param {Mixed} [req_params_form] 请求参数,如果请求方式是form参数是Array数组其他格式请求参数是字符串
* @param {String} [req_params_form[].name] 请求参数名
* @param {String} [req_params_form[].value] 请求参数值可填写生成规则mock。如@email随机生成一条email
* @param {String} [req_params_form[].type] 请求参数类型,有["text", "file"]两种
* @param {String} [req_params_other] 非form类型的请求参数可保存到此字段
* @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] 接口描述
@ -102,7 +103,7 @@ class interfaceController extends baseController {
desc: params.desc,
method: params.method,
req_headers: params.req_headers,
req_params_type: params.req_params_type,
req_body_type: params.req_body_type,
res_body: params.res_body,
res_body_type: params.res_body_type,
uid: this.getUid(),
@ -110,11 +111,18 @@ class interfaceController extends baseController {
up_time: yapi.commons.time()
};
if (params.req_params_form) {
data.req_params_form = params.req_params_form;
if (params.req_query) {
data.req_query = params.req_query;
}
if (params.req_params_other) {
data.req_params_other = params.req_params_other;
if (params.req_body_form) {
data.req_body_form = params.req_body_form;
}
if (params.req_params) {
data.req_params = params.req_params;
}
if (params.req_body_other) {
data.req_body_other = params.req_body_other;
}
let result = await this.Model.save(data);
@ -188,12 +196,12 @@ class interfaceController extends baseController {
* @param {String} [req_headers[].value] 请求的header信息值
* @param {Boolean} [req_headers[].required] 是否是必须,默认为否
* @param {String} [req_headers[].desc] header描述
* @param {String} [req_params_type] 请求参数方式,有["form", "json", "text", "xml"]四种
* @param {Mixed} [req_params_form] 请求参数,如果请求方式是form参数是Array数组其他格式请求参数是字符串
* @param {String} [req_params_form[].name] 请求参数名
* @param {String} [req_params_form[].value] 请求参数值可填写生成规则mock。如@email随机生成一条email
* @param {String} [req_params_form[].type] 请求参数类型,有["text", "file"]两种
* @param {String} [req_params_other] 非form类型的请求参数可保存到此字段
* @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] 接口描述
@ -214,7 +222,7 @@ class interfaceController extends baseController {
params.method = params.method.toUpperCase();
let id = ctx.request.body.id;
if (!id) {
return ctx.body = yapi.commons.resReturn(null, 400, '接口id不能为空');
}
@ -252,11 +260,17 @@ class interfaceController extends baseController {
data.req_headers = params.req_headers;
}
if (params.req_params_form) {
data.req_params_form = params.req_params_form;
if (params.req_body_form) {
data.req_body_form = params.req_body_form;
}
if (params.req_params_other) {
data.req_params_other = params.req_params_other;
if (params.req_params) {
data.req_params = params.req_params;
}
if (params.req_query) {
data.req_query = params.req_query;
}
if (params.req_body_other) {
data.req_body_other = params.req_body_other;
}
if (params.res_body_type) {

@ -44,14 +44,18 @@ class interfaceColController extends baseController{
* @category col
* @foldnumber 10
* @param {String} project_id email名称不能为空
* @returns {Object}
* @example
* @returns {Object}
* @example
*/
async list(ctx){
try {
let id = ctx.query.project_id;
let inst = this.colModel(interfaceColModel);
let result = await inst.list(id);
let result = await this.colModel.list(id);
for(let i=0; i< result.length;i++){
result[i] = result[i].toObject();
result[i].caseList = await this.caseModel.list(result[i]._id)
}
ctx.body = yapi.commons.resReturn(result);
} catch (e) {
ctx.body = yapi.commons.resReturn(null, 402, e.message);
@ -67,8 +71,8 @@ class interfaceColController extends baseController{
* @param {Number} project_id
* @param {String} name
* @param {String} desc
* @returns {Object}
* @example
* @returns {Object}
* @example
*/
async addCol(ctx){
@ -79,14 +83,14 @@ class interfaceColController extends baseController{
project_id: 'number',
desc: 'string'
});
if (!params.project_id) {
return ctx.body = yapi.commons.resReturn(null, 400, '项目id不能为空');
return ctx.body = yapi.commons.resReturn(null, 400, '项目id不能为空');
}
if(!params.name){
return ctx.body = yapi.commons.resReturn(null, 400, '名称不能为空');
return ctx.body = yapi.commons.resReturn(null, 400, '名称不能为空');
}
let result = await this.colModel.save({
name: params.name,
project_id: params.project_id,
@ -109,8 +113,8 @@ class interfaceColController extends baseController{
* @category col
* @foldnumber 10
* @param {String} col_id 接口集id
* @returns {Object}
* @example
* @returns {Object}
* @example
*/
async getCaseList(ctx){
@ -133,17 +137,17 @@ class interfaceColController extends baseController{
* @param {String} casename
* @param {Number} col_id
* @param {Number} project_id
* @param {String} env
* @param {String} env
* @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 {Array} req_body_form
* @param {String} req_body_other
* @returns {Object}
* @example
* @returns {Object}
* @example
*/
async addCase(ctx){
@ -157,23 +161,17 @@ class interfaceColController extends baseController{
domain: 'string',
method: 'string'
});
if (!params.project_id) {
return ctx.body = yapi.commons.resReturn(null, 400, '项目id不能为空');
return ctx.body = yapi.commons.resReturn(null, 400, '项目id不能为空');
}
if (!params.col_id) {
return ctx.body = yapi.commons.resReturn(null, 400, '项目id不能为空');
return ctx.body = yapi.commons.resReturn(null, 400, '接口集id不能为空');
}
if (!params.env) {
return ctx.body = yapi.commons.resReturn(null, 400, '缺少环境配置');
}
if (!params.path) {
return ctx.body = yapi.commons.resReturn(null, 400, 'path 不能为空');
}
if(!params.casename){
return ctx.body = yapi.commons.resReturn(null, 400, '用例名称不能为空');
return ctx.body = yapi.commons.resReturn(null, 400, '用例名称不能为空');
}
params.uid = this.getUid();
@ -181,7 +179,7 @@ class interfaceColController extends baseController{
params.add_time = yapi.commons.time();
params.up_time = yapi.commons.time();
let result = await this.caseModel.save(params);
ctx.body = yapi.commons.resReturn(result);
}catch(e){
@ -196,8 +194,8 @@ class interfaceColController extends baseController{
* @category col
* @foldnumber 10
* @param {String} caseid
* @returns {Object}
* @example
* @returns {Object}
* @example
*/
async getCase(ctx){
@ -218,8 +216,8 @@ class interfaceColController extends baseController{
* @foldnumber 10
* @param {String} name
* @param {String} desc
* @returns {Object}
* @example
* @returns {Object}
* @example
*/
async upCol(ctx){
@ -243,8 +241,8 @@ class interfaceColController extends baseController{
* @category col
* @foldnumber 10
* @param {Array} [id, index]
* @returns {Object}
* @example
* @returns {Object}
* @example
*/
async upCaseIndex(ctx){
@ -259,9 +257,9 @@ class interfaceColController extends baseController{
yapi.commons.log(err.message, 'error')
})
}
})
return ctx.body = yapi.commons.resReturn('success')
}catch(e){
ctx.body = yapi.commons.resReturn(null, 400, e.message)
@ -274,9 +272,9 @@ class interfaceColController extends baseController{
* @method GET
* @category col
* @foldnumber 10
* @param {String}
* @returns {Object}
* @example
* @param {String}
* @returns {Object}
* @example
*/
async delCol(ctx){
@ -304,8 +302,8 @@ class interfaceColController extends baseController{
}
/**
*
* @param {*} ctx
*
* @param {*} ctx
*/
async delCase(ctx){
@ -336,6 +334,7 @@ class interfaceColController extends baseController{
}
module.exports = interfaceColController
</pre>
</div>
</div>

@ -72,8 +72,6 @@ class projectController extends baseController {
* @foldnumber 10
* @param {String} name 项目名称,不能为空
* @param {String} basepath 项目基本路径,不能为空
* @param {String} prd_host 项目线上域名不能为空。可通过配置的域名访问到mock数据
* @param {String} protocol 线上域名协议,不能为空
* @param {Number} group_id 项目分组id不能为空
* @param {String} project_type private public
* @param {String} [desc] 项目描述
@ -85,8 +83,6 @@ class projectController extends baseController {
params = yapi.commons.handleParams(params, {
name: 'string',
basepath: 'string',
prd_host: 'string',
protocol: 'string',
group_id: 'number',
desc: 'string'
});
@ -109,35 +105,16 @@ class projectController extends baseController {
return ctx.body = yapi.commons.resReturn(null, 401, '已存在的项目名');
}
if (!params.prd_host) {
return ctx.body = yapi.commons.resReturn(null, 400, '项目domain不能为空');
}
params.basepath = params.basepath || '';
if ((params.basepath = this.handleBasepath(params.basepath)) === false) {
return ctx.body = yapi.commons.resReturn(null, 401, 'basepath格式有误');
}
if (!this.verifyDomain(params.prd_host)) {
return ctx.body = yapi.commons.resReturn(null, 401, '线上域名格式有误');
}
let checkRepeatDomain = await this.Model.checkDomainRepeat(params.prd_host, params.basepath);
if (checkRepeatDomain > 0) {
return ctx.body = yapi.commons.resReturn(null, 401, '已存在domain和basepath');
}
let data = {
name: params.name,
desc: params.desc,
prd_host: params.prd_host,
basepath: params.basepath,
protocol: params.protocol || 'http',
members: [],
project_type: params.project_type || 'private',
uid: this.getUid(),
@ -322,6 +299,9 @@ class projectController extends baseController {
}
try {
let result = await this.Model.get(params.id);
result = result.toObject();
delete result.members;
result.role = await this.getProjectRole(params.id, 'project');
ctx.body = yapi.commons.resReturn(result);
} catch (e) {
ctx.body = yapi.commons.resReturn(null, 402, e.message);
@ -361,8 +341,7 @@ class projectController extends baseController {
_users[item._id] = item;
});
ctx.body = yapi.commons.resReturn({
list: result,
userinfo: _users
list: result
});
} catch (e) {
ctx.body = yapi.commons.resReturn(null, 402, e.message);
@ -453,7 +432,6 @@ class projectController extends baseController {
* @param {Number} id 项目id不能为空
* @param {String} name 项目名称,不能为空
* @param {String} basepath 项目基本路径,不能为空
* @param {String} prd_host 项目线上域名不能为空。可通过配置的域名访问到mock数据
* @param {String} [desc] 项目描述
* @param {Array} [env] 项目环境配置
* @param {String} [env[].name] 环境名称
@ -470,8 +448,6 @@ class projectController extends baseController {
params = yapi.commons.handleParams(params, {
name: 'string',
basepath: 'string',
prd_host: 'string',
protocol: 'string',
group_id: 'number',
desc: 'string'
});
@ -489,17 +465,9 @@ class projectController extends baseController {
return ctx.body = yapi.commons.resReturn(null, 401, 'basepath格式有误');
}
if (!this.verifyDomain(params.prd_host)) {
return ctx.body = yapi.commons.resReturn(null, 401, '线上域名格式有误');
}
if (projectData.name === params.name) {
delete params.name;
}
if (projectData.basepath === params.basepath && projectData.prd_host === params.prd_host) {
delete params.basepath;
delete params.prd_host;
}
if (params.name) {
let checkRepeat = await this.Model.checkNameRepeat(params.name);
@ -508,24 +476,15 @@ class projectController extends baseController {
}
}
if (params.basepath && params.prd_host) {
let checkRepeatDomain = await this.Model.checkDomainRepeat(params.prd_host, params.basepath);
if (checkRepeatDomain > 0) {
return ctx.body = yapi.commons.resReturn(null, 401, '已存在domain和basepath');
}
}
let data = {
up_time: yapi.commons.time()
};
if (params.name) data.name = params.name;
if (params.desc) data.desc = params.desc;
if (params.prd_host ) {
data.prd_host = params.prd_host;
if (params.basepath ) {
data.basepath = params.basepath;
}
if (params.protocol) data.protocol = params.protocol;
if (params.env) data.env = params.env;
let result = await this.Model.up(id, data);
@ -577,7 +536,6 @@ class projectController extends baseController {
'members',
{ key: 'group_id', alias: 'groupId' },
{ key: 'up_time', alias: 'upTime' },
{ key: 'prd_host', alias: 'prdHost' },
{ key: 'add_time', alias: 'addTime' }
];
let groupRules = [