Merge branch 'dev' into 'master'

Dev to master

See merge request !15
This commit is contained in:
苏文雄 2017-07-27 11:57:29 +08:00
commit ca4c5b75d2
63 changed files with 1039 additions and 813 deletions

42
.eslintrc.js Normal file
View File

@ -0,0 +1,42 @@
module.exports = {
"env": {
"browser": true,
"commonjs": true,
"es6": true,
"node": true
},
"extends": "eslint:recommended",
"parser": "babel-eslint",
"parserOptions": {
"ecmaFeatures": {
"experimentalObjectRestSpread": true,
"jsx": true
},
"sourceType": "module"
},
"plugins": [
"react"
],
"rules": {
"indent": [
"error",
4,
{
"SwitchCase": 1
}
],
"linebreak-style": [
"error",
"unix"
],
"quotes": [
"error",
"single"
],
"semi": [
"error",
"always"
],
"strict": 0
}
};

View File

@ -1,22 +1,22 @@
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 { Route, HashRouter, Redirect, Switch } from 'react-router-dom' import { Route, HashRouter, Redirect, Switch } from 'react-router-dom';
import { Home, ProjectGroups, Interface, News, AddInterface } from './containers/index' import { Home, ProjectGroups, Interface, News, AddInterface } from './containers/index';
import User from './containers/User/User.js' import User from './containers/User/User.js';
import Header from './components/Header/Header' import Header from './components/Header/Header';
import Footer from './components/Footer/Footer' import Footer from './components/Footer/Footer';
import Loading from './components/Loading/Loading' import Loading from './components/Loading/Loading';
import { checkLoginState } from './actions/login' import { checkLoginState } from './actions/login';
import { requireAuthentication } from './components/AuthenticatedComponent'; import { requireAuthentication } from './components/AuthenticatedComponent';
const LOADING_STATUS = 0; const LOADING_STATUS = 0;
@connect( @connect(
state => { state => {
return{ return {
loginState:state.login.loginState loginState: state.login.loginState
} };
}, },
{ {
checkLoginState checkLoginState
@ -27,20 +27,22 @@ export default class App extends Component {
super(props); super(props);
this.state = { this.state = {
login: LOADING_STATUS login: LOADING_STATUS
} };
} }
static propTypes = { static propTypes = {
checkLoginState:PropTypes.func, checkLoginState: PropTypes.func,
loginState:PropTypes.number loginState: PropTypes.number
} };
componentDidMount() { componentDidMount() {
this.props.checkLoginState(); this.props.checkLoginState();
} }
route = (status) => { route = (status) => {
let r; let r;
if (status === LOADING_STATUS) { if (status === LOADING_STATUS) {
return <Loading visible/> return <Loading visible />;
} else { } else {
r = ( r = (
<HashRouter> <HashRouter>
@ -50,20 +52,21 @@ export default class App extends Component {
<Route path="/" component={Home} exact /> <Route path="/" component={Home} exact />
<Switch> <Switch>
<Redirect exact from='/group' to='/group/1' /> <Redirect exact from='/group' to='/group/1' />
<Route exact path="/group/:groupName" component={ requireAuthentication(ProjectGroups) } /> <Route exact path="/group/:groupName" component={requireAuthentication(ProjectGroups)} />
</Switch> </Switch>
<Route path="/Interface" component={requireAuthentication(Interface)} /> <Route path="/Interface" component={requireAuthentication(Interface)} />
<Route path="/user" component={requireAuthentication(User)} /> <Route path="/user" component={requireAuthentication(User)} />
<Route path="/News" component={requireAuthentication(News)} /> <Route path="/News" component={requireAuthentication(News)} />
<Route path="/AddInterface" component={ requireAuthentication(AddInterface) } /> <Route path="/AddInterface" component={requireAuthentication(AddInterface)} />
</div> </div>
<Footer/> <Footer />
</div> </div>
</HashRouter> </HashRouter>
) )
} }
return r return r;
} }
render() { render() {
return this.route(this.props.loginState); return this.route(this.props.loginState);
} }

View File

@ -3,9 +3,18 @@ import { connect } from 'react-redux';
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import { changeMenuItem } from '../actions/menu' import { changeMenuItem } from '../actions/menu'
@connect(
(state) => {
return{
isAuthenticated: state.login.isLogin
}
},
{
changeMenuItem
}
)
export function requireAuthentication(Component) { export function requireAuthentication(Component) {
class AuthenticatedComponent extends React.Component { return class AuthenticatedComponent extends React.Component {
constructor(props){ constructor(props){
super(props); super(props);
} }
@ -37,19 +46,8 @@ export function requireAuthentication(Component) {
} }
</div> </div>
) )
} }
} }
return connect(
(state) => {
return{
isAuthenticated: state.login.isLogin
}
},
{
changeMenuItem
}
)(AuthenticatedComponent);
} }

View File

@ -43,9 +43,9 @@ class FootItem extends Component {
render () { render () {
return ( return (
<div className = 'footItem'> <div className = 'footItem'>
<h4><Icon type= { this.props.iconType } style={{ fontSize: 16 }} /> { this.props.title } </h4> <h4><Icon type= { this.props.iconType } style={{ fontSize: 16 }} />&nbsp;&nbsp; { this.props.title } </h4>
{ this.props.linkList.map(function(item,i){ { this.props.linkList.map(function(item,i){
return (<div key = {i}><a href = { item.itemLink }><span>{ item.itemTitle }</span></a></div>); return (<div key = {i}>&nbsp;&nbsp;<a href = { item.itemLink }><span>{ item.itemTitle }</span></a></div>);
}) } }) }
</div> </div>
); );

View File

@ -1,4 +1,5 @@
@import '../../styles/common.scss'; @import '../../styles/common.scss';
@import '../../styles/mixin.scss';
.footer{ .footer{
// max-width: 12rem; // max-width: 12rem;
@ -31,7 +32,7 @@
z-index: 1; z-index: 1;
} }
.footContent{ .footContent{
max-width: 11rem; @include row-width-limit;
width:90%; width:90%;
margin:0px auto; margin:0px auto;
overflow: hidden; overflow: hidden;
@ -47,12 +48,12 @@
} }
a{ a{
font-weight: 200; font-weight: 200;
color: #108ee9; color: #b3bdc1;
&:hover{ &:hover{
color: white; color: white;
} }
} }
} }
.copyRight{ .copyRight{
padding: 24px 2%; padding: 24px 2%;
@ -68,4 +69,4 @@
position: relative; position: relative;
text-indent: 0em; text-indent: 0em;
} }
} }

View File

@ -66,8 +66,26 @@ ToolUser.propTypes={
}; };
@connect(
(state) => {
return{
user: state.login.userName,
uid: state.login.uid,
msg: null,
login:state.login.isLogin,
curKey: state.menu.curKey
}
},
{
loginTypeAction,
logoutActions,
checkLoginState,
changeMenuItem
}
)
@withRouter @withRouter
class HeaderCom extends Component { export default class HeaderCom extends Component {
constructor(props) { constructor(props) {
super(props); super(props);
} }
@ -128,15 +146,16 @@ class HeaderCom extends Component {
} }
render () { render () {
const { login, user, msg, uid, curKey } = this.props; const { login, user, msg, uid, curKey } = this.props;
const headerStyle = { const headerImgStyle = login?{}:{
'background': 'url(./image/bg-img.jpg) no-repeat center', 'background': 'url(./image/header-bg-img.jpg) no-repeat',
'backgroundSize':'cover' 'backgroundSize':'100% 100%'
} };
const headerShadeStyle = login?{}:{
'background': 'linear-gradient(to bottom,rgba(0,0,0,0.6),rgba(0,0,0,0.5))'
};
return ( return (
<acticle className={`header-box`} style={headerStyle}> <acticle className={`header-box`} style={headerImgStyle}>
<Header style={{ <Header style={headerShadeStyle}>
background: "linear-gradient(to bottom,rgba(64,64,64,1),rgba(64,64,64,0.9))"
}}>
<div className="content"> <div className="content">
<div className="logo"> <div className="logo">
<Link to="/" onClick={this.relieveLink}>YAPI</Link> <Link to="/" onClick={this.relieveLink}>YAPI</Link>
@ -173,22 +192,4 @@ class HeaderCom extends Component {
</acticle> </acticle>
) )
} }
} }
export default connect(
(state) => {
return{
user: state.login.userName,
uid: state.login.uid,
msg: null,
login:state.login.isLogin,
curKey: state.menu.curKey
}
},
{
loginTypeAction,
logoutActions,
checkLoginState,
changeMenuItem
}
)(HeaderCom)

View File

@ -1,26 +1,19 @@
@import '../../styles/common.scss'; @import '../../styles/common.scss';
@import '../../styles/mixin.scss';
$color-white : #fff; $color-white : #fff;
$color-blue : #108ee9; $color-blue : #108ee9;
$color-blue-deeper: #34495E; $color-blue-deeper: #34495E;
$color-grey-deep : #929aac; $color-grey-deep : #929aac;
$color-black-light : #404040; $color-black-light : #404040;
/* .header-box.css */ /* .header-box.css */
//.light{
// background-color: #f7fafc;
// color: $color-blue;
//}
//.dark {
// background-color: $color-black-light;
// color: $color-white;
//}
.header-box { .header-box {
display: block; display: block;
font-size: 0.14rem; font-size: 0.14rem;
z-index: 99; z-index: 99;
// 内容宽度 // 内容宽度
.content { .content {
max-width: 11rem; @include row-width-limit;
margin: 0 auto; margin: 0 auto;
zoom: 1; zoom: 1;
overflow: hidden; overflow: hidden;
@ -85,4 +78,3 @@ $color-black-light : #404040;
} }
} }
} }

View File

@ -1,7 +1,3 @@
export default { export default {
PAGE_LIMIT: 10, // 默认每页展示10条数据 PAGE_LIMIT: 10 // 默认每页展示10条数据
// layout
ROW_MIN_WIDTH: '9.7rem', // 适应小屏幕分辨率
ROW_MAX_WIDTH: '11.7rem' // 适应大屏幕分辨率
} }

View File

@ -254,9 +254,6 @@ class AddInterface extends Component {
<Result isSave={isSave} mockJson={mockJson} /> <Result isSave={isSave} mockJson={mockJson} />
<MockUrl mockURL={mockURL} /> <MockUrl mockURL={mockURL} />
</TabPane> </TabPane>
{
// <TabPane tab="Mock" key="2">mock</TabPane>
}
<TabPane tab="请求接口" key="3"> <TabPane tab="请求接口" key="3">
<InterfaceTest /> <InterfaceTest />
</TabPane> </TabPane>

View File

@ -1,3 +1,5 @@
@import '../../styles/mixin.scss';
.add-interface-box { .add-interface-box {
-webkit-box-flex: 1; -webkit-box-flex: 1;
font-size: .14rem; font-size: .14rem;
@ -5,7 +7,7 @@
overflow-y: auto; overflow-y: auto;
.content { .content {
max-width: 11rem; @include row-width-limit;
margin: 24px auto; margin: 24px auto;
border-radius: 4px; border-radius: 4px;
box-shadow: 0 2px 4px 0 rgba(0,0,0,0.20); box-shadow: 0 2px 4px 0 rgba(0,0,0,0.20);
@ -169,7 +171,7 @@
color: #CCC; color: #CCC;
} }
} }
/* .mock-url-box.css */ /* .mock-url-box.css */
.mock-url-box { .mock-url-box {
clear: both; clear: both;

View File

@ -1,7 +1,7 @@
import React, { Component } from 'react' import React, { Component } from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import { connect } from 'react-redux' import { connect } from 'react-redux'
import { Button, Input } from 'antd' import { Button, Input, Select, Card, Alert } from 'antd'
import { autobind } from 'core-decorators'; import { autobind } from 'core-decorators';
import crossRequest from 'cross-request'; import crossRequest from 'cross-request';
import { withRouter } from 'react-router'; import { withRouter } from 'react-router';
@ -13,6 +13,8 @@ import {
import './InterfaceTest.scss' import './InterfaceTest.scss'
const { TextArea } = Input; const { TextArea } = Input;
const InputGroup = Input.Group;
const Option = Select.Option;
@connect( @connect(
state => ({ state => ({
@ -40,8 +42,16 @@ export default class InterfaceTest extends Component {
} }
state = { state = {
res: {}, res: '',
header: {} method: 'GET',
domains: [],
pathname: '',
query: {},
params: {},
paramsNotJson: false,
headers: {},
search: '',
currDomain: ''
} }
constructor(props) { constructor(props) {
@ -49,32 +59,72 @@ export default class InterfaceTest extends Component {
} }
componentWillMount() { componentWillMount() {
this.interfacePropsToState()
}
componentWillReceiveProps(nextProps) {
this.interfacePropsToState(nextProps)
}
@autobind
interfacePropsToState(nextProps) {
const props = nextProps || this.props;
const { method, url, seqGroup, interfaceProject } = props;
const { prd_host, basepath, protocol, env } = interfaceProject;
const pathname = (basepath + url).replace(/\/+/g, '/');
const domains = {prd: protocol + '://' + prd_host};
env.forEach(item => {
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;
}
if (method === 'GET') {
Object.keys(reqParams).forEach(key => {
const value = typeof reqParams[key] === 'object' ? JSON.stringify(reqParams[key]) : reqParams[key].toString();
query[key] = value;
})
} else if (method === 'POST') {
params = reqParams;
}
const headers = {}
seqGroup.forEach((headerItem) => {
if (headerItem.name) {
headers[headerItem.name] = headerItem.value;
}
})
this.setState({
domains,
pathname,
query,
params,
paramsNotJson,
headers,
currDomain: domains.prd
});
} }
@autobind @autobind
testInterface() { testInterface() {
const { method, url, seqGroup, interfaceProject } = this.props; const { method } = this.props;
const { prd_host, basepath, protocol } = interfaceProject; const { pathname, query, headers, params, currDomain } = this.state;
const reqParams = JSON.parse(this.props.reqParams); const urlObj = URL.parse(currDomain);
const headers = {}
let query = {};
if (method === 'GET') {
Object.keys(reqParams).forEach(key => {
const value = typeof reqParams[key] === 'object' ? JSON.stringify(reqParams) : reqParams.toString();
query[key] = value;
})
}
seqGroup.forEach((headerItem) => {
headers[headerItem.name] = headerItem.value;
})
const href = URL.format({ const href = URL.format({
protocol: protocol || 'http', protocol: urlObj.protocol || 'http',
host: prd_host, host: urlObj.host,
pathname: (basepath + url).replace(/\/+/g, '/'), pathname,
query query
}); });
@ -82,9 +132,7 @@ export default class InterfaceTest extends Component {
url: href, url: href,
method, method,
headers, headers,
data: { data: params,
a:1
},
success: (res, header) => { success: (res, header) => {
console.log(header) console.log(header)
this.setState({res}) this.setState({res})
@ -92,71 +140,143 @@ export default class InterfaceTest extends Component {
}) })
} }
@autobind
changeDomain(value) {
const domain = this.state.domains[value];
this.setState({ currDomain: domain });
}
@autobind
changeHeader(e, key) {
const headers = JSON.parse(JSON.stringify(this.state.headers));
headers[key] = e.target.value;
this.setState({ headers });
}
@autobind
changeQuery(e, key) {
const query = JSON.parse(JSON.stringify(this.state.query));
query[key] = e.target.value;
this.setState({ query });
}
@autobind
changeParams(e, key) {
const params = JSON.parse(JSON.stringify(this.state.params));
params[key] = e.target.value;
this.setState({ params });
}
hasCrossRequestPlugin() {
const dom = document.getElementById('y-request');
return dom.getAttribute('key') === 'yapi';
}
render () { render () {
const { method, url, seqGroup, interfaceName, interfaceProject } = this.props;
const { prd_host, basepath, protocol } = interfaceProject;
const reqParams = JSON.parse(this.props.reqParams);
let query = {};
if (method === 'GET') { const { interfaceName, method } = this.props;
Object.keys(reqParams).forEach(key => { const { domains, pathname, query, headers, params, paramsNotJson } = this.state;
const value = typeof reqParams[key] === 'object' ? JSON.stringify(reqParams[key]) : reqParams[key].toString(); const search = URL.format({
query[key] = value;
})
}
const href = URL.format({
protocol: protocol || 'http',
host: prd_host,
pathname: (basepath + url).replace(/\/+/g, '/'),
query query
}); });
const hasPlugin = this.hasCrossRequestPlugin();
return ( return (
<div className="interface-test"> <div className="interface-test">
<div style={{padding: '0 20%'}}>
{ 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>
<div className="interface-name">{interfaceName}</div> <div className="interface-name">{interfaceName}</div>
<div className="req-part"> <div className="req-part">
<div className="req-row method"> <div className="req-row href">
METHOD<Input value={method} disabled style={{display: 'inline-block', width: 200}} /> <InputGroup compact style={{display: 'inline-block', width: 680}}>
</div> <Input value={method} disabled style={{display: 'inline-block', width: 80}} />
<div className="req-row url"> <Select defaultValue="prd" style={{display: 'inline-block', width: 300}} onChange={this.changeDomain}>
URL<Input value={href} style={{display: 'inline-block', width: 800, margin: 10}} /> {
<Button onClick={this.testInterface} type="primary">发送</Button> Object.keys(domains).map((key, index) => (<Option value={key} key={index}>{domains[key]}</Option>))
</div> }
<div className="req-row headers"> </Select>
HEADERS <Input value={pathname+search} disabled style={{display: 'inline-block', width: 300}} />
{ </InputGroup>
seqGroup.map((headerItem, index) => { <Button onClick={this.testInterface} type="primary" style={{marginLeft: 10}}>发送</Button>
return (
<div key={index}>
<Input disabled value={headerItem.name} style={{display: 'inline-block', width: 200, margin: 10}} />{' = '}
<Input value={headerItem.value} style={{display: 'inline-block', width: 200, margin: 10}} />
</div>
)
})
}
</div>
<div className="req-row params">
请求参数
{
Object.keys(reqParams).map((key, index) => {
const value = typeof reqParams[key] === 'object' ? JSON.stringify(reqParams[key]) : reqParams[key].toString();
return (
<div key={index}>
<Input disabled value={key} style={{display: 'inline-block', width: 200, margin: 10}} />{' = '}
<Input value={value} style={{display: 'inline-block', width: 200, margin: 10}} />
</div>
)
})
}
</div> </div>
<Card title="HEADERS" noHovering style={{marginTop: 10}} className={Object.keys(headers).length ? '' : 'hidden'}>
<div className="req-row headers">
{
Object.keys(headers).map((key, index) => {
return (
<div key={index}>
<Input disabled value={key} style={{display: 'inline-block', width: 200, margin: 10}} />{' = '}
<Input value={headers[key]} onChange={e => this.changeHeader(e, key)} style={{display: 'inline-block', width: 200, margin: 10}} />
</div>
)
})
}
</div>
</Card>
<Card title="Query" noHovering style={{marginTop: 10}} className={Object.keys(query).length ? '' : 'hidden'}>
<div className="req-row query">
{
Object.keys(query).map((key, index) => {
const value = typeof query[key] === 'object' ? JSON.stringify(query[key]) : query[key].toString();
return (
<div key={index}>
<Input disabled value={key} style={{display: 'inline-block', width: 200, margin: 10}} />{' = '}
<Input value={value} onChange={e => this.changeQuery(e, key)} style={{display: 'inline-block', width: 200, margin: 10}} />
</div>
)
})
}
</div>
</Card>
<Card title="Body" noHovering style={{marginTop: 10}} className={Object.keys(params).length ? '' : 'hidden'}>
<div className="req-row params">
{ paramsNotJson ?
<TextArea
value={params}
style={{margin: 10}}
autosize={{ minRows: 2, maxRows: 6 }}
></TextArea> :
Object.keys(params).map((key, index) => {
const value = typeof params[key] === 'object' ? JSON.stringify(params[key]) : params[key].toString();
return (
<div key={index}>
<Input disabled value={key} style={{display: 'inline-block', width: 200, margin: 10}} />{' = '}
<Input value={value} onChange={e => this.changeParams(e, key)} style={{display: 'inline-block', width: 200, margin: 10}} />
</div>
)
})
}
</div>
</Card>
</div> </div>
<div className="res-part"> <Card title="返回结果" noHovering style={{marginTop: 10}}>
返回结果 <div className="res-part">
<TextArea value={JSON.stringify(this.state.res, 2)}></TextArea> <div>
</div> <TextArea
value={this.state.res ? JSON.stringify(this.state.res, 2) : ''}
style={{margin: 10}}
autosize={{ minRows: 2, maxRows: 6 }}
></TextArea>
</div>
</div>
</Card>
</div> </div>
) )
} }

View File

@ -24,7 +24,7 @@ class MockUrl extends Component {
clipboard () { clipboard () {
const btn = document.querySelector('#mock-clipboard') const btn = document.querySelector('#mock-clipboard')
const txt = document.querySelector('#mock-p').innerHTML const txt = document.querySelector('#mock-p').innerHTML
console.log('txt', txt)
new Clipboard(btn, { new Clipboard(btn, {
text: () => txt, text: () => txt,
target () { target () {

View File

@ -6,7 +6,8 @@ import { autobind } from 'core-decorators'
import { import {
reqTagValue, reqTagValue,
reqHeaderValue, reqHeaderValue,
deleteReqHeader deleteReqHeader,
addReqHeader
} from '../../../actions/addInterface.js' } from '../../../actions/addInterface.js'
@connect( @connect(
@ -20,7 +21,8 @@ import {
{ {
reqTagValue, reqTagValue,
reqHeaderValue, reqHeaderValue,
deleteReqHeader deleteReqHeader,
addReqHeader
} }
) )
@ -30,6 +32,7 @@ class ReqList extends Component {
reqTagValue: PropTypes.func, reqTagValue: PropTypes.func,
reqHeaderValue: PropTypes.func, reqHeaderValue: PropTypes.func,
deleteReqHeader: PropTypes.func, deleteReqHeader: PropTypes.func,
addReqHeader: PropTypes.func,
_id: PropTypes.number, _id: PropTypes.number,
dataNum: PropTypes.number, dataNum: PropTypes.number,
value: PropTypes.object value: PropTypes.object
@ -43,12 +46,32 @@ class ReqList extends Component {
handleChange (value) { handleChange (value) {
const dir = 'AddInterface/edit' const dir = 'AddInterface/edit'
const url = location.href const url = location.href
const newObject = []
if (url.includes(dir)) { if (url.includes(dir)) {
const { seqGroup, value: { id } } = this.props const { seqGroup, value: { id } } = this.props
seqGroup[id].name = value seqGroup.forEach(v => {
if (id == v.id) {
v.name = value
}
})
seqGroup.forEach(v => {
const {id, name, value} = v
newObject.push({id, name, value})
})
this.props.addReqHeader( newObject )
} else { } else {
const { seqGroup, dataNum } = this.props const { seqGroup, dataNum } = this.props
seqGroup[dataNum].name = value seqGroup.forEach(v => {
if (dataNum == v.id) {
v.name = value
}
})
seqGroup.forEach(v => {
const {id, name, value} = v
newObject.push({id, name, value})
})
this.props.addReqHeader(newObject)
} }
} }
@ -56,7 +79,17 @@ class ReqList extends Component {
handleBlur (e) { handleBlur (e) {
const value = e.target.value const value = e.target.value
const { seqGroup, value: { id } } = this.props const { seqGroup, value: { id } } = this.props
seqGroup[id].value = value const newObject = []
seqGroup.forEach(v => {
if (id == v.id) {
v.value = value
}
})
seqGroup.forEach(v => {
const {id, name, value} = v
newObject.push({id, name, value})
})
this.props.addReqHeader(newObject)
} }
@autobind @autobind
@ -76,13 +109,13 @@ class ReqList extends Component {
render () { render () {
const propsValue = this.props.value const propsValue = this.props.value
const Option = Select.Option const Option = Select.Option
const value = propsValue.value const value = propsValue.value || ''
const name = propsValue.name || '' const name = propsValue.name || ''
console.log(name)
return ( return (
<li> <li>
<em className="title">头部标签</em> <em className="title">头部标签</em>
<Select defaultValue={name} style={{ width: 220 }} onChange={this.handleChange} size="large"> <Select value={name} style={{ width: 220 }} onChange={this.handleChange} size="large">
<Option value="">选择请求头</Option> <Option value="">选择请求头</Option>
<Option value="Accept">Accept</Option> <Option value="Accept">Accept</Option>
<Option value="Accept-Charset">Accept-Charset</Option> <Option value="Accept-Charset">Accept-Charset</Option>
@ -91,7 +124,7 @@ class ReqList extends Component {
<Option value="Accept-Ranges">Accept-Ranges</Option> <Option value="Accept-Ranges">Accept-Ranges</Option>
</Select> </Select>
<em className="title">头部内容</em> <em className="title">头部内容</em>
<Input defaultValue={value} placeholder="Basic usage" className="req-content" size="large" onBlur={this.handleBlur} /> <Input value={value} placeholder="Basic usage" className="req-content" size="large" onInput={this.handleBlur} />
<Icon className="dynamic-delete-button" type="minus-circle-o" onClick={this.deleteReqHeader} /> <Icon className="dynamic-delete-button" type="minus-circle-o" onClick={this.deleteReqHeader} />
</li> </li>
) )

View File

@ -58,6 +58,7 @@ class ReqMethod extends Component {
render () { render () {
const { Option } = Select const { Option } = Select
const { url, interfaceName, method } = this.props const { url, interfaceName, method } = this.props
return ( return (
<table> <table>
<tbody> <tbody>
@ -65,7 +66,7 @@ class ReqMethod extends Component {
<th>协议 :</th> <th>协议 :</th>
<td> <td>
<span className="h3">请求方式</span> <span className="h3">请求方式</span>
<Select defaultValue={method} style={{ width: 220 }} onChange={this.handleChange} size="large"> <Select value={method} style={{ width: 220 }} onChange={this.handleChange} size="large">
<Option value="POST">POST</Option> <Option value="POST">POST</Option>
<Option value="GET">GET</Option> <Option value="GET">GET</Option>
<Option value="PUT">PUT</Option> <Option value="PUT">PUT</Option>

View File

@ -27,7 +27,7 @@ class Result extends Component {
render () { render () {
const TabPane = Tabs.TabPane const TabPane = Tabs.TabPane
const { mockJson } = this.props const { mockJson } = this.props
console.log('mockJson', typeof mockJson, mockJson)
return ( return (
<div className="result"> <div className="result">
<Tabs defaultActiveKey="1"> <Tabs defaultActiveKey="1">

View File

@ -17,19 +17,19 @@ const imgAnim = { y: '+=50', opacity: 0, type: 'from', ease: 'easeOutQuad', dura
const style = { const style = {
'height':'100%', 'height':'100%',
'width':'100%', 'width':'100%',
'background': 'url(./image/bg-img.jpg) no-repeat center', 'background': 'url(./image/bg-img.jpg) no-repeat',
'backgroundSize':'cover' 'backgroundSize':'100% 100%'
} }
const HomeGuest = (props) => ( const HomeGuest = (props) => (
<div> <div>
<div className="main-one" style = {style}> <div className="main-one" style = {style}>
<div style={{ background: "linear-gradient(to bottom,rgba(64,64,64,0.9),rgba(64,64,64,0.5))"}}> <div style={{ background: "linear-gradient(to bottom,rgba(0,0,0,0.5),rgba(0,0,0,0.2))"}}>
<div className="container"> <div className="container">
<Row> <Row>
<Col span={24}> <Col span={24}>
<div className="home-des"> <div className="home-des">
<p className="title">YAPI</p> <p className="title">YAPI</p>
<div className="detail">一个高效易用可部署的Api管理系统</div> <div className="detail">高效易用可部署的API管理平台旨在为开发产品测试人员提供更优雅的接口管理服务</div>
</div> </div>
</Col> </Col>
</Row> </Row>
@ -62,7 +62,7 @@ const HomeGuest = (props) => (
<TweenOne <TweenOne
key="feat-motion-one" key="feat-motion-one"
animation={oneAnim} animation={oneAnim}
component="h3" component="p"
> >
<span>特性</span> <span>特性</span>
</TweenOne> </TweenOne>

View File

@ -1,4 +1,5 @@
@import '../../styles/common.scss'; @import '../../styles/common.scss';
@import '../../styles/mixin.scss';
$color-white : #fff; $color-white : #fff;
$color-blue-lighter : #f1f5ff; $color-blue-lighter : #f1f5ff;
@ -12,7 +13,7 @@ $color-black-lighter: #404040;
-webkit-box-orient: vertical; -webkit-box-orient: vertical;
.main-one{ .main-one{
.home-des{ .home-des{
color: $color-white; color: $color-blue-grey-lighter;
padding: .5rem 0 0; padding: .5rem 0 0;
.title{ .title{
font-size: .6rem; font-size: .6rem;
@ -29,7 +30,8 @@ $color-black-lighter: #404040;
img{ img{
width: 100%; width: 100%;
height: 100%; height: 100%;
box-shadow : 0 0 3px 0 $color-black-lighter; //box-shadow : 0 0 3px 0 $color-black-lighter;
box-shadow : 0 30px 60px rgba(0,0,0,0.2);
border-radius: .03rem; border-radius: .03rem;
} }
} }
@ -39,16 +41,17 @@ $color-black-lighter: #404040;
} }
.main-one-right{ .main-one-right{
padding-left: .5rem; padding-left: .5rem;
padding-top: .3rem;
} }
} }
.user-home{ .user-home{
display: flex; display: flex;
align-items: center; align-items: center;
height: 100%; height: 100%;
max-width: 11rem; @include row-width-limit;
margin: 1rem auto 0; margin: 1rem auto 0;
.user-des{ .user-des{
max-width: 11rem; @include row-width-limit;
margin: 0 auto .5rem; margin: 0 auto .5rem;
text-align: center; text-align: center;
.title{ .title{
@ -82,13 +85,13 @@ $color-black-lighter: #404040;
.feat-part{ .feat-part{
padding: .9rem .5rem; padding: .9rem .5rem;
background-color: $color-white; background-color: $color-white;
h3{ p{
display: flex; display: flex;
height: .3rem; height: .3rem;
align-items: center; align-items: center;
padding: 0 .1rem; padding: 0 .1rem;
margin-bottom: .2rem; margin-bottom: .2rem;
color: #333; //color: #333;
&:before, &:after{ &:before, &:after{
content: ""; content: "";
display: inline-block; display: inline-block;
@ -104,7 +107,7 @@ $color-black-lighter: #404040;
} }
} }
.container{ .container{
max-width: 11rem; @include row-width-limit;
margin: 0 auto; margin: 0 auto;
height:100%; height:100%;
position: relative; position: relative;
@ -129,7 +132,6 @@ $color-black-lighter: #404040;
.feat-title { .feat-title {
font-size: .16rem; font-size: .16rem;
line-height: .3rem; line-height: .3rem;
color: #333;
} }
&:first-child { &:first-child {
.feat-img { .feat-img {
@ -153,5 +155,3 @@ $color-black-lighter: #404040;
} }
} }
} }

View File

@ -58,7 +58,7 @@ class Interface extends Component {
.then(result => { .then(result => {
result = result.data.data result = result.data.data
result.map(value => { result.map(value => {
value.add_time = moment(value.add_time).format('YYYY-MM-DD HH:mm:ss') value.add_time = moment(value.add_time*1000).format('YYYY-MM-DD HH:mm:ss')
return value return value
}) })
this.props.fetchInterfaceData(result) this.props.fetchInterfaceData(result)

View File

@ -1,6 +1,8 @@
@import '../../styles/mixin.scss';
/* .interface-box.css */ /* .interface-box.css */
.interface-box { .interface-box {
max-width: 11rem; @include row-width-limit;
margin: 24px auto; margin: 24px auto;
border-radius: 4px; border-radius: 4px;
box-shadow: 0 2px 4px 0 rgba(0,0,0,0.20); box-shadow: 0 2px 4px 0 rgba(0,0,0,0.20);

View File

@ -65,10 +65,14 @@ class InterfaceTable extends Component {
title: '接口名称', title: '接口名称',
dataIndex: 'title', dataIndex: 'title',
key: 'title' key: 'title'
}, { },{
title: '接口URL', title: '接口URL',
dataIndex: 'path', dataIndex: 'path',
key: 'path' key: 'path'
},{
title: '请求方式',
dataIndex: 'method',
key: 'method'
},{ },{
title: '更新日期', title: '更新日期',
dataIndex: 'add_time', dataIndex: 'add_time',
@ -77,14 +81,11 @@ class InterfaceTable extends Component {
title: '功能', title: '功能',
'key': 'action', 'key': 'action',
render: (data) => { render: (data) => {
// const deleteInterface = this.deleteInterface.bind(this, data._id)
const confirm = this.confirm.bind(this, data._id) const confirm = this.confirm.bind(this, data._id)
return ( return (
<span> <span>
<Link to={`/AddInterface/edit/${data._id}`}><span>编辑</span></Link> <Link to={`/AddInterface/edit/${data._id}`}><span>编辑</span></Link>
<span className="ant-divider" /> <span className="ant-divider" />
<Link to={`/AddInterface/edit/${data._id}`}><span>测试</span></Link>
<span className="ant-divider" />
<Popconfirm title="是否删除接口!" onConfirm={confirm} okText="Yes" cancelText="No"> <Popconfirm title="是否删除接口!" onConfirm={confirm} okText="Yes" cancelText="No">
<a href="">删除</a> <a href="">删除</a>
</Popconfirm> </Popconfirm>

View File

@ -18,14 +18,14 @@
.qsso-breakline{ .qsso-breakline{
display: flex; display: flex;
align-items: center; align-items: center;
color: #999; color: #f7fafc;
margin: .2rem auto; margin: .2rem auto;
&:before, &:after{ &:before, &:after{
content: ""; content: "";
display: inline-block; display: inline-block;
height: .02rem; height: .02rem;
flex: 1; flex: 1;
border-top: .01rem solid #bbb; border-top: .01rem solid #f7fafc;
} }
.qsso-breakword{ .qsso-breakword{
padding: 0 .1rem; padding: 0 .1rem;

View File

@ -7,8 +7,12 @@ import RegForm from './Reg';
import './Login.scss'; import './Login.scss';
const TabPane = Tabs.TabPane; const TabPane = Tabs.TabPane;
@connect(
class LoginWrap extends Component { state =>({
loginWrapActiveKey: state.login.loginWrapActiveKey
})
)
export default class LoginWrap extends Component {
constructor(props){ constructor(props){
super(props); super(props);
} }
@ -32,9 +36,3 @@ class LoginWrap extends Component {
); );
} }
} }
export default connect(
state =>({
loginWrapActiveKey: state.login.loginWrapActiveKey
})
)(LoginWrap)

View File

@ -1,6 +1,8 @@
@import '../../styles/mixin.scss';
/* .interface-box.css */ /* .interface-box.css */
.news-box { .news-box {
max-width: 11rem; @include row-width-limit;
display: -webkit-box; display: -webkit-box;
-webkit-box-flex: 1; -webkit-box-flex: 1;
margin: .88rem auto 0 auto; margin: .88rem auto 0 auto;

View File

@ -37,14 +37,15 @@
} }
.group-operate { .group-operate {
height: 48px; height: 48px;
min-width: 263px;
padding: 10px 6px; padding: 10px 6px;
background: #eee; background: #eee;
display: flex;
justify-content: space-around;
.search { .search {
display: inline-block;
margin-right: 6px; margin-right: 6px;
width: 162px; flex-grow: 1;
} }
} }
.group-list { .group-list {
max-height: 650px; max-height: 650px;

View File

@ -1,4 +1,6 @@
@import '../../styles/mixin.scss';
.g-doc { .g-doc {
max-width: 11rem; @include row-width-limit;
margin: .24rem auto; margin: .24rem auto;
} }

View File

@ -206,13 +206,18 @@ class ProjectList extends Component {
componentWillReceiveProps(nextProps) { componentWillReceiveProps(nextProps) {
// 切换分组 // 切换分组
if (this.props.currGroup !== nextProps.currGroup) { if (this.props.currGroup !== nextProps.currGroup) {
this.props.fetchProjectList(nextProps.currGroup._id, this.props.currPage).then((res) => { if (nextProps.currGroup._id) {
if (res.payload.data.errcode) { this.props.fetchProjectList(nextProps.currGroup._id, this.props.currPage).then((res) => {
message.error(res.payload.data.errmsg); if (res.payload.data.errcode) {
} else { message.error(res.payload.data.errmsg);
this.props.changeTableLoading(false); } else {
} this.props.changeTableLoading(false);
}); }
});
} else {
// 无分组的时候停止loading状态
this.props.changeTableLoading(false);
}
} }
// 切换项目列表 // 切换项目列表
@ -306,7 +311,9 @@ class ProjectList extends Component {
</Form> </Form>
</Modal> </Modal>
<UpDateModal/> <UpDateModal/>
<Button className="m-btn" icon="plus" type="primary" onClick={this.showAddProjectModal}>创建项目</Button> <Button className="m-btn" icon="plus" type="primary"
onClick={this.showAddProjectModal}
disabled={this.props.currGroup._id ? false : true}>创建项目</Button>
<Table <Table
className="m-table" className="m-table"
bordered={true} bordered={true}

View File

@ -1,16 +1,18 @@
@import '../../styles/mixin.scss';
/* .user-box.css */ /* .user-box.css */
.user-box { .user-box {
max-width: 11rem; @include row-width-limit;
display: -webkit-box; display: -webkit-box;
-webkit-box-flex: 1; -webkit-box-flex: 1;
margin: .88rem auto 0 auto; margin: .88rem auto 0 auto;
// font-size: 0.14rem; // font-size: 0.14rem;
margin-top: 40px; margin-top: 40px;
margin-bottom: 55px; margin-bottom: 55px;
.user-list { .user-list {
width: 216px; width: 216px;
@ -19,7 +21,7 @@
border-radius:5px; border-radius:5px;
margin-top: 15px; margin-top: 15px;
.search{ .search{
margin: 5px; margin: 5px;
} }
ul{border:none} ul{border:none}
} }
@ -38,7 +40,7 @@
.user-table { .user-table {
-webkit-box-flex: 1; -webkit-box-flex: 1;
padding: 24px; padding: 24px;
margin-left: 15px; margin-left: 15px;
border-radius:5px; border-radius:5px;
box-shadow: 0 2px 4px 0 rgba(0,0,0,0.20); box-shadow: 0 2px 4px 0 rgba(0,0,0,0.20);
background: #FFF; background: #FFF;
@ -96,4 +98,4 @@
} }
} }

View File

@ -48,3 +48,7 @@ em {
min-height:calc(100% - 2.47rem); min-height:calc(100% - 2.47rem);
} }
} }
.hidden {
display: none;
}

4
client/styles/mixin.scss Normal file
View File

@ -0,0 +1,4 @@
@mixin row-width-limit {
max-width: 11.7rem;
min-width: 9.7rem;
}

View File

@ -1,35 +0,0 @@
### 1.User
- /user/get //获取用户个人信息
- /user/list //获取用户列表,需要提供分页功能
- /user/del //删除用户
- /user/up //更新用户个人信息
- /uesr/login //登录
- /user/reg //注册
- /user/login/status //获取用户登录状态
### 2.Group
- /group/list //获取项目分组列表
- /group/add //添加
- /group/up //更新
- /group/del //删除
### 3.Project
- /project/list/:group_id
- /project/add //添加项目
- /project/up //编辑项目
- /project/del //删除项目
- /project/add_member //添加项目成员
- /project/del_member //删除项目成员
- /project/get //获取一个项目详细信息
### 4.interface
- /interface/list/:project_id
- /interface/add
- /interface/up
- /interface/del
- /interface/mock //执行用户定义的mock返回mock结果
### 5. mock服务器
用户修改host指到yapi服务器yapi服务器收到请求后根据domain/basepath 找到相对应的项目根据req信息找到对应的接口执行用户定义的mock数据返回给用户相应的结果

View File

@ -1,93 +0,0 @@
### 数据库字典
### 数据库基于mongodb
#### 1.User数据表表名:user
```json
{
_id: (int)
username: (string)
password: (sha1)
passsalt: (string)
email : (string)
role : (string)
add_time: (int)
up_time: (int)
}
````
#### 2.Project 数据表,表名:project
```json
{
_id: (int)
uid : (int)
name: (string)
basepath: (string)
desc: (string)
group_id: (int)
members: [
... //成员uid
]
prd_host: (string)//网站上线的domain,可用来获取mock数据
env:(object){
'local环境' : 'http://127.0.0.1'
}
add_time: (int)
up_time: (int)
}
````
#### 3.api 数据表,表名:interface
```json
{
_id: (int)
uid: (int) //负责人uid
path: (string)
group_id: (int)
status: (int)
desc : (string)
add_time: (int)
up_time : (int)
req_headers:(Object){
"header_name":(Object){
default_value: (string),
desc: (string),
mock: (string)
}
}
req_params_type: (form|raw)
req_params: (Object){
"key" : (Object){
default_value: (string),
desc: (string),
mock: (string)
}
}
res_header: (Object){
"header_name":(Object){
default_value: (string),
desc: (string),
mock: (string)
}
}
res_body_type: (text|json),
res_body: (Object){
"key":(Object){
default_value: (string),
desc: (string),
mock: (string)
}
}
}
```
#### 4.项目分组,表名: group
```json
{
_id: (int),
uid: (int),
group_name: (string),
group_desc: (string),
add_time: (int),
up_time: (int)
}
```

View File

@ -1,11 +1,12 @@
const fs = require('fs-extra'); const fs = require('fs-extra');
const path = require('path'); const path = require('path');
const gulp = require('gulp'); const gulp = require('gulp');
const watch = require('gulp-watch');
const babel = require('gulp-babel'); const babel = require('gulp-babel');
const ora = require('ora'); const ora = require('ora');
const chalk = require('chalk'); const chalk = require('chalk');
const { spawn } = require('child_process'); const { spawn } = require('child_process');
let spinner = ora('请稍等...').start(); let spinner = null;
const DIST = 'server_dist/'; const DIST = 'server_dist/';
const SRC = 'server/**/*.js'; const SRC = 'server/**/*.js';
@ -30,21 +31,18 @@ function generateBabel(status) {
} }
function excuteCmd(cmd, args, opts) { function excuteCmd(cmd, args, opts) {
const command = spawn(cmd, args, opts); const NAME = cmd === 'ykit' ? chalk.cyan('[ykit]') : chalk.blue('[dev-server]');
let command = spawn(cmd, args, opts);
command.stdout.on('data', data => { command.stdout.on('data', data => {
output('log', `${cmd}: ${data.toString()}`, true); output('log', `${NAME} ${data.toString()}`, true);
}); });
command.stderr.on('data', data => { command.stderr.on('data', data => {
output('log', `${cmd}: ${data.toString()}`, true); output('log', `${NAME} ${data.toString()}`, true);
}); });
command.on('close', code => { return command;
if (code !== 0) {
output('log', `${cmd}: ${data.toString()}`);
}
});
} }
function output(type, message, restart = false) { function output(type, message, restart = false) {
@ -64,25 +62,29 @@ function output(type, message, restart = false) {
} }
} }
function waitingSpinner() {
spinner = ora({
text: '等待文件变更...',
spinner: 'circleQuarters',
color: 'cyan'
}).start();
}
gulp.task('removeDist', [], function () { gulp.task('removeDist', [], function () {
return fs.removeSync(DIST) return fs.removeSync(DIST)
}); });
gulp.task('initialBuild', ['removeDist'], () => { gulp.task('initialBuild', ['removeDist'], () => {
spinner.text = '初始编译...'; spinner = ora('初始编译...').start();
return gulp.src(SRC) return gulp.src(SRC)
.pipe(generateBabel()) .pipe(generateBabel())
.pipe(gulp.dest(DIST)) .pipe(gulp.dest(DIST))
.on('end', () => { .on('end', () => {
output('success', '初始编译成功!'); output('success', '初始编译成功!');
spinner = ora({ waitingSpinner();
text: '等待文件变更...',
spinner: 'pong',
color: 'green'
}).start();
excuteCmd('node_modules/.bin/nodemon', ['-q', 'server_dist/app.js', 'dev'], { excuteCmd('node_modules/.bin/nodemon', ['-q', 'server_dist/app.js'], {
cwd: __dirname cwd: __dirname
}); });
@ -94,28 +96,41 @@ gulp.task('initialBuild', ['removeDist'], () => {
gulp.task('default', ['initialBuild'], () => { gulp.task('default', ['initialBuild'], () => {
gulp.watch(SRC, (event) => { gulp.watch(SRC, (event) => {
let originFilePath = path.relative(path.join(__dirname, 'server'), event.path)
let distPath = path.resolve(DIST, path.join(originFilePath))
spinner.text = `正在编译 ${event.path}...`; spinner.text = `正在编译 ${event.path}...`;
gulp.src(event.path).pipe(generateBabel()) gulp.src(event.path).pipe(generateBabel())
.pipe(gulp.dest(DIST)).on('end', () => { .pipe(gulp.dest(path.parse(distPath).dir)).on('end', () => {
output('success', `成功编译 ${event.path}`); output('success', `成功编译 ${originFilePath}`);
spinner = ora({ output('success', '正在重启服务器...');
text: 'waiting changes...', waitingSpinner();
spinner: 'pong',
color: 'green'
});
spinner.start();
}); });
}); });
}); });
gulp.task('buildNode', () => {
return gulp.src(SRC)
.pipe(generateBabel())
.pipe(gulp.dest(DIST));
});
gulp.task('watchNode', ['buildNode'], () => {
return watch(SRC, {
verbose: true,
ignoreInitial: false
})
.pipe(generateBabel())
.pipe(gulp.dest(DIST));
});
gulp.task('build', () => { gulp.task('build', () => {
let status = { let status = {
count: 0 count: 0
}; };
let ykitOutput = ''; let ykitOutput = '';
spinner.text = '正在编译...'; spinner = ora('正在编译,请稍等...').start();
gulp.src(SRC) gulp.src(SRC)
.pipe(generateBabel(status)) .pipe(generateBabel(status))

View File

@ -5,10 +5,11 @@
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
"build-server": "babel server -d server_dist", "build-server": "babel server -d server_dist",
"dev-server": "nodemon server_dist/app.js", "dev-server": "nodemon server_dist/app.js -L",
"install-server" : "node server_dist/install.js", "install-server": "node server_dist/install.js",
"dev": "gulp --silent", "dev": "gulp --silent",
"build": "gulp build --silent" "build": "gulp build --silent",
"only-watch": "gulp watchNode"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
@ -67,7 +68,7 @@
"babel": "^6.5.2", "babel": "^6.5.2",
"babel-cli": "^6.24.1", "babel-cli": "^6.24.1",
"babel-core": "^6.8.0", "babel-core": "^6.8.0",
"babel-eslint": "^6.0.4", "babel-eslint": "^7.2.3",
"babel-loader": "^6.2.4", "babel-loader": "^6.2.4",
"babel-plugin-transform-decorators-legacy": "^1.3.4", "babel-plugin-transform-decorators-legacy": "^1.3.4",
"babel-plugin-transform-object-rest-spread": "^6.8.0", "babel-plugin-transform-object-rest-spread": "^6.8.0",

View File

@ -2,35 +2,22 @@ import yapi from './yapi.js';
import commons from './utils/commons'; import commons from './utils/commons';
yapi.commons = commons; yapi.commons = commons;
import dbModule from './utils/db.js'; import dbModule from './utils/db.js';
import mockServer from './middleware/mockServer.js' import mockServer from './middleware/mockServer.js';
import Koa from 'koa' import Koa from 'koa';
import convert from 'koa-convert' import convert from 'koa-convert';
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';
yapi.connect = dbModule.connect() yapi.connect = dbModule.connect();
const app = new Koa() const app = new Koa();
app.use(mockServer)
app.use(bodyParser()) app.use(mockServer);
app.use(router.routes()) app.use(bodyParser());
app.use(router.allowedMethods()) app.use(router.routes());
app.use(router.allowedMethods());
app.use(koaStatic( app.use(koaStatic(
yapi.path.join(yapi.WEBROOT, 'static') yapi.path.join(yapi.WEBROOT, 'static')
)) ));
app.listen(yapi.WEBCONFIG.port) app.listen(yapi.WEBCONFIG.port);
commons.log(`the server is start at port ${yapi.WEBCONFIG.port}`) commons.log(`the server is start at port ${yapi.WEBCONFIG.port}`);

View File

@ -1,18 +1,29 @@
module.exports = { import path from 'path'
"port": "3000", /**
* config.js是用来第一次安装初始化网站配置如果不用默认的runtime_path可以直接修改runtime_path路径
*/
let runtime_path = path.join(path.resolve(__dirname, '../'), 'runtime')
let config = {
"port": 80,
"runtime_path": runtime_path,
"webhost": "yapi.local.qunar.com", "webhost": "yapi.local.qunar.com",
"adminAccount": "admin@admin.com", "adminAccount": "admin@admin.com",
"db": { "db": {
"servername": "127.0.0.1", "servername": "127.0.0.1",
"DATABASE": "yapi", "DATABASE": "yapi",
"port": 27017 "port": 27017
}, },
"mail": { "mail": {
"host": "smtp.mail.com", "host": "smtp.mail.com",
"from": "****@mail.com",
"port": 4652, "port": 4652,
"auth": { "auth": {
"user": "****@mail.com", "user": "****@mail.com",
"pass": "**********" "pass": "**********"
} }
} }
} }
module.exports = config

View File

@ -98,7 +98,7 @@ class userController extends baseController {
}) })
}) })
}, },
tokenField: 'token', tokenField: 'token'
} }
} }

View File

@ -1,27 +1,27 @@
import fs from 'fs-extra' import fs from 'fs-extra';
import path from 'path'
import initConfig from './utils/initConfig.js' import initConfig from './utils/initConfig.js'
import yapi from './yapi.js'; import yapi from './yapi.js';
import commons from './utils/commons'; import commons from './utils/commons';
import dbModule from './utils/db.js'; import dbModule from './utils/db.js';
import userModel from './models/user.js' import userModel from './models/user.js';
yapi.commons = commons; yapi.commons = commons;
yapi.connect = dbModule.connect() yapi.connect = dbModule.connect();
function install(){ function install() {
let exist = yapi.commons.fileExist(yapi.path.join(yapi.WEBROOT_RUNTIME, 'init.lock')) let exist = yapi.commons.fileExist(yapi.path.join(yapi.WEBROOT_RUNTIME, 'init.lock'));
if(exist){
return yapi.commons.log('runtime/init.lock文件已存在请确认您是否已安装。如果需要重新安装请删掉runtime/init.lock文件'); if (exist) {
process.exit(1); yapi.commons.log('runtime/init.lock文件已存在请确认您是否已安装。如果需要重新安装请删掉runtime/init.lock文件');
process.exit(0);
} }
setupSql(); setupSql();
} }
function setupSql(){ function setupSql() {
let userInst = yapi.getInst(userModel); let userInst = yapi.getInst(userModel);
let passsalt = yapi.commons.randStr(); let passsalt = yapi.commons.randStr();
let result = userInst.save({ let result = userInst.save({
username: yapi.WEBCONFIG.adminAccount.substr(0, yapi.WEBCONFIG.adminAccount.indexOf('@')), username: yapi.WEBCONFIG.adminAccount.substr(0, yapi.WEBCONFIG.adminAccount.indexOf('@')),
@ -31,15 +31,16 @@ function setupSql(){
role: 'admin', role: 'admin',
add_time: yapi.commons.time(), add_time: yapi.commons.time(),
up_time: yapi.commons.time() up_time: yapi.commons.time()
}) });
result.then(function(success){
result.then(function () {
fs.ensureFileSync(yapi.path.join(yapi.WEBROOT_RUNTIME, 'init.lock'));
console.log(`初始化管理员账号 "${yapi.WEBCONFIG.adminAccount}" 成功`); console.log(`初始化管理员账号 "${yapi.WEBCONFIG.adminAccount}" 成功`);
yapi.fs.ensureFileSync(yapi.path.join(yapi.WEBROOT_RUNTIME, 'init.lock')); process.exit(0);
process.exit(1) }, function (err) {
}, function(err){
console.log(`初始化管理员账号 "${yapi.WEBCONFIG.adminAccount}" 失败, ${err.message}`); console.log(`初始化管理员账号 "${yapi.WEBCONFIG.adminAccount}" 失败, ${err.message}`);
process.exit(1) process.exit(0);
}) });
} }
install() install();

View File

@ -1,24 +1,23 @@
import yapi from '../yapi.js' import yapi from '../yapi.js';
import mongoose from 'mongoose' import mongoose from 'mongoose';
import autoIncrement from 'mongoose-auto-increment' import autoIncrement from 'mongoose-auto-increment';
/** /**
* 所有的model都需要继承baseModel, 且需要 getSchema和getName方法不然会报错 * 所有的model都需要继承baseModel, 且需要 getSchema和getName方法不然会报错
*/ */
class baseModel{ class baseModel{
constructor(){ constructor(){
this.schema = new mongoose.Schema(this.getSchema()) this.schema = new mongoose.Schema(this.getSchema());
this.name = this.getName() this.name = this.getName();
if(this.isNeedAutoIncrement() === true){ if(this.isNeedAutoIncrement() === true){
this.schema.plugin(autoIncrement.plugin, { this.schema.plugin(autoIncrement.plugin, {
model: this.name, model: this.name,
field: this.getPrimaryKey(), field: this.getPrimaryKey(),
startAt: 101, startAt: 101,
incrementBy: yapi.commons.rand(1, 100) incrementBy: yapi.commons.rand(1, 100)
}) });
} }
this.model = yapi.db(this.name, this.schema); this.model = yapi.db(this.name, this.schema);
@ -32,22 +31,19 @@ class baseModel{
* 可通过覆盖此方法生成其他自增字段 * 可通过覆盖此方法生成其他自增字段
*/ */
getPrimaryKey(){ getPrimaryKey(){
return '_id' return '_id';
} }
/** /**
* 获取collection的schema结构 * 获取collection的schema结构
*/ */
getSchema(){ getSchema(){
yapi.commons.log('Model Class need getSchema function', 'error') yapi.commons.log('Model Class need getSchema function', 'error');
} }
getName(){ getName(){
yapi.commons.log('Model Class need name', 'error') yapi.commons.log('Model Class need name', 'error');
} }
} }
module.exports = baseModel; module.exports = baseModel;

View File

@ -1,57 +1,61 @@
import yapi from '../yapi.js' import yapi from '../yapi.js';
import mongoose from 'mongoose' import mongoose from 'mongoose';
import baseModel from './base.js' import baseModel from './base.js';
class groupModel extends baseModel{ class groupModel extends baseModel {
getName(){ getName() {
return 'group' return 'group';
} }
getSchema(){ getSchema() {
return { return {
uid: Number, uid: Number,
group_name: String, group_name: String,
group_desc: String, group_desc: String,
add_time: Number, add_time: Number,
up_time: Number up_time: Number
} };
} }
save(data) { save(data) {
let m = new this.model(data); let m = new this.model(data);
return m.save(); return m.save();
} }
checkRepeat(name) {
checkRepeat(name) {
return this.model.count({ return this.model.count({
group_name: name group_name: name
}) });
} }
list(){
return this.model.find().select("group_name _id group_desc add_time up_time").exec() list() {
return this.model.find().select("group_name _id group_desc add_time up_time").exec();
} }
del (id) {
del(id) {
return this.model.deleteOne({ return this.model.deleteOne({
_id: id _id: id
}) });
} }
up (id, data) {
return this.model.update({ up(id, data) {
_id: id, return this.model.update(
}, { {
group_name: data.group_name, _id: id
group_desc: data.group_desc, }, {
up_time: yapi.commons.time() group_name: data.group_name,
}) group_desc: data.group_desc,
up_time: yapi.commons.time()
}
);
} }
search(keyword) { search(keyword) {
return this.model.find({ return this.model.find({
group_name: new RegExp(keyword, 'i') group_name: new RegExp(keyword, 'i')
}) })
.limit(10) .limit(10);
} }
} }
module.exports = groupModel;
module.exports= groupModel

View File

@ -1,38 +1,38 @@
import yapi from '../yapi.js' import yapi from '../yapi.js';
import baseModel from './base.js' import baseModel from './base.js';
class interfaceModel extends baseModel{ class interfaceModel extends baseModel {
getName(){ getName() {
return 'interface' return 'interface';
} }
getSchema(){ getSchema() {
return { return {
title: {type: String, required: true}, title: { type: String, required: true },
uid: {type: Number, required: true}, uid: { type: Number, required: true },
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 },
desc: String, desc: String,
add_time: Number, add_time: Number,
up_time: Number, up_time: Number,
req_headers: [{ req_headers: [{
name: String, value: String, desc: String, required: Boolean name: String, value: String, desc: String, required: Boolean
}], }],
req_params_type: { req_params_type: {
type: String, type: String,
enum: ["form", "json", "text", "xml"] enum: ['form', 'json', 'text', 'xml']
}, },
req_params_form: [{ req_params_form: [{
name: String, value: String,value_type: {type: String, enum: ["text", "file"]}, desc: String, required: Boolean name: String, value: String, value_type: { type: String, enum: ['text', 'file'] }, desc: String, required: Boolean
}], }],
req_params_other: String, req_params_other: String,
res_body_type: { res_body_type: {
type: String, type: String,
enum: ["json", "text", "xml"] enum: ['json', 'text', 'xml']
}, },
res_body: String res_body: String
} };
} }
save(data) { save(data) {
@ -40,50 +40,53 @@ class interfaceModel extends baseModel{
return m.save(); return m.save();
} }
get(id) {
get(id){
return this.model.findOne({ return this.model.findOne({
_id: id _id: id
}).exec() })
.exec();
} }
getByPath(project_id, path){ getByPath(project_id, path) {
return this.model.find({ return this.model.find({
project_id: project_id, project_id: project_id,
path: path path: path
}).exec() })
.exec();
} }
checkRepeat(id, path, method){ checkRepeat(id, path, method) {
return this.model.count({ return this.model.count({
project_id: id, project_id: id,
path: path, path: path,
method: method method: method
}) });
} }
countByProjectId(id){ countByProjectId(id) {
return this.model.count({ return this.model.count({
project_id: id project_id: id
}) });
} }
list (project_id){ list(project_id) {
return this.model.find({ return this.model.find({
project_id: project_id project_id: project_id
}).sort({_id: -1}).exec() })
.sort({ _id: -1 })
.exec();
} }
del(id){ del(id) {
return this.model.deleteOne({ return this.model.deleteOne({
_id: id _id: id
}) });
} }
up(id, data){ up(id, data) {
data.up_time = yapi.commons.time(); data.up_time = yapi.commons.time();
return this.model.update({ return this.model.update({
_id: id, _id: id,
}, data, { runValidators: true }) }, data, { runValidators: true });
} }
} }

View File

@ -9,13 +9,13 @@ class logModel extends baseModel {
getSchema() { getSchema() {
return { return {
uid: {type: Number, required: true}, uid: { type: Number, required: true },
title: {type: String, required: true}, title: { type: String, required: true },
type: {type: String, enum:['user', 'group', 'interface', 'project', 'other'], required: true}, type: { type: String, enum: ['user', 'group', 'interface', 'project', 'other'], required: true },
content: {type: String, required: true}, content: { type: String, required: true },
username: {type: String, required: true}, username: { type: String, required: true },
add_time: Number add_time: Number
} };
} }
/** /**
@ -36,21 +36,27 @@ class logModel extends baseModel {
add_time: yapi.commons.time() add_time: yapi.commons.time()
}; };
let log = new this.model(saveData); let log = new this.model(saveData);
return log.save(); return log.save();
} }
list (uid){ list(uid) {
return this.model.find({ return this.model.find({
uid: uid uid: uid
}).exec() })
.exec();
} }
listWithPaging(uid, page, limit) { listWithPaging(uid, page, limit) {
page = parseInt(page); page = parseInt(page);
limit = parseInt(limit); limit = parseInt(limit);
return this.model.find({ return this.model.find({
uid: uid uid: uid
}).skip((page - 1) * limit).limit(limit).exec(); })
.skip((page - 1) * limit)
.limit(limit)
.exec();
} }
listCount(uid) { listCount(uid) {

View File

@ -1,33 +1,34 @@
import yapi from '../yapi.js' import yapi from '../yapi.js';
import baseModel from './base.js' import baseModel from './base.js';
class projectModel extends baseModel{ class projectModel extends baseModel {
getName(){ getName() {
return 'project' return 'project';
} }
getSchema(){ getSchema() {
return { return {
uid: {type: Number, required: true}, uid: { type: Number, required: true },
name: {type: String, required: true}, name: { type: String, required: true },
basepath: {type: String, required: true, validate: { basepath: {
validator: (v) => { type: String, required: true, validate: {
console.log('basepath: ', v) validator: (v) => {
return v && v[0] === '/' return v && v[0] === '/';
}, },
message: 'basepath必须是/开头' message: 'basepath必须是/开头'
}}, }
},
desc: String, desc: String,
group_id: {type: Number, required: true}, group_id: { type: Number, required: true },
members: Array, members: Array,
protocol: {type: String, required: true}, protocol: { type: String, required: true },
prd_host: {type: String, required: true}, prd_host: { type: String, required: true },
env: [ env: [
{name: String, domain: String} { name: String, domain: String }
], ],
add_time: Number, add_time: Number,
up_time: Number up_time: Number
} };
} }
save(data) { save(data) {
@ -35,37 +36,35 @@ class projectModel extends baseModel{
return m.save(); return m.save();
} }
get(id) {
get(id){
return this.model.findOne({ return this.model.findOne({
_id: id _id: id
}).exec() }).exec();
} }
getByDomain(domain){ getByDomain(domain) {
return this.model.find({ return this.model.find({
prd_host: domain prd_host: domain
}).exec() }).exec();
} }
checkNameRepeat(name){ checkNameRepeat(name) {
return this.model.count({ return this.model.count({
name: name name: name
}) });
} }
checkDomainRepeat(domain, basepath){ checkDomainRepeat(domain, basepath) {
return this.model.count({ return this.model.count({
prd_host: domain, prd_host: domain,
basepath: basepath basepath: basepath
}) });
} }
list(group_id) {
list (group_id){
return this.model.find({ return this.model.find({
group_id: group_id group_id: group_id
}).sort({_id: -1}).exec() }).sort({ _id: -1 }).exec();
} }
listWithPaging(group_id, page, limit) { listWithPaging(group_id, page, limit) {
@ -73,7 +72,7 @@ class projectModel extends baseModel{
limit = parseInt(limit); limit = parseInt(limit);
return this.model.find({ return this.model.find({
group_id: group_id group_id: group_id
}).sort({_id: -1}).skip((page - 1) * limit).limit(limit).exec(); }).sort({ _id: -1 }).skip((page - 1) * limit).limit(limit).exec();
} }
listCount(group_id) { listCount(group_id) {
@ -82,54 +81,58 @@ class projectModel extends baseModel{
}); });
} }
countByGroupId(group_id){ countByGroupId(group_id) {
return this.model.count({ return this.model.count({
group_id: group_id group_id: group_id
}) });
} }
del(id){ del(id) {
return this.model.deleteOne({ return this.model.deleteOne({
_id: id _id: id
}) });
} }
up(id, data){
up(id, data) {
data.up_time = yapi.commons.time(); data.up_time = yapi.commons.time();
return this.model.update({ return this.model.update({
_id: id, _id: id,
}, data, { runValidators: true }) }, data, { runValidators: true });
} }
addMember(id, uid){ addMember(id, uid) {
return this.model.update({ return this.model.update(
_id: id {
}, { _id: id
$push: {members: uid} }, {
}) $push: { members: uid }
}
);
} }
delMember(id, uid){ delMember(id, uid) {
return this.model.update({ return this.model.update(
_id: id {
}, { _id: id
$pull: {members: uid} }, {
}) $pull: { members: uid }
}
);
} }
checkMemberRepeat(id, uid){ checkMemberRepeat(id, uid) {
return this.model.count({ return this.model.count({
_id: id, _id: id,
members:[uid] members: [uid]
}) });
} }
search(keyword) { search(keyword) {
return this.model.find({ return this.model.find({
name: new RegExp(keyword, 'ig') name: new RegExp(keyword, 'ig')
}) })
.limit(10) .limit(10);
} }
} }
module.exports = projectModel; module.exports = projectModel;

View File

@ -1,87 +1,95 @@
import yapi from '../yapi.js' import baseModel from './base.js';
import mongoose from 'mongoose'
import baseModel from './base.js'
class userModel extends baseModel{ class userModel extends baseModel {
getName(){ getName() {
return 'user' return 'user';
} }
getSchema(){ getSchema() {
return{ return {
username: { username: {
type: String, type: String,
required: true required: true
}, },
password:{ password: {
type:String, type: String,
required: true required: true
}, },
email: { email: {
type: String, type: String,
required: true required: true
}, },
passsalt: String, passsalt: String,
role: String, role: String,
add_time: Number, add_time: Number,
up_time: Number up_time: Number
} };
} }
save(data){
save(data) {
let user = new this.model(data); let user = new this.model(data);
return user.save(); return user.save();
} }
checkRepeat(email){
checkRepeat(email) {
return this.model.count({ return this.model.count({
email: email email: email
}) });
} }
list(){
return this.model.find().select("_id username email role add_time up_time").exec() //显示id name email role list() {
return this.model.find().select('_id username email role add_time up_time').exec(); //显示id name email role
} }
findByUids(uids){
findByUids(uids) {
return this.model.find({ return this.model.find({
_id: {$in: uids} _id: { $in: uids }
}).select("_id username email role add_time up_time").exec() }).select('_id username email role add_time up_time').exec();
} }
listWithPaging(page, limit) { listWithPaging(page, limit) {
page = parseInt(page); page = parseInt(page);
limit = parseInt(limit); limit = parseInt(limit);
return this.model.find().sort({_id: -1}).skip((page - 1) * limit).limit(limit).select("_id username email role add_time up_time").exec(); return this.model.find().sort({ _id: -1 }).skip((page - 1) * limit).limit(limit).select('_id username email role add_time up_time').exec();
} }
listCount() { listCount() {
return this.model.count(); return this.model.count();
} }
findByEmail(email){
return this.model.findOne({email: email}) findByEmail(email) {
return this.model.findOne({ email: email });
} }
findById(id){
findById(id) {
return this.model.findById({ return this.model.findById({
_id: id _id: id
}) });
} }
del (id) {
del(id) {
return this.model.deleteOne({ return this.model.deleteOne({
_id: id _id: id
}) });
} }
update(id,data){
update(id, data) {
return this.model.update({ return this.model.update({
_id: id _id: id
}, data) }, data);
} }
search(keyword) { search(keyword) {
return this.model.find({ return this.model.find({
$or: [ $or: [
{ email: new RegExp(keyword, 'i') }, { email: new RegExp(keyword, 'i') },
{ username: new RegExp(keyword, 'i')} { username: new RegExp(keyword, 'i') }
] ]
}, { }, {
passsalt: 0, passsalt: 0,
password: 0 password: 0
}).limit(10) }).limit(10);
} }
} }
module.exports = userModel module.exports = userModel;

View File

@ -1,12 +1,11 @@
import koaRouter from 'koa-router' import koaRouter from 'koa-router';
import interfaceController from './controllers/interface.js' import interfaceController from './controllers/interface.js';
import groupController from './controllers/group.js' import groupController from './controllers/group.js';
import userController from './controllers/user.js' import userController from './controllers/user.js';
import yapi from './yapi.js'
import projectController from './controllers/project.js'
import logController from './controllers/log.js'
import yapi from './yapi.js';
import projectController from './controllers/project.js';
import logController from './controllers/log.js';
const router = koaRouter(); const router = koaRouter();
@ -34,47 +33,46 @@ const INTERFACE_CONFIG = {
}; };
//group //group
createAction('group', 'list', 'get', 'list') createAction('group', 'list', 'get', 'list');
createAction('group', 'add', 'post', 'add') createAction('group', 'add', 'post', 'add');
createAction('group', 'up', 'post', 'up') createAction('group', 'up', 'post', 'up');
createAction('group', 'del', 'post', 'del') createAction('group', 'del', 'post', 'del');
//user //user
createAction('user', 'login', 'post', 'login') createAction('user', 'login', 'post', 'login');
createAction('user', 'reg', 'post', 'reg') createAction('user', 'reg', 'post', 'reg');
createAction('user', 'list', 'get', 'list') createAction('user', 'list', 'get', 'list');
createAction('user', 'find', 'get', 'findById') createAction('user', 'find', 'get', 'findById');
createAction('user', 'update', 'post', 'update') createAction('user', 'update', 'post', 'update');
createAction('user', 'del', 'post', 'del') createAction('user', 'del', 'post', 'del');
createAction('user', 'status', 'get', 'getLoginStatus') createAction('user', 'status', 'get', 'getLoginStatus');
createAction('user', 'logout', 'get', 'logout') createAction('user', 'logout', 'get', 'logout');
createAction('user', 'login_by_token', 'post', 'loginByToken') createAction('user', 'login_by_token', 'post', 'loginByToken');
createAction('user', 'change_password', 'post', 'changePassword') createAction('user', 'change_password', 'post', 'changePassword');
createAction('user', 'search', 'get', 'search') createAction('user', 'search', 'get', 'search');
//project //project
createAction('project', 'add', 'post', 'add') createAction('project', 'add', 'post', 'add');
createAction('project', 'list', 'get', 'list') createAction('project', 'list', 'get', 'list');
createAction('project', 'get', 'get', 'get') createAction('project', 'get', 'get', 'get');
createAction('project', 'up', 'post', 'up') createAction('project', 'up', 'post', 'up');
createAction('project', 'del', 'post', 'del') createAction('project', 'del', 'post', 'del');
createAction('project', 'add_member', 'post', 'addMember') createAction('project', 'add_member', 'post', 'addMember');
createAction('project', 'del_member', 'post', 'delMember') createAction('project', 'del_member', 'post', 'delMember');
createAction('project', 'get_member_list', 'get', 'getMemberList') createAction('project', 'get_member_list', 'get', 'getMemberList');
createAction('project', 'search', 'get', 'search') createAction('project', 'search', 'get', 'search');
//interface //interface
createAction('interface', 'add', 'post', 'add') createAction('interface', 'add', 'post', 'add');
createAction('interface', 'list', 'get', 'list') createAction('interface', 'list', 'get', 'list');
createAction('interface', 'get', 'get', 'get') createAction('interface', 'get', 'get', 'get');
createAction('interface', 'up', 'post', 'up') createAction('interface', 'up', 'post', 'up');
createAction('interface', 'del', 'post', 'del') createAction('interface', 'del', 'post', 'del');
//node //node
createAction('log', 'list', 'get', 'list'); createAction('log', 'list', 'get', 'list');
/** /**
* *
* @param {*} controller controller_name * @param {*} controller controller_name
@ -82,18 +80,20 @@ createAction('log', 'list', 'get', 'list');
* @param {*} method request_method , post get put delete ... * @param {*} method request_method , post get put delete ...
* @param {*} action controller_action_name * @param {*} action controller_action_name
*/ */
function createAction(controller, path, method, action){ function createAction(controller, path, method, action) {
router[method](INTERFACE_CONFIG[controller].prefix + path, async (ctx) => { router[method](INTERFACE_CONFIG[controller].prefix + path, async (ctx) => {
let inst = new INTERFACE_CONFIG[controller].controller(ctx); let inst = new INTERFACE_CONFIG[controller].controller(ctx);
await inst.init(ctx); await inst.init(ctx);
if(inst.$auth === true){
if (inst.$auth === true) {
await inst[action].call(inst, ctx); await inst[action].call(inst, ctx);
}else{ } else {
ctx.body = yapi.commons.resReturn(null, 400, 'Without Permission.'); ctx.body = yapi.commons.resReturn(null, 400, 'Without Permission.');
} }
}) });
} }
module.exports = router module.exports = router;

View File

@ -1,35 +1,36 @@
import fs from 'fs-extra';
import fs from 'fs-extra' import path from 'path';
import path from 'path' import yapi from '../yapi.js';
import yapi from '../yapi.js' import sha1 from 'sha1';
import sha1 from 'sha1'
exports.resReturn = (data, num, errmsg) => { exports.resReturn = (data, num, errmsg) => {
num = num || 0; num = num || 0;
return { return {
errcode: num, errcode: num,
errmsg: errmsg || 'success', errmsg: errmsg || 'success',
data: data data: data
} };
} };
const MSGTYPE = {
'log': 'Log',
'warn': 'warning',
'error': 'Error'
}
exports.log = (msg, type) => { exports.log = (msg, type) => {
if (!msg) return; if (!msg) {
return;
}
type = type || 'log'; type = type || 'log';
let f; let f;
switch (type) { switch (type) {
case 'log': f = console.log; break; case 'log': f = console.log; break;
case 'warn': f = console.warn; break; case 'warn': f = console.warn; break;
case 'error': f = console.error; break; case 'error': f = console.error; break;
default: f = console.log; break; default: f = console.log; break;
} }
f(type + ':', msg); f(type + ':', msg);
let date = new Date(); let date = new Date();
let year = date.getFullYear(); let year = date.getFullYear();
let month = date.getMonth(); let month = date.getMonth();
@ -40,14 +41,13 @@ exports.log = (msg, type) => {
if (msg instanceof Error) msg = msg.message; if (msg instanceof Error) msg = msg.message;
else msg = JSON.stringify(msg); else msg = JSON.stringify(msg);
} }
let data = (new Date).toLocaleTimeString() + "\t|\t" + type + "\t|\t" + msg;
let data = (new Date).toLocaleTimeString() + '\t|\t' + type + '\t|\t' + msg;
fs.writeFileSync(logfile, data, { fs.writeFileSync(logfile, data, {
flag: 'w+' flag: 'w+'
}); });
};
}
exports.fileExist = (filePath) => { exports.fileExist = (filePath) => {
try { try {
@ -55,80 +55,88 @@ exports.fileExist = (filePath) => {
} catch (err) { } catch (err) {
return false; return false;
} }
} };
exports.time = () => { exports.time = () => {
return Date.parse(new Date()) / 1000; return Date.parse(new Date()) / 1000;
} };
exports.fieldSelect = (data, field) => { exports.fieldSelect = (data, field) => {
if (!data || !field || !Array.isArray(field)) return null; if (!data || !field || !Array.isArray(field)) {
return null;
}
var arr = {}; var arr = {};
field.forEach((f) => { field.forEach((f) => {
data[f] && (arr[f] = data[f]); data[f] && (arr[f] = data[f]);
}) });
return arr; return arr;
} };
exports.rand = (min, max) => { exports.rand = (min, max) => {
return Math.floor(Math.random() * (max - min) + min); return Math.floor(Math.random() * (max - min) + min);
} };
exports.json_parse = (json) => { exports.json_parse = (json) => {
try { try {
return JSON.parse(json); return JSON.parse(json);
} catch (e) { } catch (e) {
return json return json;
} }
} };
exports.randStr = () => { exports.randStr = () => {
return Math.random().toString(36).substr(2) return Math.random().toString(36).substr(2);
} };
exports.generatePassword = (password, passsalt) => { exports.generatePassword = (password, passsalt) => {
return sha1(password + sha1(passsalt)); return sha1(password + sha1(passsalt));
} };
exports.expireDate = (day) => { exports.expireDate = (day) => {
let date = new Date(); let date = new Date();
date.setTime(date.getTime() + day * 86400000); date.setTime(date.getTime() + day * 86400000);
return date; return date;
} };
exports.sendMail = (options, cb) => { exports.sendMail = (options, cb) => {
if (!yapi.mail) return false; if (!yapi.mail) return false;
options.subject = options.subject ? options.subject + '-yapi平台' : 'ypai平台'; options.subject = options.subject ? options.subject + '-yapi平台' : 'ypai平台';
cb = cb || function (err, info) {
cb = cb || function (err) {
if (err) { if (err) {
yapi.commons.log('send mail ' + options.to + ' error,' + err.message, 'error'); yapi.commons.log('send mail ' + options.to + ' error,' + err.message, 'error');
} else { } else {
yapi.commons.log('send mail ' + options.to + ' success'); yapi.commons.log('send mail ' + options.to + ' success');
} }
};
}
try { try {
yapi.mail.sendMail({ yapi.mail.sendMail({
from: yapi.WEBCONFIG.mail.auth.user, from: yapi.WEBCONFIG.mail.from,
to: options.to, to: options.to,
subject: 'yapi平台', subject: 'yapi平台',
html: options.contents html: options.contents
}, cb) }, cb);
} catch (e) { } catch (e) {
console.error(e.message) console.error(e.message);
} }
} };
exports.validateSearchKeyword = keyword => { exports.validateSearchKeyword = keyword => {
if (/^\*|\?|\+|\$|\^|\\|\.$/.test(keyword)) { if (/^\*|\?|\+|\$|\^|\\|\.$/.test(keyword)) {
return false; return false;
} }
return true; return true;
} };
exports.filterRes = (list, rules) => { exports.filterRes = (list, rules) => {
return list.map(item => { return list.map(item => {
let filteredRes = {}; let filteredRes = {};
rules.forEach(rule => { rules.forEach(rule => {
if (typeof rule == 'string') { if (typeof rule == 'string') {
filteredRes[rule] = item[rule]; filteredRes[rule] = item[rule];
@ -136,38 +144,51 @@ exports.filterRes = (list, rules) => {
filteredRes[rule.alias] = item[rule.key]; filteredRes[rule.alias] = item[rule.key];
} }
}); });
return filteredRes; return filteredRes;
}) });
} };
exports.verifyPath = (path) => { exports.verifyPath = (path) => {
if (/^\/[a-zA-Z0-9\-\/_:\.]+$/.test(path)) { if (/^\/[a-zA-Z0-9\-\/_:\.]+$/.test(path)) {
if (path[path.length - 1] === '/') { if (path[path.length - 1] === '/') {
return false; return false;
} else { } else {
return true return true;
} }
} else { } else {
return false; return false;
} }
} };
function trim(str) { function trim(str) {
if (!str) return str; if (!str) {
return str;
}
str = str + ''; str = str + '';
return str.replace(/(^\s*)|(\s*$)/g, "");
return str.replace(/(^\s*)|(\s*$)/g, '');
} }
function ltrim(str) { function ltrim(str) {
if (!str) return str; if (!str) {
return str;
}
str = str + ''; str = str + '';
return str.replace(/(^\s*)/g, "");
return str.replace(/(^\s*)/g, '');
} }
function rtrim(str) { function rtrim(str) {
if (!str) return str; if (!str) {
return str;
}
str = str + ''; str = str + '';
return str.replace(/(\s*$)/g, "");
return str.replace(/(\s*$)/g, '');
} }
exports.trim = trim; exports.trim = trim;
@ -175,7 +196,10 @@ exports.ltrim = ltrim;
exports.rtrim = rtrim; exports.rtrim = rtrim;
exports.handleParams = (params, keys) => { exports.handleParams = (params, keys) => {
if (!params || typeof params !== 'object' || !keys || typeof keys !== 'object') return false; if (!params || typeof params !== 'object' || !keys || typeof keys !== 'object') {
return false;
}
for (var key in keys) { for (var key in keys) {
var filter = keys[key]; var filter = keys[key];
if (params[key]) { if (params[key]) {
@ -186,5 +210,6 @@ exports.handleParams = (params, keys) => {
} }
} }
} }
return params; return params;
} };

View File

@ -1,31 +1,35 @@
import mongoose from 'mongoose' import mongoose from 'mongoose';
import yapi from '../yapi.js' import yapi from '../yapi.js';
import autoIncrement from 'mongoose-auto-increment' import autoIncrement from 'mongoose-auto-increment';
function model(model, schema){ function model(model, schema){
if(schema instanceof mongoose.Schema === false){ if(schema instanceof mongoose.Schema === false){
schema = new mongoose.Schema(schema); schema = new mongoose.Schema(schema);
} }
schema.set('autoIndex', false); schema.set('autoIndex', false);
return yapi.connect.model(model, schema, model)
return yapi.connect.model(model, schema, model);
} }
function connect(){ function connect(){
mongoose.Promise = global.Promise; mongoose.Promise = global.Promise;
let config = yapi.WEBCONFIG; let config = yapi.WEBCONFIG;
let options = {}; let options = {};
if(config.user){ if(config.user){
options.user = config.db.user, options.user = config.db.user,
options.pass = config.db.pass options.pass = config.db.pass;
} }
let db = mongoose.connect(`mongodb://${config.db.servername}:${config.db.port}/${config.db.DATABASE}`, options); let db = mongoose.connect(`mongodb://${config.db.servername}:${config.db.port}/${config.db.DATABASE}`, options);
db.then(function (res) { db.then(function (res) {
yapi.commons.log('mongodb load success...') yapi.commons.log('mongodb load success...');
}, function (err) { }, function (err) {
yapi.commons.log(err, 'Mongo connect error'); yapi.commons.log(err, 'Mongo connect error');
}) });
autoIncrement.initialize(db); autoIncrement.initialize(db);
return db; return db;
@ -33,12 +37,7 @@ function connect(){
yapi.db = model; yapi.db = model;
module.exports = { module.exports = {
model: model, model: model,
connect: connect connect: connect
}; };

View File

@ -1,10 +1,13 @@
import path from 'path' import path from 'path';
import fs from 'fs-extra' import fs from 'fs-extra';
import config from '../config.js' import config from '../config.js';
let configPath = path.join(path.resolve(__dirname, '../../'), 'runtime', 'config.json') let runtimePath = config.runtime_path;
fs.ensureDirSync( runtimePath );
fs.ensureDirSync( path.join(runtimePath, 'log'));
let configPath = path.join(runtimePath, 'config.json')
fs.writeFileSync(configPath, fs.writeFileSync(configPath,
JSON.stringify(config, null, "\t"), JSON.stringify(config, null, '\t'),
{encoding: 'utf8'} { encoding: 'utf8' }
) );

View File

@ -1,24 +1,22 @@
import path from 'path' import path from 'path';
import fs from 'fs-extra' import fs from 'fs-extra';
import nodemailer from 'nodemailer'; import nodemailer from 'nodemailer';
import config from '../runtime/config.json' import config from '../runtime/config.json';
var insts = new Map(); let insts = new Map();
let mail; let mail;
const WEBROOT = path.resolve(__dirname, '..'); //路径 const WEBROOT = path.resolve(__dirname, '..'); //路径
const WEBROOT_SERVER = __dirname; const WEBROOT_SERVER = __dirname;
const WEBROOT_RUNTIME = path.join(WEBROOT, 'runtime'); const WEBROOT_RUNTIME = config.runtime_path;
const WEBROOT_LOG = path.join(WEBROOT_RUNTIME, 'log'); const WEBROOT_LOG = path.join(WEBROOT_RUNTIME, 'log');
const WEBCONFIG = config; const WEBCONFIG = config;
fs.ensureDirSync(WEBROOT_RUNTIME); fs.ensureDirSync(WEBROOT_RUNTIME);
fs.ensureDirSync(WEBROOT_LOG); fs.ensureDirSync(WEBROOT_LOG);
if (WEBCONFIG.mail) {
mail = nodemailer.createTransport(WEBCONFIG.mail);
if(WEBCONFIG.mail){
mail = nodemailer.createTransport(WEBCONFIG.mail)
} }
/** /**
@ -27,18 +25,18 @@ if(WEBCONFIG.mail){
* @example * @example
* yapi.getInst(groupModel, arg1, arg2) * yapi.getInst(groupModel, arg1, arg2)
*/ */
function getInst(m, ...args){ function getInst(m, ...args) {
if(!insts.get(m)){ if (!insts.get(m)) {
insts.set(m, new m(args)) insts.set(m, new m(args));
} }
return insts.get(m) return insts.get(m);
} }
function delInst(m){ function delInst(m) {
try{ try {
insts.delete(m) insts.delete(m);
}catch(err){ } catch (err) {
console.error(err) console.error(err);
} }
} }
@ -53,6 +51,6 @@ let r = {
getInst: getInst, getInst: getInst,
delInst: delInst, delInst: delInst,
getInsts: insts getInsts: insts
} };
if(mail) r.mail = mail; if (mail) r.mail = mail;
module.exports = r; module.exports = r;

View File

@ -43,6 +43,7 @@ _yapi2.default.commons = _commons2.default;
_yapi2.default.connect = _db2.default.connect(); _yapi2.default.connect = _db2.default.connect();
var app = new _koa2.default(); var app = new _koa2.default();
app.use(_mockServer2.default); app.use(_mockServer2.default);
app.use((0, _koaBodyparser2.default)()); app.use((0, _koaBodyparser2.default)());
app.use(_router2.default.routes()); app.use(_router2.default.routes());

View File

@ -1,7 +1,18 @@
"use strict"; 'use strict';
module.exports = { var _path = require('path');
"port": "3000",
var _path2 = _interopRequireDefault(_path);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
* config.js是用来第一次安装初始化网站配置如果不用默认的runtime_path可以直接修改runtime_path路径
*/
var runtime_path = _path2.default.join(_path2.default.resolve(__dirname, '../'), 'runtime');
var config = {
"port": 80,
"runtime_path": runtime_path,
"webhost": "yapi.local.qunar.com", "webhost": "yapi.local.qunar.com",
"adminAccount": "admin@admin.com", "adminAccount": "admin@admin.com",
"db": { "db": {
@ -11,10 +22,13 @@ module.exports = {
}, },
"mail": { "mail": {
"host": "smtp.mail.com", "host": "smtp.mail.com",
"from": "****@mail.com",
"port": 4652, "port": 4652,
"auth": { "auth": {
"user": "****@mail.com", "user": "****@mail.com",
"pass": "**********" "pass": "**********"
} }
} }
}; };
module.exports = config;

View File

@ -4,10 +4,6 @@ var _fsExtra = require('fs-extra');
var _fsExtra2 = _interopRequireDefault(_fsExtra); var _fsExtra2 = _interopRequireDefault(_fsExtra);
var _path = require('path');
var _path2 = _interopRequireDefault(_path);
var _initConfig = require('./utils/initConfig.js'); var _initConfig = require('./utils/initConfig.js');
var _initConfig2 = _interopRequireDefault(_initConfig); var _initConfig2 = _interopRequireDefault(_initConfig);
@ -35,10 +31,12 @@ _yapi2.default.connect = _db2.default.connect();
function install() { function install() {
var exist = _yapi2.default.commons.fileExist(_yapi2.default.path.join(_yapi2.default.WEBROOT_RUNTIME, 'init.lock')); var exist = _yapi2.default.commons.fileExist(_yapi2.default.path.join(_yapi2.default.WEBROOT_RUNTIME, 'init.lock'));
if (exist) { if (exist) {
return _yapi2.default.commons.log('runtime/init.lock文件已存在请确认您是否已安装。如果需要重新安装请删掉runtime/init.lock文件'); _yapi2.default.commons.log('runtime/init.lock文件已存在请确认您是否已安装。如果需要重新安装请删掉runtime/init.lock文件');
process.exit(1); process.exit(0);
} }
setupSql(); setupSql();
} }
@ -54,13 +52,14 @@ function setupSql() {
add_time: _yapi2.default.commons.time(), add_time: _yapi2.default.commons.time(),
up_time: _yapi2.default.commons.time() up_time: _yapi2.default.commons.time()
}); });
result.then(function (success) {
result.then(function () {
_fsExtra2.default.ensureFileSync(_yapi2.default.path.join(_yapi2.default.WEBROOT_RUNTIME, 'init.lock'));
console.log('\u521D\u59CB\u5316\u7BA1\u7406\u5458\u8D26\u53F7 "' + _yapi2.default.WEBCONFIG.adminAccount + '" \u6210\u529F'); console.log('\u521D\u59CB\u5316\u7BA1\u7406\u5458\u8D26\u53F7 "' + _yapi2.default.WEBCONFIG.adminAccount + '" \u6210\u529F');
_yapi2.default.fs.ensureFileSync(_yapi2.default.path.join(_yapi2.default.WEBROOT_RUNTIME, 'init.lock')); process.exit(0);
process.exit(1);
}, function (err) { }, function (err) {
console.log('\u521D\u59CB\u5316\u7BA1\u7406\u5458\u8D26\u53F7 "' + _yapi2.default.WEBCONFIG.adminAccount + '" \u5931\u8D25, ' + err.message); console.log('\u521D\u59CB\u5316\u7BA1\u7406\u5458\u8D26\u53F7 "' + _yapi2.default.WEBCONFIG.adminAccount + '" \u5931\u8D25, ' + err.message);
process.exit(1); process.exit(0);
}); });
} }

View File

@ -32,6 +32,7 @@ var baseModel = function () {
this.schema = new _mongoose2.default.Schema(this.getSchema()); this.schema = new _mongoose2.default.Schema(this.getSchema());
this.name = this.getName(); this.name = this.getName();
if (this.isNeedAutoIncrement() === true) { if (this.isNeedAutoIncrement() === true) {
this.schema.plugin(_mongooseAutoIncrement2.default.plugin, { this.schema.plugin(_mongooseAutoIncrement2.default.plugin, {
model: this.name, model: this.name,

View File

@ -60,15 +60,15 @@ var interfaceModel = function (_baseModel) {
}], }],
req_params_type: { req_params_type: {
type: String, type: String,
enum: ["form", "json", "text", "xml"] enum: ['form', 'json', 'text', 'xml']
}, },
req_params_form: [{ req_params_form: [{
name: String, value: String, value_type: { type: String, enum: ["text", "file"] }, desc: String, required: Boolean name: String, value: String, value_type: { type: String, enum: ['text', 'file'] }, desc: String, required: Boolean
}], }],
req_params_other: String, req_params_other: String,
res_body_type: { res_body_type: {
type: String, type: String,
enum: ["json", "text", "xml"] enum: ['json', 'text', 'xml']
}, },
res_body: String res_body: String
}; };

View File

@ -127,6 +127,7 @@ var logModel = function (_baseModel) {
value: function listWithPaging(uid, page, limit) { value: function listWithPaging(uid, page, limit) {
page = parseInt(page); page = parseInt(page);
limit = parseInt(limit); limit = parseInt(limit);
return this.model.find({ return this.model.find({
uid: uid uid: uid
}).skip((page - 1) * limit).limit(limit).exec(); }).skip((page - 1) * limit).limit(limit).exec();

View File

@ -49,13 +49,14 @@ var projectModel = function (_baseModel) {
return { return {
uid: { type: Number, required: true }, uid: { type: Number, required: true },
name: { type: String, required: true }, name: { type: String, required: true },
basepath: { type: String, required: true, validate: { basepath: {
type: String, required: true, validate: {
validator: function validator(v) { validator: function validator(v) {
console.log('basepath: ', v);
return v && v[0] === '/'; return v && v[0] === '/';
}, },
message: 'basepath必须是/开头' message: 'basepath必须是/开头'
} }, }
},
desc: String, desc: String,
group_id: { type: Number, required: true }, group_id: { type: Number, required: true },
members: Array, members: Array,

View File

@ -20,14 +20,6 @@ var _inherits2 = require('babel-runtime/helpers/inherits');
var _inherits3 = _interopRequireDefault(_inherits2); var _inherits3 = _interopRequireDefault(_inherits2);
var _yapi = require('../yapi.js');
var _yapi2 = _interopRequireDefault(_yapi);
var _mongoose = require('mongoose');
var _mongoose2 = _interopRequireDefault(_mongoose);
var _base = require('./base.js'); var _base = require('./base.js');
var _base2 = _interopRequireDefault(_base); var _base2 = _interopRequireDefault(_base);
@ -85,21 +77,21 @@ var userModel = function (_baseModel) {
}, { }, {
key: 'list', key: 'list',
value: function list() { value: function list() {
return this.model.find().select("_id username email role add_time up_time").exec(); //显示id name email role return this.model.find().select('_id username email role add_time up_time').exec(); //显示id name email role
} }
}, { }, {
key: 'findByUids', key: 'findByUids',
value: function findByUids(uids) { value: function findByUids(uids) {
return this.model.find({ return this.model.find({
_id: { $in: uids } _id: { $in: uids }
}).select("_id username email role add_time up_time").exec(); }).select('_id username email role add_time up_time').exec();
} }
}, { }, {
key: 'listWithPaging', key: 'listWithPaging',
value: function listWithPaging(page, limit) { value: function listWithPaging(page, limit) {
page = parseInt(page); page = parseInt(page);
limit = parseInt(limit); limit = parseInt(limit);
return this.model.find().sort({ _id: -1 }).skip((page - 1) * limit).limit(limit).select("_id username email role add_time up_time").exec(); return this.model.find().sort({ _id: -1 }).skip((page - 1) * limit).limit(limit).select('_id username email role add_time up_time').exec();
} }
}, { }, {
key: 'listCount', key: 'listCount',

View File

@ -28,6 +28,7 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
exports.resReturn = function (data, num, errmsg) { exports.resReturn = function (data, num, errmsg) {
num = num || 0; num = num || 0;
return { return {
errcode: num, errcode: num,
errmsg: errmsg || 'success', errmsg: errmsg || 'success',
@ -35,16 +36,15 @@ exports.resReturn = function (data, num, errmsg) {
}; };
}; };
var MSGTYPE = {
'log': 'Log',
'warn': 'warning',
'error': 'Error'
};
exports.log = function (msg, type) { exports.log = function (msg, type) {
if (!msg) return; if (!msg) {
return;
}
type = type || 'log'; type = type || 'log';
var f = void 0; var f = void 0;
switch (type) { switch (type) {
case 'log': case 'log':
f = console.log;break; f = console.log;break;
@ -55,7 +55,9 @@ exports.log = function (msg, type) {
default: default:
f = console.log;break; f = console.log;break;
} }
f(type + ':', msg); f(type + ':', msg);
var date = new Date(); var date = new Date();
var year = date.getFullYear(); var year = date.getFullYear();
var month = date.getMonth(); var month = date.getMonth();
@ -65,7 +67,9 @@ exports.log = function (msg, type) {
if ((typeof msg === 'undefined' ? 'undefined' : (0, _typeof3.default)(msg)) === 'object') { if ((typeof msg === 'undefined' ? 'undefined' : (0, _typeof3.default)(msg)) === 'object') {
if (msg instanceof Error) msg = msg.message;else msg = (0, _stringify2.default)(msg); if (msg instanceof Error) msg = msg.message;else msg = (0, _stringify2.default)(msg);
} }
var data = new Date().toLocaleTimeString() + "\t|\t" + type + "\t|\t" + msg;
var data = new Date().toLocaleTimeString() + '\t|\t' + type + '\t|\t' + msg;
_fsExtra2.default.writeFileSync(logfile, data, { _fsExtra2.default.writeFileSync(logfile, data, {
flag: 'w+' flag: 'w+'
}); });
@ -84,11 +88,16 @@ exports.time = function () {
}; };
exports.fieldSelect = function (data, field) { exports.fieldSelect = function (data, field) {
if (!data || !field || !Array.isArray(field)) return null; if (!data || !field || !Array.isArray(field)) {
return null;
}
var arr = {}; var arr = {};
field.forEach(function (f) { field.forEach(function (f) {
data[f] && (arr[f] = data[f]); data[f] && (arr[f] = data[f]);
}); });
return arr; return arr;
}; };
@ -121,16 +130,18 @@ exports.expireDate = function (day) {
exports.sendMail = function (options, cb) { exports.sendMail = function (options, cb) {
if (!_yapi2.default.mail) return false; if (!_yapi2.default.mail) return false;
options.subject = options.subject ? options.subject + '-yapi平台' : 'ypai平台'; options.subject = options.subject ? options.subject + '-yapi平台' : 'ypai平台';
cb = cb || function (err, info) {
cb = cb || function (err) {
if (err) { if (err) {
_yapi2.default.commons.log('send mail ' + options.to + ' error,' + err.message, 'error'); _yapi2.default.commons.log('send mail ' + options.to + ' error,' + err.message, 'error');
} else { } else {
_yapi2.default.commons.log('send mail ' + options.to + ' success'); _yapi2.default.commons.log('send mail ' + options.to + ' success');
} }
}; };
try { try {
_yapi2.default.mail.sendMail({ _yapi2.default.mail.sendMail({
from: _yapi2.default.WEBCONFIG.mail.auth.user, from: _yapi2.default.WEBCONFIG.mail.from,
to: options.to, to: options.to,
subject: 'yapi平台', subject: 'yapi平台',
html: options.contents html: options.contents
@ -144,12 +155,14 @@ exports.validateSearchKeyword = function (keyword) {
if (/^\*|\?|\+|\$|\^|\\|\.$/.test(keyword)) { if (/^\*|\?|\+|\$|\^|\\|\.$/.test(keyword)) {
return false; return false;
} }
return true; return true;
}; };
exports.filterRes = function (list, rules) { exports.filterRes = function (list, rules) {
return list.map(function (item) { return list.map(function (item) {
var filteredRes = {}; var filteredRes = {};
rules.forEach(function (rule) { rules.forEach(function (rule) {
if (typeof rule == 'string') { if (typeof rule == 'string') {
filteredRes[rule] = item[rule]; filteredRes[rule] = item[rule];
@ -157,6 +170,7 @@ exports.filterRes = function (list, rules) {
filteredRes[rule.alias] = item[rule.key]; filteredRes[rule.alias] = item[rule.key];
} }
}); });
return filteredRes; return filteredRes;
}); });
}; };
@ -174,21 +188,33 @@ exports.verifyPath = function (path) {
}; };
function trim(str) { function trim(str) {
if (!str) return str; if (!str) {
return str;
}
str = str + ''; str = str + '';
return str.replace(/(^\s*)|(\s*$)/g, "");
return str.replace(/(^\s*)|(\s*$)/g, '');
} }
function ltrim(str) { function ltrim(str) {
if (!str) return str; if (!str) {
return str;
}
str = str + ''; str = str + '';
return str.replace(/(^\s*)/g, "");
return str.replace(/(^\s*)/g, '');
} }
function rtrim(str) { function rtrim(str) {
if (!str) return str; if (!str) {
return str;
}
str = str + ''; str = str + '';
return str.replace(/(\s*$)/g, "");
return str.replace(/(\s*$)/g, '');
} }
exports.trim = trim; exports.trim = trim;
@ -196,7 +222,10 @@ exports.ltrim = ltrim;
exports.rtrim = rtrim; exports.rtrim = rtrim;
exports.handleParams = function (params, keys) { exports.handleParams = function (params, keys) {
if (!params || (typeof params === 'undefined' ? 'undefined' : (0, _typeof3.default)(params)) !== 'object' || !keys || (typeof keys === 'undefined' ? 'undefined' : (0, _typeof3.default)(keys)) !== 'object') return false; if (!params || (typeof params === 'undefined' ? 'undefined' : (0, _typeof3.default)(params)) !== 'object' || !keys || (typeof keys === 'undefined' ? 'undefined' : (0, _typeof3.default)(keys)) !== 'object') {
return false;
}
for (var key in keys) { for (var key in keys) {
var filter = keys[key]; var filter = keys[key];
if (params[key]) { if (params[key]) {
@ -210,5 +239,6 @@ exports.handleParams = function (params, keys) {
} }
} }
} }
return params; return params;
}; };

View File

@ -18,14 +18,18 @@ function model(model, schema) {
if (schema instanceof _mongoose2.default.Schema === false) { if (schema instanceof _mongoose2.default.Schema === false) {
schema = new _mongoose2.default.Schema(schema); schema = new _mongoose2.default.Schema(schema);
} }
schema.set('autoIndex', false); schema.set('autoIndex', false);
return _yapi2.default.connect.model(model, schema, model); return _yapi2.default.connect.model(model, schema, model);
} }
function connect() { function connect() {
_mongoose2.default.Promise = global.Promise; _mongoose2.default.Promise = global.Promise;
var config = _yapi2.default.WEBCONFIG; var config = _yapi2.default.WEBCONFIG;
var options = {}; var options = {};
if (config.user) { if (config.user) {
options.user = config.db.user, options.pass = config.db.pass; options.user = config.db.user, options.pass = config.db.pass;
} }

View File

@ -18,6 +18,9 @@ var _config2 = _interopRequireDefault(_config);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var configPath = _path2.default.join(_path2.default.resolve(__dirname, '../../'), 'runtime', 'config.json'); var runtimePath = _config2.default.runtime_path;
_fsExtra2.default.ensureDirSync(runtimePath);
_fsExtra2.default.ensureDirSync(_path2.default.join(runtimePath, 'log'));
var configPath = _path2.default.join(runtimePath, 'config.json');
_fsExtra2.default.writeFileSync(configPath, (0, _stringify2.default)(_config2.default, null, "\t"), { encoding: 'utf8' }); _fsExtra2.default.writeFileSync(configPath, (0, _stringify2.default)(_config2.default, null, '\t'), { encoding: 'utf8' });

View File

@ -27,7 +27,7 @@ var mail = void 0;
var WEBROOT = _path2.default.resolve(__dirname, '..'); //路径 var WEBROOT = _path2.default.resolve(__dirname, '..'); //路径
var WEBROOT_SERVER = __dirname; var WEBROOT_SERVER = __dirname;
var WEBROOT_RUNTIME = _path2.default.join(WEBROOT, 'runtime'); var WEBROOT_RUNTIME = _config2.default.runtime_path;
var WEBROOT_LOG = _path2.default.join(WEBROOT_RUNTIME, 'log'); var WEBROOT_LOG = _path2.default.join(WEBROOT_RUNTIME, 'log');
var WEBCONFIG = _config2.default; var WEBCONFIG = _config2.default;

39
service.sh Normal file
View File

@ -0,0 +1,39 @@
#!/bin/bash
prog="/var/soft/node-v6.11.1-linux-x64/bin/pm2"
app="/home/q/www/yapi.beta.corp.qunar.com/webapp/server_dist/app.js"
start() {
echo "Starting Server..."
eval "$prog start $app --name=yapi"
}
stop() {
echo "Stopping Server..."
eval "$prog stop yapi"
}
restart() {
echo "Restart Server..."
eval "$prog restart yapi"
}
case "$1" in
start)
start && exit 0
;;
stop)
stop || exit 0
;;
restart)
restart || exit 0
;;
*)
echo $"Usage: $0 {start|stop|restart}"
exit 2
esac

Binary file not shown.

Before

Width:  |  Height:  |  Size: 98 KiB

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB