forked from p15670423/monkey
add new ui files
This commit is contained in:
parent
61fa2540d8
commit
8f13092e56
|
@ -1,56 +0,0 @@
|
|||
.arrow {
|
||||
float: right;
|
||||
line-height: 1.42857;
|
||||
}
|
||||
|
||||
.glyphicon.arrow:before {
|
||||
content: "\e079";
|
||||
}
|
||||
|
||||
.active > a > .glyphicon.arrow:before {
|
||||
content: "\e114";
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Require Font-Awesome
|
||||
* http://fortawesome.github.io/Font-Awesome/
|
||||
*/
|
||||
|
||||
|
||||
.fa.arrow:before {
|
||||
content: "\f104";
|
||||
}
|
||||
|
||||
.active > a > .fa.arrow:before {
|
||||
content: "\f107";
|
||||
}
|
||||
|
||||
.plus-times {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.fa.plus-times:before {
|
||||
content: "\f067";
|
||||
}
|
||||
|
||||
.active > a > .fa.plus-times {
|
||||
filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=1);
|
||||
-webkit-transform: rotate(45deg);
|
||||
-moz-transform: rotate(45deg);
|
||||
-ms-transform: rotate(45deg);
|
||||
-o-transform: rotate(45deg);
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
|
||||
.plus-minus {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.fa.plus-minus:before {
|
||||
content: "\f067";
|
||||
}
|
||||
|
||||
.active > a > .fa.plus-minus:before {
|
||||
content: "\f068";
|
||||
}
|
|
@ -1,354 +0,0 @@
|
|||
/*!
|
||||
* Start Bootstrap - SB Admin 2 Bootstrap Admin Theme (http://startbootstrap.com)
|
||||
* Code licensed under the Apache License v2.0.
|
||||
* For details, see http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*/
|
||||
|
||||
body {
|
||||
background-color: #f8f8f8;
|
||||
}
|
||||
|
||||
#wrapper {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#page-wrapper {
|
||||
padding: 0 15px;
|
||||
min-height: 568px;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
@media(min-width:768px) {
|
||||
#page-wrapper {
|
||||
position: inherit;
|
||||
margin: 0 0 0 250px;
|
||||
padding: 0 30px;
|
||||
border-left: 1px solid #e7e7e7;
|
||||
}
|
||||
}
|
||||
|
||||
.navbar-top-links {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.navbar-top-links li {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.navbar-top-links li:last-child {
|
||||
margin-right: 15px;
|
||||
}
|
||||
|
||||
.navbar-top-links li a {
|
||||
padding: 15px;
|
||||
min-height: 50px;
|
||||
}
|
||||
|
||||
.navbar-top-links .dropdown-menu li {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.navbar-top-links .dropdown-menu li:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.navbar-top-links .dropdown-menu li a {
|
||||
padding: 3px 20px;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.navbar-top-links .dropdown-menu li a div {
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
.navbar-top-links .dropdown-messages,
|
||||
.navbar-top-links .dropdown-tasks,
|
||||
.navbar-top-links .dropdown-alerts {
|
||||
width: 310px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.navbar-top-links .dropdown-messages {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.navbar-top-links .dropdown-tasks {
|
||||
margin-left: -59px;
|
||||
}
|
||||
|
||||
.navbar-top-links .dropdown-alerts {
|
||||
margin-left: -123px;
|
||||
}
|
||||
|
||||
.navbar-top-links .dropdown-user {
|
||||
right: 0;
|
||||
left: auto;
|
||||
}
|
||||
|
||||
.sidebar .sidebar-nav.navbar-collapse {
|
||||
padding-right: 0;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.sidebar .sidebar-search {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.sidebar ul li {
|
||||
border-bottom: 1px solid #e7e7e7;
|
||||
}
|
||||
|
||||
.sidebar ul li a.active {
|
||||
background-color: #eee;
|
||||
}
|
||||
|
||||
.sidebar .arrow {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.sidebar .fa.arrow:before {
|
||||
content: "\f104";
|
||||
}
|
||||
|
||||
.sidebar .active>a>.fa.arrow:before {
|
||||
content: "\f107";
|
||||
}
|
||||
|
||||
.sidebar .nav-second-level li,
|
||||
.sidebar .nav-third-level li {
|
||||
border-bottom: 0!important;
|
||||
}
|
||||
|
||||
.sidebar .nav-second-level li a {
|
||||
padding-left: 37px;
|
||||
}
|
||||
|
||||
.sidebar .nav-third-level li a {
|
||||
padding-left: 52px;
|
||||
}
|
||||
|
||||
@media(min-width:768px) {
|
||||
.sidebar {
|
||||
z-index: 1;
|
||||
position: absolute;
|
||||
width: 250px;
|
||||
margin-top: 51px;
|
||||
}
|
||||
|
||||
.navbar-top-links .dropdown-messages,
|
||||
.navbar-top-links .dropdown-tasks,
|
||||
.navbar-top-links .dropdown-alerts {
|
||||
margin-left: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-outline {
|
||||
color: inherit;
|
||||
background-color: transparent;
|
||||
transition: all .5s;
|
||||
}
|
||||
|
||||
.btn-primary.btn-outline {
|
||||
color: #428bca;
|
||||
}
|
||||
|
||||
.btn-success.btn-outline {
|
||||
color: #5cb85c;
|
||||
}
|
||||
|
||||
.btn-info.btn-outline {
|
||||
color: #5bc0de;
|
||||
}
|
||||
|
||||
.btn-warning.btn-outline {
|
||||
color: #f0ad4e;
|
||||
}
|
||||
|
||||
.btn-danger.btn-outline {
|
||||
color: #d9534f;
|
||||
}
|
||||
|
||||
.btn-primary.btn-outline:hover,
|
||||
.btn-success.btn-outline:hover,
|
||||
.btn-info.btn-outline:hover,
|
||||
.btn-warning.btn-outline:hover,
|
||||
.btn-danger.btn-outline:hover {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.chat {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.chat li {
|
||||
margin-bottom: 10px;
|
||||
padding-bottom: 5px;
|
||||
border-bottom: 1px dotted #999;
|
||||
}
|
||||
|
||||
.chat li.left .chat-body {
|
||||
margin-left: 60px;
|
||||
}
|
||||
|
||||
.chat li.right .chat-body {
|
||||
margin-right: 60px;
|
||||
}
|
||||
|
||||
.chat li .chat-body p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.panel .slidedown .glyphicon,
|
||||
.chat .glyphicon {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.chat-panel .panel-body {
|
||||
height: 350px;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
.login-panel {
|
||||
margin-top: 25%;
|
||||
}
|
||||
|
||||
.flot-chart {
|
||||
display: block;
|
||||
height: 400px;
|
||||
}
|
||||
|
||||
.flot-chart-content {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.dataTables_wrapper {
|
||||
position: relative;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
table.dataTable thead .sorting,
|
||||
table.dataTable thead .sorting_asc,
|
||||
table.dataTable thead .sorting_desc,
|
||||
table.dataTable thead .sorting_asc_disabled,
|
||||
table.dataTable thead .sorting_desc_disabled {
|
||||
background: 0 0;
|
||||
}
|
||||
|
||||
table.dataTable thead .sorting_asc:after {
|
||||
content: "\f0de";
|
||||
float: right;
|
||||
font-family: fontawesome;
|
||||
}
|
||||
|
||||
table.dataTable thead .sorting_desc:after {
|
||||
content: "\f0dd";
|
||||
float: right;
|
||||
font-family: fontawesome;
|
||||
}
|
||||
|
||||
table.dataTable thead .sorting:after {
|
||||
content: "\f0dc";
|
||||
float: right;
|
||||
font-family: fontawesome;
|
||||
color: rgba(50,50,50,.5);
|
||||
}
|
||||
|
||||
.btn-circle {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
padding: 6px 0;
|
||||
border-radius: 15px;
|
||||
text-align: center;
|
||||
font-size: 12px;
|
||||
line-height: 1.428571429;
|
||||
}
|
||||
|
||||
.btn-circle.btn-lg {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
padding: 10px 16px;
|
||||
border-radius: 25px;
|
||||
font-size: 18px;
|
||||
line-height: 1.33;
|
||||
}
|
||||
|
||||
.btn-circle.btn-xl {
|
||||
width: 70px;
|
||||
height: 70px;
|
||||
padding: 10px 16px;
|
||||
border-radius: 35px;
|
||||
font-size: 24px;
|
||||
line-height: 1.33;
|
||||
}
|
||||
|
||||
.show-grid [class^=col-] {
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
border: 1px solid #ddd;
|
||||
background-color: #eee!important;
|
||||
}
|
||||
|
||||
.show-grid {
|
||||
margin: 15px 0;
|
||||
}
|
||||
|
||||
.huge {
|
||||
font-size: 40px;
|
||||
}
|
||||
|
||||
.panel-green {
|
||||
border-color: #5cb85c;
|
||||
}
|
||||
|
||||
.panel-green .panel-heading {
|
||||
border-color: #5cb85c;
|
||||
color: #fff;
|
||||
background-color: #5cb85c;
|
||||
}
|
||||
|
||||
.panel-green a {
|
||||
color: #5cb85c;
|
||||
}
|
||||
|
||||
.panel-green a:hover {
|
||||
color: #3d8b3d;
|
||||
}
|
||||
|
||||
.panel-red {
|
||||
border-color: #d9534f;
|
||||
}
|
||||
|
||||
.panel-red .panel-heading {
|
||||
border-color: #d9534f;
|
||||
color: #fff;
|
||||
background-color: #d9534f;
|
||||
}
|
||||
|
||||
.panel-red a {
|
||||
color: #d9534f;
|
||||
}
|
||||
|
||||
.panel-red a:hover {
|
||||
color: #b52b27;
|
||||
}
|
||||
|
||||
.panel-yellow {
|
||||
border-color: #f0ad4e;
|
||||
}
|
||||
|
||||
.panel-yellow .panel-heading {
|
||||
border-color: #f0ad4e;
|
||||
color: #fff;
|
||||
background-color: #f0ad4e;
|
||||
}
|
||||
|
||||
.panel-yellow a {
|
||||
color: #f0ad4e;
|
||||
}
|
||||
|
||||
.panel-yellow a:hover {
|
||||
color: #df8a13;
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"presets": [
|
||||
"es2015",
|
||||
"stage-0",
|
||||
"react"
|
||||
]
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
|
@ -0,0 +1,35 @@
|
|||
{
|
||||
"parser": "babel-eslint",
|
||||
"plugins": [
|
||||
"react"
|
||||
],
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 6,
|
||||
"sourceType": "module",
|
||||
"ecmaFeatures": {
|
||||
"jsx": true
|
||||
}
|
||||
},
|
||||
"env": {
|
||||
"browser": true,
|
||||
"amd": true,
|
||||
"es6": true,
|
||||
"node": true,
|
||||
"mocha": true
|
||||
},
|
||||
"rules": {
|
||||
"comma-dangle": 1,
|
||||
"quotes": [ 1, "single" ],
|
||||
"no-undef": 1,
|
||||
"global-strict": 0,
|
||||
"no-extra-semi": 1,
|
||||
"no-underscore-dangle": 0,
|
||||
"no-console": 1,
|
||||
"no-unused-vars": 1,
|
||||
"no-trailing-spaces": [1, { "skipBlankLines": true }],
|
||||
"no-unreachable": 1,
|
||||
"no-alert": 0,
|
||||
"react/jsx-uses-react": 1,
|
||||
"react/jsx-uses-vars": 1
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
# Logs
|
||||
logs
|
||||
*.log
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
|
||||
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (http://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directory
|
||||
# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
|
||||
node_modules
|
||||
|
||||
# Bower
|
||||
bower_components/
|
||||
|
||||
# IDE/Editor data
|
||||
.idea
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"generator-react-webpack": {}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
'use strict';
|
||||
let path = require('path');
|
||||
let defaultSettings = require('./defaults');
|
||||
|
||||
// Additional npm or bower modules to include in builds
|
||||
// Add all foreign plugins you may need into this array
|
||||
// @example:
|
||||
// let npmBase = path.join(__dirname, '../node_modules');
|
||||
// let additionalPaths = [ path.join(npmBase, 'react-bootstrap') ];
|
||||
let additionalPaths = [];
|
||||
|
||||
module.exports = {
|
||||
additionalPaths: additionalPaths,
|
||||
port: defaultSettings.port,
|
||||
debug: true,
|
||||
devtool: 'eval',
|
||||
output: {
|
||||
path: path.join(__dirname, '/../dist/assets'),
|
||||
filename: 'app.js',
|
||||
publicPath: defaultSettings.publicPath
|
||||
},
|
||||
devServer: {
|
||||
contentBase: './src/',
|
||||
historyApiFallback: true,
|
||||
hot: true,
|
||||
port: defaultSettings.port,
|
||||
publicPath: defaultSettings.publicPath,
|
||||
noInfo: false
|
||||
},
|
||||
resolve: {
|
||||
extensions: ['', '.js', '.jsx'],
|
||||
alias: {
|
||||
actions: `${defaultSettings.srcPath}/actions/`,
|
||||
components: `${defaultSettings.srcPath}/components/`,
|
||||
sources: `${defaultSettings.srcPath}/sources/`,
|
||||
stores: `${defaultSettings.srcPath}/stores/`,
|
||||
styles: `${defaultSettings.srcPath}/styles/`,
|
||||
config: `${defaultSettings.srcPath}/config/` + process.env.REACT_WEBPACK_ENV,
|
||||
'react/lib/ReactMount': 'react-dom/lib/ReactMount'
|
||||
}
|
||||
},
|
||||
module: {}
|
||||
};
|
|
@ -0,0 +1,68 @@
|
|||
/**
|
||||
* Function that returns default values.
|
||||
* Used because Object.assign does a shallow instead of a deep copy.
|
||||
* Using [].push will add to the base array, so a require will alter
|
||||
* the base array output.
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
const path = require('path');
|
||||
const srcPath = path.join(__dirname, '/../src');
|
||||
const dfltPort = 8000;
|
||||
|
||||
/**
|
||||
* Get the default modules object for webpack
|
||||
* @return {Object}
|
||||
*/
|
||||
function getDefaultModules() {
|
||||
return {
|
||||
preLoaders: [
|
||||
{
|
||||
test: /\.(js|jsx)$/,
|
||||
include: srcPath,
|
||||
loader: 'eslint-loader'
|
||||
}
|
||||
],
|
||||
loaders: [
|
||||
{
|
||||
test: /\.css$/,
|
||||
loader: 'style-loader!css-loader'
|
||||
},
|
||||
{
|
||||
test: /\.sass/,
|
||||
loader: 'style-loader!css-loader!sass-loader?outputStyle=expanded&indentedSyntax'
|
||||
},
|
||||
{
|
||||
test: /\.scss/,
|
||||
loader: 'style-loader!css-loader!sass-loader?outputStyle=expanded'
|
||||
},
|
||||
{
|
||||
test: /\.less/,
|
||||
loader: 'style-loader!css-loader!less-loader'
|
||||
},
|
||||
{
|
||||
test: /\.styl/,
|
||||
loader: 'style-loader!css-loader!stylus-loader'
|
||||
},
|
||||
{
|
||||
test: /\.(png|jpg|gif)$/,
|
||||
loader: 'url-loader?limit=8192'
|
||||
},
|
||||
{
|
||||
test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/,
|
||||
loader: 'url-loader?limit=10000&mimetype=application/font-woff'
|
||||
},
|
||||
{
|
||||
test: /\.(ttf|eot|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
|
||||
loader: 'file-loader'
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
srcPath: srcPath,
|
||||
publicPath: '/assets/',
|
||||
port: dfltPort,
|
||||
getDefaultModules: getDefaultModules
|
||||
};
|
|
@ -0,0 +1,47 @@
|
|||
'use strict';
|
||||
|
||||
let path = require('path');
|
||||
let webpack = require('webpack');
|
||||
let baseConfig = require('./base');
|
||||
let defaultSettings = require('./defaults');
|
||||
|
||||
// Add needed plugins here
|
||||
let BowerWebpackPlugin = require('bower-webpack-plugin');
|
||||
|
||||
let config = Object.assign({}, baseConfig, {
|
||||
entry: [
|
||||
'webpack-dev-server/client?http://127.0.0.1:' + defaultSettings.port,
|
||||
'webpack/hot/only-dev-server',
|
||||
'./src/index'
|
||||
],
|
||||
cache: true,
|
||||
devtool: 'eval-source-map',
|
||||
plugins: [
|
||||
new webpack.HotModuleReplacementPlugin(),
|
||||
new webpack.NoErrorsPlugin(),
|
||||
new BowerWebpackPlugin({
|
||||
searchResolveModulesDirectories: false
|
||||
})
|
||||
],
|
||||
module: defaultSettings.getDefaultModules()
|
||||
});
|
||||
|
||||
// Add needed loaders to the defaults here
|
||||
config.module.loaders.push({
|
||||
test: /\.(js|jsx)$/,
|
||||
loader: 'react-hot!babel-loader',
|
||||
include: [].concat(
|
||||
config.additionalPaths,
|
||||
[ path.join(__dirname, '/../src') ]
|
||||
)
|
||||
});
|
||||
|
||||
// proxy to backend server
|
||||
config.devServer.proxy = {
|
||||
'/api': {
|
||||
target: 'https://localhost:5000',
|
||||
secure: false
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = config;
|
|
@ -0,0 +1,42 @@
|
|||
'use strict';
|
||||
|
||||
let path = require('path');
|
||||
let webpack = require('webpack');
|
||||
|
||||
let baseConfig = require('./base');
|
||||
let defaultSettings = require('./defaults');
|
||||
|
||||
// Add needed plugins here
|
||||
let BowerWebpackPlugin = require('bower-webpack-plugin');
|
||||
|
||||
let config = Object.assign({}, baseConfig, {
|
||||
entry: path.join(__dirname, '../src/index'),
|
||||
cache: false,
|
||||
devtool: 'sourcemap',
|
||||
plugins: [
|
||||
new webpack.optimize.DedupePlugin(),
|
||||
new webpack.DefinePlugin({
|
||||
'process.env.NODE_ENV': '"production"'
|
||||
}),
|
||||
new BowerWebpackPlugin({
|
||||
searchResolveModulesDirectories: false
|
||||
}),
|
||||
new webpack.optimize.UglifyJsPlugin(),
|
||||
new webpack.optimize.OccurenceOrderPlugin(),
|
||||
new webpack.optimize.AggressiveMergingPlugin(),
|
||||
new webpack.NoErrorsPlugin()
|
||||
],
|
||||
module: defaultSettings.getDefaultModules()
|
||||
});
|
||||
|
||||
// Add needed loaders to the defaults here
|
||||
config.module.loaders.push({
|
||||
test: /\.(js|jsx)$/,
|
||||
loader: 'babel',
|
||||
include: [].concat(
|
||||
config.additionalPaths,
|
||||
[ path.join(__dirname, '/../src') ]
|
||||
)
|
||||
});
|
||||
|
||||
module.exports = config;
|
|
@ -0,0 +1,58 @@
|
|||
'use strict';
|
||||
|
||||
let path = require('path');
|
||||
let srcPath = path.join(__dirname, '/../src/');
|
||||
|
||||
let baseConfig = require('./base');
|
||||
|
||||
// Add needed plugins here
|
||||
let BowerWebpackPlugin = require('bower-webpack-plugin');
|
||||
|
||||
module.exports = {
|
||||
devtool: 'eval',
|
||||
module: {
|
||||
preLoaders: [
|
||||
{
|
||||
test: /\.(js|jsx)$/,
|
||||
loader: 'isparta-instrumenter-loader',
|
||||
include: [
|
||||
path.join(__dirname, '/../src')
|
||||
]
|
||||
}
|
||||
],
|
||||
loaders: [
|
||||
{
|
||||
test: /\.(png|jpg|gif|woff|woff2|css|sass|scss|less|styl)$/,
|
||||
loader: 'null-loader'
|
||||
},
|
||||
{
|
||||
test: /\.(js|jsx)$/,
|
||||
loader: 'babel-loader',
|
||||
include: [].concat(
|
||||
baseConfig.additionalPaths,
|
||||
[
|
||||
path.join(__dirname, '/../src'),
|
||||
path.join(__dirname, '/../test')
|
||||
]
|
||||
)
|
||||
}
|
||||
]
|
||||
},
|
||||
resolve: {
|
||||
extensions: [ '', '.js', '.jsx' ],
|
||||
alias: {
|
||||
actions: srcPath + 'actions/',
|
||||
helpers: path.join(__dirname, '/../test/helpers'),
|
||||
components: srcPath + 'components/',
|
||||
sources: srcPath + 'sources/',
|
||||
stores: srcPath + 'stores/',
|
||||
styles: srcPath + 'styles/',
|
||||
config: srcPath + 'config/' + process.env.REACT_WEBPACK_ENV
|
||||
}
|
||||
},
|
||||
plugins: [
|
||||
new BowerWebpackPlugin({
|
||||
searchResolveModulesDirectories: false
|
||||
})
|
||||
]
|
||||
};
|
|
@ -0,0 +1,36 @@
|
|||
var webpackCfg = require('./webpack.config');
|
||||
|
||||
// Set node environment to testing
|
||||
process.env.NODE_ENV = 'test';
|
||||
|
||||
module.exports = function(config) {
|
||||
config.set({
|
||||
basePath: '',
|
||||
browsers: [ 'PhantomJS' ],
|
||||
files: [
|
||||
'test/loadtests.js'
|
||||
],
|
||||
port: 8000,
|
||||
captureTimeout: 60000,
|
||||
frameworks: [ 'mocha', 'chai' ],
|
||||
client: {
|
||||
mocha: {}
|
||||
},
|
||||
singleRun: true,
|
||||
reporters: [ 'mocha', 'coverage' ],
|
||||
preprocessors: {
|
||||
'test/loadtests.js': [ 'webpack', 'sourcemap' ]
|
||||
},
|
||||
webpack: webpackCfg,
|
||||
webpackServer: {
|
||||
noInfo: true
|
||||
},
|
||||
coverageReporter: {
|
||||
dir: 'coverage/',
|
||||
reporters: [
|
||||
{ type: 'html' },
|
||||
{ type: 'text' }
|
||||
]
|
||||
}
|
||||
});
|
||||
};
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,82 @@
|
|||
{
|
||||
"private": true,
|
||||
"version": "0.0.1",
|
||||
"description": "YOUR DESCRIPTION - Generated by generator-react-webpack",
|
||||
"main": "",
|
||||
"scripts": {
|
||||
"clean": "rimraf dist/*",
|
||||
"copy": "copyfiles -f ./src/index.html ./src/favicon.ico ./dist",
|
||||
"dist": "npm run copy & webpack --env=dist",
|
||||
"lint": "eslint ./src",
|
||||
"posttest": "npm run lint",
|
||||
"release:major": "npm version major && npm publish && git push --follow-tags",
|
||||
"release:minor": "npm version minor && npm publish && git push --follow-tags",
|
||||
"release:patch": "npm version patch && npm publish && git push --follow-tags",
|
||||
"serve": "node server.js --env=dev",
|
||||
"serve:dist": "node server.js --env=dist",
|
||||
"start": "node server.js --env=dev",
|
||||
"test": "karma start",
|
||||
"test:watch": "karma start --autoWatch=true --singleRun=false"
|
||||
},
|
||||
"repository": "",
|
||||
"keywords": [],
|
||||
"author": "Your name here",
|
||||
"devDependencies": {
|
||||
"babel-core": "^6.25.0",
|
||||
"babel-eslint": "^6.0.0",
|
||||
"babel-loader": "^6.4.1",
|
||||
"babel-polyfill": "^6.3.14",
|
||||
"babel-preset-es2015": "^6.24.1",
|
||||
"babel-preset-react": "^6.24.1",
|
||||
"babel-preset-stage-0": "^6.5.0",
|
||||
"bower-webpack-plugin": "^0.1.9",
|
||||
"chai": "^3.2.0",
|
||||
"copyfiles": "^1.0.0",
|
||||
"css-loader": "^0.23.1",
|
||||
"eslint": "^3.0.0",
|
||||
"eslint-loader": "^1.0.0",
|
||||
"eslint-plugin-react": "^6.0.0",
|
||||
"file-loader": "^0.9.0",
|
||||
"glob": "^7.0.0",
|
||||
"isparta-instrumenter-loader": "^1.0.0",
|
||||
"karma": "^1.0.0",
|
||||
"karma-chai": "^0.1.0",
|
||||
"karma-coverage": "^1.0.0",
|
||||
"karma-mocha": "^1.0.0",
|
||||
"karma-mocha-reporter": "^2.0.0",
|
||||
"karma-phantomjs-launcher": "^1.0.0",
|
||||
"karma-sourcemap-loader": "^0.3.5",
|
||||
"karma-webpack": "^1.7.0",
|
||||
"minimist": "^1.2.0",
|
||||
"mocha": "^3.0.0",
|
||||
"null-loader": "^0.1.1",
|
||||
"open": "0.0.5",
|
||||
"phantomjs-prebuilt": "^2.0.0",
|
||||
"react-addons-test-utils": "^15.0.0",
|
||||
"react-hot-loader": "^1.2.9",
|
||||
"rimraf": "^2.4.3",
|
||||
"style-loader": "^0.13.2",
|
||||
"url-loader": "^0.5.9",
|
||||
"webpack": "^1.15.0",
|
||||
"webpack-dev-server": "^1.12.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"bootstrap": "^3.3.7",
|
||||
"core-js": "^2.0.0",
|
||||
"fetch": "^1.1.0",
|
||||
"normalize.css": "^4.0.0",
|
||||
"prop-types": "^15.5.10",
|
||||
"react": "^15.6.1",
|
||||
"react-bootstrap": "^0.31.2",
|
||||
"react-copy-to-clipboard": "^5.0.0",
|
||||
"react-data-grid": "^2.0.58",
|
||||
"react-data-grid-addons": "^2.0.58",
|
||||
"react-dom": "^15.6.1",
|
||||
"react-fa": "^4.2.0",
|
||||
"react-graph-vis": "^0.1.3",
|
||||
"react-json-tree": "^0.10.9",
|
||||
"react-json-view": "^1.12.0",
|
||||
"react-jsonschema-form": "^0.49.0",
|
||||
"react-router-dom": "^4.1.2"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
/*eslint no-console:0 */
|
||||
'use strict';
|
||||
require('core-js/fn/object/assign');
|
||||
const webpack = require('webpack');
|
||||
const WebpackDevServer = require('webpack-dev-server');
|
||||
const config = require('./webpack.config');
|
||||
const open = require('open');
|
||||
|
||||
/**
|
||||
* Flag indicating whether webpack compiled for the first time.
|
||||
* @type {boolean}
|
||||
*/
|
||||
let isInitialCompilation = true;
|
||||
|
||||
const compiler = webpack(config);
|
||||
|
||||
new WebpackDevServer(compiler, config.devServer)
|
||||
.listen(config.port, 'localhost', (err) => {
|
||||
if (err) {
|
||||
console.log(err);
|
||||
}
|
||||
console.log('Listening at localhost:' + config.port);
|
||||
});
|
||||
|
||||
compiler.plugin('done', () => {
|
||||
if (isInitialCompilation) {
|
||||
// Ensures that we log after webpack printed its stats (is there a better way?)
|
||||
setTimeout(() => {
|
||||
console.log('\n✓ The bundle is now ready for serving!\n');
|
||||
console.log(' Open in iframe mode:\t\x1b[33m%s\x1b[0m', 'http://localhost:' + config.port + '/webpack-dev-server/');
|
||||
console.log(' Open in inline mode:\t\x1b[33m%s\x1b[0m', 'http://localhost:' + config.port + '/\n');
|
||||
console.log(' \x1b[33mHMR is active\x1b[0m. The bundle will automatically rebuild and live-update on changes.')
|
||||
}, 350);
|
||||
}
|
||||
isInitialCompilation = false;
|
||||
});
|
|
@ -0,0 +1,90 @@
|
|||
import React from 'react';
|
||||
import {NavLink, Route, BrowserRouter as Router} from 'react-router-dom';
|
||||
import {Col, Grid, Row} from 'react-bootstrap';
|
||||
import {Icon} from 'react-fa';
|
||||
|
||||
import RunServerPage from 'components/pages/RunServerPage';
|
||||
import ConfigurePage from 'components/pages/ConfigurePage';
|
||||
import RunMonkeyPage from 'components/pages/RunMonkeyPage';
|
||||
import MapPage from 'components/pages/MapPage';
|
||||
import FullLogsPage from 'components/pages/FullLogsPage';
|
||||
|
||||
require('normalize.css/normalize.css');
|
||||
require('styles/App.css');
|
||||
|
||||
let logoImage = require('../images/monkey-logo.png');
|
||||
|
||||
class AppComponent extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<Router>
|
||||
<Grid fluid={true}>
|
||||
<Row>
|
||||
<Col sm={3} md={2} className="sidebar">
|
||||
<div className="header">
|
||||
<img src={logoImage} alt="Infection Monkey"/>
|
||||
by GuardiCore
|
||||
</div>
|
||||
|
||||
<ul className="navigation">
|
||||
<li>
|
||||
<NavLink to="/" exact={true}>
|
||||
<span className="number">1.</span>
|
||||
Run Server
|
||||
<Icon name="check" className="pull-right checkmark text-success"/>
|
||||
</NavLink>
|
||||
</li>
|
||||
<li>
|
||||
<NavLink to="/configure">
|
||||
<span className="number">2.</span>
|
||||
Configure
|
||||
</NavLink>
|
||||
</li>
|
||||
<li>
|
||||
<NavLink to="/run-monkey">
|
||||
<span className="number">3.</span>
|
||||
Run Monkey
|
||||
</NavLink>
|
||||
</li>
|
||||
<li>
|
||||
<a className="disabled">
|
||||
<span className="number">4.</span>
|
||||
Infection
|
||||
</a>
|
||||
<ul>
|
||||
<li><NavLink to="/infection/map">Map</NavLink></li>
|
||||
<li><NavLink to="/infection/logs">Full Logs</NavLink></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<NavLink to="/report">
|
||||
<span className="number">5.</span>
|
||||
Pen. Test Report
|
||||
</NavLink>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<hr/>
|
||||
<ul>
|
||||
<li><a>Clear DB</a></li>
|
||||
<li><a>Kill All Monkeys</a></li>
|
||||
</ul>
|
||||
</Col>
|
||||
<Col sm={9} md={10} smOffset={3} mdOffset={2} className="main">
|
||||
<Route exact path="/" component={RunServerPage}/>
|
||||
<Route path="/configure" component={ConfigurePage}/>
|
||||
<Route path="/run-monkey" component={RunMonkeyPage}/>
|
||||
<Route path="/infection/map" component={MapPage}/>
|
||||
<Route path="/infection/logs" component={FullLogsPage}/>
|
||||
{/*<Route path="/report" component={ReportPage}/>*/}
|
||||
</Col>
|
||||
</Row>
|
||||
</Grid>
|
||||
</Router>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
AppComponent.defaultProps = {};
|
||||
|
||||
export default AppComponent;
|
|
@ -0,0 +1,62 @@
|
|||
import React from 'react';
|
||||
import Form from 'react-jsonschema-form';
|
||||
import {Col} from 'react-bootstrap';
|
||||
|
||||
class ConfigurePageComponent extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
// set schema from server
|
||||
this.state = {
|
||||
schema: {},
|
||||
configuration: {},
|
||||
saved: false
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
fetch('/api/configuration')
|
||||
.then(res => res.json())
|
||||
.then(res => this.setState({
|
||||
schema: res.schema,
|
||||
configuration: res.configuration
|
||||
}));
|
||||
}
|
||||
|
||||
onSubmit = ({formData}) => {
|
||||
fetch('/api/configuration',
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify(formData)
|
||||
})
|
||||
.then(res => res.json())
|
||||
.then(res => {
|
||||
this.setState({
|
||||
saved: true,
|
||||
schema: res.schema,
|
||||
configuration: res.configuration
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Col xs={8}>
|
||||
<h1 className="page-title">Monkey Configuration</h1>
|
||||
<div className="alert alert-info">
|
||||
<i className="glyphicon glyphicon-info-sign" style={{'marginRight': '5px'}}/>
|
||||
This configuration will only apply on new infections.
|
||||
</div>
|
||||
<Form schema={this.state.schema}
|
||||
formData={this.state.configuration}
|
||||
onSubmit={this.onSubmit}/>
|
||||
{ this.state.saved ?
|
||||
<p>Configuration saved successfully.</p>
|
||||
: ''}
|
||||
</Col>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default ConfigurePageComponent;
|
|
@ -0,0 +1,147 @@
|
|||
import React from 'react';
|
||||
import {Col} from 'react-bootstrap';
|
||||
import ReactJson from 'react-json-view'
|
||||
import JSONTree from 'react-json-tree'
|
||||
import ReactDataGrid, {Row} from 'react-data-grid';
|
||||
import {Icon} from "react-fa";
|
||||
const { Toolbar, Data: { Selectors } } = require('react-data-grid-addons');
|
||||
|
||||
// Custom Formatter component
|
||||
const JsonCellFormatter = React.createClass({
|
||||
render() {
|
||||
return (
|
||||
<ReactJson src={this.props.value} collapsed={true} />
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
const RowRenderer = React.createClass({
|
||||
render() {
|
||||
return (
|
||||
<Row ref={ node => this.row = node } {...this.props}/>
|
||||
);
|
||||
}
|
||||
// height: '50px',
|
||||
//
|
||||
// onClick() {
|
||||
// this.height = '200px';
|
||||
// },
|
||||
//
|
||||
// render() {
|
||||
// return (
|
||||
// <div style={{height: this.height}} onClick={this.onClick()}>
|
||||
// <Icon name="expand" className="pull-right"/>
|
||||
// <Row style={{minHeight: '100px'}} ref={ node => this.row = node } {...this.props}/>
|
||||
// </div>
|
||||
// );
|
||||
// }
|
||||
});
|
||||
|
||||
class FullLogsPageComponent extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
...this.getInitialState()
|
||||
};
|
||||
}
|
||||
|
||||
getInitialState() {
|
||||
this._columns = [
|
||||
{
|
||||
key: 'telem_type',
|
||||
name: 'Type',
|
||||
width: 200,
|
||||
filterable: true,
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
key: 'monkey_guid',
|
||||
name: 'Monkey ID',
|
||||
filterable: true,
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
key: 'timestamp',
|
||||
name: 'Time',
|
||||
filterable: true,
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
key: 'data',
|
||||
name: 'More Info',
|
||||
formatter: JsonCellFormatter
|
||||
}
|
||||
];
|
||||
|
||||
return { rows: [], filters: {}, sortColumn: null, sortDirection: null };
|
||||
}
|
||||
|
||||
getRandomDate(start, end) {
|
||||
return new Date(start.getTime() + Math.random() * (end.getTime() - start.getTime())).toLocaleDateString();
|
||||
}
|
||||
|
||||
getRows() {
|
||||
return Selectors.getRows(this.state);
|
||||
}
|
||||
|
||||
getSize() {
|
||||
return this.getRows().length;
|
||||
}
|
||||
|
||||
rowGetter = (rowIdx) => {
|
||||
const rows = this.getRows();
|
||||
return rows[rowIdx];
|
||||
};
|
||||
|
||||
handleGridSort = (sortColumn, sortDirection) => {
|
||||
this.setState({ sortColumn: sortColumn, sortDirection: sortDirection });
|
||||
};
|
||||
|
||||
handleFilterChange = (filter) => {
|
||||
let newFilters = Object.assign({}, this.state.filters);
|
||||
if (filter.filterTerm) {
|
||||
newFilters[filter.column.key] = filter;
|
||||
} else {
|
||||
delete newFilters[filter.column.key];
|
||||
}
|
||||
|
||||
this.setState({ filters: newFilters });
|
||||
};
|
||||
|
||||
onClearFilters = () => {
|
||||
this.setState({ filters: {} });
|
||||
};
|
||||
|
||||
componentDidMount = () => {
|
||||
this.dataGrid.setState({canFilter: true}, () => this.hideFilterButton());
|
||||
fetch('/api/telemetry')
|
||||
.then(res => res.json())
|
||||
.then(res => this.setState({rows: res.objects}));
|
||||
};
|
||||
|
||||
hideFilterButton = () => document.getElementsByClassName('react-grid-Toolbar')[0].style.display = 'none';
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Col xs={12}>
|
||||
<h1 className="page-title">Full Logs</h1>
|
||||
<div>
|
||||
<ReactDataGrid
|
||||
ref={(grid) => { this.dataGrid = grid; }}
|
||||
rowRenderer={RowRenderer}
|
||||
rowHeight={50}
|
||||
minHeight={500}
|
||||
columns={this._columns}
|
||||
toolbar={<Toolbar enableFilter={true}/>}
|
||||
rowGetter={this.rowGetter}
|
||||
rowsCount={this.getSize()}
|
||||
onGridSort={this.handleGridSort}
|
||||
onAddFilter={this.handleFilterChange}
|
||||
onClearFilters={this.onClearFilters} />
|
||||
</div>
|
||||
</Col>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default FullLogsPageComponent;
|
|
@ -0,0 +1,125 @@
|
|||
import React from 'react';
|
||||
import {Col} from 'react-bootstrap';
|
||||
import Graph from 'react-graph-vis';
|
||||
import {Icon} from 'react-fa'
|
||||
|
||||
|
||||
let options = {
|
||||
layout: {
|
||||
improvedLayout: false
|
||||
},
|
||||
groups: {
|
||||
manuallyInfected: {
|
||||
shape: 'icon',
|
||||
icon: {
|
||||
face: 'FontAwesome',
|
||||
code: '\uf120',
|
||||
size: 50,
|
||||
color: '#8f5a0b'
|
||||
}
|
||||
},
|
||||
infected: {
|
||||
shape: 'icon',
|
||||
icon: {
|
||||
face: 'FontAwesome',
|
||||
code: '\uf06d',
|
||||
size: 50,
|
||||
color: '#d30d09'
|
||||
}
|
||||
},
|
||||
clean: {
|
||||
shape: 'icon',
|
||||
icon: {
|
||||
face: 'FontAwesome',
|
||||
code: '\uf108',
|
||||
size: 50,
|
||||
color: '#999'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
class MapPageComponent extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
graph: {nodes: [], edges: []}
|
||||
};
|
||||
}
|
||||
|
||||
events = {
|
||||
select: event => this.selectionChanged(event)
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
fetch('/api/netmap')
|
||||
.then(res => res.json())
|
||||
.then(res => this.setState({graph: res}));
|
||||
}
|
||||
|
||||
selectionChanged(event) {
|
||||
if (event.nodes.length === 1) {
|
||||
console.log('selected node:', event.nodes[0]);
|
||||
}
|
||||
else if (event.edges.length === 1) {
|
||||
console.log('selected edge:', event.edges[0]);
|
||||
}
|
||||
else {
|
||||
console.log('no preview.');
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<Col xs={12}>
|
||||
<h1 className="page-title">Infection Map</h1>
|
||||
</Col>
|
||||
<Col xs={8}>
|
||||
<div className="pull-left">
|
||||
<input placeholder="Search" />
|
||||
</div>
|
||||
<Graph graph={this.state.graph} options={options} events={this.events}/>
|
||||
</Col>
|
||||
<Col xs={4}>
|
||||
<div className="panel panel-default preview">
|
||||
<div className="panel-heading">
|
||||
<h3>
|
||||
<Icon name="fire"/>
|
||||
vm4
|
||||
<small>Infected Asset</small>
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
<div className="panel-body">
|
||||
<h4>Machine Info</h4>
|
||||
<p>...</p>
|
||||
|
||||
<h4 style={{'marginTop': '2em'}}>Exploit Method</h4>
|
||||
<p>...</p>
|
||||
|
||||
<h4 style={{'marginTop': '2em'}}>Timeline</h4>
|
||||
<ul className="timeline">
|
||||
<li>
|
||||
<div className="bullet"></div>
|
||||
failed attempt1
|
||||
</li>
|
||||
<li>
|
||||
<div className="bullet"></div>
|
||||
failed attempt2
|
||||
</li>
|
||||
<li>
|
||||
<div className="bullet bad"></div>
|
||||
Infection!
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</Col>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default MapPageComponent;
|
|
@ -0,0 +1,71 @@
|
|||
import React from 'react';
|
||||
import {Button, Col, Well} from 'react-bootstrap';
|
||||
import CopyToClipboard from 'react-copy-to-clipboard';
|
||||
import {Icon} from 'react-fa';
|
||||
|
||||
class RunMonkeyPageComponent extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
ip: '0.0.0.0',
|
||||
cmd: '-',
|
||||
isRunning: true
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
fetch('/api')
|
||||
.then(res => res.json())
|
||||
.then(res => this.setState({
|
||||
ip: res.ip,
|
||||
cmd: this.generateCmd(res.ip)
|
||||
}));
|
||||
|
||||
fetch('/api/local-monkey')
|
||||
.then(res => res.json())
|
||||
.then(res => this.setState({
|
||||
isRunning: res['is_running']
|
||||
}));
|
||||
}
|
||||
|
||||
generateCmd(ip) {
|
||||
return `curl http://${ip}:5000/get-monkey | sh`;
|
||||
}
|
||||
|
||||
runLocalMonkey() {
|
||||
fetch('/api/local-monkey/run', {method: 'POST'})
|
||||
.then(res => res.json())
|
||||
.then(res => {
|
||||
this.setState({
|
||||
isRunning: res['is_running']
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Col xs={8}>
|
||||
<h1 className="page-title">Run the Monkey</h1>
|
||||
<p>Run this snippet on a host for manually infecting it with a Monkey:</p>
|
||||
<Well>
|
||||
<CopyToClipboard text={this.state.cmd} className="pull-right">
|
||||
<Button style={{margin: '-0.5em'}} title="Copy to Clipboard">
|
||||
<Icon name="clipboard"/>
|
||||
</Button>
|
||||
</CopyToClipboard>
|
||||
<code>{this.state.cmd}</code>
|
||||
</Well>
|
||||
<p>
|
||||
Or simply click here to <a onClick={this.runLocalMonkey()}
|
||||
className="btn btn-default btn-sm"
|
||||
style={{'marginLeft': '0.2em'}}>Run on <b>{this.state.ip}</b></a>
|
||||
{ this.state.isRunning ?
|
||||
<i className="text-success" style={{'marginLeft': '5px'}}>Running...</i>
|
||||
: ''}
|
||||
</p>
|
||||
</Col>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default RunMonkeyPageComponent;
|
|
@ -0,0 +1,32 @@
|
|||
import React from 'react';
|
||||
import {Col} from 'react-bootstrap';
|
||||
import {Link} from 'react-router-dom';
|
||||
|
||||
class RunServerPageComponent extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {ip: '0.0.0.0'};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
fetch('/api')
|
||||
.then(res => res.json())
|
||||
.then(res => this.setState({ip: res.ips.join(', ')}));
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Col xs={8}>
|
||||
<h1 className="page-title">Monkey Island C&C Server</h1>
|
||||
<div style={{'fontSize': '1.5em'}}>
|
||||
<p>Your Monkey Island server is up and running on <b>{this.state.ip}</b> 👏 👏</p>
|
||||
<p>
|
||||
Now <Link to="/configure">configure the monkey</Link> (or just stick with the default configuration) and <Link to="/run-monkey">run the monkey</Link>.
|
||||
</p>
|
||||
</div>
|
||||
</Col>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default RunServerPageComponent;
|
|
@ -0,0 +1,35 @@
|
|||
# About this folder
|
||||
|
||||
This folder holds configuration files for different environments.
|
||||
You can use it to provide your app with different settings based on the
|
||||
current environment, e.g. to configure different API base urls depending on
|
||||
whether your setup runs in dev mode or is built for distribution.
|
||||
You can include the configuration into your code like this:
|
||||
|
||||
**ES2015 Modules**
|
||||
|
||||
```js
|
||||
import config from 'config';
|
||||
```
|
||||
|
||||
**Common JS**
|
||||
|
||||
Due to Babel6 we need to append `.default`.
|
||||
|
||||
```js
|
||||
let config = require('config').default;
|
||||
```
|
||||
|
||||
**Example**
|
||||
|
||||
```javascript
|
||||
import React from 'react';
|
||||
import config from 'config';
|
||||
|
||||
class MyComponent extends React.Component {
|
||||
constructor(props, ctx) {
|
||||
super(props, ctx);
|
||||
let currentAppEnv = config.appEnv;
|
||||
}
|
||||
}
|
||||
```
|
|
@ -0,0 +1,5 @@
|
|||
'use strict';
|
||||
|
||||
// Settings configured here will be merged into the final config object.
|
||||
export default {
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
'use strict';
|
||||
|
||||
import baseConfig from './base';
|
||||
|
||||
let config = {
|
||||
appEnv: 'dev' // feel free to remove the appEnv property here
|
||||
};
|
||||
|
||||
export default Object.freeze(Object.assign({}, baseConfig, config));
|
|
@ -0,0 +1,9 @@
|
|||
'use strict';
|
||||
|
||||
import baseConfig from './base';
|
||||
|
||||
let config = {
|
||||
appEnv: 'dist' // feel free to remove the appEnv property here
|
||||
};
|
||||
|
||||
export default Object.freeze(Object.assign({}, baseConfig, config));
|
|
@ -0,0 +1,9 @@
|
|||
'use strict';
|
||||
|
||||
import baseConfig from './base';
|
||||
|
||||
let config = {
|
||||
appEnv: 'test' // don't remove the appEnv property here
|
||||
};
|
||||
|
||||
export default Object.freeze(Object.assign(baseConfig, config));
|
Binary file not shown.
After Width: | Height: | Size: 24 KiB |
Binary file not shown.
After Width: | Height: | Size: 38 KiB |
Binary file not shown.
After Width: | Height: | Size: 55 KiB |
|
@ -0,0 +1,16 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Monkey Island C&C Server</title>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
<meta name="description" content="">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||||
</head>
|
||||
<body>
|
||||
<div id="app">Loading...</div>
|
||||
|
||||
<script>__REACT_DEVTOOLS_GLOBAL_HOOK__ = parent.__REACT_DEVTOOLS_GLOBAL_HOOK__</script>
|
||||
<script type="text/javascript" src="/assets/app.js"></script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,8 @@
|
|||
import 'core-js/fn/object/assign';
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import App from './components/Main';
|
||||
import Bootstrap from 'bootstrap/dist/css/bootstrap.css'; // eslint-disable-line no-unused-vars
|
||||
|
||||
// Render the main component into the dom
|
||||
ReactDOM.render(<App />, document.getElementById('app'));
|
|
@ -0,0 +1,209 @@
|
|||
@import url('https://fonts.googleapis.com/css?family=Open+Sans');
|
||||
@import url('https://fonts.googleapis.com/css?family=Alegreya');
|
||||
|
||||
/* Base Application Styles */
|
||||
body {
|
||||
color: #fff;
|
||||
background: #222;
|
||||
}
|
||||
|
||||
#app {
|
||||
font-family: 'Open Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sidebar
|
||||
*/
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.sidebar {
|
||||
position: fixed !important;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: 1000;
|
||||
display: block;
|
||||
padding: 1em;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto; /* Scrollable contents if viewport is shorter than content. */
|
||||
background-color: #f5f5f5;
|
||||
border-right: 1px solid #e8e8e8;
|
||||
}
|
||||
|
||||
.header {
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
.header img {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.navigation {
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style: none;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
li {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
li .number {
|
||||
color: #666;
|
||||
display: inline-block;
|
||||
width: 1.1em;
|
||||
}
|
||||
|
||||
li a {
|
||||
/*color: #d30d09;*/
|
||||
color: #666;
|
||||
display: block;
|
||||
padding: 0.5em 1em;
|
||||
margin: 0.1em 0;
|
||||
}
|
||||
li a:hover {
|
||||
color: #000;
|
||||
background: #e9e9e9;
|
||||
text-decoration: none;
|
||||
}
|
||||
li a.active {
|
||||
background: #fff;
|
||||
text-decoration: none;
|
||||
color: #d30d09;
|
||||
}
|
||||
li a.active:hover {
|
||||
color: #d30d09;
|
||||
}
|
||||
li a.disabled {
|
||||
color: #666;
|
||||
cursor: auto;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
li ul {
|
||||
margin-left: 1.5em;
|
||||
}
|
||||
|
||||
li .checkmark {
|
||||
font-size: 1.3em;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Main content
|
||||
*/
|
||||
|
||||
.main {
|
||||
padding: 2em 1em;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1em;
|
||||
padding-bottom: 0.5em;
|
||||
border-bottom: 2px dotted #d30d09;
|
||||
font-size: 2.5em;
|
||||
color: #d30d09;
|
||||
font-family: 'Alegreya', serif;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.main {
|
||||
padding-right: 40px;
|
||||
padding-left: 40px;
|
||||
}
|
||||
}
|
||||
.main .page-header {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.index img {
|
||||
margin: 40px auto;
|
||||
border-radius: 4px;
|
||||
background: #fff;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.index .notice {
|
||||
margin: 1em auto;
|
||||
padding: 15px 0;
|
||||
text-align: center;
|
||||
border: 1px solid #000;
|
||||
border-width: 1px 0;
|
||||
background: #666;
|
||||
}
|
||||
|
||||
/*
|
||||
* Map Preview Pane
|
||||
*/
|
||||
|
||||
.preview.well {
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
.preview h3 {
|
||||
margin: 0;
|
||||
|
||||
}
|
||||
.preview h3 small {
|
||||
margin-top: 0.5em;
|
||||
display: block;
|
||||
}
|
||||
.preview h3 .fa {
|
||||
margin-right: 5px;
|
||||
}
|
||||
.preview h4 {
|
||||
text-transform: uppercase;
|
||||
color: #999;
|
||||
font-size: 1em;
|
||||
margin-top: 0;
|
||||
}
|
||||
.preview p, .preview .timeline {
|
||||
margin-left: 1em;
|
||||
}
|
||||
|
||||
.timeline {
|
||||
position: relative;
|
||||
padding: 0.5em 0;
|
||||
}
|
||||
|
||||
.timeline:before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 5px;
|
||||
width: 2px;
|
||||
height: 100%;
|
||||
background: #ccc;
|
||||
z-index: 1
|
||||
}
|
||||
|
||||
.timeline li {
|
||||
margin-left: 1.5em;
|
||||
position: relative;
|
||||
overflow: visible;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.timeline .bullet {
|
||||
width: 16px;
|
||||
background: #ccc;
|
||||
position: absolute;
|
||||
right: 100%;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
margin: 2px 0.5em;
|
||||
z-index: 2;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.timeline .bullet.bad {
|
||||
background: #d30d09;
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
'use strict';
|
||||
|
||||
const path = require('path');
|
||||
const args = require('minimist')(process.argv.slice(2));
|
||||
|
||||
// List of allowed environments
|
||||
const allowedEnvs = ['dev', 'dist', 'test'];
|
||||
|
||||
// Set the correct environment
|
||||
let env;
|
||||
if (args._.length > 0 && args._.indexOf('start') !== -1) {
|
||||
env = 'test';
|
||||
} else if (args.env) {
|
||||
env = args.env;
|
||||
} else {
|
||||
env = 'dev';
|
||||
}
|
||||
process.env.REACT_WEBPACK_ENV = env;
|
||||
|
||||
/**
|
||||
* Build the webpack configuration
|
||||
* @param {String} wantedEnv The wanted environment
|
||||
* @return {Object} Webpack config
|
||||
*/
|
||||
function buildConfig(wantedEnv) {
|
||||
let isValid = wantedEnv && wantedEnv.length > 0 && allowedEnvs.indexOf(wantedEnv) !== -1;
|
||||
let validEnv = isValid ? wantedEnv : 'dev';
|
||||
let config = require(path.join(__dirname, 'cfg/' + validEnv));
|
||||
return config;
|
||||
}
|
||||
|
||||
module.exports = buildConfig(env);
|
Loading…
Reference in New Issue