Merge branch 'dev' of gitlab.corp.qunar.com:mfe/yapi into dev

This commit is contained in:
zwjamnsss 2017-08-17 11:41:04 +08:00
commit 14311f1637
23 changed files with 376 additions and 111 deletions

View File

@ -1,6 +1,8 @@
import React, { Component } from 'react' import React, { Component } from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import { Row, Col, Tabs } from 'antd'; import { Row, Col, Tabs } from 'antd';
import { Route } from 'react-router-dom';
import './interface.scss' import './interface.scss'
import InterfaceMenu from './InterfaceList/InterfaceMenu.js' import InterfaceMenu from './InterfaceList/InterfaceMenu.js'
@ -9,37 +11,46 @@ import InterfaceContent from './InterfaceList/InterfaceContent.js'
import InterfaceColMenu from './InterfaceCol/InterfaceColMenu.js' import InterfaceColMenu from './InterfaceCol/InterfaceColMenu.js'
import InterfaceColContent from './InterfaceCol/InterfaceColContent.js' import InterfaceColContent from './InterfaceCol/InterfaceColContent.js'
const InterfaceRoute = (props) => {
let C;
if (props.match.params.action === 'api') {
C = InterfaceContent;
} else if (props.match.params.action === 'col') {
C = InterfaceColContent;
}
return <C />
}
InterfaceRoute.propTypes = {
match: PropTypes.object
}
class Interface extends Component { class Interface extends Component {
static propTypes = { static propTypes = {
match: PropTypes.object match: PropTypes.object
} }
constructor(props) { constructor(props) {
super(props) super(props)
this.state = { this.state = {
contentView: 'list' curkey: this.props.match.params.action
} }
} }
handleTab = (key) => { onChange = (key) => {
this.setState({ this.setState({
contentView: key curkey: key
}) })
} }
render() { render() {
const {contentView} = this.state;
let content;
content = contentView === 'list' ?
<InterfaceContent />
:
<InterfaceColContent />
return <div className="web-content g-row" style={{ marginBottom: "15px" }}> return <div className="web-content g-row" style={{ marginBottom: "15px" }}>
<Row gutter={16} > <Row gutter={16} >
<Col span={6}> <Col span={6}>
<div className="left-menu"> <div className="left-menu">
<Tabs defaultActiveKey="list" type="card" onChange={this.handleTab}> <Tabs type="card" activeKey={this.state.curkey} onChange={this.onChange}>
<Tabs.TabPane tab="接口列表" key="list"> <Tabs.TabPane tab="接口列表" key="api">
<InterfaceMenu projectId={this.props.match.params.id} /> <InterfaceMenu projectId={this.props.match.params.id} />
</Tabs.TabPane> </Tabs.TabPane>
<Tabs.TabPane tab="接口集合" key="col" > <Tabs.TabPane tab="接口集合" key="col" >
@ -48,10 +59,11 @@ class Interface extends Component {
</Tabs> </Tabs>
</div> </div>
</Col> </Col>
<Col span={18} > <Col span={18} >
<div className="right-content"> <div className="right-content">
{content} <Route path="/project/:id/interface/:action/:actionId" component={InterfaceRoute} />
</div> </div>
</Col> </Col>
</Row> </Row>
@ -59,4 +71,6 @@ class Interface extends Component {
} }
} }
export default Interface export default Interface

View File

@ -44,6 +44,28 @@ class InterfaceEdit extends Component{
} }
} }
componentWillMount(){
let s = new WebSocket('ws://yapi.local.qunar.com:3000/api/interface/solve_conflict?id=1');
s.onopen = (e)=>{
console.log('open',e)
s.send('abc')
}
s.onclose = (e)=>{
console.log('close',e)
}
s.onmessage = (e)=>{
console.log('message',e)
}
s.onerror = (e)=>{
console.log('error',e)
}
}
render(){ render(){
return <div className="interface-edit"> return <div className="interface-edit">
<InterfaceEditForm mockUrl={this.state.mockUrl} basepath={this.props.currProject.basepath} onSubmit={this.onSubmit} curdata={this.props.curdata} /> <InterfaceEditForm mockUrl={this.state.mockUrl} basepath={this.props.currProject.basepath} onSubmit={this.onSubmit} curdata={this.props.curdata} />

View File

@ -1,25 +1,93 @@
import React from 'react' import React, { Component } from 'react'
import { connect } from 'react-redux';
import PropTypes from 'prop-types'
import { Tabs } from 'antd'; import { Tabs } from 'antd';
import Edit from './Edit.js' import Edit from './Edit.js'
import View from './View.js' import View from './View.js'
import { fetchInterfaceData } from '../../../../reducer/modules/interface.js';
import { withRouter } from 'react-router-dom';
import Run from './Run/Run.js' import Run from './Run/Run.js'
const TabPane = Tabs.TabPane;
const Content = () => { const TabPane = Tabs.TabPane;
return <div className="interface-content"> @connect(
<Tabs defaultActiveKey="1" > state => {
<TabPane tab="预览" key="1"> return {
<View /> curdata: state.inter.curdata,
list: state.inter.list
}
},
{
fetchInterfaceData
}
)
class Content extends Component {
static propTypes = {
match: PropTypes.object,
list: PropTypes.array,
curdata: PropTypes.object,
fetchInterfaceData: PropTypes.func
}
constructor(props) {
super(props)
this.state = {
curtab: 'view'
}
this._actionId = 0;
}
componentWillReceiveProps(nextProps){
this.handleRequest(nextProps)
}
handleRequest(nextProps){
let matchParams = nextProps.match.params;
let _actionId;
_actionId = matchParams.actionId;
_actionId = parseInt(matchParams.actionId, 10);
if(!nextProps.curdata)return;
if(this._actionId !== _actionId){
this._actionId = _actionId;
this.props.fetchInterfaceData(_actionId)
}
this.setState({
curtab: 'view'
})
}
onChange = (key)=>{
this.setState({
curtab: key
})
}
render() {
const tabs = <Tabs onChange={this.onChange} activeKey={this.state.curtab} defaultActiveKey="view" >
<TabPane tab="预览" key="view">
{/* <View /> */}
</TabPane> </TabPane>
<TabPane tab="编辑" key="2"> <TabPane tab="编辑" key="edit">
<Edit />
</TabPane> </TabPane>
<TabPane tab="运行" key="3"> <TabPane tab="运行" key="run">
<Run /> {/* <Run /> */}
</TabPane> </TabPane>
</Tabs> </Tabs>;
</div> let tabContent;
if (this.state.curtab === 'view') {
tabContent = <View />;
} else if (this.state.curtab === 'edit') {
tabContent = <Edit />
} else if (this.state.curtab === 'run') {
tabContent = <Run />
}
return <div className="interface-content">
{tabs}
{tabContent}
</div>
}
} }
export default Content export default withRouter(Content)

View File

@ -1,10 +1,11 @@
import React, { Component } from 'react' import React, { Component } from 'react'
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import { fetchInterfaceList, fetchInterfaceData, changeInterfaceId, addInterfaceData, deleteInterfaceData } from '../../../../reducer/modules/interface.js'; import { fetchInterfaceList, fetchInterfaceData, addInterfaceData, deleteInterfaceData } from '../../../../reducer/modules/interface.js';
import { Menu, Input, Icon, Tag, Modal, message } from 'antd'; import { Menu, Input, Icon, Tag, Modal, message } from 'antd';
import AddInterfaceForm from './AddInterfaceForm'; import AddInterfaceForm from './AddInterfaceForm';
import axios from 'axios' import axios from 'axios'
import { Link,withRouter } from 'react-router-dom';
const confirm = Modal.confirm; const confirm = Modal.confirm;
@ -14,29 +15,27 @@ const confirm = Modal.confirm;
state => { state => {
return { return {
list: state.inter.list, list: state.inter.list,
curProject: state.project.curProject, curProject: state.project.curProject
interfaceId: state.inter.interfaceId
} }
}, },
{ {
fetchInterfaceList, fetchInterfaceList,
fetchInterfaceData, fetchInterfaceData,
changeInterfaceId,
addInterfaceData, addInterfaceData,
deleteInterfaceData deleteInterfaceData
} }
) )
class InterfaceMenu extends Component { class InterfaceMenu extends Component {
static propTypes = { static propTypes = {
match: PropTypes.object,
projectId: PropTypes.string, projectId: PropTypes.string,
interfaceId: PropTypes.number,
list: PropTypes.array, list: PropTypes.array,
fetchInterfaceList: PropTypes.func, fetchInterfaceList: PropTypes.func,
curProject: PropTypes.object, curProject: PropTypes.object,
fetchInterfaceData: PropTypes.func, fetchInterfaceData: PropTypes.func,
changeInterfaceId: PropTypes.func,
addInterfaceData: PropTypes.func, addInterfaceData: PropTypes.func,
deleteInterfaceData: PropTypes.func deleteInterfaceData: PropTypes.func,
history: PropTypes.object
} }
showModal = () => { showModal = () => {
@ -59,26 +58,21 @@ class InterfaceMenu extends Component {
delIcon: null, delIcon: null,
filter: '' filter: ''
} }
} }
async handleRequest() { async handleRequest() {
let result = await this.props.fetchInterfaceList(this.props.projectId); let result = await this.props.fetchInterfaceList(this.props.projectId);
let interfaces = result.payload.data; let params = this.props.match.params;
if (interfaces.length > 0) { if(!params.actionId){
this.props.changeInterfaceId(interfaces[0]._id) this.props.history.replace('/project/'+params.id + '/interface/api/' + result.payload.data[0]._id)
await this.props.fetchInterfaceData(interfaces[0]._id)
} }
} }
componentWillMount() { componentWillMount() {
this.handleRequest() this.handleRequest()
} }
onSelect = (item) => {
this.props.changeInterfaceId(parseInt(item.key, 10))
this.props.fetchInterfaceData(parseInt(item.key, 10))
}
handleAddInterface = (data) => { handleAddInterface = (data) => {
data.project_id = this.props.projectId; data.project_id = this.props.projectId;
axios.post('/api/interface/add', data).then((res) => { axios.post('/api/interface/add', data).then((res) => {
@ -127,6 +121,7 @@ class InterfaceMenu extends Component {
render() { render() {
const items = []; const items = [];
const matchParams = this.props.match.params;
this.props.list.forEach((item) => { this.props.list.forEach((item) => {
let color, filter = this.state.filter; let color, filter = this.state.filter;
if(filter && item.title.indexOf(filter) === -1 && item.path.indexOf(filter) === -1){ if(filter && item.title.indexOf(filter) === -1 && item.path.indexOf(filter) === -1){
@ -141,9 +136,10 @@ class InterfaceMenu extends Component {
} }
items.push( items.push(
<Menu.Item onMouseEnter={this.enterItem} onMouseLeave={this.leaveItem} key={"" + item._id}> <Menu.Item onMouseEnter={this.enterItem} onMouseLeave={this.leaveItem} key={"" + item._id}>
<Tag className="btn-http" color={color}>{item.method} </Tag> <Tag className="btn-http" color={color}>{item.method} </Tag>
{item.title} <Link className="interface-item" to={"/project/" + matchParams.id + "/interface/api/" + item._id} >{item.title}</Link>
<Icon type="delete" onClick={()=> {this.showConfirm(item._id)}} style={{ display: this.state.delIcon == item._id ? 'block' : 'none' }} className="interface-delete-icon" /> <Icon type="delete" onClick={()=> {this.showConfirm(item._id)}} style={{ display: this.state.delIcon == item._id ? 'block' : 'none' }} className="interface-delete-icon" />
</Menu.Item> </Menu.Item>
) )
@ -163,7 +159,7 @@ class InterfaceMenu extends Component {
<AddInterfaceForm onCancel={this.handleCancel} onSubmit={this.handleAddInterface} /> <AddInterfaceForm onCancel={this.handleCancel} onSubmit={this.handleAddInterface} />
</Modal> </Modal>
</div> </div>
<Menu selectedKeys={[this.props.interfaceId + ""]} className="interface-list" onSelect={this.onSelect}> <Menu selectedKeys={[this.props.match.params.actionId + ""]} className="interface-list">
{items} {items}
</Menu> </Menu>
</div> </div>
@ -171,4 +167,4 @@ class InterfaceMenu extends Component {
} }
} }
export default InterfaceMenu export default withRouter(InterfaceMenu)

View File

@ -45,6 +45,9 @@
padding: 0 5px; padding: 0 5px;
width: 40px; width: 40px;
} }
.interface-item{
display: inline
}
.interface-delete-icon{ .interface-delete-icon{
position: absolute; position: absolute;

View File

@ -43,7 +43,7 @@ export default class Project extends Component {
default={'动态'} default={'动态'}
data={[{ data={[{
name: '接口', name: '接口',
path: `/project/${match.params.id}/interface` path: `/project/${match.params.id}/interface/api`
}, { }, {
name: '设置', name: '设置',
path: `/project/${match.params.id}/setting` path: `/project/${match.params.id}/setting`
@ -54,7 +54,7 @@ export default class Project extends Component {
<Switch> <Switch>
<Redirect exact from="/project/:id" to={`/project/${match.params.id}/activity`}/> <Redirect exact from="/project/:id" to={`/project/${match.params.id}/activity`}/>
<Route path="/project/:id/activity" component={Activity} /> <Route path="/project/:id/activity" component={Activity} />
<Route path="/project/:id/interface" component={Interface} /> <Route path="/project/:id/interface/:action" component={Interface} />
<Route path="/project/:id/setting" component={Setting} /> <Route path="/project/:id/setting" component={Setting} />
</Switch> </Switch>
</div> </div>

View File

@ -1,6 +1,7 @@
import { message } from 'antd'; import { message } from 'antd';
export default () => next => action => { export default () => next => action => {
if(!action) return;
if (action.error) { if (action.error) {
message.error((action.payload && action.payload.message) || '服务器错误'); message.error((action.payload && action.payload.message) || '服务器错误');
} else if (action.payload && action.payload.data && action.payload.data.errcode && action.payload.data.errcode !== 40011) { } else if (action.payload && action.payload.data && action.payload.data.errcode && action.payload.data.errcode !== 40011) {

View File

@ -2,7 +2,6 @@ import axios from 'axios'
// Actions // Actions
const FETCH_INTERFACE_DATA = 'yapi/interface/FETCH_INTERFACE_DATA'; const FETCH_INTERFACE_DATA = 'yapi/interface/FETCH_INTERFACE_DATA';
const FETCH_INTERFACE_LIST = 'yapi/interface/FETCH_INTERFACE_LIST'; const FETCH_INTERFACE_LIST = 'yapi/interface/FETCH_INTERFACE_LIST';
const CHANGE_INTERFACE_ID = 'yapi/interface/CHANGE_INTERFACE_ID';
const ADD_INTERFACE_DATA = 'yapi/interface/ADD_INTERFACE_DATA'; const ADD_INTERFACE_DATA = 'yapi/interface/ADD_INTERFACE_DATA';
const DELETE_INTERFACE_DATA = 'yapi/interface/DELETE_INTERFACE_DATA'; const DELETE_INTERFACE_DATA = 'yapi/interface/DELETE_INTERFACE_DATA';
const UPDATE_INTERFACE_DATA = 'yapi/interface/UPDATE_INTERFACE_DATA'; const UPDATE_INTERFACE_DATA = 'yapi/interface/UPDATE_INTERFACE_DATA';
@ -11,7 +10,6 @@ const UPDATE_INTERFACE_DATA = 'yapi/interface/UPDATE_INTERFACE_DATA';
// Reducer // Reducer
const initialState = { const initialState = {
interfaceId: 0,
curdata: {}, curdata: {},
list: [] list: []
} }
@ -26,20 +24,17 @@ export default (state = initialState, action) => {
case DELETE_INTERFACE_DATA: case DELETE_INTERFACE_DATA:
return (() => { return (() => {
let newlist = state.list.filter(data => data._id !== action.payload), newid, curdata; let newlist = state.list.filter(data => data._id !== action.payload), curdata;
if (state.interfaceId === action.payload && state.list.length > 0) { if (state.list.length > 0) {
newid = state.list[0]._id
curdata = state.list[0] curdata = state.list[0]
} else if (state.list.length == 0) { } else if (state.list.length == 0) {
newid = 0;
curdata = {} curdata = {}
} }
return { return {
...state, ...state,
curdata: curdata, curdata: curdata,
interfaceId: newid,
list: newlist list: newlist
} }
})() })()
@ -48,13 +43,7 @@ export default (state = initialState, action) => {
return { return {
...state, ...state,
curdata: action.payload, curdata: action.payload,
list: [].concat(state.list, action.payload), list: [].concat(state.list, action.payload)
interfaceId: action.payload._id
}
case CHANGE_INTERFACE_ID:
return {
...state,
interfaceId: action.payload
} }
case FETCH_INTERFACE_DATA: case FETCH_INTERFACE_DATA:
return { return {
@ -65,20 +54,13 @@ export default (state = initialState, action) => {
return { return {
...state, ...state,
list: action.payload.data, list: action.payload.data,
curdata: action.payload.data.length > 0 ? action.payload.data[0] : {} curdata: {}
} }
default: default:
return state return state
} }
} }
export function changeInterfaceId(id) {
return {
type: CHANGE_INTERFACE_ID,
payload: id
}
}
export function updateInterfaceData(updata) { export function updateInterfaceData(updata) {
return { return {
@ -94,13 +76,14 @@ export async function deleteInterfaceData(id) {
await axios.post('/api/interface/del', { id: id }) await axios.post('/api/interface/del', { id: id })
return { return {
type: DELETE_INTERFACE_DATA, type: DELETE_INTERFACE_DATA,
payload: id payload: true
} }
} }
// Action Creators // Action Creators
export async function fetchInterfaceData(interfaceId) { export async function fetchInterfaceData(interfaceId) {
let result = await axios.get('/api/interface/get?id=' + interfaceId); let result = await axios.get('/api/interface/get?id=' + interfaceId);
return { return {
type: FETCH_INTERFACE_DATA, type: FETCH_INTERFACE_DATA,
payload: result.data payload: result.data

View File

@ -64,6 +64,7 @@
"koa-session-minimal": "^3.0.3", "koa-session-minimal": "^3.0.3",
"koa-static": "^3.0.0", "koa-static": "^3.0.0",
"koa-views": "^5.2.0", "koa-views": "^5.2.0",
"koa-websocket": "^4.0.0",
"mock": "^0.1.1", "mock": "^0.1.1",
"mockjs": "^1.0.1-beta3", "mockjs": "^1.0.1-beta3",
"moment": "^2.18.1", "moment": "^2.18.1",

View File

@ -7,9 +7,12 @@ import Koa from 'koa';
import koaStatic from 'koa-static'; import koaStatic from 'koa-static';
import bodyParser from 'koa-bodyparser'; import bodyParser from 'koa-bodyparser';
import router from './router.js'; import router from './router.js';
import websockify from 'koa-websocket';
import websocket from './websocket.js'
yapi.connect = dbModule.connect(); yapi.connect = dbModule.connect();
const app = new Koa(); const app = websockify(new Koa());
let indexFile = process.argv[2] === 'dev' ? 'dev.html' : 'index.html'; let indexFile = process.argv[2] === 'dev' ? 'dev.html' : 'index.html';
@ -18,7 +21,7 @@ app.use(bodyParser());
app.use(router.routes()); app.use(router.routes());
app.use(router.allowedMethods()); app.use(router.allowedMethods());
websocket(app);
app.use( async (ctx, next) => { app.use( async (ctx, next) => {
if( /^\/(?!api)[a-zA-Z0-9\/\-]*$/.test(ctx.path) ){ if( /^\/(?!api)[a-zA-Z0-9\/\-]*$/.test(ctx.path) ){

View File

@ -127,6 +127,7 @@ class groupController extends baseController {
* @foldnumber 10 * @foldnumber 10
* @param {String} id 项目分组id * @param {String} id 项目分组id
* @param {String} member_uid 项目分组成员uid * @param {String} member_uid 项目分组成员uid
* @param {String} role 成员角色owner or dev
* @returns {Object} * @returns {Object}
* @example * @example
*/ */
@ -144,11 +145,13 @@ class groupController extends baseController {
return ctx.body = yapi.commons.resReturn(null, 400, '分组id不能为空'); return ctx.body = yapi.commons.resReturn(null, 400, '分组id不能为空');
} }
params.role = params.role === 'owner' ? 'owner' : 'dev';
var check = await groupInst.checkMemberRepeat(params.id, params.member_uid); var check = await groupInst.checkMemberRepeat(params.id, params.member_uid);
if (check > 0) { if (check > 0) {
return ctx.body = yapi.commons.resReturn(null, 400, '成员已存在'); return ctx.body = yapi.commons.resReturn(null, 400, '成员已存在');
} }
let groupUserdata = await this.getUserdata(params.member_uid); let groupUserdata = await this.getUserdata(params.member_uid, params.role);
if (groupUserdata === null) { if (groupUserdata === null) {
return ctx.body = yapi.commons.resReturn(null, 400, '组长uid不存在') return ctx.body = yapi.commons.resReturn(null, 400, '组长uid不存在')
} }

View File

@ -304,6 +304,17 @@ class interfaceController extends baseController {
ctx.body = yapi.commons.resReturn(null, 402, err.message); ctx.body = yapi.commons.resReturn(null, 402, err.message);
} }
} }
async solveConflict(ctx) {
let id = parseInt(ctx.query.id, 10);
if(!id) return ctx.websocket.send("id 参数有误");
ctx.websocket.send('Hello World');
ctx.websocket.on('message', function (message) {
// do something with the message from client
console.log(message);
});
}
} }
module.exports = interfaceController; module.exports = interfaceController;

View File

@ -141,7 +141,9 @@ class projectController extends baseController {
return ctx.body = yapi.commons.resReturn(null, 400, '项目成员已存在'); return ctx.body = yapi.commons.resReturn(null, 400, '项目成员已存在');
} }
let userdata = await this.getUserdata(params.member_uid); params.role = params.role === 'owner' ? 'owner' : 'dev';
let userdata = await this.getUserdata(params.member_uid, params.role);
if(userdata === null){ if(userdata === null){
return ctx.body = yapi.commons.resReturn(null, 400, '成员uid不存在') return ctx.body = yapi.commons.resReturn(null, 400, '成员uid不存在')
} }

View File

@ -13,6 +13,7 @@ class interfaceModel extends baseModel {
path: { type: String, required: true }, path: { type: String, required: true },
method: { type: String, required: true }, method: { type: String, required: true },
project_id: { type: Number, required: true }, project_id: { type: Number, required: true },
edit_uid: {type: Number},
status: {type: String, enum: ['undone', 'done'], default: 'undone'}, status: {type: String, enum: ['undone', 'done'], default: 'undone'},
desc: String, desc: String,
add_time: Number, add_time: Number,

View File

@ -19,6 +19,9 @@ class interfaceCase extends baseModel {
domain: {type: String }, domain: {type: String },
path: { type: String }, path: { type: String },
method: { type: String }, method: { type: String },
req_params: [{
name: String, value: String
}],
req_query: [{ req_query: [{
name: String, value: String name: String, value: String
}], }],

23
server/websocket.js Normal file
View File

@ -0,0 +1,23 @@
import koaRouter from 'koa-router';
const route = require('koa-route');
import interfaceController from './controllers/interface.js';
function websocket(app) {
console.log('load websocket...')
app.ws.use(function (ctx, next) {
return next(ctx);
});
app.ws.use(route.all('/api/interface/solve_conflict', async function (ctx) {
let inst = new interfaceController(ctx);
await inst.init(ctx);
if (inst.$auth === true) {
await inst.solveConflict.call(inst, ctx);
} else {
ctx.ws.send('请登录...');
}
}));
}
module.exports = websocket

View File

@ -40,13 +40,21 @@ var _router = require('./router.js');
var _router2 = _interopRequireDefault(_router); var _router2 = _interopRequireDefault(_router);
var _koaWebsocket = require('koa-websocket');
var _koaWebsocket2 = _interopRequireDefault(_koaWebsocket);
var _websocket = require('./websocket.js');
var _websocket2 = _interopRequireDefault(_websocket);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
_yapi2.default.commons = _commons2.default; _yapi2.default.commons = _commons2.default;
_yapi2.default.connect = _db2.default.connect(); _yapi2.default.connect = _db2.default.connect();
var app = new _koa2.default(); var app = (0, _koaWebsocket2.default)(new _koa2.default());
var indexFile = process.argv[2] === 'dev' ? 'dev.html' : 'index.html'; var indexFile = process.argv[2] === 'dev' ? 'dev.html' : 'index.html';
app.use(_mockServer2.default); app.use(_mockServer2.default);
@ -54,6 +62,8 @@ app.use((0, _koaBodyparser2.default)());
app.use(_router2.default.routes()); app.use(_router2.default.routes());
app.use(_router2.default.allowedMethods()); app.use(_router2.default.allowedMethods());
(0, _websocket2.default)(app);
app.use(function () { app.use(function () {
var _ref = (0, _asyncToGenerator3.default)(_regenerator2.default.mark(function _callee(ctx, next) { var _ref = (0, _asyncToGenerator3.default)(_regenerator2.default.mark(function _callee(ctx, next) {
return _regenerator2.default.wrap(function _callee$(_context) { return _regenerator2.default.wrap(function _callee$(_context) {

View File

@ -325,6 +325,7 @@ var groupController = function (_baseController) {
* @foldnumber 10 * @foldnumber 10
* @param {String} id 项目分组id * @param {String} id 项目分组id
* @param {String} member_uid 项目分组成员uid * @param {String} member_uid 项目分组成员uid
* @param {String} role 成员角色owner or dev
* @returns {Object} * @returns {Object}
* @example * @example
*/ */
@ -357,66 +358,69 @@ var groupController = function (_baseController) {
return _context4.abrupt('return', ctx.body = _yapi2.default.commons.resReturn(null, 400, '分组id不能为空')); return _context4.abrupt('return', ctx.body = _yapi2.default.commons.resReturn(null, 400, '分组id不能为空'));
case 6: case 6:
_context4.next = 8;
params.role = params.role === 'owner' ? 'owner' : 'dev';
_context4.next = 9;
return groupInst.checkMemberRepeat(params.id, params.member_uid); return groupInst.checkMemberRepeat(params.id, params.member_uid);
case 8: case 9:
check = _context4.sent; check = _context4.sent;
if (!(check > 0)) { if (!(check > 0)) {
_context4.next = 11; _context4.next = 12;
break; break;
} }
return _context4.abrupt('return', ctx.body = _yapi2.default.commons.resReturn(null, 400, '成员已存在')); return _context4.abrupt('return', ctx.body = _yapi2.default.commons.resReturn(null, 400, '成员已存在'));
case 11: case 12:
_context4.next = 13; _context4.next = 14;
return this.getUserdata(params.member_uid); return this.getUserdata(params.member_uid, params.role);
case 13: case 14:
groupUserdata = _context4.sent; groupUserdata = _context4.sent;
if (!(groupUserdata === null)) { if (!(groupUserdata === null)) {
_context4.next = 16; _context4.next = 17;
break; break;
} }
return _context4.abrupt('return', ctx.body = _yapi2.default.commons.resReturn(null, 400, '组长uid不存在')); return _context4.abrupt('return', ctx.body = _yapi2.default.commons.resReturn(null, 400, '组长uid不存在'));
case 16: case 17:
if (!(groupUserdata._role === 'admin')) { if (!(groupUserdata._role === 'admin')) {
_context4.next = 18; _context4.next = 19;
break; break;
} }
return _context4.abrupt('return', ctx.body = _yapi2.default.commons.resReturn(null, 400, '不能邀请管理员')); return _context4.abrupt('return', ctx.body = _yapi2.default.commons.resReturn(null, 400, '不能邀请管理员'));
case 18: case 19:
delete groupUserdata._role; delete groupUserdata._role;
_context4.prev = 19; _context4.prev = 20;
_context4.next = 22; _context4.next = 23;
return groupInst.addMember(params.id, groupUserdata); return groupInst.addMember(params.id, groupUserdata);
case 22: case 23:
result = _context4.sent; result = _context4.sent;
ctx.body = _yapi2.default.commons.resReturn(result); ctx.body = _yapi2.default.commons.resReturn(result);
_context4.next = 29; _context4.next = 30;
break; break;
case 26: case 27:
_context4.prev = 26; _context4.prev = 27;
_context4.t0 = _context4['catch'](19); _context4.t0 = _context4['catch'](20);
ctx.body = _yapi2.default.commons.resReturn(null, 402, _context4.t0.message); ctx.body = _yapi2.default.commons.resReturn(null, 402, _context4.t0.message);
case 29: case 30:
case 'end': case 'end':
return _context4.stop(); return _context4.stop();
} }
} }
}, _callee4, this, [[19, 26]]); }, _callee4, this, [[20, 27]]);
})); }));
function addMember(_x5) { function addMember(_x5) {

View File

@ -589,6 +589,46 @@ var interfaceController = function (_baseController) {
return del; return del;
}() }()
}, {
key: 'solveConflict',
value: function () {
var _ref6 = (0, _asyncToGenerator3.default)(_regenerator2.default.mark(function _callee6(ctx) {
var id;
return _regenerator2.default.wrap(function _callee6$(_context6) {
while (1) {
switch (_context6.prev = _context6.next) {
case 0:
id = parseInt(ctx.query.id, 10);
if (id) {
_context6.next = 3;
break;
}
return _context6.abrupt('return', ctx.websocket.send("id 参数有误"));
case 3:
ctx.websocket.send('Hello World');
ctx.websocket.on('message', function (message) {
// do something with the message from client
console.log(message);
});
case 5:
case 'end':
return _context6.stop();
}
}
}, _callee6, this);
}));
function solveConflict(_x6) {
return _ref6.apply(this, arguments);
}
return solveConflict;
}()
}]); }]);
return interfaceController; return interfaceController;
}(_base2.default); }(_base2.default);

View File

@ -313,25 +313,28 @@ var projectController = function (_baseController) {
return _context2.abrupt('return', ctx.body = _yapi2.default.commons.resReturn(null, 400, '项目成员已存在')); return _context2.abrupt('return', ctx.body = _yapi2.default.commons.resReturn(null, 400, '项目成员已存在'));
case 15: case 15:
_context2.next = 17;
return this.getUserdata(params.member_uid);
case 17: params.role = params.role === 'owner' ? 'owner' : 'dev';
_context2.next = 18;
return this.getUserdata(params.member_uid, params.role);
case 18:
userdata = _context2.sent; userdata = _context2.sent;
if (!(userdata === null)) { if (!(userdata === null)) {
_context2.next = 20; _context2.next = 21;
break; break;
} }
return _context2.abrupt('return', ctx.body = _yapi2.default.commons.resReturn(null, 400, '成员uid不存在')); return _context2.abrupt('return', ctx.body = _yapi2.default.commons.resReturn(null, 400, '成员uid不存在'));
case 20: case 21:
_context2.prev = 20; _context2.prev = 21;
_context2.next = 23; _context2.next = 24;
return this.Model.addMember(params.id, userdata); return this.Model.addMember(params.id, userdata);
case 23: case 24:
result = _context2.sent; result = _context2.sent;
username = this.getUsername(); username = this.getUsername();
@ -343,21 +346,21 @@ var projectController = function (_baseController) {
typeid: params.id typeid: params.id
}); });
ctx.body = _yapi2.default.commons.resReturn(result); ctx.body = _yapi2.default.commons.resReturn(result);
_context2.next = 32; _context2.next = 33;
break; break;
case 29: case 30:
_context2.prev = 29; _context2.prev = 30;
_context2.t1 = _context2['catch'](20); _context2.t1 = _context2['catch'](21);
ctx.body = _yapi2.default.commons.resReturn(null, 402, _context2.t1.message); ctx.body = _yapi2.default.commons.resReturn(null, 402, _context2.t1.message);
case 32: case 33:
case 'end': case 'end':
return _context2.stop(); return _context2.stop();
} }
} }
}, _callee2, this, [[20, 29]]); }, _callee2, this, [[21, 30]]);
})); }));
function addMember(_x2) { function addMember(_x2) {

View File

@ -52,6 +52,7 @@ var interfaceModel = function (_baseModel) {
path: { type: String, required: true }, path: { type: String, required: true },
method: { type: String, required: true }, method: { type: String, required: true },
project_id: { type: Number, required: true }, project_id: { type: Number, required: true },
edit_uid: { type: Number },
status: { type: String, enum: ['undone', 'done'], default: 'undone' }, status: { type: String, enum: ['undone', 'done'], default: 'undone' },
desc: String, desc: String,
add_time: Number, add_time: Number,

View File

@ -58,6 +58,9 @@ var interfaceCase = function (_baseModel) {
domain: { type: String }, domain: { type: String },
path: { type: String }, path: { type: String },
method: { type: String }, method: { type: String },
req_params: [{
name: String, value: String
}],
req_query: [{ req_query: [{
name: String, value: String name: String, value: String
}], }],

70
server_dist/websocket.js Normal file
View File

@ -0,0 +1,70 @@
'use strict';
var _regenerator = require('babel-runtime/regenerator');
var _regenerator2 = _interopRequireDefault(_regenerator);
var _asyncToGenerator2 = require('babel-runtime/helpers/asyncToGenerator');
var _asyncToGenerator3 = _interopRequireDefault(_asyncToGenerator2);
var _koaRouter = require('koa-router');
var _koaRouter2 = _interopRequireDefault(_koaRouter);
var _interface = require('./controllers/interface.js');
var _interface2 = _interopRequireDefault(_interface);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var route = require('koa-route');
function websocket(app) {
console.log('load websocket...');
app.ws.use(function (ctx, next) {
return next(ctx);
});
app.ws.use(route.all('/api/interface/solve_conflict', function () {
var _ref = (0, _asyncToGenerator3.default)(_regenerator2.default.mark(function _callee(ctx) {
var inst;
return _regenerator2.default.wrap(function _callee$(_context) {
while (1) {
switch (_context.prev = _context.next) {
case 0:
inst = new _interface2.default(ctx);
_context.next = 3;
return inst.init(ctx);
case 3:
if (!(inst.$auth === true)) {
_context.next = 8;
break;
}
_context.next = 6;
return inst.solveConflict.call(inst, ctx);
case 6:
_context.next = 9;
break;
case 8:
ctx.ws.send('请登录...');
case 9:
case 'end':
return _context.stop();
}
}
}, _callee, this);
}));
return function (_x) {
return _ref.apply(this, arguments);
};
}()));
}
module.exports = websocket;