mirror of
https://github.com/HackPlan/RootPanel.git
synced 2026-04-24 03:35:59 +08:00
Admin component & Fix unit test
This commit is contained in:
@@ -2,6 +2,5 @@
|
||||
|
||||
Root = require './core'
|
||||
|
||||
Root.findConfig(__dirname).done (config) ->
|
||||
global.root = new Root config
|
||||
root.start()
|
||||
global.root = new Root Root.loadConfig()
|
||||
root.start()
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
$ ->
|
||||
$('.action-generate-code').click ->
|
||||
request '/admin/generate_coupon_code',
|
||||
expired: $('.input-expired').val()
|
||||
available_times: parseInt $('.input-available_times').val()
|
||||
type: $('.input-type').val()
|
||||
meta: JSON.parse $('.input-meta').val()
|
||||
count: parseInt $('.input-count').val()
|
||||
, (coupon_codes) ->
|
||||
for coupon_code in coupon_codes
|
||||
$('.output-coupon-code').append "#{coupon_code.code}<br />"
|
||||
@@ -19,19 +19,18 @@ require('node-jsx').install
|
||||
Class: Root object for control RootPanel, An instance is always available as the `root` global.
|
||||
###
|
||||
module.exports = class Root extends EventEmitter
|
||||
###
|
||||
Public: Find and load configure file.
|
||||
@loadConfig: ->
|
||||
{existsSync} = require 'fs'
|
||||
|
||||
* `root_path` {String} e.g. `/home/rpadmin/RootPanel`
|
||||
defaultPath = path.resolve 'sample/core.config.coffee'
|
||||
configPath = path.resolve process.env.ROOTPANEL_CONFIG ? 'config.coffee'
|
||||
|
||||
Return {Object}.
|
||||
###
|
||||
@findConfig: (root_path) ->
|
||||
configPath = path.resolve root_path, 'config.coffee'
|
||||
defaultPath = path.resolve root_path, 'sample/core.config.coffee'
|
||||
|
||||
fs.exists(configPath).then (exists) ->
|
||||
return require if exists then configPath else defaultPath
|
||||
if existsSync configPath
|
||||
return _.extend require(configPath),
|
||||
config_path: configPath
|
||||
else
|
||||
return _.extend require(defaultPath),
|
||||
config_path: defaultPath
|
||||
|
||||
log: console.log
|
||||
|
||||
|
||||
@@ -36,8 +36,12 @@ router.get '/dashboard', (req, res, next) ->
|
||||
return _.mapValues _.indexBy(result, 'name'), 'count'
|
||||
]).done ([accounts, components, tickets, accountsInPlan]) ->
|
||||
props =
|
||||
accounts: accounts
|
||||
components: components
|
||||
accounts: accounts.map (account) ->
|
||||
return _.extend account, _id: account._id.toString()
|
||||
components: components.map (component) ->
|
||||
return _.extend component,
|
||||
_id: component._id.toString()
|
||||
account_id: component.account_id.toString()
|
||||
tickets: tickets
|
||||
package: root.package
|
||||
plans: root.billing.all().map (plan) ->
|
||||
|
||||
7
core/test/before.coffee
Normal file
7
core/test/before.coffee
Normal file
@@ -0,0 +1,7 @@
|
||||
before ->
|
||||
{Account, Component} = root
|
||||
|
||||
Q.all [
|
||||
Account.remove()
|
||||
Component.remove()
|
||||
]
|
||||
@@ -1,14 +1,16 @@
|
||||
{createAgent, randomAccount} = helpers
|
||||
|
||||
describe 'router.account', ->
|
||||
agent = createAgent
|
||||
baseUrl: '/account'
|
||||
|
||||
account_id = null
|
||||
username = null
|
||||
password = null
|
||||
email = null
|
||||
token = null
|
||||
agent = null
|
||||
|
||||
before ->
|
||||
agent = createAgent
|
||||
baseUrl: '/account'
|
||||
|
||||
it 'GET login', ->
|
||||
agent.get '/login'
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
{createAdminAgent} = helpers
|
||||
|
||||
describe 'router.admin', ->
|
||||
agent = createAdminAgent()
|
||||
agent = null
|
||||
|
||||
before ->
|
||||
agent = createAdminAgent()
|
||||
|
||||
it 'GET /admin/dashboard', ->
|
||||
agent.get '/admin/dashboard'
|
||||
|
||||
@@ -2,12 +2,13 @@
|
||||
{Account, Component} = root
|
||||
|
||||
describe 'router.component', ->
|
||||
agent = createLoggedAgent
|
||||
baseUrl: '/components'
|
||||
|
||||
agent = null
|
||||
component_id = null
|
||||
|
||||
before ->
|
||||
agent = createLoggedAgent
|
||||
baseUrl: '/components'
|
||||
|
||||
{BillingPlan} = require '../../billing-manager'
|
||||
|
||||
root.billing.plans['sample'].components['built-in.sample'] = {}
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
{createLoggedAgent} = helpers
|
||||
|
||||
describe 'router.tickets', ->
|
||||
agent = createLoggedAgent
|
||||
baseUrl: '/tickets'
|
||||
|
||||
agent = null
|
||||
ticket_id = null
|
||||
|
||||
before ->
|
||||
agent = createLoggedAgent
|
||||
baseUrl: '/tickets'
|
||||
|
||||
it 'POST tickets', ->
|
||||
agent.post '/',
|
||||
json:
|
||||
|
||||
@@ -2,8 +2,6 @@ var React = require('react');
|
||||
var {Table, Button, DropdownButton, MenuItem, Modal, Input} = require('react-bootstrap');
|
||||
var _ = require('lodash');
|
||||
var agent = require('../scripts/agent.coffee');
|
||||
var Cookies = require('js-cookie');
|
||||
var $ = require('jquery');
|
||||
|
||||
module.exports = AdminAccounts = React.createClass({
|
||||
getInitialState: function() {
|
||||
@@ -14,12 +12,6 @@ module.exports = AdminAccounts = React.createClass({
|
||||
};
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
$.ajaxSetup({
|
||||
headers: {'X-Token': Cookies.get('token')}
|
||||
});
|
||||
},
|
||||
|
||||
showAccountDetails: function(account_id) {
|
||||
this.setState({
|
||||
accountDetailsModal: account_id
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
Backbone = require 'backbone'
|
||||
Cookies = require 'js-cookie'
|
||||
React = require 'react'
|
||||
$ = require 'jquery'
|
||||
|
||||
AdminDashboard = require './dashboard.jsx'
|
||||
|
||||
$.ajaxSetup
|
||||
headers:
|
||||
'X-Token': Cookies.get('token')
|
||||
|
||||
getInitializeProps = ->
|
||||
return JSON.parse $('#initialize-props').html()
|
||||
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
.tab-pane {
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
99
core/view/admin/components.jsx
Normal file
99
core/view/admin/components.jsx
Normal file
@@ -0,0 +1,99 @@
|
||||
var React = require('react');
|
||||
var {Table, Button, DropdownButton, MenuItem, Modal} = require('react-bootstrap');
|
||||
var _ = require('lodash');
|
||||
var agent = require('../scripts/agent.coffee');
|
||||
|
||||
module.exports = AdminComponents = React.createClass({
|
||||
getInitialState: function() {
|
||||
return {
|
||||
components: this.props.components,
|
||||
componentDetailsModal: null
|
||||
};
|
||||
},
|
||||
|
||||
showComponentDetails: function(component_id) {
|
||||
this.setState({
|
||||
componentDetailsModal: component_id
|
||||
});
|
||||
},
|
||||
|
||||
closeComponentDetails: function() {
|
||||
this.setState({
|
||||
componentDetailsModal: null
|
||||
});
|
||||
},
|
||||
|
||||
deleteComponent: function(component_id) {
|
||||
agent.delete(`/components/${component_id}`).then( () => {
|
||||
this.setState({
|
||||
components: this.state.components.filter(function(originalComponent) {
|
||||
return originalComponent._id != component_id;
|
||||
})
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return (
|
||||
<Table hover>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>用户名</th>
|
||||
<th>类型</th>
|
||||
<th>节点</th>
|
||||
<th>状态</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{this.state.components.map( component => {
|
||||
var account = _.findWhere(this.props.accounts, {
|
||||
_id: component.account_id
|
||||
});
|
||||
|
||||
if (!account) {
|
||||
account = {
|
||||
username: 'Not found'
|
||||
};
|
||||
}
|
||||
|
||||
var showComponentDetails = this.showComponentDetails.bind(this, component._id);
|
||||
var deleteComponent = this.deleteComponent.bind(this, component._id);
|
||||
|
||||
if (this.state.componentDetailsModal == component._id) {
|
||||
var componentDetailsModal = <Modal show={true} onHide={this.closeComponentDetails} bsSize='large'>
|
||||
<Modal.Header closeButton>
|
||||
<Modal.Title>{component._id}</Modal.Title>
|
||||
</Modal.Header>
|
||||
<Modal.Body>
|
||||
<pre>{JSON.stringify(component, null, ' ')}</pre>
|
||||
</Modal.Body>
|
||||
<Modal.Footer>
|
||||
<Button onClick={this.closeComponentDetails}>关闭</Button>
|
||||
</Modal.Footer>
|
||||
</Modal>;
|
||||
}
|
||||
|
||||
return (
|
||||
<tr key={component._id}>
|
||||
<td>{account.username}</td>
|
||||
<td>{component.type}</td>
|
||||
<td>{component.node}</td>
|
||||
<td>{component.status}</td>
|
||||
<td>
|
||||
<Button bsStyle='info' bsSize='small' onClick={showComponentDetails}>
|
||||
详情
|
||||
</Button>
|
||||
<DropdownButton title='操作' bsStyle='primary' bsSize='small'>
|
||||
<MenuItem onSelect={deleteComponent}>删除元件</MenuItem>
|
||||
</DropdownButton>
|
||||
{componentDetailsModal}
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</Table>
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -1,22 +0,0 @@
|
||||
table.table.table-hover
|
||||
thead
|
||||
th 用户
|
||||
th 元件模板
|
||||
th 节点
|
||||
th 状态
|
||||
tbody
|
||||
for component in components
|
||||
tr
|
||||
td= component.account_id
|
||||
td= component.type
|
||||
td= component.node
|
||||
td= component.status
|
||||
td
|
||||
button.btn.btn-info.btn-sm.action-details(type='button')= t('common.details')
|
||||
.btn-group
|
||||
button(type='button', data-toggle='dropdown').btn.btn-primary.btn-sm.dropdown-toggle
|
||||
| #{t('common.actions')}
|
||||
span.caret
|
||||
ul.dropdown-menu
|
||||
li
|
||||
a.action-destroy-component(href='#') 删除元件
|
||||
@@ -1,29 +0,0 @@
|
||||
form.form-horizontal
|
||||
.form-group
|
||||
label.col-sm-2.control-label= t('view.admin.expired')
|
||||
.col-sm-5
|
||||
input.input-expired.form-control(type='text', placeholder=t('view.admin.empty_expired_tips'))
|
||||
.form-group
|
||||
label.col-sm-2.control-label= t('view.admin.available_times')
|
||||
.col-sm-5
|
||||
input.input-available_times.form-control(type='text', value='1')
|
||||
.form-group
|
||||
label.col-sm-2.control-label= t('common.type')
|
||||
.col-sm-5
|
||||
select.input-type.form-control
|
||||
for type in root.couponTypes.all()
|
||||
option= type.name
|
||||
.form-group
|
||||
label.col-sm-2.control-label= t('view.admin.count')
|
||||
.col-sm-5
|
||||
input.input-count.form-control(type='text', value='1')
|
||||
.form-group
|
||||
label.col-sm-2.control-label= t('view.admin.meta')
|
||||
.col-sm-5
|
||||
input.input-meta.form-control(type='text', value='{"amount": 5, "category": "2014"}')
|
||||
.form-group
|
||||
label.col-sm-2.control-label
|
||||
.col-sm-5
|
||||
button.action-generate-code.btn.btn-lg.btn-primary(type='button')= t('common.generate')
|
||||
|
||||
pre.output-coupon-code
|
||||
@@ -1,7 +1,9 @@
|
||||
var React = require('react');
|
||||
var {TabbedArea, TabPane} = require('react-bootstrap');
|
||||
|
||||
var AdminExtensions = require('./extensions.jsx');
|
||||
var AdminAccounts = require('./accounts.jsx');
|
||||
var AdminComponents = require('./components.jsx');
|
||||
|
||||
module.exports = AdminDashboard = React.createClass({
|
||||
render: function() {
|
||||
@@ -17,8 +19,10 @@ module.exports = AdminDashboard = React.createClass({
|
||||
<AdminAccounts {...this.props} />
|
||||
</TabPane>
|
||||
<TabPane eventKey='tickets' tab='工单'></TabPane>
|
||||
<TabPane eventKey='coupons' tab='优惠和兑换'></TabPane>
|
||||
<TabPane eventKey='compontents' tab='元件'></TabPane>
|
||||
<TabPane eventKey='orders' tab='订单'></TabPane>
|
||||
<TabPane eventKey='compontents' tab='元件'>
|
||||
<AdminComponents {...this.props} />
|
||||
</TabPane>
|
||||
<TabPane eventKey='logs' tab='系统日志'></TabPane>
|
||||
</TabbedArea>
|
||||
);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
var {Row, Col, Panel} = require('react-bootstrap');
|
||||
var {Grid, Row, Col, Panel} = require('react-bootstrap');
|
||||
var React = require('react');
|
||||
var _ = require('lodash');
|
||||
|
||||
@@ -7,9 +7,11 @@ module.exports = AdminExtensions = React.createClass({
|
||||
var {plugins, plans} = this.props;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Grid fluid>
|
||||
<Row>
|
||||
<header>付费方案</header>
|
||||
<Col md={12}>
|
||||
<header>付费方案</header>
|
||||
</Col>
|
||||
{plans.map(function(plan) {
|
||||
return (
|
||||
<Col md={3} key={plan.name}>
|
||||
@@ -24,7 +26,9 @@ module.exports = AdminExtensions = React.createClass({
|
||||
})}
|
||||
</Row>
|
||||
<Row>
|
||||
<header>插件</header>
|
||||
<Col md={12}>
|
||||
<header>插件</header>
|
||||
</Col>
|
||||
{plugins.map(function(plugin) {
|
||||
return (
|
||||
<Col md={6} key={plugin.name}>
|
||||
@@ -42,7 +46,7 @@ module.exports = AdminExtensions = React.createClass({
|
||||
);
|
||||
})}
|
||||
</Row>
|
||||
</div>
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -2,6 +2,9 @@ extends ../layout
|
||||
|
||||
prepend header
|
||||
title= helpers.title('admin.admin_panel')
|
||||
|
||||
append header
|
||||
link(rel='stylesheet', href='/public/admin.css')
|
||||
|
||||
block main
|
||||
!= mainBlock
|
||||
|
||||
@@ -4,6 +4,8 @@ module.exports = class Builtin extends root.Plugin
|
||||
activate: ->
|
||||
@injector.couponType 'cash', cashCoupon
|
||||
|
||||
@injector.paymentProvider 'manual', manualPayment
|
||||
|
||||
@injector.router('/').get '/', (req, res) ->
|
||||
res.redirect '/panel/'
|
||||
|
||||
@@ -45,3 +47,6 @@ sampleComponent =
|
||||
initialize: (component) ->
|
||||
|
||||
destroy: (component) ->
|
||||
|
||||
manualPayment =
|
||||
populateFinancial: (req, financial) ->
|
||||
|
||||
@@ -14,8 +14,6 @@ module.exports =
|
||||
plugins:
|
||||
'built-in':
|
||||
enable: true
|
||||
linux:
|
||||
enable: true
|
||||
|
||||
server:
|
||||
ssh:
|
||||
|
||||
@@ -5,6 +5,8 @@ Q = require 'q'
|
||||
|
||||
expect = chai.expect
|
||||
|
||||
{config} = root
|
||||
|
||||
methods = ['get', 'post', 'delete', 'put', 'patch', 'head', 'options']
|
||||
|
||||
module.exports = (agent_options) ->
|
||||
@@ -73,6 +75,6 @@ printHttpResponse = ({httpVersion, statusCode, statusMessage, headers, body}) ->
|
||||
else if headers['content-type']?.match /application\/json/
|
||||
body = JSON.stringify body, null, ' '
|
||||
|
||||
message += "\n#{body}"
|
||||
message += "\n#{body}\n"
|
||||
|
||||
return message
|
||||
|
||||
@@ -60,6 +60,8 @@ createAdminAgent = (options) ->
|
||||
root.Account.search(agent.account.username).then (account) ->
|
||||
account.joinGroup 'root'
|
||||
|
||||
return agent
|
||||
|
||||
module.exports = {
|
||||
ifEnabled
|
||||
unlessTravis
|
||||
|
||||
@@ -1,17 +1,10 @@
|
||||
process.env.NODE_ENV = 'test'
|
||||
process.env.ROOTPANEL_CONFIG ?= 'sample/core.config.coffee'
|
||||
|
||||
chai = require 'chai'
|
||||
_ = require 'lodash'
|
||||
Q = require 'q'
|
||||
|
||||
Root = require '../core'
|
||||
|
||||
global.config = require '../sample/core.config.coffee'
|
||||
global.helpers = require './helpers'
|
||||
global.root = new Root config
|
||||
|
||||
root.start()
|
||||
|
||||
_.extend global,
|
||||
expect: chai.expect
|
||||
Q: Q
|
||||
@@ -21,3 +14,13 @@ chai.should()
|
||||
chai.config.includeStack = true
|
||||
|
||||
Q.longStackSupport = true
|
||||
|
||||
Root = require '../core'
|
||||
|
||||
config = Root.loadConfig()
|
||||
config.mongodb.name = 'RootPanel-test'
|
||||
|
||||
global.root = new Root config
|
||||
global.helpers = require './helpers'
|
||||
|
||||
root.start()
|
||||
|
||||
Reference in New Issue
Block a user