├── static
└── .gitkeep
├── src
├── view
│ ├── user_manager
│ │ ├── index.js
│ │ ├── user_list
│ │ │ ├── events.js
│ │ │ ├── enable_user.js
│ │ │ ├── search_bar.js
│ │ │ ├── user_list.js
│ │ │ ├── index.js
│ │ │ └── edit_user.js
│ │ └── operation_log
│ │ │ ├── events.js
│ │ │ ├── log_list.js
│ │ │ ├── log_pane.js
│ │ │ ├── index.js
│ │ │ └── search_bar.js
│ ├── router_manager
│ │ ├── gateway_manage
│ │ │ ├── eventBus.js
│ │ │ ├── events.js
│ │ │ ├── search_bar.js
│ │ │ ├── gateway_list.js
│ │ │ ├── index.js
│ │ │ ├── add_gateway.js
│ │ │ └── edit_gateway.js
│ │ ├── TargetModal.js
│ │ ├── host_qps.js
│ │ └── addhost_qps.js
│ ├── login.js
│ ├── plugin_manager
│ │ ├── plugin_general.js
│ │ └── property_isolated.js
│ └── general
│ │ └── dashboard.js
├── favicon.ico
├── common
│ ├── css
│ │ ├── no-data.less
│ │ ├── import.less
│ │ ├── text-overflow.less
│ │ ├── toast.less
│ │ ├── position.less
│ │ ├── loading-toast.less
│ │ ├── base.css
│ │ ├── border.less
│ │ ├── iconfont.css
│ │ ├── vendor-prefixes.less
│ │ └── loading.less
│ └── less
│ │ └── private.less
├── models
│ ├── logs.model.js
│ ├── plugin.models.js
│ ├── general_models.js
│ ├── gateway_manage_models.js
│ ├── host_manager_models.js
│ ├── user_manage_models.js
│ ├── gray_divide_models.js
│ ├── anti_sql_injection_models.js
│ ├── proprety_ratelimit_models.js
│ ├── firewall.models.js
│ └── router_manager_models.js
├── index.js
├── index.tpl.html
├── util
│ ├── loadable.js
│ ├── util.js
│ ├── eventBus.js
│ ├── extend.js
│ └── base64.js
├── store
│ ├── business.store.js
│ └── store.base.js
├── ui
│ ├── ui.modal.js
│ ├── ui.fiedld.js
│ └── sidermenu.js
├── core
│ ├── nav.js
│ ├── view.base.js
│ ├── change_password.js
│ └── model.base.js
└── router.js
├── .browserslistrc
├── .gitignore
├── docker
├── dist.tar.gz
├── ngradminportal.conf
└── Dockerfile
├── images
├── Add_AB.png
├── Add_host.png
├── Overview.png
├── Plugins.png
├── Target.png
├── Rate_limit.png
├── Add_gateway.png
└── Add_router_rule.png
├── pm2.json
├── .editorconfig
├── .babelrc
├── postcss.config.js
├── app.js
├── config
└── index.js
├── README.md
├── bin
└── www
├── package.json
└── LICENSE
/static/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/view/user_manager/index.js:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/view/router_manager/gateway_manage/eventBus.js:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.browserslistrc:
--------------------------------------------------------------------------------
1 | >1%
2 | not dead
3 | not ie <= 11
4 | not op_mini all
--------------------------------------------------------------------------------
/src/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gogo-easy/ngrAdminPortal/HEAD/src/favicon.ico
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/*
2 | package-lock.json
3 | .idea/
4 | dist/
5 | public/
6 | .DS_Store
7 |
--------------------------------------------------------------------------------
/docker/dist.tar.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gogo-easy/ngrAdminPortal/HEAD/docker/dist.tar.gz
--------------------------------------------------------------------------------
/images/Add_AB.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gogo-easy/ngrAdminPortal/HEAD/images/Add_AB.png
--------------------------------------------------------------------------------
/images/Add_host.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gogo-easy/ngrAdminPortal/HEAD/images/Add_host.png
--------------------------------------------------------------------------------
/images/Overview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gogo-easy/ngrAdminPortal/HEAD/images/Overview.png
--------------------------------------------------------------------------------
/images/Plugins.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gogo-easy/ngrAdminPortal/HEAD/images/Plugins.png
--------------------------------------------------------------------------------
/images/Target.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gogo-easy/ngrAdminPortal/HEAD/images/Target.png
--------------------------------------------------------------------------------
/images/Rate_limit.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gogo-easy/ngrAdminPortal/HEAD/images/Rate_limit.png
--------------------------------------------------------------------------------
/images/Add_gateway.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gogo-easy/ngrAdminPortal/HEAD/images/Add_gateway.png
--------------------------------------------------------------------------------
/images/Add_router_rule.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gogo-easy/ngrAdminPortal/HEAD/images/Add_router_rule.png
--------------------------------------------------------------------------------
/pm2.json:
--------------------------------------------------------------------------------
1 | {
2 | "apps": [
3 | {
4 | "script": "./bin/www"
5 | }
6 | ]
7 | }
8 |
--------------------------------------------------------------------------------
/src/view/user_manager/user_list/events.js:
--------------------------------------------------------------------------------
1 | import { EventEmitter } from '../../../util/eventBus';
2 |
3 | export const eventBus = new EventEmitter();
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | [*.{js,jsx,ts,tsx}]
2 | indent_style = space
3 | indent_size = 2
4 | trim_trailing_whitespace = true
5 | insert_final_newline = true
6 |
--------------------------------------------------------------------------------
/src/view/user_manager/operation_log/events.js:
--------------------------------------------------------------------------------
1 | import { EventEmitter } from '../../../util/eventBus';
2 |
3 | export const eventBus = new EventEmitter();
--------------------------------------------------------------------------------
/src/view/router_manager/gateway_manage/events.js:
--------------------------------------------------------------------------------
1 | import { EventEmitter } from '../../../util/eventBus';
2 |
3 | export const eventBus = new EventEmitter();
--------------------------------------------------------------------------------
/docker/ngradminportal.conf:
--------------------------------------------------------------------------------
1 | server {
2 | listen 3000;
3 | root /tmp/dist;
4 | index index.html;
5 | location / {
6 | try_files $uri $uri/ /index.html;
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/docker/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM nginx
2 | # Run 'npm instsall' and 'npm run build' to generate the 'dist' directory, and make a tarball
3 | ADD dist.tar.gz /tmp
4 | COPY ngradminportal.conf /etc/nginx/conf.d/ngradminportal.conf
5 | EXPOSE 3000
6 |
--------------------------------------------------------------------------------
/src/common/css/no-data.less:
--------------------------------------------------------------------------------
1 | .no-data-content{
2 | width: 100%;
3 | text-align: center;
4 |
5 | .top-img{
6 | width: 100px;
7 | margin:160px auto 20px;
8 | display: block;
9 | }
10 | .gray-tip{
11 | font-size: 14px;
12 | color:#666;
13 | }
14 | }
--------------------------------------------------------------------------------
/src/models/logs.model.js:
--------------------------------------------------------------------------------
1 | import BaseModel from '../core/model.base'
2 |
3 | /*
4 | 用户查询列表
5 | */
6 | export class LogListModel extends BaseModel {
7 |
8 | constructor(props) {
9 |
10 | super(props);
11 |
12 | this.url = 'query_user_log';
13 | this.method = 'GET';
14 |
15 | }
16 |
17 | }
--------------------------------------------------------------------------------
/src/common/css/import.less:
--------------------------------------------------------------------------------
1 | @import './base.css';
2 | @import './vendor-prefixes.less';
3 | @import './position.less';
4 | @import './border.less';
5 | @import './loading.less';
6 | @import './loading-toast.less';
7 | @import './toast.less';
8 | @import './text-overflow.less';
9 | @import './no-data.less';
10 | @import './iconfont.css';
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | "@babel/preset-env",
4 | "@babel/preset-react"
5 | ],
6 | "plugins": [
7 | "@babel/plugin-proposal-function-bind",
8 | "@babel/plugin-proposal-class-properties",
9 | ["import", {
10 | "libraryName": "antd",
11 | "libraryDirectory": "es",
12 | "style": true // `style: true` 会加载 less 文件
13 | }]
14 | ]
15 | }
--------------------------------------------------------------------------------
/src/common/css/text-overflow.less:
--------------------------------------------------------------------------------
1 | // Text overflow
2 | // Requires inline-block or block for proper styling
3 |
4 | .ellipsis {
5 | overflow: hidden;
6 | text-overflow: ellipsis;
7 | white-space: nowrap;
8 | }
9 |
10 | .multi-ellipsis(@lines: 2) {
11 | -webkit-box-orient: vertical;
12 | display: -webkit-box;
13 | -webkit-line-clamp: @lines;
14 | overflow: hidden;
15 | }
16 |
--------------------------------------------------------------------------------
/src/common/css/toast.less:
--------------------------------------------------------------------------------
1 | .g-toast(@bg: rgba(40,40,40,0.75)) {
2 | position: fixed;
3 | top: 180px;
4 | left: 75px;
5 | right: 75px;
6 | z-index: 1200;
7 | padding: 28px 10px;
8 | background: @bg;
9 | border-radius: 5px;
10 | overflow: hidden;
11 | line-height: 20px;
12 | color: #fff;
13 | font-size: 15px;
14 | text-align: center;
15 | }
16 |
17 | .g-toast {
18 | .g-toast();
19 | }
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | const config = require('./config')
2 |
3 | module.exports = {
4 | plugins: [
5 | require('autoprefixer'),
6 | require('postcss-flexbugs-fixes'),
7 | require('postcss-preset-env')({
8 | autoprefixer: {
9 | flexbox: 'no-2009',
10 | },
11 | stage: 3,
12 | }),
13 | require('postcss-adaptive')({
14 | remUnit: config.common.remUnit,
15 | autoRem: config.common.autoRem
16 | })
17 | ]
18 | }
19 |
--------------------------------------------------------------------------------
/src/common/css/position.less:
--------------------------------------------------------------------------------
1 | .vertical-center {
2 | position: absolute;
3 | top: 50%;
4 | .translate(0,-50%);
5 | }
6 |
7 | .horizontal-center {
8 | position: absolute;
9 | left: 50%;
10 | .translate(-50%,0);
11 | }
12 |
13 | .center {
14 | position: absolute;
15 | top: 50%;
16 | left: 50%;
17 | .translate(-50%,-50%);
18 | }
19 |
20 | .scroll-controller {
21 | position: fixed;
22 | overflow: hidden !important;
23 | width: 100%;
24 | height: 100%;
25 | }
26 | @blue:#2db2f0;
--------------------------------------------------------------------------------
/src/models/plugin.models.js:
--------------------------------------------------------------------------------
1 | import BaseModel from '../core/model.base'
2 | /*
3 | 插件列表
4 | */
5 | export class PluginListModel extends BaseModel {
6 |
7 | constructor(props) {
8 |
9 | super(props);
10 |
11 | this.url = 'plugins' ;
12 | this.method = 'GET';
13 |
14 | this.notParallelism = false;
15 |
16 | }
17 |
18 | }
19 |
20 | /*
21 | 切换插件状态
22 | */
23 | export class TogglePluginEnableModel extends BaseModel {
24 |
25 | constructor(props) {
26 |
27 | super(props);
28 |
29 | }
30 |
31 | }
32 |
33 |
--------------------------------------------------------------------------------
/src/models/general_models.js:
--------------------------------------------------------------------------------
1 | import BaseModel from '../core/model.base'
2 | /*
3 | 概况报表
4 | */
5 | export class DashboardListModel extends BaseModel {
6 |
7 | constructor(props) {
8 |
9 | super(props);
10 |
11 | this.url = 'dashboard/show' ;
12 | this.method = 'GET';
13 |
14 | }
15 |
16 | }
17 |
18 | /*
19 | 登陆接口
20 | */
21 | export class LoginModel extends BaseModel {
22 |
23 | constructor(props) {
24 |
25 | super(props);
26 |
27 | this.url = 'login' ;
28 | // this.method = 'GET';
29 |
30 | }
31 |
32 | }
33 |
34 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { render } from 'react-dom'
3 | import { BrowserRouter,Route } from 'react-router-dom';
4 | // import 'normalize.css'
5 | import Router from './router'
6 |
7 |
8 | render(
9 |
10 |
11 | {
12 | Router.map((item,idx)=>{
13 | return
14 | })
15 | }
16 |
17 | ,
18 | document.getElementById('root')
19 | );
--------------------------------------------------------------------------------
/app.js:
--------------------------------------------------------------------------------
1 | var express = require('express');
2 | var path = require('path');
3 |
4 | var app = express();
5 |
6 | // 设置模板引擎
7 | app.set('views', path.join(__dirname, 'dist'));
8 | app.engine('.html', require('ejs').__express);
9 | app.set('view engine', 'html');
10 |
11 | app.use(express.json());
12 | app.use(express.urlencoded({ extended: false }));
13 | app.use(express.static(path.join(__dirname, 'dist')));
14 |
15 | // 接收请求,返回根页面,前端处理页面路由(单页应用)
16 | app.use('/', function(req, res, next) {
17 | res.render('index');
18 | });
19 |
20 |
21 | module.exports = app;
22 |
--------------------------------------------------------------------------------
/src/index.tpl.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | NgRouter API网关管理控制台
9 |
10 |
11 |
12 |
13 | You need to enable JavaScript to run NgRouter API网关管理控制台.
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/src/util/loadable.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Loadable from 'react-loadable';
3 |
4 | import {Spin} from 'antd';
5 | //通用的过场组件
6 | const loadingComponent =()=>{
7 | return (
8 |
9 |
10 |
11 | )
12 | }
13 |
14 | //过场组件默认采用通用的,若传入了loading,则采用传入的过场组件
15 | export default (loader,loading = loadingComponent)=>{
16 | return Loadable({
17 | loader,
18 | loading
19 | });
20 | }
--------------------------------------------------------------------------------
/src/common/css/loading-toast.less:
--------------------------------------------------------------------------------
1 | .g-loading-toast {
2 | &:extend(.horizontal-center);
3 |
4 | position: fixed;
5 | top: 180px;
6 | z-index: 1200;
7 | width: 144px;
8 | padding: 28px 10px;
9 | background: rgba(40, 40, 40, 0.75);
10 | border-radius: 5px;
11 | overflow: hidden;
12 | line-height: 20px;
13 | color: #fff;
14 | font-size:15px;
15 | text-align: center;
16 |
17 | .toast-text {
18 | margin-top: 10px;
19 | }
20 | }
21 |
22 | .g-loading-toast {
23 | width: auto;
24 | height: auto;
25 | min-width: 60px;
26 | padding: 13px 0 14px;
27 | background: rgba(0,0,0,0.8);
28 |
29 | .toast-text {
30 | padding: 0 10px;
31 | }
32 | }
33 |
34 | .g-toast {
35 | background: rgba(0,0,0,0.8);
36 | }
--------------------------------------------------------------------------------
/config/index.js:
--------------------------------------------------------------------------------
1 |
2 | var path = require('path')
3 | const NODE_ENV = process.env.NODE_ENV
4 |
5 | module.exports = {
6 | build: {
7 | restfulApi:'http://127.0.0.1:7777',
8 | mode: NODE_ENV,
9 | sourceMap: false,
10 | assetsRoot: path.resolve(__dirname, '../dist'),
11 | assetsSubDirectory: 'static',
12 | assetsPublicPath: '/',
13 | bundleAnalyzerReport: process.env.analyz
14 | },
15 | dev: {
16 | restfulApi:'http://127.0.0.1:7777',
17 | mode: NODE_ENV,
18 | sourceMap: 'source-map',
19 | assetsRoot: path.resolve(__dirname, '../dist'),
20 | assetsSubDirectory: 'static',
21 | assetsPublicPath: '/',
22 | port: 3000,
23 | autoOpenBrowser: true,
24 | overlay: true,
25 | historyApiFallback: true,
26 | noInfo: true
27 | },
28 | }
29 |
--------------------------------------------------------------------------------
/src/models/gateway_manage_models.js:
--------------------------------------------------------------------------------
1 | import BaseModel from '../core/model.base'
2 |
3 | /*
4 | gateway list
5 | */
6 | export class GateWayListModel extends BaseModel {
7 |
8 | constructor(props) {
9 |
10 | super(props);
11 |
12 | this.url = 'gateway/query';
13 | this.method = 'GET';
14 |
15 | }
16 |
17 | }
18 |
19 | /*
20 | 设置网关QPS限流信息
21 | */
22 | export class SetLimitModel extends BaseModel {
23 |
24 | constructor(props) {
25 |
26 | super(props);
27 |
28 | this.url = 'gateway/set_limit_count';
29 | this.method = 'POST';
30 |
31 | }
32 |
33 | }
34 |
35 | /*
36 | 新增网关
37 | */
38 | export class AddGatewayModel extends BaseModel {
39 |
40 | constructor(props) {
41 |
42 | super(props);
43 |
44 | this.url = 'gateway/add';
45 | this.method = 'POST';
46 |
47 | }
48 |
49 | }
50 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # NgRouter Admin Portal - NgRouter网关管理控制台
2 |
3 | [](https://github.com/gogo-easy/ngrAdminPortal/blob/master/LICENSE) [](https://github.com/gogo-easy/ngrAdminPortal/releases)
4 |
5 | ## How to use
6 |
7 | - [User Guide](https://github.com/gogo-easy/ngrAdminPortal/wiki/Using-Guide)
8 |
9 | ## Deployment & Installation
10 |
11 | - [Deployment & Installation](https://github.com/gogo-easy/ngrAdminPortal/wiki/Quick-Start)
12 |
13 | ## Release
14 |
15 | - [版本发布](https://github.com/gogo-easy/ngrAdminPortal/releases)
16 |
17 | ## 贡献者
18 |
19 | - [@Spring Future](https://github.com/gogo-easy)
20 |
21 | ## License
22 |
23 | The project is licensed by [Apache 2.0](https://github.com/gogo-easy/ngrAdminPortal/blob/master/LICENSE)
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/src/store/business.store.js:
--------------------------------------------------------------------------------
1 | import BaseStore from './store.base'
2 |
3 |
4 | //登陆用户信息
5 | export class UserInfoStore extends BaseStore {
6 |
7 | constructor(props) {
8 | super(props);
9 |
10 | // 缓存数据标志
11 | this.key = '__ngr_user_info__';
12 |
13 | // 缓存时间,支持单位 天-"D", 时-"H", 分钟-"M"
14 | // 如 "30D", "0.5H"
15 | this.lifetime = '7D';
16 | }
17 |
18 | }
19 |
20 | // 用户auth信息
21 | export class HttpAuthInfoStore extends BaseStore {
22 |
23 | constructor(props) {
24 | super(props);
25 |
26 | // 缓存数据标志
27 | this.key = '__ngr_http_auth__';
28 |
29 | // 缓存时间,支持单位 天-"D", 时-"H", 分钟-"M"
30 | // 如 "30D", "0.5H"
31 | this.lifetime = '2H';
32 | }
33 |
34 | }
35 | // 用户auth信息
36 | export class PluginInfoStore extends BaseStore {
37 |
38 | constructor(props) {
39 | super(props);
40 |
41 | // 缓存数据标志
42 | this.key = '__ngr_plugin_info__';
43 |
44 | // 缓存时间,支持单位 天-"D", 时-"H", 分钟-"M"
45 | // 如 "30D", "0.5H"
46 | this.lifetime = '7D';
47 | }
48 |
49 | }
50 |
--------------------------------------------------------------------------------
/src/models/host_manager_models.js:
--------------------------------------------------------------------------------
1 | import BaseModel from '../core/model.base'
2 |
3 | /*
4 | hsot 列表
5 | */
6 | export class HostListModel extends BaseModel {
7 | constructor(props) {
8 | super(props);
9 | this.url = 'host/query';
10 | this.method = 'GET';
11 | }
12 | }
13 |
14 | export class HostQPSModel extends BaseModel {
15 | constructor(props) {
16 | super(props);
17 | this.url = 'host/set_limit_count';
18 | this.method = 'POST';
19 | }
20 | }
21 |
22 | export class AddHostQPSModel extends BaseModel {
23 | constructor(props) {
24 | super(props);
25 | this.url = 'host/add';
26 | this.method = 'POST';
27 | }
28 | }
29 |
30 | /*
31 | gateway 列表
32 | */
33 | export class GatewayListModel extends BaseModel {
34 | constructor(props) {
35 | super(props);
36 | this.url = 'gateway/query';
37 | this.method = 'GET';
38 | }
39 | }
40 |
41 | /*
42 | 启禁用
43 | */
44 | export class ToggleStatusModel extends BaseModel {
45 | constructor(props) {
46 | super(props);
47 | this.url = 'host/enable';
48 | this.method = 'POST';
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/models/user_manage_models.js:
--------------------------------------------------------------------------------
1 | import BaseModel from '../core/model.base'
2 |
3 | /*
4 | 用户查询列表
5 | */
6 | export class UserListModel extends BaseModel {
7 |
8 | constructor(props) {
9 |
10 | super(props);
11 |
12 | this.url = 'query_user_list';
13 | this.method = 'GET';
14 |
15 | }
16 |
17 | }
18 |
19 | export class UserAddModel extends BaseModel {
20 |
21 | constructor(props) {
22 |
23 | super(props);
24 |
25 | this.url = 'create_user';
26 | this.method = 'POST';
27 |
28 | }
29 |
30 | }
31 |
32 |
33 | export class UserModifyModel extends BaseModel {
34 |
35 | constructor(props) {
36 |
37 | super(props);
38 |
39 | this.url = 'modify_user';
40 | this.method = 'POST';
41 |
42 | }
43 |
44 | }
45 |
46 | export class PassWordModifyModel extends BaseModel {
47 |
48 | constructor(props) {
49 |
50 | super(props);
51 |
52 | this.url = 'modify_passowrd';
53 | this.method = 'POST';
54 |
55 | }
56 |
57 | }
58 |
59 |
60 | export class UserEnableModel extends BaseModel {
61 |
62 | constructor(props) {
63 |
64 | super(props);
65 |
66 | this.url = 'user_enable';
67 | this.method = 'POST';
68 |
69 | }
70 |
71 | }
--------------------------------------------------------------------------------
/src/common/css/base.css:
--------------------------------------------------------------------------------
1 | html,body,div, dl, dt, dd, ul, ol, li, h1, h2, h3, h4, h5, h6, pre, code, form, fieldset, legend, input, textarea, p, blockquote, th, td, em, button {
2 | margin:0;
3 | padding:0;
4 | }
5 |
6 | article,aside,details,figcaption,figure,footer,header,hgroup,menu,nav,section,summary {
7 | display:block;
8 | }
9 |
10 | html {
11 | height: 100%;
12 | }
13 |
14 | img {
15 | border:0;
16 | margin:0;
17 | padding:0;
18 | vertical-align: top;
19 | }
20 |
21 | input,textarea,select {
22 | border:none;
23 | font-family:inherit;
24 | font-size:inherit;
25 | font-weight:inherit;
26 | resize:none;
27 | outline:0;
28 | box-shadow: none;
29 | -webkit-appearance: none;
30 | }
31 |
32 | table {
33 | border-collapse:collapse;
34 | }
35 |
36 | a {
37 | text-decoration:none;
38 | }
39 |
40 | h1, h2, h3, h4, h5,h6 {
41 | font-weight:normal;
42 | }
43 |
44 | ul,li {
45 | list-style: none;
46 | }
47 |
48 | dfn,em,i {
49 | font-style:normal;
50 | }
51 |
52 | body {
53 | height: 100%;
54 | background-color:#f7f7f7;
55 | font-size: 14px;
56 | line-height: 1;
57 | font-family: PingFangSC-Regular, Helvetica, 'Microsoft Yahei', tahoma, sans-serif;;
58 | -webkit-user-select: none;
59 | -webkit-touch-callout: none;
60 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
61 | -webkit-text-size-adjust:none;
62 | }
63 | #root,#g_body{
64 | width: 100vw;
65 | height: 100vh;
66 | }
67 |
68 |
--------------------------------------------------------------------------------
/src/util/util.js:
--------------------------------------------------------------------------------
1 | import {HttpAuthInfoStore} from '../store/business.store'
2 |
3 | const httpAuthInfoStore = HttpAuthInfoStore.getInstance();
4 |
5 |
6 | /*
7 | 获取用户的登陆信息
8 | */
9 |
10 | function getHttpAuth(success,faild){
11 |
12 | const authInfo = httpAuthInfoStore.getData() || {};
13 |
14 | if(authInfo.auth){
15 | success(authInfo.auth);
16 | }else{
17 | if(faild){
18 | faild();
19 | }else{
20 | gotoPage('/login?urlpath=' + location.pathname)
21 | }
22 | }
23 |
24 | }
25 |
26 | function gotoPage(urlPath){
27 |
28 | location.href = urlPath;
29 |
30 | }
31 |
32 | /*
33 | format参数,去除前后空格,
34 | */
35 | function formatObj(obj){
36 |
37 | var newObj = {};
38 |
39 | for(var k in obj){
40 | if(obj[k] instanceof Array){
41 | newObj[k] = obj[k];
42 | }else{
43 | newObj[k] = obj[k].toString().replace(/(^\s*)|(\s*$)/g,"");
44 | }
45 | }
46 |
47 | return newObj
48 |
49 | }
50 |
51 | function parseQuery(queryStr) {
52 | var query = {};
53 | var idx = queryStr.lastIndexOf("?");
54 | var str = queryStr.substr(idx + 1);
55 | if(str == "" || idx == -1) {
56 | return {};
57 | }
58 | var pairs = str.split('&');
59 | for (var i = 0; i < pairs.length; i++) {
60 | var pair = pairs[i].split('=');
61 | query[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1] || '');
62 | }
63 | return query;
64 | }
65 |
66 | export {
67 | getHttpAuth,
68 | gotoPage,
69 | formatObj,
70 | parseQuery
71 | }
72 |
73 |
74 |
75 |
76 |
--------------------------------------------------------------------------------
/src/util/eventBus.js:
--------------------------------------------------------------------------------
1 |
2 | // 构造一个事件中心解决兄弟组件通信问题,
3 |
4 | export class EventEmitter {
5 | constructor() {
6 | this.events = Object.create(null);
7 | }
8 | on(eventName, handler) {
9 | if (!handler) {
10 | console.warn(`You must define a handler to handle event ${eventName} !`);
11 | return;
12 | }
13 | if (!this.events[eventName]) {
14 | this.events[eventName] = [];
15 | }
16 | this.events[eventName].push(handler);
17 | console.warn(`You define the handler ${handler.name} to handle event ${eventName} successfully!`);
18 | }
19 |
20 | emit(eventName, ...args) {
21 | if (!this.events[eventName]) {
22 | console.warn(`Event ${eventName} does not have any handler!`);
23 | } else {
24 | for (let handler of this.events[eventName]) {
25 | handler.apply(this, args);
26 | }
27 | }
28 | }
29 |
30 | // 事件移除,
31 | off(eventName, handler) {
32 | if (this.events[eventName]) {
33 | if (!handler) {
34 | this.events[eventName] = undefined;
35 | console.warn(`Event ${eventName}'s all handlers has been removed !`);
36 | } else {
37 | const handles = this.events[eventName];
38 | handles.splice(handles.findIndex(el => el === handler), 1);
39 | console.warn(`Event ${eventName}'s handler ${handler.name} has been removed !`);
40 | }
41 | return;
42 | }
43 | console.warn(`Event ${eventName} has not been defined !`);
44 | }
45 |
46 | // 注册事件只调用一次
47 | once(eventName, handler) {
48 |
49 | }
50 | }
--------------------------------------------------------------------------------
/src/view/router_manager/gateway_manage/search_bar.js:
--------------------------------------------------------------------------------
1 | import {
2 | Form, Input, Button, Row, Col
3 | } from 'antd';
4 | import React, { Component } from 'react'
5 | import { eventBus } from './events';
6 |
7 | class SearchForm extends React.Component {
8 | constructor(props) {
9 | super(props)
10 | }
11 |
12 | handleSearch = (e) => {
13 | e.preventDefault();
14 | this.props.form.validateFields((err, values) => {
15 | if (!err) {
16 | // 触发事件中心的搜索事件
17 | eventBus.emit("search", values);
18 | }
19 | });
20 | }
21 |
22 | handleReset = () => {
23 | this.props.form.resetFields();
24 | }
25 |
26 | render() {
27 |
28 | const { getFieldDecorator } = this.props.form;
29 | return (
30 |
52 | );
53 | }
54 | }
55 |
56 | const WrappedSearchForm = Form.create({ name: 'gateway_search_form' })(SearchForm);
57 |
58 | export default WrappedSearchForm;
--------------------------------------------------------------------------------
/src/view/user_manager/operation_log/log_list.js:
--------------------------------------------------------------------------------
1 | import { Table, Divider, Tag, Pagination } from 'antd';
2 | import React, { Component } from 'react';
3 | import { LogPane } from './log_pane'
4 |
5 |
6 |
7 | const genCol = () => [
8 | {
9 | title: '序号',
10 | dataIndex: 'index',
11 | key: 'index',
12 | width: "50px",
13 | align: 'center'
14 | }, {
15 | title: '操作用户',
16 | dataIndex: 'username',
17 | key: 'username',
18 | width: "150px",
19 | align: 'center'
20 |
21 | }, {
22 | title: '操作时间',
23 | dataIndex: 'create_at',
24 | key: 'create_at',
25 | width: "150px",
26 | align: 'center'
27 |
28 | }, {
29 | title: '操作类型',
30 | dataIndex: 'remark',
31 | key: 'remark',
32 | width: "150px",
33 | align: 'center'
34 |
35 | }, {
36 | title: '操作',
37 | key: 'action',
38 | width: "50px",
39 | align: 'center',
40 | render: (text, record) => {
41 | return (
42 |
43 |
44 |
45 | )
46 | }
47 | }];
48 |
49 | export class LogList extends Component {
50 |
51 | constructor(props) {
52 | super(props)
53 | }
54 |
55 | render() {
56 | let pagenationObj = {
57 | pageSize: 5,
58 | total: this.props.logs.length,
59 | showTotal: (total, range) => `${range[0]}-${range[1]} of ${total} items`
60 |
61 | };
62 |
63 |
64 | return (
65 |
69 |
70 | )
71 | }
72 | }
--------------------------------------------------------------------------------
/src/common/css/border.less:
--------------------------------------------------------------------------------
1 | .border-top(@color: #dcdcdc, @pseudo: before) {
2 | position: relative;
3 | @pseudo-selector: ~":@{pseudo}";
4 |
5 | &@{pseudo-selector} {
6 | content: " ";
7 | position: absolute;
8 | left: 0;
9 | top: 0;
10 | width: 100%;
11 | height: 1px;
12 | border-top: 1px solid @color;
13 | -webkit-transform-origin: 0 0;
14 | transform-origin: 0 0;
15 | -webkit-transform: scaleY(0.5);
16 | transform: scaleY(0.5);
17 | }
18 | }
19 |
20 | .border-bottom(@color: #dcdcdc, @pseudo: after) {
21 | position: relative;
22 | @pseudo-selector: ~":@{pseudo}";
23 |
24 | &@{pseudo-selector} {
25 | content: " ";
26 | position: absolute;
27 | left: 0;
28 | bottom: 0;
29 | width: 100%;
30 | height: 1px;
31 | border-bottom: 1px solid @color;
32 | -webkit-transform-origin: 0 100%;
33 | transform-origin: 0 100%;
34 | -webkit-transform: scaleY(0.5);
35 | transform: scaleY(0.5);
36 | }
37 | }
38 |
39 | .border-left(@color: #dcdcdc, @pseudo: before) {
40 | position: relative;
41 | @pseudo-selector: ~":@{pseudo}";
42 |
43 | &@{pseudo-selector} {
44 | content: " ";
45 | position: absolute;
46 | left: 0;
47 | top: 0;
48 | width: 1px;
49 | height: 100%;
50 | border-left: 1px solid @color;
51 | -webkit-transform-origin: 0 0;
52 | transform-origin: 0 0;
53 | -webkit-transform: scaleX(0.5);
54 | transform: scaleX(0.5);
55 | }
56 | }
57 |
58 | .border-right(@color: #dcdcdc, @pseudo: after) {
59 | position: relative;
60 | @pseudo-selector: ~":@{pseudo}";
61 |
62 | &@{pseudo-selector} {
63 | content: " ";
64 | position: absolute;
65 | top: 0;
66 | right: 0;
67 | width: 1px;
68 | height: 100%;
69 | border-right: 1px solid @color;
70 | -webkit-transform-origin: 100% 0;
71 | transform-origin: 100% 0;
72 | -webkit-transform: scaleX(0.5);
73 | transform: scaleX(0.5);
74 | }
75 | }
--------------------------------------------------------------------------------
/src/view/user_manager/user_list/enable_user.js:
--------------------------------------------------------------------------------
1 | import { Popover, Button, Icon, Divider, notification } from 'antd';
2 | import React from "react";
3 | import { UserEnableModel } from "../../../models/user_manage_models";
4 |
5 | const userEnableModelInstance = UserEnableModel.getInstance();
6 | import { eventBus } from './events';
7 |
8 | export default class App extends React.Component {
9 |
10 | state = {
11 | visible: false,
12 | }
13 |
14 | hide = () => {
15 | this.setState({
16 | visible: false,
17 | });
18 | }
19 |
20 | handleVisibleChange = (visible) => {
21 | this.setState({ visible });
22 | }
23 |
24 | change = () => {
25 | const { id, enable } = this.props;
26 | userEnableModelInstance.setParam({
27 | id,
28 | enable: enable === 1 ? 0 : 1
29 | }, true);
30 |
31 | userEnableModelInstance.excute(res => {
32 |
33 | notification.open({
34 | message: '切换成功',
35 | description: res["msg"]
36 | });
37 | eventBus.emit("findAll");
38 |
39 | }, err => {
40 | notification.open({
41 | message: '切换失败',
42 | description: err["msg"]
43 | });
44 | })
45 |
46 | }
47 |
48 |
49 | render() {
50 |
51 | const content = (
52 |
53 |
确定需要更改用户状态吗?
54 |
55 |
取消
56 |
57 |
改变
58 |
59 |
60 |
61 |
62 | );
63 | return (
64 |
70 | 启禁用
71 |
72 | );
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/bin/www:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | /**
4 | * Module dependencies.
5 | */
6 |
7 | var app = require('../app');
8 | var debug = require('debug')('ngr_webadmin:server');
9 | var http = require('http');
10 |
11 | /**
12 | * Get port from environment and store in Express.
13 | */
14 |
15 | var port = normalizePort('3000');
16 | app.set('port', port);
17 |
18 | /**
19 | * Create HTTP server.
20 | */
21 |
22 | var server = http.createServer(app);
23 |
24 | /**
25 | * Listen on provided port, on all network interfaces.
26 | */
27 |
28 | server.listen(port);
29 | server.on('error', onError);
30 | server.on('listening', onListening);
31 |
32 | /**
33 | * Normalize a port into a number, string, or false.
34 | */
35 |
36 | function normalizePort(val) {
37 | var port = parseInt(val, 10);
38 |
39 | if (isNaN(port)) {
40 | // named pipe
41 | return val;
42 | }
43 |
44 | if (port >= 0) {
45 | // port number
46 | return port;
47 | }
48 |
49 | return false;
50 | }
51 |
52 | /**
53 | * Event listener for HTTP server "error" event.
54 | */
55 |
56 | function onError(error) {
57 | if (error.syscall !== 'listen') {
58 | throw error;
59 | }
60 |
61 | var bind = typeof port === 'string'
62 | ? 'Pipe ' + port
63 | : 'Port ' + port;
64 |
65 | // handle specific listen errors with friendly messages
66 | switch (error.code) {
67 | case 'EACCES':
68 | console.error(bind + ' requires elevated privileges');
69 | process.exit(1);
70 | break;
71 | case 'EADDRINUSE':
72 | console.error(bind + ' is already in use');
73 | process.exit(1);
74 | break;
75 | default:
76 | throw error;
77 | }
78 | }
79 |
80 | /**
81 | * Event listener for HTTP server "listening" event.
82 | */
83 |
84 | function onListening() {
85 | var addr = server.address();
86 | var bind = typeof addr === 'string'
87 | ? 'pipe ' + addr
88 | : 'port ' + addr.port;
89 | debug('Listening on ' + bind);
90 | }
91 |
--------------------------------------------------------------------------------
/src/view/user_manager/operation_log/log_pane.js:
--------------------------------------------------------------------------------
1 | // log 详情面板
2 |
3 | import { Button, Modal, Card } from 'antd';
4 | import React, { Component } from 'react';
5 |
6 | import JSONInput from 'react-json-editor-ajrm';
7 | import locale from 'react-json-editor-ajrm/locale/zh-cn';
8 |
9 | export class LogPane extends React.Component {
10 | state = { visible: false }
11 |
12 | showModal = () => {
13 | this.setState({
14 | visible: true,
15 | });
16 | }
17 |
18 | hide = () => {
19 | this.setState({
20 | visible: false,
21 | });
22 | }
23 |
24 |
25 | handleCancel = (e) => {
26 | this.setState({
27 | visible: false,
28 | });
29 | }
30 |
31 | render() {
32 |
33 | const { log } = this.props;
34 |
35 |
36 | return (
37 |
38 |
39 | 查看详情
40 |
41 | {
42 | this.state.visible ?
52 |
53 |
54 | remark : {log.remark}
55 | username : {log.username}
56 | module_desc : {log.module_desc}
57 | operation_desc : {log.operation_desc}
58 | in_param :
59 |
67 |
68 |
69 | : ""
70 | }
71 |
72 |
73 | );
74 | }
75 | }
76 |
77 |
78 |
--------------------------------------------------------------------------------
/src/view/user_manager/user_list/search_bar.js:
--------------------------------------------------------------------------------
1 | import {
2 | Form, Row, Col, Input, Button, Icon
3 | } from 'antd';
4 | import React, { Component } from 'react'
5 | import { UserInfoEdit } from "./edit_user";
6 |
7 | class SearchForm extends React.Component {
8 | constructor(props) {
9 | super(props)
10 | }
11 |
12 | handleSearch = (e) => {
13 | e.preventDefault();
14 | this.props.form.validateFields((err, values) => {
15 | if (!err) {
16 | this.props.search(
17 | values["userName"],
18 | values["phoneNumber"])
19 | }
20 | });
21 | }
22 |
23 | handleReset = () => {
24 | this.props.form.resetFields();
25 | }
26 |
27 | render() {
28 | const { getFieldDecorator } = this.props.form;
29 | return (
30 |
61 | );
62 | }
63 | }
64 |
65 | const WrappedSearchForm = Form.create({ name: 'user_search_form' })(SearchForm);
66 |
67 | export default WrappedSearchForm;
--------------------------------------------------------------------------------
/src/view/router_manager/gateway_manage/gateway_list.js:
--------------------------------------------------------------------------------
1 | import {Button, Table,Spin} from 'antd';
2 | import React, { Component } from 'react';
3 |
4 | import { SetLimit } from "./edit_gateway";
5 | import { AddGateway } from "./add_gateway";
6 |
7 | const genCol = () => [
8 | {
9 | title: '序号',
10 | dataIndex: 'index',
11 | key: 'index',
12 | width: "50px",
13 | align: 'center'
14 | }, {
15 | title: '网关编码',
16 | dataIndex: 'gateway_code',
17 | key: 'gateway_code',
18 | width: "150px",
19 | align: 'center'
20 |
21 | }, {
22 | title: '网关描述',
23 | dataIndex: 'gateway_desc',
24 | key: 'gateway_desc',
25 | width: "150px",
26 | align: 'center'
27 |
28 | }, {
29 | title: 'QPS限流阈值(单机)',
30 | dataIndex: 'limit_count',
31 | key: 'limit_count',
32 | width: "150px",
33 | align: 'center'
34 |
35 | }, {
36 | title: '操作',
37 | key: 'action',
38 | width: "50px",
39 | align: 'center',
40 | render: (text, record) => {
41 | return (
42 |
43 |
44 |
45 | )
46 | }
47 | }];
48 |
49 | export class GateWayList extends Component {
50 |
51 | constructor(props) {
52 | super(props)
53 | }
54 |
55 | render() {
56 | let pagenationObj = {
57 | pageSize: 5,
58 | total: this.props.gateWayList ? this.props.gateWayList.length : 0,
59 | showTotal: (total, range) => `${range[0]}-${range[1]} of ${total} items`
60 |
61 | };
62 | return (
63 |
64 |
65 |
66 | (网关列表 )}
72 | dataSource={this.props.gateWayList} />
73 |
74 |
75 |
76 | )
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/src/models/gray_divide_models.js:
--------------------------------------------------------------------------------
1 | import BaseModel from '../core/model.base'
2 |
3 |
4 | export class GrayDivideQueryModel extends BaseModel {
5 | constructor(props){
6 | super(props);
7 | this.url = 'gray_divide/query';
8 | this.method = 'GET';
9 | }
10 | }
11 |
12 |
13 | export class GrayDivideAdddModel extends BaseModel {
14 | constructor(props){
15 | super(props);
16 | this.url = 'gray_divide/add';
17 | //this.contentType ="application/json";
18 |
19 | this.method = 'POST';
20 | }
21 | }
22 |
23 |
24 | export class GrayDivideUpdateModel extends BaseModel {
25 | constructor(props){
26 | super(props);
27 | this.url = 'gray_divide/update';
28 | this.method = 'POST';
29 | }
30 | }
31 |
32 | export class GrayDivideDeleteModel extends BaseModel {
33 | constructor(props){
34 | super(props);
35 | this.url = 'gray_divide/delete';
36 | this.method = 'POST';
37 | }
38 | }
39 |
40 |
41 | export class GrayDivideEnableModel extends BaseModel {
42 | constructor(props){
43 | super(props);
44 | this.url = 'gray_divide/enable';
45 | this.method = 'POST';
46 | }
47 | }
48 |
49 |
50 | export class TargetsQueryModel extends BaseModel {
51 | constructor(props){
52 | super(props);
53 | this.url = 'api_router/query_target';
54 | this.method = 'GET';
55 | }
56 | }
57 |
58 | export class ConditionAddModel extends BaseModel{
59 | constructor(props){
60 | super(props);
61 | this.url = 'selector_condition/add';
62 | this.method = 'POST';
63 | }
64 | }
65 |
66 | export class ConditionUpdateModel extends BaseModel{
67 | constructor(props){
68 | super(props);
69 | this.url = 'selector_condition/update';
70 | this.method = 'POST';
71 | }
72 | }
73 |
74 | export class ConditionDeleteModel extends BaseModel{
75 | constructor(props){
76 | super(props);
77 | this.url = 'selector_condition/delete_by_id';
78 | this.method = 'POST';
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/src/ui/ui.modal.js:
--------------------------------------------------------------------------------
1 | import React, {Component} from 'react'
2 | import { Modal} from 'antd'
3 |
4 |
5 | class WkModal extends Component {
6 |
7 | constructor(props) {
8 |
9 | super(props);
10 |
11 | this.state = {
12 | title:'',
13 | cancelText:'',
14 | okText:'',
15 | visible:false,
16 | width:'',
17 | className:'',
18 | content:'',
19 | maskClosable:false,
20 | destroyOnClose:true,
21 | iconType:'none',
22 | onOk:function(){},
23 | onCancel:this.hide.bind(this)
24 | }
25 |
26 | }
27 |
28 |
29 | show(config){
30 | this.setState({
31 | title:config.title || '',
32 | cancelText:config.cancelText || '',
33 | okText:config.okText || '',
34 | visible:true,
35 | width:config.width || '',
36 | className:config.className || '',
37 | content:config.content || '',
38 | maskClosable:config.maskClosable || false,
39 | destroyOnClose:config.destroyOnClose || true,
40 | onOk:config.onOk || function(){},
41 | onCancel:config.onCancel || this.hide.bind(this)
42 | });
43 |
44 | this.renderContent = config.content || function(){};
45 | }
46 |
47 | hide(){
48 | this.setState({
49 | visible:false
50 | })
51 |
52 | }
53 |
54 | render() {
55 | const {
56 | title,
57 | cancelText,
58 | okText,
59 | visible,
60 | onOk,
61 | onCancel,
62 | width,
63 | className,
64 | content,
65 | maskClosable,
66 | destroyOnClose,
67 | iconType
68 |
69 | } = this.state;
70 |
71 | return (
72 |
86 |
87 | {this.renderContent && this.renderContent()}
88 |
89 |
90 | );
91 |
92 | }
93 |
94 | }
95 |
96 | export default WkModal;
--------------------------------------------------------------------------------
/src/core/nav.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react";
2 |
3 | import { Popover, Button, Avatar, Modal } from 'antd';
4 |
5 | import { ModifyPassWordForm } from "./change_password";
6 | import {UserInfoStore} from "../store/business.store";
7 |
8 | const userInfo = UserInfoStore.getInstance().getData();
9 |
10 |
11 | const logout = () => {
12 | setTimeout(() => {
13 | localStorage.clear();
14 | location.href = '/login';
15 | }, 1000);
16 | }
17 |
18 |
19 | class ModifyPassWord extends React.Component {
20 | state = { visible: false }
21 |
22 | show = () => {
23 | this.setState({
24 | visible: true,
25 | });
26 | }
27 |
28 | hide = () => {
29 | this.setState({
30 | visible: false,
31 | });
32 | }
33 |
34 |
35 |
36 | submit = (e) => {
37 | console.log("e")
38 | }
39 |
40 |
41 |
42 | render() {
43 | return (
44 |
45 |
46 | 修改密码
47 |
48 |
56 |
57 |
58 |
59 | );
60 | }
61 | }
62 |
63 | const content = (
64 |
65 |
66 | 退出
67 |
68 | );
69 |
70 | export default class Nav extends Component {
71 | render() {
72 |
73 | const navStyle = { position: "relative", height: 50, display: "flex", alignItems: "center"}
74 | const avatarStyle = { position: "absolute", right: 20 }
75 |
76 | return (
77 |
78 |
79 |
80 | 欢迎您, {(userInfo && userInfo.userName) ? userInfo.userName : ""}
81 |
82 |
83 |
84 |
85 |
86 |
87 | );
88 | }
89 |
90 | }
91 |
--------------------------------------------------------------------------------
/src/view/user_manager/user_list/user_list.js:
--------------------------------------------------------------------------------
1 | import { Table, Divider, Tag, Pagination, Button, Popover } from 'antd';
2 | import React, { Component } from 'react';
3 | import { UserInfoEdit } from "./edit_user";
4 | import Enable from "./enable_user";
5 |
6 |
7 | const genCol = (trigger) => [
8 | {
9 | title: '序号',
10 | dataIndex: 'index',
11 | key: 'index',
12 | width: "50px",
13 | align: 'center'
14 | }, {
15 | title: '用户名',
16 | dataIndex: 'username',
17 | key: 'username',
18 | width: "150px",
19 | align: 'center'
20 |
21 | }, {
22 | title: '手机',
23 | dataIndex: 'mobile',
24 | key: 'mobile',
25 | width: "150px",
26 | align: 'center'
27 |
28 | }, {
29 | title: '邮箱',
30 | dataIndex: 'email',
31 | key: 'email',
32 | width: "150px",
33 | align: 'center'
34 |
35 | }, {
36 | title: '当前状态',
37 | key: 'enable',
38 | width: "100px",
39 | align: 'center',
40 | render(text, record) {
41 | const { enable } = record;
42 |
43 | return (
44 | {enable === 1 ? "启用" : "禁用"}
45 | )
46 | }
47 |
48 | }, {
49 | title: '操作',
50 | key: 'action',
51 | align: 'center',
52 | render: (text, record) => (
53 |
59 | ),
60 | }];
61 |
62 |
63 |
64 |
65 | export class UserList extends Component {
66 |
67 | render() {
68 | let pagenationObj = {
69 | pageSize: 10,
70 | total: this.props.userInfos.length,
71 | showTotal: (total, range) => `${range[0]}-${range[1]} of ${total} items`
72 |
73 | };
74 |
75 | const trigger = this.props.modifyUser;
76 | return (
77 |
78 |
79 |
(用户信息表 )}
86 | />
87 |
88 |
89 | )
90 | }
91 | }
--------------------------------------------------------------------------------
/src/core/view.base.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { render } from 'react-dom'
3 | import { Layout, Avatar, Icon } from 'antd'
4 |
5 | // import 'antd/dist/antd.min.css';
6 | import '../common/less/private.less';
7 |
8 | import { parseQuery } from '../util/util';
9 |
10 | import SideMenu from '../ui/sidermenu'
11 | import Nav from "./nav"
12 |
13 | const { Content, Sider } = Layout;
14 |
15 | class BaseView extends Component {
16 |
17 | constructor(props) {
18 |
19 | super(props);
20 |
21 | this.urlQuery = parseQuery(window.location.search);
22 |
23 | this.state = {
24 | pluginStatus: {},
25 | isMini:false
26 | }
27 | }
28 |
29 | updateSlider(pluginStatus) {
30 |
31 | this.setState({
32 | pluginStatus: pluginStatus
33 | });
34 |
35 | }
36 | isminiFn(){
37 | this.setState({
38 | isMini:!this.state.isMini
39 | })
40 | }
41 | renderSider() {
42 |
43 | return (
44 |
45 |
{!this.state.isMini? : }
46 |
47 |
48 |
49 |
50 | );
51 |
52 | }
53 |
54 | renderMain() {
55 |
56 | return ('');
57 |
58 | }
59 |
60 | render() {
61 |
62 | return (
63 | { this.viewContainer = viewContainer }}>
64 |
65 | {this.renderSider()}
66 |
67 |
68 | {this.renderMain()}
69 | Copyright @ 2019-2020 GoGo Easy Team
70 |
71 |
72 |
73 | );
74 | }
75 | }
76 |
77 |
78 | export default BaseView;
79 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ngr_webadmin",
3 | "version": "1.0.0",
4 | "private": true,
5 | "scripts": {
6 | "start": "cross-env NODE_ENV=development webpack-dev-server --config build/webpack.dev.conf.js",
7 | "build": "cross-env NODE_ENV=production webpack --config build/webpack.prod.conf.js --progress",
8 | "analyz": "cross-env analyz=true npm run build",
9 | "express_start": "node ./bin/www",
10 | "pm2_start": "cross-env NODE_ENV=production pm2 start pm2.json"
11 | },
12 | "devDependencies": {
13 | "@babel/core": "^7.1.2",
14 | "@babel/plugin-proposal-class-properties": "^7.1.0",
15 | "@babel/plugin-proposal-function-bind": "^7.0.0",
16 | "@babel/preset-env": "^7.1.0",
17 | "@babel/preset-react": "^7.0.0",
18 | "@electerm/antd-dark-theme": "^0.0.4",
19 | "@ice-point/webpack-server-qrcode": "^1.0.0",
20 | "autoprefixer": "^9.1.5",
21 | "babel-loader": "^8.0.4",
22 | "babel-plugin-import": "^1.11.0",
23 | "clean-webpack-plugin": "^0.1.19",
24 | "copy-webpack-plugin": "^4.5.2",
25 | "cross-env": "^5.2.0",
26 | "css-loader": "^1.0.0",
27 | "file-loader": "^3.0.1",
28 | "html-webpack-plugin": "^3.2.0",
29 | "less": "^3.8.1",
30 | "less-loader": "^4.1.0",
31 | "mini-css-extract-plugin": "^0.4.4",
32 | "optimize-css-assets-webpack-plugin": "^5.0.1",
33 | "postcss-adaptive": "^0.5.0",
34 | "postcss-flexbugs-fixes": "^4.1.0",
35 | "postcss-loader": "^3.0.0",
36 | "postcss-preset-env": "^6.4.0",
37 | "react-json-editor-ajrm": "^2.5.9",
38 | "style-loader": "^0.23.0",
39 | "url-loader": "^1.1.2",
40 | "webpack": "^4.20.2",
41 | "webpack-bundle-analyzer": "^3.0.3",
42 | "webpack-cli": "^3.1.1",
43 | "webpack-dev-server": "^3.1.9",
44 | "webpack-merge": "^4.1.4"
45 | },
46 | "dependencies": {
47 | "@ant-design/dark-theme": "^1.0.3",
48 | "antd": "^3.4.3",
49 | "axios": "^0.18.0",
50 | "body-parser": "~1.17.1",
51 | "classnames": "^2.2.6",
52 | "cookie-parser": "~1.4.3",
53 | "ejs": "~2.5.6",
54 | "express": "^4.16.4",
55 | "history": "^4.2.0",
56 | "jquery": "^3.3.1",
57 | "moment": "^2.24.0",
58 | "pm2": "^3.5.1",
59 | "react": "^16.8.5",
60 | "react-dom": "^16.8.5",
61 | "react-json-editor-ajrm": "^2.5.9",
62 | "react-loadable": "^5.5.0",
63 | "react-router-dom": "^4.3.1"
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/src/view/user_manager/operation_log/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import BaseView from '../../../core/view.base'
3 |
4 | import SearchBar from "./search_bar";
5 | import { LogList } from "./log_list";
6 |
7 | import { LogListModel } from '../../../models/logs.model';
8 |
9 | import { eventBus } from './events';
10 | import moment from 'moment';
11 | import 'moment/locale/zh-cn';
12 | moment.locale('zh-cn');
13 | import { notification } from 'antd';
14 |
15 | const logListModelInstance = LogListModel.getInstance();
16 |
17 | class LogView extends BaseView {
18 |
19 | constructor(props) {
20 | super(props);
21 | this.state = {
22 | logs: []
23 | }
24 |
25 | this.searchLogs = this.searchLogs.bind(this);
26 | }
27 |
28 | componentWillMount() {
29 | eventBus.on("search", this.searchLogs);
30 | }
31 |
32 | componentDidMount() {
33 | // 初始化数据
34 |
35 | setTimeout(() => {
36 | const now = moment();
37 | const lastFiveDays = moment().subtract(10, 'days');
38 | eventBus.emit("search", {
39 | "range_time": [lastFiveDays, now]
40 | }, true);
41 | }, 200);
42 |
43 | }
44 |
45 | componentWillUnmount() {
46 | eventBus.off("search");
47 | }
48 |
49 |
50 |
51 |
52 | searchLogs(values) {
53 |
54 | const username = values["user_name"];
55 | const operationType = values["operation_type"];
56 | const rangeTimeValue = values['range_time'];
57 | let start_time, end_time;
58 | if (rangeTimeValue) {
59 | start_time = rangeTimeValue[0].format('YYYY-MM-DD HH:mm:ss').toString();
60 | end_time = rangeTimeValue[1].format('YYYY-MM-DD HH:mm:ss').toString();
61 | }
62 | logListModelInstance.setParam({
63 | start_time,
64 | end_time,
65 | username
66 | }, true);
67 | logListModelInstance.excute(res => {
68 | res.data.forEach((element, index) => {
69 | element.index = index + 1
70 | });
71 | this.setState({
72 | logs: res.data
73 | })
74 | }, err => {
75 | notification.open({
76 | message: '查询失败',
77 | description: err["msg"]
78 | });
79 | })
80 | }
81 |
82 |
83 |
84 |
85 | renderMain() {
86 | return (
87 |
88 |
89 |
90 |
91 |
92 |
93 | )
94 | }
95 | }
96 |
97 | export default LogView;
--------------------------------------------------------------------------------
/src/models/anti_sql_injection_models.js:
--------------------------------------------------------------------------------
1 | import BaseModel from '../core/model.base'
2 |
3 | /*
4 | sql防控列表
5 | */
6 | export class AntiSqlInjectionListModel extends BaseModel {
7 |
8 | constructor(props) {
9 |
10 | super(props);
11 |
12 | this.url = 'anti_sql_injection/query';
13 | this.method = 'GET';
14 |
15 | }
16 |
17 | }
18 |
19 | /*
20 | sql防控创建
21 | */
22 | export class AntiSqlInjectionCreateModel extends BaseModel {
23 |
24 | constructor(props) {
25 |
26 | super(props);
27 |
28 | this.url = 'anti_sql_injection/create';
29 | this.method = 'POST';
30 |
31 | }
32 |
33 | }
34 |
35 | /*
36 | sql 防控更新
37 | */
38 | export class AntiSqlInjectionUpdateModel extends BaseModel {
39 |
40 | constructor(props) {
41 |
42 | super(props);
43 |
44 | this.url = 'anti_sql_injection/update';
45 | this.method = 'POST';
46 |
47 | }
48 |
49 | }
50 |
51 |
52 |
53 | /*
54 | sql 防控删除
55 | */
56 | export class AntiSqlInjectionDeleteModel extends BaseModel {
57 |
58 | constructor(props) {
59 |
60 | super(props);
61 |
62 | this.url = 'anti_sql_injection/delete';
63 |
64 | }
65 |
66 | }
67 |
68 | /*
69 | sql 防控启禁用
70 | */
71 | export class AntiSqlInjectionUpdateEnableModel extends BaseModel {
72 |
73 | constructor(props) {
74 |
75 | super(props);
76 |
77 | this.url = 'anti_sql_injection/update_enable';
78 |
79 | }
80 |
81 | }
82 |
83 | /*
84 | 防控参数添加
85 | */
86 | export class ASICOParameterCreateModel extends BaseModel {
87 |
88 | constructor(props) {
89 |
90 | super(props);
91 |
92 | this.url = 'anti_sql_injection/co_parameter/create';
93 |
94 | }
95 |
96 | }
97 |
98 | /*
99 | 防控参数修改
100 |
101 | */
102 | export class ASICOParameterUpdateModel extends BaseModel {
103 |
104 | constructor(props) {
105 |
106 | super(props);
107 |
108 | this.url = 'anti_sql_injection/co_parameter/update';
109 |
110 | }
111 |
112 | }
113 |
114 | /*
115 | 防控参数删除
116 | */
117 | export class ASICOParameterDeleteModel extends BaseModel {
118 |
119 | constructor(props) {
120 |
121 | super(props);
122 |
123 | this.url = 'anti_sql_injection/co_parameter/delete';
124 |
125 | }
126 |
127 | }
128 |
129 | // 删除特征信息接口
130 | export class ASICOParameterQueryModel extends BaseModel {
131 |
132 | constructor(props) {
133 |
134 | super(props);
135 |
136 | this.url = 'anti_sql_injection/co_parameter/query';
137 |
138 | }
139 |
140 | }
141 |
--------------------------------------------------------------------------------
/src/models/proprety_ratelimit_models.js:
--------------------------------------------------------------------------------
1 | import BaseModel from '../core/model.base'
2 | /*
3 | 特征限速列表
4 | */
5 | export class PropretyListModel extends BaseModel {
6 |
7 | constructor(props) {
8 |
9 | super(props);
10 |
11 | this.url = 'property_rate_limit/query';
12 | this.method = 'GET';
13 |
14 | }
15 |
16 | }
17 |
18 | /*
19 | 查询被阻止访问的特征列表
20 | */
21 | export class BlockedListModel extends BaseModel {
22 |
23 | constructor(props) {
24 |
25 | super(props);
26 |
27 | this.url = 'property_rate_limit/blocked/list';
28 | this.method = 'GET';
29 |
30 | }
31 |
32 | }
33 |
34 | /*
35 | 查询被限速访问的特征列表
36 | */
37 | export class LimitListModel extends BaseModel {
38 |
39 | constructor(props) {
40 |
41 | super(props);
42 |
43 | this.url = 'property_rate_limit/limit/list';
44 | this.method = 'GET';
45 |
46 | }
47 |
48 | }
49 |
50 |
51 |
52 | /*
53 | 添加特征限速
54 | */
55 | export class AddPropretyLimitModel extends BaseModel {
56 |
57 | constructor(props) {
58 |
59 | super(props);
60 |
61 | this.url = 'property_rate_limit/add';
62 |
63 | }
64 |
65 | }
66 |
67 | /*
68 | 删除特征限速
69 | */
70 | export class DelPropretyLimitModel extends BaseModel {
71 |
72 | constructor(props) {
73 |
74 | super(props);
75 |
76 | this.url = 'property_rate_limit/delete';
77 |
78 | }
79 |
80 | }
81 |
82 | /*
83 | 编辑特征限速
84 |
85 | */
86 | export class EditPropretyLimitModel extends BaseModel {
87 |
88 | constructor(props) {
89 |
90 | super(props);
91 |
92 | this.url = 'property_rate_limit/update';
93 |
94 | }
95 |
96 | }
97 |
98 | /*
99 | 启禁用特征防刷
100 | */
101 | export class UpdateEnableModel extends BaseModel {
102 |
103 | constructor(props) {
104 |
105 | super(props);
106 |
107 | this.url = 'property_rate_limit/update_enable';
108 |
109 | }
110 |
111 | }
112 |
113 | // 删除特征信息接口
114 | export class DeleteRespTemplate extends BaseModel {
115 |
116 | constructor(props) {
117 |
118 | super(props);
119 |
120 | this.url = 'property_rate_limit/delete_detail';
121 |
122 | }
123 |
124 | }
125 |
126 | // 增加特征信息接口
127 | export class AddRespTemplate extends BaseModel {
128 |
129 | constructor(props) {
130 |
131 | super(props);
132 |
133 | this.url = 'property_rate_limit/add_detail'
134 |
135 | }
136 |
137 | }
138 |
139 | // 编辑特征信息接口
140 | export class EditRespTemplate extends BaseModel {
141 |
142 | constructor(props) {
143 |
144 | super(props);
145 |
146 | this.url = 'property_rate_limit/update_detail'
147 |
148 | }
149 |
150 | }
151 |
--------------------------------------------------------------------------------
/src/common/css/iconfont.css:
--------------------------------------------------------------------------------
1 | @font-face {font-family: "iconfont";
2 | src:url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAVQAAsAAAAACmgAAAUBAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHEIGVgCDcgqHEIVpATYCJAMgCxIABCAFhG0HaRviCBEVpLuR/TywYzqG05qr6/FKzwXX5+CBx017Pwk0JUADdZuZADOx9Jx1JlWlZyLImabnDrgDsJEbABbOOcAY9cJ0JGfRow9cWv7g4H+OmS6twfLb1i4Z8Xy0N0oaD2hgGddUbTfQO5ET83ugFybw8tUQgI0seiAmTJrhQUXBWSQAsXXThlWoJQtKD1mg6q0tO5UccQ4LquSXXgbOxr8vvqGfUEHCIuNca876iWsZY9LZJhtRg6XyNdSu5wKkfSADPQAFxDWtnqPIaaYHMjbjrxTmAHZUJBRTMt1mX/O7zrZo1MR0lRROpcCOjtvlBQULMUj/8KwIZBBWUGNn7M6sCiY+EwqYggkLmBITMWC61NS1bpTk2r5owora79CF4NqlaEfeYTegA04QGoiNOLgBiNmjIK7oy0Viapw7Rc+zud12NbZ4Q8i7pCPSv7JWhMO+YNBjVKfeEVpS2pzY3PVyYjtGVcKlhLaCS022Z8N5jZFlCZ4B3yInFPJGIgsawnuMhJWXWgKVyQt8Vyo6WhNXXT6ktzyaJl8OeTHaC7XWjmQRiSRLZQ+vL20Ho63AdiUgREmHpsVnttlOtkE4nCQqHyvWRO1jgx3Nj2SiBaeUyqWgR64IFNma2sCxpTRx0eWytpYkn+dSU6AiecnVDSXJa642t5cleLyXy9uaklZfORL83BM2fZHOBaEvti/StCUlwrao9bZb1/9ko/1I+MWCpvvHlAV94Uj3jseyr4Z9TwYKWoJD7r89o+odb+iTBQ9FsiufLFiw9dpt174bk99zmuXYLLt83e13XCe5wlCYBu2zjllaHvm7JY7r716k+zctL46/u3qTX1909/WOOZMmWayTpj5lLTjZevrmI66yuUnKxG+X3ilYyGdRJiZcnUfTCWdZ7D9lRr7q//EzDPvVfKP8NYzPWbb9x0Dci2vN9Wfze+sTeJQJcb3P5Jvr174YF6h+3jVRL3JNYPePHleR7tUfi5voerR+vryzBeamteVxgR+/mEEA7ccAM75YA1FO33SmoJZ1riKNaIHT/0+sX+1nlG2Z/rHM6KdyPyw+y/gx4rASzMv6wBK0ZGUFKz/IygsqHyh5ebnx9sWiv+afLjMK35nj9Sbp3lwPQ43HxnUf7FmV4XKXJLRW7i/hyDfzxvQZnTy972TX33N2TkjbPvXf/FfHZ9wdfvaNs3uXj5rRb0Xxwj0cAyBqyM2yASA1S1V8qwblhLwMQM6QCgGkFqkq+lt0la3yH3ZTAeuT/nxqRdyI31RNAeDt9rdDsWQc8oSGH03LaOkfaVL+7UH8rTDljlVVdKnf/G8uwUj0N1jL6mGzlATY3Db4FwNuPlxTm7ijJPSpJOYgEUMmyKgUIBVsD7CgMQCsqIwGG92ZvF8jnt3ICCUW6Mb1AAKd20DCyWMgo/MKUsF+ABaS+A6s6EIGGz4Rf0aNIoGrxSlAJhhSf6o1D9azJ5HU31HmToaiPKa8EWIdp32vPtX8DQPCGmvipzykZMkG7ukK10PXMbnADXTaVCm547b3+rYX2mjuR4JTgEwwpP5Uax5sODiJ2s/fUeZOhp6GhuAbIdaj036zG4G8aYZRDc8yPn7KQ0qW59nAPV3Rhc7NMLn2aQ102lQzUu64pVZ2rGazv66/00NIj/P6K4LEFM2wnMRa6WsZqzpiZvg7zP745pUMgb9FdiLZ3MGm2VeGYe4qHjAaAQAAAA==') format('woff2'); /* iOS 4.1- */
3 | }
4 |
5 | .iconfont {
6 | font-family: "iconfont" !important;
7 | font-size: 16px;
8 | font-style: normal;
9 | -webkit-font-smoothing: antialiased;
10 | -moz-osx-font-smoothing: grayscale;
11 | }
12 |
13 | .icon-daqiashise:before {
14 | content: "\e6ed";
15 | }
16 |
17 | .icon-down:before {
18 | content: "\e611";
19 | }
20 |
21 | .icon-right:before {
22 | content: "\e612";
23 | }
24 |
25 | .icon-yewu:before {
26 | content: "\e7a8";
27 | }
28 |
29 | .icon-arrow-up:before {
30 | content: "\e600";
31 | }
32 |
33 | .icon-arrow-left:before {
34 | content: "\e601";
35 | }
36 |
37 | .icon-warn:before {
38 | content: "\e631";
39 | }
40 |
41 | .icon-phone:before {
42 | content: "\e602";
43 | }
44 |
45 |
--------------------------------------------------------------------------------
/src/view/user_manager/operation_log/search_bar.js:
--------------------------------------------------------------------------------
1 | import {
2 | Form, Row, Col, Input, Button, Icon
3 | } from 'antd';
4 | import React, { Component } from 'react'
5 | import { DatePicker } from 'antd';
6 | import locale from 'antd/lib/date-picker/locale/zh_CN';
7 | import moment from 'moment';
8 | import 'moment/locale/zh-cn';
9 | moment.locale('zh-cn');
10 | const { RangePicker } = DatePicker;
11 |
12 | import { eventBus } from './events';
13 |
14 | class SearchForm extends React.Component {
15 | constructor(props) {
16 | super(props)
17 | }
18 |
19 | handleSearch = (e) => {
20 | e.preventDefault();
21 | this.props.form.validateFields((err, values) => {
22 | if (!err) {
23 | // 触发事件中心的搜索事件
24 | eventBus.emit("search", values);
25 | }
26 | });
27 | }
28 |
29 |
30 |
31 | handleReset = () => {
32 | this.props.form.resetFields();
33 | }
34 |
35 | render() {
36 | const { getFieldDecorator } = this.props.form;
37 | const now = moment();
38 | const lastFiveDays = moment().subtract(10, 'days');;
39 | const defaultValue = [lastFiveDays, now]
40 | const rangeConfig = {
41 | rules: [{ type: 'array', required: true, message: '时间段不能为空!' }],
42 | initialValue: defaultValue
43 | };
44 |
45 | return (
46 |
83 | );
84 | }
85 | }
86 |
87 | const WrappedSearchForm = Form.create({ name: 'user_log_search_form' })(SearchForm);
88 |
89 | export default WrappedSearchForm;
--------------------------------------------------------------------------------
/src/router.js:
--------------------------------------------------------------------------------
1 | import Login from './view/login';
2 | import loadable from '../src/util/loadable'
3 | const Dashboard = loadable(()=>import('./view/general/dashboard'))
4 |
5 | const UserListView = loadable(()=>import('./view/user_manager/user_list'))
6 | const OperationLog = loadable(()=>import('./view/user_manager/operation_log'))
7 |
8 | const PluginGeneral = loadable(()=>import('./view/plugin_manager/plugin_general'))
9 | const Firewall = loadable(()=>import('./view/plugin_manager/firewall'))
10 |
11 | const FirewallIsolated = loadable(()=>import('./view/plugin_manager/firewall_isolated'))
12 | const PropertyRatelimit = loadable(()=>import('./view/plugin_manager/property_ratelimit'))
13 | const PropertyIsolated = loadable(()=>import('./view/plugin_manager/property_isolated'))
14 | const AntiAqlInjection = loadable(()=>import('./view/plugin_manager/anti_sql_injection'))
15 |
16 | const HostManage = loadable(()=>import('./view/router_manager/host_manage'))
17 | const GatewayManage = loadable(()=>import('./view/router_manager/gateway_manage'))
18 | const RouteGroup = loadable(()=>import('./view/router_manager/route_group'))
19 | const GrayDivide = loadable(()=>import('./view/router_manager/gray_divide'))
20 |
21 | const basePath ='';
22 |
23 |
24 | const Router = [
25 |
26 | {
27 | path:basePath + '/',
28 | page:Dashboard
29 | },
30 | {
31 | path:basePath + '/dashboard',
32 | page:Dashboard
33 | },
34 | {
35 | path:basePath + '/general/dashboard',
36 | page:Dashboard
37 | },
38 | {
39 | path:basePath + '/login',
40 | page:Login
41 | },
42 | {
43 | path:basePath + '/user_manager/user_list',
44 | page:UserListView
45 | },
46 | {
47 | path:basePath + '/user_manager/operation_log',
48 | page:OperationLog
49 | },
50 |
51 | {
52 | path:basePath + '/plugin_manager/plugin_general',
53 | page:PluginGeneral
54 | },
55 | {
56 | path:basePath + '/plugin_manager/firewall',
57 | page:Firewall
58 | },
59 | {
60 | path:basePath + '/plugin_manager/firewall_isolated',
61 | page:FirewallIsolated
62 | },
63 | {
64 | path:basePath + '/plugin_manager/property_ratelimit',
65 | page:PropertyRatelimit
66 | },
67 | {
68 | path:basePath + '/plugin_manager/property_isolated',
69 | page:PropertyIsolated
70 | },
71 | {
72 | path:basePath + '/plugin_manager/anti_sql_injection',
73 | page:AntiAqlInjection
74 | },
75 |
76 | {
77 | path:basePath + '/router_manager/host_manage',
78 | page:HostManage
79 | },
80 | {
81 | path:basePath + '/router_manager/gateway_manage',
82 | page:GatewayManage
83 | },
84 | {
85 | path:basePath + '/router_manager/route_group',
86 | page:RouteGroup
87 | },
88 | {
89 | path:basePath + '/router_manager/gray_divide',
90 | page:GrayDivide
91 | },
92 |
93 | ];
94 |
95 | export default Router;
96 |
--------------------------------------------------------------------------------
/src/models/firewall.models.js:
--------------------------------------------------------------------------------
1 | import BaseModel from '../core/model.base'
2 | /*
3 | 防火墙列表
4 | */
5 | export class FirewallListModel extends BaseModel {
6 |
7 | constructor(props) {
8 |
9 | super(props);
10 |
11 | this.url = 'waf/query_waf' ;
12 | this.method = 'GET';
13 |
14 | }
15 | }
16 |
17 | /*
18 | 删除防火墙
19 | */
20 | export class DelFirewallModel extends BaseModel {
21 |
22 | constructor(props) {
23 |
24 | super(props);
25 |
26 | this.url = 'waf/delete_waf' ;
27 |
28 | }
29 |
30 | }
31 | /*
32 | 新增防火墙
33 |
34 | */
35 | export class AddFirewallModel extends BaseModel {
36 |
37 | constructor(props) {
38 |
39 | super(props);
40 |
41 | this.url = 'waf/create_waf' ;
42 |
43 | }
44 |
45 | }
46 |
47 | /*
48 | 编辑防火墙
49 |
50 | */
51 | export class EditFirewallModel extends BaseModel {
52 |
53 | constructor(props) {
54 |
55 | super(props);
56 |
57 | this.url = 'waf/update_waf' ;
58 |
59 | }
60 |
61 | }
62 |
63 | /*
64 | 启禁用防火墙
65 |
66 | */
67 | export class UpdateEnableModel extends BaseModel {
68 |
69 | constructor(props) {
70 |
71 | super(props);
72 |
73 | this.url = 'waf/update_enable' ;
74 |
75 | }
76 |
77 | }
78 |
79 | /*
80 | 查询防火墙规则列表
81 |
82 | */
83 | export class FwRuleListModel extends BaseModel {
84 |
85 | constructor(props) {
86 |
87 | super(props);
88 |
89 | this.url = 'waf/query_condition' ;
90 | this.method = 'GET';
91 |
92 | }
93 |
94 | }
95 | /*
96 | 新增防火墙规则列表
97 |
98 | */
99 | export class AddFwRuleModel extends BaseModel {
100 |
101 | constructor(props) {
102 |
103 | super(props);
104 |
105 | this.url = 'waf/create_condition';
106 |
107 | }
108 |
109 | }
110 | /*
111 | 编辑防火墙规则列表
112 |
113 | */
114 | export class EditFwRuleModel extends BaseModel {
115 |
116 | constructor(props) {
117 |
118 | super(props);
119 |
120 | this.url = 'waf/updated_condition';
121 |
122 | }
123 |
124 | }
125 | /*
126 | 删除防火墙规则列表 跟据条件ID
127 |
128 | */
129 | export class DelFwRuleModel extends BaseModel {
130 |
131 | constructor(props) {
132 |
133 | super(props);
134 |
135 | this.url = 'waf/delete_condition_by_id' ;
136 |
137 | }
138 |
139 | }
140 | /*
141 | 删除防火墙规则列表 跟据防火墙ID
142 |
143 | */
144 | export class DelFwRuleByFwIdModel extends BaseModel {
145 |
146 | constructor(props) {
147 |
148 | super(props);
149 |
150 | this.url = 'waf/delete_condition_by_waf_id' ;
151 |
152 | }
153 |
154 | }
155 |
156 | /*
157 | 查看某一天防火墙拒绝记录
158 |
159 | */
160 | export class IsolatedByFwModel extends BaseModel {
161 |
162 | constructor(props) {
163 |
164 | super(props);
165 |
166 | this.url = 'waf/query_judge_record' ;
167 | this.method = 'GET';
168 |
169 | }
170 |
171 | }
172 |
173 |
--------------------------------------------------------------------------------
/src/util/extend.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var hasOwn = Object.prototype.hasOwnProperty;
4 | var toStr = Object.prototype.toString;
5 |
6 | var isArray = function isArray(arr) {
7 | if (typeof Array.isArray === 'function') {
8 | return Array.isArray(arr);
9 | }
10 |
11 | return toStr.call(arr) === '[object Array]';
12 | };
13 |
14 | var isPlainObject = function isPlainObject(obj) {
15 | if (!obj || toStr.call(obj) !== '[object Object]') {
16 | return false;
17 | }
18 |
19 | var hasOwnConstructor = hasOwn.call(obj, 'constructor');
20 | var hasIsPrototypeOf = obj.constructor && obj.constructor.prototype && hasOwn.call(obj.constructor.prototype, 'isPrototypeOf');
21 | // Not own constructor property must be Object
22 | if (obj.constructor && !hasOwnConstructor && !hasIsPrototypeOf) {
23 | return false;
24 | }
25 |
26 | // Own properties are enumerated firstly, so to speed up,
27 | // if last one is own, then all properties are own.
28 | var key;
29 | for (key in obj) { /**/ }
30 |
31 | return typeof key === 'undefined' || hasOwn.call(obj, key);
32 | };
33 |
34 | export default function extend() {
35 | var options, name, src, copy, copyIsArray, clone;
36 | var target = arguments[0];
37 | var i = 1;
38 | var length = arguments.length;
39 | var deep = false;
40 |
41 | // Handle a deep copy situation
42 | if (typeof target === 'boolean') {
43 | deep = target;
44 | target = arguments[1] || {};
45 | // skip the boolean and the target
46 | i = 2;
47 | } else if ((typeof target !== 'object' && typeof target !== 'function') || target == null) {
48 | target = {};
49 | }
50 |
51 | for (; i < length; ++i) {
52 | options = arguments[i];
53 | // Only deal with non-null/undefined values
54 | if (options != null) {
55 | // Extend the base object
56 | for (name in options) {
57 | src = target[name];
58 | copy = options[name];
59 |
60 | // Prevent never-ending loop
61 | if (target !== copy) {
62 | // Recurse if we're merging plain objects or arrays
63 | if (deep && copy && (isPlainObject(copy) || (copyIsArray = isArray(copy)))) {
64 | if (copyIsArray) {
65 | copyIsArray = false;
66 | clone = src && isArray(src) ? src : [];
67 | } else {
68 | clone = src && isPlainObject(src) ? src : {};
69 | }
70 |
71 | // Never move original objects, clone them
72 | target[name] = extend(deep, clone, copy);
73 |
74 | // Don't bring in undefined values
75 | } else if (typeof copy !== 'undefined') {
76 | target[name] = copy;
77 | }
78 | }
79 | }
80 | }
81 | }
82 |
83 | // Return the modified object
84 | return target;
85 | };
--------------------------------------------------------------------------------
/src/core/change_password.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {
3 | Form, Icon, Input, Button, Checkbox, notification
4 | } from 'antd';
5 |
6 | import { PassWordModifyModel } from '../models/user_manage_models';
7 |
8 | const passWordModifyModelInstance = PassWordModifyModel.getInstance();
9 | import { UserInfoStore } from '../store/business.store';
10 |
11 | const trim = val => val && val.trim() ? val.trim() : undefined
12 | class NormalLoginForm extends React.Component {
13 |
14 | checkPassWord = (rule, value, callback) => {
15 | const form = this.props.form;
16 |
17 | if (!trim(value)) {
18 | callback('密码不能为空 或者 空格!');
19 | }
20 | else if (trim(value) && (trim(value).length > 20 || trim(value).length < 8)) {
21 | callback('密码长度在 8 - 20之间!');
22 | } else {
23 | callback();
24 | }
25 |
26 | }
27 | handleSubmit = (e) => {
28 | e.preventDefault();
29 | this.props.form.validateFields((err, values) => {
30 | if (!err) {
31 | // console.log('Received values of form: ', values);
32 | // console.log('Received values of form: ', this.props.history);
33 |
34 | const [old_password, password] = [values["old_password"], values["new_password"]]
35 |
36 | const userInfoStore = UserInfoStore.getInstance();
37 | const userInfo = userInfoStore.getData() || {};
38 |
39 | // console.log(userInfo)
40 |
41 | passWordModifyModelInstance.setParam({
42 | old_password, password,
43 | username: userInfo["userName"]
44 | }, true);
45 |
46 | passWordModifyModelInstance.excute(res => {
47 | notification.open({
48 | message: '修改成功',
49 | description: "",
50 | type: "success"
51 |
52 | });
53 | setTimeout(() => {
54 | localStorage.clear();
55 | location.href = '/login';
56 | }, 1000);
57 |
58 | }, err => {
59 | notification.open({
60 | message: '修改失败',
61 | description: err["msg"],
62 | type: "error"
63 | });
64 | })
65 |
66 | }
67 | });
68 | }
69 |
70 | render() {
71 | const { getFieldDecorator } = this.props.form;
72 | const style = {
73 |
74 | }
75 | return (
76 |
78 | {getFieldDecorator('old_password', {
79 | rules: [{ required: true, message: '请输入原始密码!' }],
80 | })(
81 | } type="password" placeholder="原密码" />
82 | )}
83 |
84 |
85 | {getFieldDecorator('new_password', {
86 | rules: [{
87 | required: true,
88 | validator: this.checkPassWord
89 | }
90 | ]
91 | })(
92 | } type="password" placeholder="新密码" />
93 | )}
94 |
95 |
96 |
97 |
98 | 点击重置
99 |
100 |
101 |
102 | );
103 | }
104 | }
105 |
106 | export const ModifyPassWordForm = Form.create({ name: 'normal_login' })(NormalLoginForm);
107 |
--------------------------------------------------------------------------------
/src/models/router_manager_models.js:
--------------------------------------------------------------------------------
1 | import BaseModel from '../core/model.base'
2 | /*
3 | 路由组列表
4 | */
5 | export class ApiGroupListModel extends BaseModel {
6 |
7 | constructor(props) {
8 |
9 | super(props);
10 |
11 | this.url = 'api_router/query_api_group' ;
12 | this.method = 'GET';
13 |
14 | }
15 |
16 | }
17 |
18 | export class SimpleGroupQueryModel extends BaseModel{
19 | constructor(props) {
20 |
21 | super(props);
22 |
23 | this.url = 'api_router/query_simple_group_info' ;
24 | this.method = 'GET';
25 |
26 | }
27 | }
28 |
29 | /*
30 | 路由组列表(带限速信息)
31 | */
32 | export class ApiGroupRateListModel extends BaseModel {
33 |
34 | constructor(props) {
35 |
36 | super(props);
37 |
38 | this.url = 'api_router/query_api_group_rate_limit' ;
39 | this.method = 'GET';
40 |
41 | }
42 |
43 | }
44 |
45 | /*
46 | 删除路由组
47 | */
48 | export class DelApiGroupModel extends BaseModel {
49 |
50 | constructor(props) {
51 |
52 | super(props);
53 |
54 | this.url = 'api_router/delete_api_group' ;
55 |
56 | }
57 |
58 | }
59 | /*
60 | 新增路由规则组
61 |
62 | */
63 | export class AddApiGroupModel extends BaseModel {
64 |
65 | constructor(props) {
66 |
67 | super(props);
68 |
69 | this.url = 'api_router/add_api_group' ;
70 |
71 | }
72 |
73 | }
74 |
75 | /*
76 | 编辑路由组
77 |
78 | */
79 | export class EditApiGroupModel extends BaseModel {
80 |
81 | constructor(props) {
82 |
83 | super(props);
84 |
85 | this.url = 'api_router/update_api_group' ;
86 |
87 | }
88 |
89 | }
90 |
91 | /*
92 | 编辑路由组
93 |
94 | */
95 | export class EnableApiGroupModel extends BaseModel {
96 |
97 |
98 | constructor(props) {
99 |
100 | super(props);
101 |
102 | this.url = 'api_router/enable_api_group' ;
103 |
104 | }
105 |
106 | }
107 |
108 | /*
109 | 查询路由组限速列表
110 |
111 | */
112 | export class ApiGroupRateLimitListModel extends BaseModel {
113 |
114 | constructor(props) {
115 |
116 | super(props);
117 |
118 | this.url = 'group_rate_limit/get/' ;
119 | this.method = 'GET';
120 |
121 | }
122 |
123 | }
124 | /*
125 | 新增路由规则组限速列表
126 |
127 | */
128 | export class AddGroupRateLimitModel extends BaseModel {
129 |
130 | constructor(props) {
131 |
132 | super(props);
133 |
134 | this.url = 'group_rate_limit/add' ;
135 |
136 | }
137 |
138 | }
139 | /*
140 | 编辑路由规则组限速列表
141 |
142 | */
143 | export class EditGroupRateLimitModel extends BaseModel {
144 |
145 | constructor(props) {
146 |
147 | super(props);
148 |
149 | this.url = 'group_rate_limit/update';
150 |
151 | }
152 |
153 | }
154 | /*
155 | 删除路由组限速列表
156 |
157 | */
158 | export class DelGroupRateLimitModel extends BaseModel {
159 |
160 | constructor(props) {
161 |
162 | super(props);
163 |
164 | this.url = 'group_rate_limit/delete'
165 |
166 | }
167 |
168 | }
169 |
170 | export class AddGroupTargetModel extends BaseModel {
171 | constructor(props) {
172 | super(props)
173 | this.url = 'api_router/add_target'
174 | }
175 | }
176 |
177 | export class UpdateGroupTargetModel extends BaseModel {
178 | constructor(props) {
179 | super(props)
180 | this.url = 'api_router/update_target'
181 | }
182 | }
183 |
184 | export class DeleteGroupTargetModel extends BaseModel {
185 | constructor(props) {
186 | super(props)
187 | this.url = 'api_router/delete_target'
188 | }
189 | }
190 |
191 | /*
192 | 启禁用target
193 | */
194 | export class EnableTargetModel extends BaseModel {
195 |
196 |
197 | constructor(props) {
198 |
199 | super(props);
200 |
201 | this.url = 'api_router/enable_target' ;
202 |
203 | }
204 |
205 | }
206 |
--------------------------------------------------------------------------------
/src/view/user_manager/user_list/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import BaseView from '../../../core/view.base'
3 | import { notification } from 'antd';
4 |
5 | import SearchBar from "./search_bar";
6 | import { UserList } from "./user_list";
7 |
8 | import {
9 | UserListModel,
10 | UserAddModel,
11 | UserModifyModel
12 | } from '../../../models/user_manage_models';
13 | import { eventBus } from './events';
14 |
15 | const userListModelInstance = UserListModel.getInstance();
16 | const userAddModelInstance = UserAddModel.getInstance();
17 | const userModifyModelInstance = UserModifyModel.getInstance();
18 |
19 | const trim = val => val && val.trim() ? val.trim() : undefined
20 |
21 | class UserListView extends BaseView {
22 |
23 | state = {
24 | userLists: []
25 | }
26 |
27 |
28 |
29 | componentDidMount() {
30 |
31 | eventBus.on("findAll", this.findAll);
32 | // 初始化数据
33 |
34 | setTimeout(() => {
35 | this.findAll();
36 | }, 100);
37 | }
38 |
39 | componentWillMount() {
40 | eventBus.off("findAll");
41 | }
42 |
43 |
44 |
45 |
46 | // 添加用户
47 | addUser = (values, hide) => {
48 |
49 | const { username, mobile, password, email, superior } = values;
50 |
51 | userAddModelInstance.setParam({
52 | username: trim(username),
53 | mobile: trim(mobile),
54 | password: trim(password),
55 | email: trim(email),
56 | superior: trim(superior),
57 |
58 | }, true)
59 |
60 | userAddModelInstance.excute(res => {
61 |
62 | if (res["success"]) {
63 |
64 | notification.open({
65 | message: '新用户创建成功',
66 | description: ''
67 | });
68 | setTimeout(() => {
69 | hide();
70 | this.findAll()
71 | }, 1000);
72 |
73 | }
74 | }, err => {
75 | notification.open({
76 | message: '创建失败',
77 | description: err["msg"]
78 | });
79 | })
80 | }
81 | // 修改用户
82 | modifyUser = (values, hide) => {
83 | const { id, username, mobile, email, superior } = values;
84 |
85 | userModifyModelInstance.setParam({
86 | username: trim(username),
87 | mobile: trim(mobile),
88 | email: trim(email),
89 | superior: trim(superior),
90 | id
91 |
92 | }, true)
93 |
94 | userModifyModelInstance.excute(res => {
95 |
96 | if (res["success"]) {
97 | notification.open({
98 | message: '修改成功',
99 | description: ''
100 | });
101 | setTimeout(() => {
102 | hide();
103 | this.findAll()
104 | }, 1000);
105 |
106 | }
107 | }, err => {
108 | notification.open({
109 | message: '修改失败',
110 | description: err["msg"]
111 | });
112 | })
113 |
114 | }
115 |
116 | findAll = (username, mobile) => {
117 | userListModelInstance.setParam({
118 | username: trim(username),
119 | mobile: trim(mobile)
120 | }, true)
121 | userListModelInstance.excute(res => {
122 | this.setState(prevState => {
123 | res.data.forEach((element, index) => {
124 | element.index = index + 1
125 | });
126 | return {
127 | userLists: res.data
128 | }
129 | })
130 |
131 | }, err => {
132 | notification.open({
133 | message: '查询失败',
134 | description: err["msg"]
135 | });
136 | })
137 | }
138 |
139 | renderMain() {
140 | return (
141 |
142 |
143 |
144 |
145 | );
146 | }
147 | }
148 |
149 |
150 |
151 | export default UserListView;
--------------------------------------------------------------------------------
/src/core/model.base.js:
--------------------------------------------------------------------------------
1 | import $ from 'jquery'
2 |
3 | import Base64 from '../util/base64'
4 |
5 | import {getHttpAuth} from '../util/util'
6 |
7 | const config = require('../../config');
8 |
9 | const env = process.env.NODE_ENV == 'development'? 'dev':'build';
10 |
11 | var sendReq = function(opt) {
12 | return $.ajax(opt);
13 | }
14 |
15 | class BaseModel {
16 | constructor(props) {
17 |
18 | this.url = '';
19 |
20 | this.params ={};
21 |
22 | this.contentType="";
23 |
24 | this.timeout = 15000;
25 |
26 | this.method = 'POST';
27 |
28 | this.async = true;
29 |
30 | this.headers = {
31 |
32 | // Authorization:'Basic' + Base64.encode('admin:ngr_admin')
33 |
34 | };
35 |
36 | this.restfulApi = config[env].restfulApi;
37 |
38 | this.notParallelism = true;
39 |
40 | this.validates = [];
41 | }
42 |
43 | getPrefix() {
44 |
45 | return this.restfulApi || '';
46 |
47 | }
48 |
49 | buildUrl(url) {
50 |
51 | if(url.indexOf('http') === 0 || url.indexOf('//') === 0) {
52 | return url;
53 | }
54 | return this.getPrefix() + '/' + url;
55 | }
56 |
57 | setHeader(h) {
58 | $.extend(this.headers, h);
59 | }
60 |
61 | getHeaders() {
62 | return this.headers;
63 | }
64 |
65 | _gethttpAuth(){
66 | const self = this;
67 |
68 | if(this.url.indexOf('login') < 0){
69 | getHttpAuth(function cb(auth){
70 | const param = {
71 | Authorization:auth
72 | }
73 | self.setHeader(param)
74 | });
75 | }
76 |
77 |
78 | }
79 |
80 | /*
81 | 设置model请求参数
82 | @param:参数
83 | @reset:是否清空原有参数
84 | */
85 | setParam(params, reset) {
86 | if(reset === true) {
87 | this.params = {};
88 | }
89 |
90 | $.extend(this.params, params);
91 |
92 |
93 | }
94 |
95 | getParam() {
96 | return this.params;
97 | }
98 |
99 | buildParam() {
100 | return this.params;
101 | }
102 |
103 | excute(onComplete, onError, timeout) {
104 |
105 | this._gethttpAuth();
106 | this.notParallelism && this.abort();
107 |
108 | onComplete = onComplete || function() {};
109 | onError = onError || function() {};
110 | var opt = {
111 | url: this.buildUrl(this.url),
112 | type: this.method,
113 | dataType: 'json',
114 | headers: this.headers,
115 | async:this.async,
116 | data: this.buildParam(),
117 | timeout: this.timeout,
118 | crossDomain: false,
119 | success: function (res, status, xhr) {
120 |
121 | if(!!res && res.success == true) {
122 | onComplete(res, status, xhr);
123 | } else {
124 | onError(res);
125 | }
126 | },
127 | error: function (err, status) {
128 |
129 | onError(err, status)
130 | }
131 | };
132 |
133 | if (opt.url.indexOf(window.location.host) === -1 || opt.url.indexOf(window.location.protocol) === -1) {
134 | opt.crossDomain = true;
135 | }
136 |
137 | this.ajaxRequest = sendReq(opt);
138 | }
139 |
140 | abort() {
141 |
142 | if (this.ajaxRequest && this.ajaxRequest.abort) {
143 | this.ajaxRequest.abort();
144 | }
145 |
146 | }
147 | }
148 |
149 |
150 | BaseModel.getInstance = function() {
151 |
152 | if(!this.instance || !(this.instance instanceof this)) {
153 | this.instance = new this();
154 | }
155 |
156 | return this.instance;
157 | }
158 |
159 | export default BaseModel;
160 |
--------------------------------------------------------------------------------
/src/view/router_manager/gateway_manage/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import BaseView from '../../../core/view.base'
3 |
4 | import SearchBar from "./search_bar";
5 | import { GateWayList } from "./gateway_list";
6 | import { eventBus } from './events';
7 | import { AddGatewayModel, GateWayListModel, SetLimitModel } from '../../../models/gateway_manage_models';
8 |
9 | const trim = val => val && val.trim() ? val.trim() : undefined;
10 | import { notification,spin } from 'antd';
11 |
12 | const gateWayListModelInstance = GateWayListModel.getInstance();
13 | const setLimitModelInstance = SetLimitModel.getInstance();
14 | const addGatewayModelInstance = AddGatewayModel.getInstance();
15 |
16 | class GateWay extends BaseView {
17 |
18 | constructor(props) {
19 | super(props);
20 | this.state = {
21 | gateWayList: [],
22 | spinflag:true
23 | }
24 |
25 | this.searchGateWayMessage = this.searchGateWayMessage.bind(this);
26 | this.modifyGateWay = this.modifyGateWay.bind(this);
27 | this.addGateWay = this.addGateWay.bind(this);
28 |
29 | }
30 |
31 | componentDidMount() {
32 | this.searchGateWayMessage();
33 | eventBus.on("search", this.searchGateWayMessage);
34 | eventBus.on("modify", this.modifyGateWay);
35 | eventBus.on( "add", this.addGateWay)
36 | }
37 |
38 | componentWillUnmount() {
39 | eventBus.off("search");
40 | eventBus.off("modify");
41 | eventBus.off("add")
42 | }
43 |
44 | // 搜索值 成功失败回调函数
45 | searchGateWayMessage(values = {}) {
46 | this.setState({
47 | spinflag:true
48 | })
49 | const { gateway_code } = values;
50 | gateWayListModelInstance.setParam({
51 | gateway_code
52 | }, true);
53 | gateWayListModelInstance.excute(res => {
54 | res.data.forEach((el, index) => {
55 | el.index = index + 1
56 | });
57 | this.setState({
58 | gateWayList: res.data,
59 | spinflag:false
60 | })
61 | }, err => {
62 | console.log(err);
63 | });
64 | }
65 | modifyGateWay(values = {}, success) {
66 | const { gateway_desc, limit_count, id, content_type, message, http_status } = values;
67 | // console.log(limit_count, id, content_type, message, http_status);
68 |
69 | setLimitModelInstance.setParam({
70 | gateway_desc,limit_count, id, content_type, message, http_status
71 | }, true);
72 | setLimitModelInstance.excute(res => {
73 | notification.open({
74 | message: '修改成功',
75 | description: '',
76 | type: "success"
77 | });
78 | success && success();
79 | this.searchGateWayMessage();
80 | }, err => {
81 | console.log(err);
82 | notification.open({
83 | message: '修改失败',
84 | description: err["msg"],
85 | type: "error"
86 | });
87 | });
88 | }
89 |
90 | addGateWay(values = {}, success) {
91 | const { gateway_code, gateway_desc, limit_count, content_type, message, http_status } = values;
92 | console.log(gateway_code,gateway_desc, limit_count, content_type, message, http_status);
93 |
94 | addGatewayModelInstance.setParam({
95 | gateway_code, gateway_desc,limit_count, content_type, message, http_status
96 | }, true);
97 | addGatewayModelInstance.excute(res => {
98 | notification.open({
99 | message: '新增成功',
100 | description: '',
101 | type: "success"
102 | });
103 | success && success();
104 | this.searchGateWayMessage();
105 | }, err => {
106 | console.log(err);
107 | notification.open({
108 | message: '新增失败',
109 | description: err["msg"]
110 | });
111 | });
112 | }
113 |
114 | renderMain() {
115 | return (
116 |
117 |
118 |
119 |
120 | );
121 | }
122 | }
123 |
124 | export default GateWay;
125 |
--------------------------------------------------------------------------------
/src/view/user_manager/user_list/edit_user.js:
--------------------------------------------------------------------------------
1 | import {
2 | Form, Row, Col, Input, Button, Icon, Modal
3 | } from 'antd';
4 | import React from 'react'
5 |
6 |
7 | // 列表项生成器
8 | const renderItem = (props) => (item, name, required = false, ) => {
9 | const { getFieldDecorator } = props.form;
10 | return (
11 |
12 |
13 | {getFieldDecorator(item, {
14 | initialValue: props[item],
15 | rules: [{ required, message: `请输入${name}` }]
16 | })(
17 |
18 | )}
19 |
20 |
21 | )
22 | }
23 |
24 |
25 | const trim = val => val && val.trim() ? val.trim() : undefined
26 |
27 | class EditUserForm extends React.Component {
28 |
29 | checkPassWord = (rule, value, callback) => {
30 | const form = this.props.form;
31 | if (!trim(value)) {
32 | callback('密码不能为空 或者 空格!');
33 | }
34 | else if (trim(value) && (trim(value).length > 20 || trim(value).length < 8)) {
35 | callback('密码长度在 8 - 20之间!');
36 | } else {
37 | callback();
38 | }
39 |
40 | }
41 |
42 |
43 | render() {
44 | const { getFieldDecorator } = this.props.form;
45 |
46 | return (
47 |
75 | );
76 | }
77 | }
78 |
79 | const WrappedEditUserForm = Form.create({ name: 'edit_user_form' })(EditUserForm);
80 |
81 | export default WrappedEditUserForm;
82 |
83 |
84 | export class UserInfoEdit extends React.Component {
85 |
86 | state = { visible: false }
87 |
88 | show = () => {
89 | this.setState({
90 | visible: true,
91 | });
92 | }
93 |
94 | hide = () => {
95 | this.setState({
96 | visible: false,
97 | });
98 | }
99 |
100 | submit = () => {
101 |
102 | this.refs.userInfo.validateFields((err, values) => {
103 | if (!err) {
104 | // 如果是编辑状态 id username 从userInfo 获得
105 | if (this.props.editStatus) {
106 | values["id"] = this.props.userInfo["id"];
107 | values["username"] = this.props.userInfo["username"];
108 | }
109 |
110 | this.props.trigger(values, this.hide)
111 |
112 | }
113 |
114 | });
115 |
116 | }
117 |
118 | render() {
119 | return (
120 |
121 |
122 | {this.props.buttonName}
123 |
124 | {
125 | this.state.visible ?
134 |
135 | : ""
136 | }
137 |
138 |
139 | );
140 | }
141 | }
142 |
--------------------------------------------------------------------------------
/src/common/less/private.less:
--------------------------------------------------------------------------------
1 | .none {
2 | display: none;
3 | }
4 |
5 | .ant-advanced-search-form {
6 | padding: 24px;
7 | border-radius: 6px;
8 |
9 | .ant-row {
10 | display: flex;
11 | flex-wrap: wrap;
12 | }
13 | }
14 |
15 | .isolated_search_content .ant-select {
16 | width: 160px !important;
17 | }
18 |
19 | .ant-advanced-search-form .ant-form-item {
20 | display: flex;
21 | flex-wrap: nowrap;
22 | }
23 |
24 | .ant-advanced-search-form .ant-form-item-control-wrapper {
25 | flex: 1;
26 | }
27 |
28 | .modal_form {
29 | padding: 24px;
30 |
31 | .ant-form-item {
32 | margin-bottom: 24px;
33 | }
34 |
35 | }
36 |
37 | .card_content {
38 | margin-bottom: 20px;
39 | border-radius: 20px;
40 | overflow: hidden;
41 |
42 | .ant-card-body {
43 | text-align: center;
44 |
45 | .ant-switch {
46 | zoom: 1.5;
47 | }
48 |
49 | }
50 |
51 | }
52 |
53 | .card_content {
54 | background: '#ECECEC';
55 | padding: '30px';
56 | }
57 |
58 |
59 | .card_title {
60 | margin-bottom: 0;
61 | text-align: center;
62 | }
63 |
64 | .content_title {
65 | font-size: 20px;
66 | line-height: 45px;
67 | height: 45px;
68 | text-align: left;
69 | font-weight: bold;
70 | }
71 |
72 |
73 | .btn_box {
74 | overflow: hidden;
75 |
76 | .right_btn {
77 | float: right;
78 | }
79 |
80 | }
81 |
82 | .dashboard_basetable {
83 | padding: 20px;
84 | }
85 |
86 | .login_content {
87 | position: absolute;
88 | width: 100%;
89 | height: 100%;
90 |
91 | .login_title {
92 | width: 100%;
93 | font-size: 20px;
94 | margin-bottom: 20px;
95 | text-align: center;
96 | }
97 |
98 | .login-form {
99 | max-width: 300px;
100 | position: relative;
101 | left: 50%;
102 | top: 50%;
103 | transform: translate(-50%, -50%);
104 | -webkit-transform: translate(-50%, -50%);
105 | }
106 |
107 | .login-form-forgot {
108 | float: right;
109 | }
110 |
111 | .login-form-button {
112 | width: 100%;
113 | }
114 |
115 | }
116 |
117 | .clear:before,
118 | .clear:after {
119 | content: "";
120 | display: block;
121 | }
122 |
123 | .clear:after {
124 | clear: both;
125 | }
126 |
127 | .fl {
128 | float: left;
129 | display: block
130 | }
131 |
132 | .fr {
133 | float: right;
134 | display: block
135 | }
136 |
137 | .search_container {
138 | .group_context_tip {
139 | display: none;
140 | }
141 | }
142 |
143 | .target_input {
144 | .ant-input-number {
145 | width: 150px;
146 | }
147 |
148 | .label {
149 | margin-right: 15px;
150 | }
151 |
152 | input {
153 | display: inline-block;
154 | width: 50%;
155 | }
156 |
157 | .ant-input-number-input {
158 | width: 100%;
159 | }
160 |
161 | .input_tips {
162 | padding-left: 5px;
163 | color: red;
164 | }
165 | }
166 |
167 | .target_container {
168 | margin-top: 10px;
169 | padding-top: 3px;
170 | border-top: 1px solid #17171f;
171 |
172 | h4 {
173 | text-align: center
174 | }
175 | }
176 |
177 | .rate_limit_container {
178 | h4 {
179 | text-align: center
180 | }
181 |
182 | .edit_rate_limit,
183 | .delete_rate_limit {
184 | position: relative;
185 | top: -5px;
186 | }
187 |
188 | .rate_limit_detail {
189 | display: inline-block;
190 |
191 | div {
192 | margin-right: 20px;
193 | }
194 | }
195 | }
196 |
197 | .group_context_tip {
198 | position: relative;
199 | top: 7px;
200 | }
201 |
202 | .seq_num_css {
203 | position: relative;
204 | top: 6px;
205 | color: #ffc12b;
206 | }
207 |
208 | .warn {
209 | color: red;
210 | position: relative;
211 | top: 8px;
212 | }
213 |
214 | .err_msg {
215 | color: red;
216 | margin-left: 30px;
217 | }
218 |
219 |
220 | .log_list,
221 | .user_list {
222 | padding: 24px;
223 | border-radius: 6px;
224 | }
225 |
226 | .log-info-pane {
227 |
228 | width: 550px !important;
229 |
230 | }
231 |
232 | tr.ant-table-expanded-row, tr.ant-table-expanded-row:hover{
233 | background:none!important;
234 | }
235 | textarea{
236 | background:#3b3b4d!important;
237 | border:1px solid rgb(23, 23, 31);
238 | border-radius:4px;
239 | }
240 | .ant-table-thead > tr > th, .ant-table-tbody > tr > td{
241 | padding: 16px 0!important;
242 | }
243 | #g_body{
244 | .ismini{
245 | width:0px!important;
246 | max-width:auto!important;
247 | min-width:auto!important;
248 | }
249 | .transition{
250 | transition-property: width, max-height,padding;
251 | transition-duration: 0.35s;
252 | }
253 | }
--------------------------------------------------------------------------------
/src/util/base64.js:
--------------------------------------------------------------------------------
1 | var Base64 = {
2 |
3 | // private property
4 | _keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
5 |
6 | // public method for encoding
7 | encode : function (input) {
8 | var output = "";
9 | var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
10 | var i = 0;
11 |
12 | input = this._utf8_encode(input);
13 |
14 | while (i < input.length) {
15 |
16 | chr1 = input.charCodeAt(i++);
17 | chr2 = input.charCodeAt(i++);
18 | chr3 = input.charCodeAt(i++);
19 |
20 | enc1 = chr1 >> 2;
21 | enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
22 | enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
23 | enc4 = chr3 & 63;
24 |
25 | if (isNaN(chr2)) {
26 | enc3 = enc4 = 64;
27 | } else if (isNaN(chr3)) {
28 | enc4 = 64;
29 | }
30 |
31 | output = output +
32 | this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) +
33 | this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4);
34 |
35 | }
36 |
37 | return output;
38 | },
39 |
40 | // public method for decoding
41 | decode : function (input) {
42 | var output = "";
43 | var chr1, chr2, chr3;
44 | var enc1, enc2, enc3, enc4;
45 | var i = 0;
46 |
47 | input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
48 |
49 | while (i < input.length) {
50 |
51 | enc1 = this._keyStr.indexOf(input.charAt(i++));
52 | enc2 = this._keyStr.indexOf(input.charAt(i++));
53 | enc3 = this._keyStr.indexOf(input.charAt(i++));
54 | enc4 = this._keyStr.indexOf(input.charAt(i++));
55 |
56 | chr1 = (enc1 << 2) | (enc2 >> 4);
57 | chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
58 | chr3 = ((enc3 & 3) << 6) | enc4;
59 |
60 | output = output + String.fromCharCode(chr1);
61 |
62 | if (enc3 != 64) {
63 | output = output + String.fromCharCode(chr2);
64 | }
65 | if (enc4 != 64) {
66 | output = output + String.fromCharCode(chr3);
67 | }
68 |
69 | }
70 |
71 | output = this._utf8_decode(output);
72 |
73 | return output;
74 |
75 | },
76 |
77 | // private method for UTF-8 encoding
78 | _utf8_encode : function (string) {
79 | string = string.replace(/\r\n/g,"\n");
80 | var utftext = "";
81 |
82 | for (var n = 0; n < string.length; n++) {
83 |
84 | var c = string.charCodeAt(n);
85 |
86 | if (c < 128) {
87 | utftext += String.fromCharCode(c);
88 | }
89 | else if((c > 127) && (c < 2048)) {
90 | utftext += String.fromCharCode((c >> 6) | 192);
91 | utftext += String.fromCharCode((c & 63) | 128);
92 | }
93 | else {
94 | utftext += String.fromCharCode((c >> 12) | 224);
95 | utftext += String.fromCharCode(((c >> 6) & 63) | 128);
96 | utftext += String.fromCharCode((c & 63) | 128);
97 | }
98 |
99 | }
100 |
101 | return utftext;
102 | },
103 |
104 | // private method for UTF-8 decoding
105 | _utf8_decode : function (utftext) {
106 | var string = "";
107 | var i = 0;
108 | var c = 0,
109 | c1 = 0,
110 | c2 = 0,
111 | c3 = 0;
112 |
113 | while ( i < utftext.length ) {
114 |
115 | c = utftext.charCodeAt(i);
116 |
117 | if (c < 128) {
118 | string += String.fromCharCode(c);
119 | i++;
120 | }
121 | else if((c > 191) && (c < 224)) {
122 | c2 = utftext.charCodeAt(i+1);
123 | string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
124 | i += 2;
125 | }
126 | else {
127 | c2 = utftext.charCodeAt(i+1);
128 | c3 = utftext.charCodeAt(i+2);
129 | string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
130 | i += 3;
131 | }
132 |
133 | }
134 |
135 | return string;
136 | }
137 | }
138 |
139 | export default Base64;
--------------------------------------------------------------------------------
/src/view/login.js:
--------------------------------------------------------------------------------
1 | import React, {Component} from 'react'
2 | import BaseView from '../core/view.base'
3 |
4 | import Base64 from '../util/base64'
5 | import {getHttpAuth} from '../util/util'
6 |
7 | import {UserInfoStore,HttpAuthInfoStore} from '../store/business.store'
8 |
9 | import {
10 | LoginModel
11 | } from '../models/general_models'
12 |
13 | import { Form, Icon, Input, Button, Checkbox } from 'antd';
14 | const FormItem = Form.Item;
15 |
16 | const userInfoStore = UserInfoStore.getInstance(),
17 | httpAuthInfoStore = HttpAuthInfoStore.getInstance();
18 | const loginModel = LoginModel.getInstance();
19 |
20 | class NormalLoginForm extends Component {
21 | handleSubmit = (e) => {
22 | e.preventDefault();
23 | const self = this;
24 |
25 | this.props.form.validateFields((err, values) => {
26 |
27 | if (!err) {
28 |
29 |
30 | self.props.loginHandle && self.props.loginHandle(values);
31 | }
32 | });
33 | }
34 |
35 | render() {
36 |
37 | const { getFieldDecorator } = this.props.form;
38 |
39 | return (
40 |
70 | );
71 | }
72 | }
73 |
74 |
75 | class Login extends Component {
76 |
77 | constructor(props) {
78 |
79 | super(props);
80 |
81 | this.state = {
82 | userName:'',
83 | password:''
84 |
85 | }
86 |
87 | this.urlQuery = this.props.location.query;
88 | }
89 |
90 | componentWillMount(){
91 |
92 | const userInfo = userInfoStore.getData() || {};
93 |
94 | if(userInfo.userName){
95 | this.setState({
96 | userName:userInfo.userName,
97 | password:userInfo.password
98 | })
99 | }
100 |
101 | }
102 |
103 | loginHandle(userInfo){
104 |
105 | const {userName,remember,password} = userInfo;
106 |
107 | const self = this;
108 |
109 | const baseCode = Base64.encode(`${userName}:${password}`);
110 | const auth = `Basic ${baseCode}`;
111 |
112 | loginModel.setHeader({
113 | Authorization:auth
114 | })
115 |
116 | loginModel.excute(res=>{
117 |
118 | if(remember){//缓存登陆账号密码7天
119 | userInfoStore.setData({
120 | userName:userName,
121 | password:password
122 | })
123 | }
124 |
125 | //缓存auth8小时
126 | httpAuthInfoStore.setData({
127 | auth:auth
128 | })
129 |
130 | self.gotoBack();
131 | },err=>{
132 |
133 | console.log(err)
134 |
135 | alert('错误')
136 |
137 | })
138 |
139 |
140 |
141 |
142 | }
143 |
144 | gotoBack(){
145 | window.history.go(-1);
146 | }
147 |
148 |
149 | render(){
150 |
151 | const WrappedNormalLoginForm = Form.create()(NormalLoginForm);
152 |
153 | return (
154 |
155 |
156 |
161 |
162 |
163 | )
164 |
165 | }
166 |
167 | }
168 |
169 | export default Login;
170 |
--------------------------------------------------------------------------------
/src/store/store.base.js:
--------------------------------------------------------------------------------
1 | import extend from '../util/extend'
2 |
3 | class BaseStore {
4 |
5 | constructor() {
6 |
7 | // 缓存数据标志
8 | this.key = '';
9 |
10 | // 缓存时间,支持单位 天-"D", 时-"H", 分钟-"M"
11 | // 如 "30D", "0.5H"
12 | this.lifetime = '30M';
13 |
14 | // 缓存容器,以浏览器localstorage为容器
15 | this.storage = window.localStorage;
16 |
17 | }
18 |
19 | //在不覆盖原有数据的是时候做更新
20 | set(data) {
21 | try {
22 | var data = extend(this.getData() || {}, data);
23 |
24 | this.setData(data);
25 | } catch(e) {
26 |
27 | }
28 | }
29 |
30 | setData(data) {
31 |
32 | var storageData;
33 |
34 | try {
35 | storageData = this._buildStorageData(data);
36 | this.storage.setItem(this.key, JSON.stringify(storageData));
37 | return true;
38 |
39 | } catch(e) {
40 |
41 | //localstorage 数据写满, 全清掉,可优化
42 | if (e.name == 'QuotaExceededError' || e.name == 'QUOTA_EXCEEDED_ERR') {
43 |
44 | this.storage.clear();
45 | this.setData(data);
46 | }
47 |
48 | this._sendStorageExceptionLog({
49 | type: 'SET',
50 | api_params: JSON.stringify(storageData || {}),
51 | api_response: JSON.stringify({
52 | type: 'SET',
53 | err: e || {},
54 | stack: (e || {}).stack || {},
55 | message: (e || {}).message || "",
56 | userAgent: window.navigator.userAgent
57 | })
58 | })
59 |
60 | }
61 |
62 | return false;
63 |
64 | }
65 |
66 | getData() {
67 |
68 | var result = null, storageItem, storageData;
69 |
70 | try {
71 |
72 | storageItem = this.storage.getItem(this.key);
73 |
74 | storageData = storageItem ? JSON.parse(storageItem) : null;
75 |
76 | if (storageData && storageData.expiretime) {
77 |
78 | if (this._isExpiredTime(storageData.expiretime)) {
79 |
80 | this.storage.removeItem(this.key);
81 |
82 | } else {
83 |
84 | result = storageData.data;
85 |
86 | }
87 |
88 | }
89 |
90 |
91 | } catch (e) {
92 |
93 | this._sendStorageExceptionLog({
94 | type: 'GET',
95 | api_params: JSON.stringify(storageData || {}),
96 | api_response: JSON.stringify({
97 | type: 'GET',
98 | err: e || {},
99 | stack: (e || {}).stack || {},
100 | message: (e || {}).message || "",
101 | userAgent: window.navigator.userAgent
102 | })
103 | })
104 |
105 | }
106 |
107 | return result;
108 |
109 | }
110 |
111 | _isExpiredTime(expiretime) {
112 |
113 | return this._getNowTime() >= expiretime;
114 |
115 | }
116 |
117 | _buildStorageData(data) {
118 |
119 | return {
120 | data: data,
121 | savetime: this._getNowTime(),
122 | expiretime: this._getExpireTime()
123 | }
124 |
125 | }
126 |
127 | _getNowTime() {
128 |
129 | return (new Date()).getTime();
130 |
131 | }
132 |
133 | _getExpireTime() {
134 |
135 | var lifetime = this.lifetime,
136 | nowTime = this._getNowTime();
137 |
138 | var durationTime = 0;
139 |
140 | var baseSeconds,
141 | unit = lifetime.slice(-1),
142 | num = Number(lifetime.slice(0, lifetime.length - 1));
143 |
144 | switch (unit) {
145 | case 'M':
146 | baseSeconds = 60 * 1000; break;
147 | case 'H':
148 | baseSeconds = 60 * 60 * 1000; break;
149 | case 'D':
150 | baseSeconds = 24 * 60 * 60 * 1000; break;
151 | }
152 |
153 | if (typeof num != 'number' || !baseSeconds) {
154 |
155 | console.log('超时时间参数错误!请检查,并重新设置!');
156 |
157 | }
158 |
159 | durationTime = num * baseSeconds;
160 |
161 | return nowTime + durationTime;
162 |
163 | }
164 |
165 | remove() {
166 |
167 | this.storage.removeItem(this.key);
168 |
169 | }
170 |
171 | _sendStorageExceptionLog(logData) {
172 |
173 | var _data = [
174 | 'api_address=' + encodeURIComponent(this.key),
175 | 'api_params=' + encodeURIComponent(logData.api_params),
176 | 'api_response=' + encodeURIComponent(logData.api_response),
177 | 'page_url=' + encodeURIComponent(window.location.href)
178 | ];
179 |
180 | this._sendLog(_data.join('&'));
181 |
182 | }
183 |
184 | // 临时使用
185 | _sendLog(data) {
186 | if(typeof __mw_send_req__ == 'function') {
187 | __mw_send_req__(data);
188 | }
189 | }
190 |
191 | }
192 |
193 | BaseStore.getInstance = function() {
194 | if(!this.instance || !(this.instance instanceof this)) {
195 | this.instance = new this();
196 | }
197 |
198 | return this.instance;
199 | }
200 |
201 | export default BaseStore;
202 |
--------------------------------------------------------------------------------
/src/view/router_manager/TargetModal.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { Modal, Input, InputNumber, Checkbox } from 'antd'
3 | import classnames from 'classnames'
4 | import PropTypes from 'prop-types'
5 |
6 | export default class TargetModal extends Component {
7 | static propTypes = {
8 | handleOk: PropTypes.func, // 确定触发的方法
9 | cancel: PropTypes.func.isRequired, // 取消方法
10 | propertyModalTitle: PropTypes.string.isRequired, // modal标题文案
11 | targetVisible: PropTypes.bool.isRequired, // modal是否可见判断值
12 | dataSource: PropTypes.object.isRequired
13 | }
14 | constructor (props) {
15 | super(props)
16 | this.state = {
17 | id: null,
18 | weight: '',
19 | host: '',
20 | port: '',
21 | is_only_ab_check:false,
22 | statusList: [true, true, true], // 各输入框输入数据状态值
23 | }
24 | }
25 | componentWillReceiveProps (nextProps) {
26 | if ( this.state.id !== nextProps.dataSource.id ) {
27 | const { group_id, id, weight, host, port ,is_only_ab_check} = nextProps.dataSource
28 | this.setState({
29 | group_id,
30 | id: id || '',
31 | weight: weight || '',
32 | host: host || '',
33 | port: port || '',
34 | is_only_ab_check: is_only_ab_check || false,
35 | statusList: [true, true, true],
36 | })
37 | }
38 | }
39 | changeInputValue (e = 1, type) {
40 | // 若为number类型,数据组件已处理好,e值会有不同
41 | let value = e.target ? e.target.value : e;
42 |
43 | if(type=='is_only_ab_check'){
44 | value = e.target.checked;
45 | }
46 |
47 | this.setState({
48 | [type]: value
49 | })
50 | }
51 | // 输入值验证,若有不符合规则的输入,则修改状态数组中对应序列值下的布尔值
52 | confirmAndHandleOk () {
53 | const { handleOk } = this.props,
54 | { id, weight, host, port, group_id, statusList,is_only_ab_check } = this.state
55 |
56 | statusList[0] = /^[1-9]\d*$/.test(weight)
57 | statusList[1] = /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(host)
58 | statusList[2] = /^[1-9]\d*$/.test(port)
59 |
60 | const cannotUpload = statusList.some(status => status === false)
61 | if (cannotUpload) {
62 | this.setState({
63 | statusList
64 | })
65 | return null
66 | }
67 | handleOk({ id, weight, host, port,is_only_ab_check, group_id })
68 | }
69 | render() {
70 | const { propertyModalTitle, targetVisible } = this.props,
71 | { weight, host, port, statusList, is_only_ab_check } = this.state
72 |
73 | return (
74 |
81 |
82 | 权重:
83 | this.changeInputValue(e, 'weight') }
88 | style={{ margin: '10px 0', width: '200px' }} />
89 | 必填项且为正整数
92 |
93 |
94 | IP地址:
95 | this.changeInputValue(e, 'host') }
100 | style={{ margin: '10px 0' }} />
101 | 必填项且必须符合IP地址
104 |
105 |
106 | 端口:
107 | this.changeInputValue(e, 'port') }
112 | style={{ margin: '10px 0' }} />
113 | 必填项且为正整数
116 |
117 |
118 | 仅用于AB分流:
119 | this.changeInputValue(e, 'is_only_ab_check') }
123 | style={{ margin: '10px 0' }}
124 | />
125 |
126 |
127 | )
128 | }
129 | }
130 |
--------------------------------------------------------------------------------
/src/view/router_manager/gateway_manage/add_gateway.js:
--------------------------------------------------------------------------------
1 | import {
2 | Form, Row, Col, Input, Button, Icon, Modal, Select, InputNumber
3 | } from 'antd';
4 | import React from 'react'
5 | import { eventBus } from './events';
6 | const { Option } = Select;
7 | const { TextArea } = Input;
8 |
9 | const renderInput = (props) => (item, name, disabled = false) => {
10 | const { getFieldDecorator } = props.form;
11 | return (
12 |
13 |
14 | {getFieldDecorator(item, {
15 | rules: [{
16 | required: true,message:"网关编码为必填且要唯一"
17 | }],
18 | initialValue: props[item],
19 | })(
20 |
21 | )}
22 |
23 |
24 | );
25 | }
26 |
27 | class __AddGatewayForm extends React.Component {
28 |
29 | checkMessge = (rule, value, callback) => {
30 | const form = this.props.form;
31 | if (!value && form.getFieldValue('content_type')) {
32 | callback('响应类型选择后,此项必填');
33 | } else {
34 | callback();
35 | }
36 | }
37 |
38 | handleChange = () => this.setState({}, () => this.props.form.validateFields(['message'], { force: true }))
39 |
40 |
41 | render() {
42 | const { props } = this;
43 | const { getFieldDecorator } = props.form;
44 | return (
45 |
128 | );
129 |
130 | }
131 | }
132 |
133 | const AddGatewayForm = Form.create({ name: 'gateway_add_form' })(__AddGatewayForm);
134 |
135 | // 限流设置弹框
136 | export class AddGateway extends React.Component {
137 |
138 | constructor(props) {
139 | super(props)
140 | this.state = {
141 | visible: false
142 | }
143 | }
144 |
145 |
146 | show = () => {
147 | this.setState({
148 | visible: true,
149 | });
150 | }
151 |
152 | hide = () => {
153 | this.setState({
154 | visible: false,
155 | });
156 | }
157 |
158 | submit = () => {
159 |
160 | this.refs.info.validateFields((err, values) => {
161 | if (!err) {
162 |
163 | eventBus.emit("add", values, this.hide)
164 | }
165 | });
166 |
167 | }
168 |
169 | render() {
170 | return (
171 |
172 |
173 | 新增网关
174 |
175 | {
176 | this.state.visible ?
185 |
186 | : ""
187 | }
188 |
189 |
190 | );
191 | }
192 | }
193 |
--------------------------------------------------------------------------------
/src/view/router_manager/gateway_manage/edit_gateway.js:
--------------------------------------------------------------------------------
1 | import {
2 | Form, Row, Col, Input, Button, Icon, Modal, Select, InputNumber
3 | } from 'antd';
4 | import React from 'react'
5 | import { eventBus } from './events';
6 | const { Option } = Select;
7 | const { TextArea } = Input;
8 |
9 | const renderInput = (props) => (item, name, disabled = false) => {
10 | const { getFieldDecorator } = props.form;
11 | return (
12 |
13 |
14 | {getFieldDecorator(item, {
15 | rules: [{
16 | required: true,message:"网关编码必填且必须唯一"
17 | }],
18 | initialValue: props[item],
19 | })(
20 |
21 | )}
22 |
23 |
24 | );
25 | }
26 |
27 | class __SetLimitForm extends React.Component {
28 |
29 | checkMessge = (rule, value, callback) => {
30 | const form = this.props.form;
31 | if (!value && form.getFieldValue('content_type')) {
32 | callback('响应类型选择后,此项必填');
33 | } else {
34 | callback();
35 | }
36 | }
37 |
38 | handleChange = () => this.setState({}, () => this.props.form.validateFields(['message'], { force: true }))
39 |
40 |
41 | render() {
42 | const { props } = this;
43 | const { getFieldDecorator } = props.form;
44 | return (
45 |
49 | {renderInput(props)("gateway_code", "网关编码:", true)}
50 |
51 |
55 | {getFieldDecorator('gateway_desc', {
56 | rules: [{
57 | required: true,message:"请输入网关描述"
58 | }],
59 | initialValue: props["gateway_desc"],
60 | })(
61 |
62 | )}
63 |
64 |
65 |
66 |
70 | {getFieldDecorator('limit_count', {
71 | rules: [{
72 | required: true,message:"请输入QPS限流阈值(单机)"
73 | }],
74 | initialValue: props["limit_count"],
75 | })(
76 |
77 | )}
78 |
79 |
80 |
81 |
85 | {getFieldDecorator('http_status', {
86 | initialValue: props["http_status"],
87 | })(
88 |
89 | )}
90 |
91 |
92 |
93 |
94 | {renderInput(props)("id", "id", true)}
95 |
96 |
97 |
98 |
102 | {getFieldDecorator('content_type', {
103 | initialValue: props["content_type"] ? props["content_type"] : undefined,
104 | })(
105 |
106 | application/json
107 | application/xml
108 | text/html
109 | text/plain
110 |
111 | )}
112 |
113 |
114 |
115 |
119 | {getFieldDecorator('message', {
120 | initialValue: props["message"],
121 | rules: [{
122 | validator: this.checkMessge,
123 | }],
124 |
125 | })(
126 |
127 | )}
128 |
129 |
130 |
131 |
132 | );
133 |
134 | }
135 | }
136 |
137 | const SetLimitForm = Form.create({ name: 'gateway_set_limit_form' })(__SetLimitForm);
138 |
139 | // 限流设置弹框
140 | export class SetLimit extends React.Component {
141 |
142 | constructor(props) {
143 | super(props)
144 | this.state = {
145 | visible: false
146 | }
147 | }
148 |
149 |
150 | show = () => {
151 | this.setState({
152 | visible: true,
153 | });
154 | }
155 |
156 | hide = () => {
157 | this.setState({
158 | visible: false,
159 | });
160 | }
161 |
162 | submit = () => {
163 |
164 | this.refs.info.validateFields((err, values) => {
165 | if (!err) {
166 |
167 | eventBus.emit("modify", values, this.hide)
168 | }
169 | });
170 |
171 | }
172 |
173 | render() {
174 | return (
175 |
176 |
177 | 设置
178 |
179 | {
180 | this.state.visible ?
189 |
190 | : ""
191 | }
192 |
193 |
194 | );
195 | }
196 | }
197 |
--------------------------------------------------------------------------------
/src/common/css/vendor-prefixes.less:
--------------------------------------------------------------------------------
1 | // Vendor Prefixes
2 |
3 | // - Animations
4 | // - Backface visibility
5 | // - Box shadow
6 | // - Box sizing
7 | // - Content columns
8 | // - Hyphens
9 | // - Placeholder text
10 | // - Transformations
11 | // - Transitions
12 | // - User Select
13 |
14 | // Animations
15 | .animation(@animation) {
16 | -webkit-animation: @animation;
17 | -o-animation: @animation;
18 | animation: @animation;
19 | }
20 | .animation-name(@name) {
21 | -webkit-animation-name: @name;
22 | animation-name: @name;
23 | }
24 | .animation-duration(@duration) {
25 | -webkit-animation-duration: @duration;
26 | animation-duration: @duration;
27 | }
28 | .animation-timing-function(@timing-function) {
29 | -webkit-animation-timing-function: @timing-function;
30 | animation-timing-function: @timing-function;
31 | }
32 | .animation-delay(@delay) {
33 | -webkit-animation-delay: @delay;
34 | animation-delay: @delay;
35 | }
36 | .animation-iteration-count(@iteration-count) {
37 | -webkit-animation-iteration-count: @iteration-count;
38 | animation-iteration-count: @iteration-count;
39 | }
40 | .animation-direction(@direction) {
41 | -webkit-animation-direction: @direction;
42 | animation-direction: @direction;
43 | }
44 | .animation-fill-mode(@fill-mode) {
45 | -webkit-animation-fill-mode: @fill-mode;
46 | animation-fill-mode: @fill-mode;
47 | }
48 |
49 | // Backface visibility
50 | // Prevent browsers from flickering when using CSS 3D transforms.
51 | // Default value is `visible`, but can be changed to `hidden`
52 |
53 | .backface-visibility(@visibility){
54 | -webkit-backface-visibility: @visibility;
55 | -moz-backface-visibility: @visibility;
56 | backface-visibility: @visibility;
57 | }
58 |
59 | // Drop shadows
60 |
61 | .box-shadow(@shadow) {
62 | -webkit-box-shadow: @shadow; // iOS <4.3 & Android <4.1
63 | box-shadow: @shadow;
64 | }
65 |
66 | // Box sizing
67 | .box-sizing(@boxmodel) {
68 | -webkit-box-sizing: @boxmodel;
69 | -moz-box-sizing: @boxmodel;
70 | box-sizing: @boxmodel;
71 | }
72 |
73 | // CSS3 Content Columns
74 | .content-columns(@column-count; @column-gap: @grid-gutter-width) {
75 | -webkit-column-count: @column-count;
76 | -moz-column-count: @column-count;
77 | column-count: @column-count;
78 | -webkit-column-gap: @column-gap;
79 | -moz-column-gap: @column-gap;
80 | column-gap: @column-gap;
81 | }
82 |
83 | // Optional hyphenation
84 | .hyphens(@mode: auto) {
85 | word-wrap: break-word;
86 | -webkit-hyphens: @mode;
87 | -moz-hyphens: @mode;
88 | hyphens: @mode;
89 | }
90 |
91 | // Placeholder text
92 | .placeholder(@color: @input-color-placeholder) {
93 | // Firefox
94 | &::-moz-placeholder {
95 | color: @color;
96 | opacity: 1;
97 | }
98 | &::-webkit-input-placeholder { color: @color; } // Safari and Chrome
99 | }
100 |
101 | // Transformations
102 | .scale(@ratio) {
103 | -webkit-transform: scale(@ratio);
104 | transform: scale(@ratio);
105 | }
106 | .scale(@ratioX; @ratioY) {
107 | -webkit-transform: scale(@ratioX, @ratioY);
108 | transform: scale(@ratioX, @ratioY);
109 | }
110 | .scaleX(@ratio) {
111 | -webkit-transform: scaleX(@ratio);
112 | transform: scaleX(@ratio);
113 | }
114 | .scaleY(@ratio) {
115 | -webkit-transform: scaleY(@ratio);
116 | transform: scaleY(@ratio);
117 | }
118 | .skew(@x; @y) {
119 | -webkit-transform: skewX(@x) skewY(@y);
120 | transform: skewX(@x) skewY(@y);
121 | }
122 | .translate(@x; @y) {
123 | -webkit-transform: translate(@x, @y);
124 | transform: translate(@x, @y);
125 | }
126 | .translate3d(@x; @y; @z) {
127 | -webkit-transform: translate3d(@x, @y, @z);
128 | transform: translate3d(@x, @y, @z);
129 | }
130 | .rotate(@degrees) {
131 | -webkit-transform: rotate(@degrees);
132 | transform: rotate(@degrees);
133 | }
134 | .rotateX(@degrees) {
135 | -webkit-transform: rotateX(@degrees);
136 | transform: rotateX(@degrees);
137 | }
138 | .rotateY(@degrees) {
139 | -webkit-transform: rotateY(@degrees);
140 | transform: rotateY(@degrees);
141 | }
142 | .perspective(@perspective) {
143 | -webkit-perspective: @perspective;
144 | -moz-perspective: @perspective;
145 | perspective: @perspective;
146 | }
147 | .perspective-origin(@perspective) {
148 | -webkit-perspective-origin: @perspective;
149 | -moz-perspective-origin: @perspective;
150 | perspective-origin: @perspective;
151 | }
152 | .transform-origin(@origin) {
153 | -webkit-transform-origin: @origin;
154 | -moz-transform-origin: @origin;
155 | transform-origin: @origin;
156 | }
157 |
158 | // Transitions
159 |
160 | .transition(@transition) {
161 | -webkit-transition: @transition;
162 | transition: @transition;
163 | }
164 | .transition-property(@transition-property) {
165 | -webkit-transition-property: @transition-property;
166 | transition-property: @transition-property;
167 | }
168 | .transition-delay(@transition-delay) {
169 | -webkit-transition-delay: @transition-delay;
170 | transition-delay: @transition-delay;
171 | }
172 | .transition-duration(@transition-duration) {
173 | -webkit-transition-duration: @transition-duration;
174 | transition-duration: @transition-duration;
175 | }
176 | .transition-timing-function(@timing-function) {
177 | -webkit-transition-timing-function: @timing-function;
178 | transition-timing-function: @timing-function;
179 | }
180 | .transition-transform(@transition) {
181 | -webkit-transition: -webkit-transform @transition;
182 | -moz-transition: -moz-transform @transition;
183 | transition: transform @transition;
184 | }
185 |
186 |
187 | // User select
188 | // For selecting text on the page
189 |
190 | .user-select(@select) {
191 | -webkit-user-select: @select;
192 | -moz-user-select: @select;
193 | user-select: @select;
194 | }
195 |
--------------------------------------------------------------------------------
/src/view/router_manager/host_qps.js:
--------------------------------------------------------------------------------
1 | import { Modal, Button, notification, Form, Row, Col, Input, Icon, Select, InputNumber } from 'antd';
2 | import React from "react"
3 | const { TextArea } = Input;
4 |
5 | import { HostQPSModel, HostListModel } from "../../models/host_manager_models";
6 |
7 | let hostListModel = HostListModel.getInstance(),
8 | hostQPSModel = HostQPSModel.getInstance()
9 |
10 | class __SetLimitForm extends React.Component {
11 |
12 | checkMessge = (rule, value, callback) => {
13 | const form = this.props.form;
14 | if (!value && form.getFieldValue('content_type')) {
15 | callback('响应类型选择后,此项必填');
16 | } else {
17 | callback();
18 | }
19 | }
20 |
21 | handleChange = () => this.setState({}, () => this.props.form.validateFields(['message'], { force: true }))
22 |
23 |
24 | render() {
25 | const { props } = this;
26 | const { getFieldDecorator } = props.form;
27 | return (
28 |
32 | 所属网关:{props.gateway_code}
33 | 主机域名:{props.host}
34 |
35 |
39 | {getFieldDecorator('host_desc', {
40 | rules: [{
41 | required: true,message:"请输入主机描述"
42 | }],
43 | initialValue: props["host_desc"],
44 | })(
45 |
46 | )}
47 |
48 |
49 |
50 |
54 | {getFieldDecorator('limit_count', {
55 | initialValue: props["limit_count"],
56 | })(
57 |
58 | )}
59 |
60 |
61 |
62 |
66 | {getFieldDecorator('http_status', {
67 | initialValue: props["http_status"],
68 | })(
69 |
70 | )}
71 |
72 |
73 |
74 |
75 |
79 | {getFieldDecorator('content_type', {
80 | initialValue: props["content_type"] ? props["content_type"] : undefined,
81 | })(
82 |
83 | application/json
84 | application/xml
85 | text/html
86 | text/plain
87 |
88 | )}
89 |
90 |
91 |
92 |
96 | {getFieldDecorator('message', {
97 | initialValue: props["message"],
98 | rules: [{
99 | validator: this.checkMessge,
100 | }],
101 |
102 | })(
103 |
104 | )}
105 |
106 |
107 |
108 |
109 | );
110 |
111 | }
112 | }
113 |
114 | const SetLimitForm = Form.create({ name: 'host_set_limit_form' })(__SetLimitForm);
115 |
116 | export class ModifyHostQPS extends React.Component {
117 | state = {
118 | visible: false,
119 | data: {}
120 | }
121 |
122 | show = () => {
123 |
124 | hostListModel.setParam({
125 | id: this.props.id,
126 | }, true)
127 | hostListModel.excute(res => {
128 | this.setState({
129 | data: res.data[0],
130 | visible: true,
131 | });
132 |
133 | }, err => {
134 | notification.open({
135 | message: '查询失败',
136 | description: err["msg"],
137 | type: "error"
138 | });
139 | })
140 | }
141 |
142 | hide = (e) => {
143 | this.setState({
144 | visible: false,
145 | });
146 | }
147 |
148 | submit = (e) => {
149 |
150 | const { id } = this.state.data
151 | this.refs.hostLimit.validateFields((err, values) => {
152 | if (!err) {
153 | // console.log("values", values)
154 | hostQPSModel.setParam({
155 | id,
156 | host:values["host"],
157 | host_desc:values["host_desc"],
158 | content_type: values["content_type"],
159 | http_status: values["http_status"],
160 | limit_count: values["limit_count"],
161 | message: values["message"],
162 |
163 | }, true);
164 |
165 | hostQPSModel.excute(res => {
166 |
167 | // console.log("res", res)
168 | notification.open({
169 | message: '修改成功',
170 | description: '',
171 | type: "success"
172 | });
173 | this.props.cb()
174 | setTimeout(() => {
175 | this.hide()
176 | }, 500);
177 |
178 | }, err => {
179 | notification.open({
180 | message: '修改失败',
181 | description: err["msg"],
182 | type: "error"
183 | });
184 | })
185 | }
186 | });
187 | }
188 |
189 | render() {
190 | return (
191 |
192 |
193 | 设置
194 |
195 |
203 |
204 |
205 |
206 | );
207 | }
208 | }
209 |
--------------------------------------------------------------------------------
/src/common/css/loading.less:
--------------------------------------------------------------------------------
1 | .g-loading {
2 | position: relative;
3 | width: 0;
4 | height: 33px;
5 | z-index: 2000000000;
6 | left: 50%;
7 | top: 50%;
8 | }
9 | .g-loading-leaf{
10 | position: absolute;
11 | top: 15px;
12 | opacity: 0.25;
13 | &:before{
14 | content:" ";
15 | position: absolute;
16 | width: 8.14px;
17 | height: 3.08px;
18 | background: rgb(209, 209, 213);
19 | box-shadow: rgba(0, 0, 0, 0.0980392) 0 0 1px;
20 | border-radius: 1px;
21 | transform-origin: left 50% 0;
22 | -webkit-transform-origin: left 50% 0;
23 | }
24 | &-0{
25 | animation: opacity-60-25-0-12 1.25s linear infinite;
26 | -webkit-animation: opacity-60-25-0-12 1.25s linear infinite;
27 | &:before{
28 | transform: rotate(0deg) translate(7.92px, 0px);
29 | -webkit-transform: rotate(0deg) translate(7.92px, 0px);
30 | }
31 | }
32 | &-1{
33 | animation: opacity-60-25-1-12 1.25s linear infinite;
34 | -webkit-animation: opacity-60-25-1-12 1.25s linear infinite;
35 | &:before{
36 | transform: rotate(30deg) translate(7.92px, 0px);
37 | -webkit-transform: rotate(30deg) translate(7.92px, 0px);
38 | }
39 | }
40 | &-2{
41 | animation: opacity-60-25-2-12 1.25s linear infinite;
42 | -webkit-animation: opacity-60-25-2-12 1.25s linear infinite;
43 | &:before{
44 | transform: rotate(60deg) translate(7.92px, 0px);
45 | -webkit-transform: rotate(60deg) translate(7.92px, 0px);
46 | }
47 | }
48 | &-3{
49 | animation: opacity-60-25-3-12 1.25s linear infinite;
50 | -webkit-animation: opacity-60-25-3-12 1.25s linear infinite;
51 | &:before{
52 | transform: rotate(90deg) translate(7.92px, 0px);
53 | -webkit-transform: rotate(90deg) translate(7.92px, 0px);
54 | }
55 | }
56 | &-4{
57 | animation: opacity-60-25-4-12 1.25s linear infinite;
58 | -webkit-animation: opacity-60-25-4-12 1.25s linear infinite;
59 | &:before{
60 | transform: rotate(120deg) translate(7.92px, 0px);
61 | -webkit-transform: rotate(120deg) translate(7.92px, 0px);
62 | }
63 | }
64 | &-5{
65 | animation: opacity-60-25-5-12 1.25s linear infinite;
66 | -webkit-animation: opacity-60-25-5-12 1.25s linear infinite;
67 | &:before{
68 | transform: rotate(150deg) translate(7.92px, 0px);
69 | -webkit-transform: rotate(150deg) translate(7.92px, 0px);
70 | }
71 | }
72 | &-6{
73 | animation: opacity-60-25-6-12 1.25s linear infinite;
74 | -webkit-animation: opacity-60-25-6-12 1.25s linear infinite;
75 | &:before{
76 | transform: rotate(180deg) translate(7.92px, 0px);
77 | -webkit-transform: rotate(180deg) translate(7.92px, 0px);
78 | }
79 | }
80 | &-7{
81 | animation: opacity-60-25-7-12 1.25s linear infinite;
82 | -webkit-animation: opacity-60-25-7-12 1.25s linear infinite;
83 | &:before{
84 | transform: rotate(210deg) translate(7.92px, 0px);
85 | -webkit-transform: rotate(210deg) translate(7.92px, 0px);
86 | }
87 | }
88 | &-8{
89 | animation: opacity-60-25-8-12 1.25s linear infinite;
90 | -webkit-animation: opacity-60-25-8-12 1.25s linear infinite;
91 | &:before{
92 | transform: rotate(240deg) translate(7.92px, 0px);
93 | -webkit-transform: rotate(240deg) translate(7.92px, 0px);
94 | }
95 | }
96 | &-9{
97 | animation: opacity-60-25-9-12 1.25s linear infinite;
98 | -webkit-animation: opacity-60-25-9-12 1.25s linear infinite;
99 | &:before{
100 | transform: rotate(270deg) translate(7.92px, 0px);
101 | -webkit-transform: rotate(270deg) translate(7.92px, 0px);
102 | }
103 | }
104 | &-10{
105 | animation: opacity-60-25-10-12 1.25s linear infinite;
106 | -webkit-animation: opacity-60-25-10-12 1.25s linear infinite;
107 | &:before{
108 | transform: rotate(300deg) translate(7.92px, 0px);
109 | -webkit-transform: rotate(300deg) translate(7.92px, 0px);
110 | }
111 | }
112 | &-11{
113 | animation: opacity-60-25-11-12 1.25s linear infinite;
114 | -webkit-animation: opacity-60-25-11-12 1.25s linear infinite;
115 | &:before{
116 | transform: rotate(330deg) translate(7.92px, 0px);
117 | -webkit-transform: rotate(330deg) translate(7.92px, 0px);
118 | }
119 | }
120 | }
121 |
122 | @-webkit-keyframes opacity-60-25-0-12 {
123 | 0% { opacity: 0.25; }
124 | 0.01% { opacity: 0.25; }
125 | 0.02% { opacity: 1; }
126 | 60.01% { opacity: 0.25; }
127 | 100% { opacity: 0.25; }
128 | }
129 | @-webkit-keyframes opacity-60-25-1-12 {
130 | 0% { opacity: 0.25; }
131 | 8.34333% { opacity: 0.25; }
132 | 8.35333% { opacity: 1; }
133 | 68.3433% { opacity: 0.25; }
134 | 100% { opacity: 0.25; }
135 | }
136 | @-webkit-keyframes opacity-60-25-2-12 {
137 | 0% { opacity: 0.25; }
138 | 16.6767% { opacity: 0.25; }
139 | 16.6867% { opacity: 1; }
140 | 76.6767% { opacity: 0.25; }
141 | 100% { opacity: 0.25; }
142 | }
143 | @-webkit-keyframes opacity-60-25-3-12 {
144 | 0% { opacity: 0.25; }
145 | 25.01% { opacity: 0.25; }
146 | 25.02% { opacity: 1; }
147 | 85.01% { opacity: 0.25; }
148 | 100% { opacity: 0.25; }
149 | }
150 | @-webkit-keyframes opacity-60-25-4-12 {
151 | 0% { opacity: 0.25; }
152 | 33.3433% { opacity: 0.25; }
153 | 33.3533% { opacity: 1; }
154 | 93.3433% { opacity: 0.25; }
155 | 100% { opacity: 0.25; }
156 | }
157 | @-webkit-keyframes opacity-60-25-5-12 {
158 | 0% { opacity: 0.270958333333333; }
159 | 41.6767% { opacity: 0.25; }
160 | 41.6867% { opacity: 1; }
161 | 1.67667% { opacity: 0.25; }
162 | 100% { opacity: 0.270958333333333; }
163 | }
164 | @-webkit-keyframes opacity-60-25-6-12 {
165 | 0% { opacity: 0.375125; }
166 | 50.01% { opacity: 0.25; }
167 | 50.02% { opacity: 1; }
168 | 10.01% { opacity: 0.25; }
169 | 100% { opacity: 0.375125; }
170 | }
171 | @-webkit-keyframes opacity-60-25-7-12 {
172 | 0% { opacity: 0.479291666666667; }
173 | 58.3433% { opacity: 0.25; }
174 | 58.3533% { opacity: 1; }
175 | 18.3433% { opacity: 0.25; }
176 | 100% { opacity: 0.479291666666667; }
177 | }
178 | @-webkit-keyframes opacity-60-25-8-12 {
179 | 0% { opacity: 0.583458333333333; }
180 | 66.6767% { opacity: 0.25; }
181 | 66.6867% { opacity: 1; }
182 | 26.6767% { opacity: 0.25; }
183 | 100% { opacity: 0.583458333333333; }
184 | }
185 | @-webkit-keyframes opacity-60-25-9-12 {
186 | 0% { opacity: 0.687625; }
187 | 75.01% { opacity: 0.25; }
188 | 75.02% { opacity: 1; }
189 | 35.01% { opacity: 0.25; }
190 | 100% { opacity: 0.687625; }
191 | }
192 | @-webkit-keyframes opacity-60-25-10-12 {
193 | 0% { opacity: 0.791791666666667; }
194 | 83.3433% { opacity: 0.25; }
195 | 83.3533% { opacity: 1; }
196 | 43.3433% { opacity: 0.25; }
197 | 100% { opacity: 0.791791666666667; }
198 | }
199 | @-webkit-keyframes opacity-60-25-11-12 {
200 | 0% { opacity: 0.895958333333333; }
201 | 91.6767% { opacity: 0.25; }
202 | 91.6867% { opacity: 1; }
203 | 51.6767% { opacity: 0.25; }
204 | 100% { opacity: 0.895958333333333; }
205 | }
--------------------------------------------------------------------------------
/src/ui/ui.fiedld.js:
--------------------------------------------------------------------------------
1 | import React, {Component} from 'react'
2 | import { Form, Input, Radio,Row,Col,Select, TimePicker, Checkbox} from 'antd'
3 | import moment from 'moment'
4 |
5 |
6 | const FormItem = Form.Item;
7 | const RadioButton = Radio.Button;
8 | const RadioGroup = Radio.Group;
9 | const Option = Select.Option;
10 |
11 | class FieldsContent extends Component {
12 |
13 | constructor(props) {
14 |
15 | super(props);
16 | }
17 |
18 | render() {
19 |
20 | const {InputChangeHandle,fieldsArr,spanNum,fieldsValue} =this.props.fieldsConfig;
21 |
22 | let content = [];
23 |
24 | fieldsArr.forEach((item,idx)=>{
25 |
26 | switch (item.type){
27 | case 'input' :
28 | content.push(
29 |
30 |
37 |
44 |
45 | { item.key === 'group_context' && (
46 | 缺省上下文
50 | ) }
51 | { item.key === 'seq_num' && (
52 | 说明:值越小越优先
53 | ) }
54 |
55 | )
56 |
57 | break;
58 |
59 | case 'switch' :
60 |
61 | content.push(
62 |
63 |
70 |
71 |
72 | 启用
73 | 禁用
74 |
75 |
76 |
77 |
78 | )
79 | break;
80 | case 'select' :
81 | let optionContent = item.options.map((optData,opIdx)=>{
82 | return {optData.desc}
83 | });
84 | let mode = item.mode || '';
85 | let value = fieldsValue[item.key] || 'defaultValue';
86 |
87 | if(mode == 'multiple' && (value== '' || value == 'defaultValue') ){
88 | value = [];
89 | }
90 |
91 |
92 | const selectContent = (
93 |
94 | {optionContent}
95 |
96 | )
97 |
98 | content.push(
99 |
100 |
107 | {selectContent}
108 |
109 |
110 | )
111 | break;
112 | case 'TimePicker': {
113 | let endTimeUseful = null
114 | if (fieldsValue['effect_s_time']) {
115 | endTimeUseful = (fieldsValue['effect_s_time'].split(':').join('') - fieldsValue['effect_e_time'].split(':').join('')) > 0
116 | }
117 |
118 | content.push(
119 |
120 |
127 |
130 |
131 | {
132 | (item.key === 'effect_e_time') && endTimeUseful && (结束时间不可早于开始时间 )
133 | }
134 |
135 | )
136 | break;
137 | }
138 | case 'textArea': {
139 | content.push(
140 |
141 |
142 | { item.label }:
143 | { (item.key === 'message') && item['disabled'] && (类型已选,内容必填 ) }
144 |
145 |
150 |
151 | )
152 | }
153 | }
154 |
155 | })
156 |
157 | const fieldContent = (
158 |
159 |
160 | {content}
161 |
162 |
163 | )
164 |
165 | return fieldContent;
166 | }
167 |
168 | }
169 |
170 | export default FieldsContent;
--------------------------------------------------------------------------------
/src/view/router_manager/addhost_qps.js:
--------------------------------------------------------------------------------
1 | import { Modal, Button, notification, Form, Row, Col, Input, Icon, Select, InputNumber } from 'antd';
2 | import React from "react"
3 | const { TextArea } = Input;
4 |
5 | import { AddHostQPSModel, HostListModel } from "../../models/host_manager_models";
6 |
7 | let hostListModel = HostListModel.getInstance(),
8 | addHostQPSModel = AddHostQPSModel.getInstance()
9 |
10 | class __AddLimitForm extends React.Component {
11 | constructor(props) {
12 | super(props)
13 | }
14 | state = {
15 | gatewayinfo: {}
16 | }
17 | checkMessge = (rule, value, callback) => {
18 | const form = this.props.form;
19 | if (!value && form.getFieldValue('content_type')) {
20 | callback('响应类型选择后,此项必填');
21 | } else {
22 | callback();
23 | }
24 | }
25 |
26 | handleChange = () => this.setState({}, () => this.props.form.validateFields(['message'], { force: true }))
27 | gatewayhandleChange(e){
28 | hostListModel.setParam({
29 | id: e
30 | }, true)
31 | // hostListModel.excute(res => {
32 | // this.setState({
33 | // gatewayinfo: res.data[0],
34 | // },()=>{
35 | // this.props.cb(this.state.gatewayinfo)
36 | // })
37 |
38 | // }, err => {
39 | // notification.open({
40 | // message: '查询失败',
41 | // description: err["msg"]
42 | // });
43 | // })
44 | }
45 |
46 | render() {
47 | const { props } = this;
48 | const { getFieldDecorator } = props.form;
49 | const gatewaylist= this.props.getewaylist;
50 | return (
51 |
55 |
56 |
60 |
61 | {getFieldDecorator('gateway_code', {
62 | rules: [{
63 | required: true,message:"请选择所属服务网关"
64 | }],
65 | initialValue: props["gateway_code"] ? props["gateway_code"] : undefined,
66 | })(
67 |
68 | {gatewaylist.map((item)=>{
69 | return (
70 | {item.gateway_code}
71 | )
72 | })}
73 |
74 | )}
75 |
76 |
77 |
78 |
82 | {getFieldDecorator('gateway_desc', {
83 | rules: [{
84 | required: true,message:"请输入主机描述"
85 | }],
86 | initialValue: props["gateway_desc"],
87 | })(
88 |
89 | )}
90 |
91 |
92 |
93 |
97 | {getFieldDecorator('host', {
98 | rules: [{
99 | required: true,message:"请输入主机域名"
100 | }],
101 | initialValue: props["host"],
102 | })(
103 |
104 | )}
105 |
106 |
107 |
108 |
112 | {getFieldDecorator('limit_count', {
113 | rules: [{
114 | required: true,message:"请设置QPS限流阈值(单机)"
115 | }],
116 | initialValue: props["limit_count"],
117 | })(
118 |
119 | )}
120 |
121 |
122 |
123 |
127 | {getFieldDecorator('http_status', {
128 | initialValue: props["http_status"],
129 | })(
130 |
131 | )}
132 |
133 |
134 |
135 |
136 |
140 | {getFieldDecorator('content_type', {
141 | initialValue: props["content_type"] ? props["content_type"] : undefined,
142 | })(
143 |
144 | application/json
145 | application/xml
146 | text/html
147 | text/plain
148 |
149 | )}
150 |
151 |
152 |
153 |
157 | {getFieldDecorator('message', {
158 | initialValue: props["message"]
159 | })(
160 |
161 | )}
162 |
163 |
164 |
165 |
166 | );
167 |
168 | }
169 | }
170 |
171 | const AddLimitForm = Form.create({ name: 'host_add_limit_form' })(__AddLimitForm);
172 |
173 | export class ModifyAddHostQPS extends React.Component {
174 | constructor(props) {
175 | super(props)
176 | }
177 | state = {
178 | visible: false,
179 | data: {}
180 | }
181 | componentDidMount(){
182 | }
183 | show = () => {
184 |
185 | hostListModel.setParam({
186 | id: this.props.id,
187 | }, true)
188 | hostListModel.excute(res => {
189 | console.log(res)
190 | this.setState({
191 | data: res.data[0],
192 | visible: true,
193 | });
194 |
195 | }, err => {
196 | notification.open({
197 | message: '查询失败',
198 | description: err["msg"],
199 | type: "error"
200 | });
201 | })
202 | }
203 |
204 | hide = (e) => {
205 | // this.setState({
206 | // visible: false,
207 | // });
208 | this.props.closeModel()
209 | }
210 | gatewayinfoFn(e){
211 | this.setState({
212 | gatewayinfo:e
213 | })
214 | }
215 | submit = (e) => {
216 |
217 | const { id } = this.state.data
218 | this.refs.hostLimit.validateFields((err, values) => {
219 | if (!err) {
220 | addHostQPSModel.setParam({
221 | host:values["host"],
222 | host_desc:values["gateway_desc"],
223 | gateway_id:values["gateway_code"],
224 | content_type: values["content_type"],
225 | http_status: values["http_status"],
226 | limit_count: values["limit_count"],
227 | message: values["message"],
228 | }, true);
229 | addHostQPSModel.excute(res => {
230 |
231 | // console.log("res", res)
232 | notification.open({
233 | message: '新增主机成功',
234 | description: "",
235 | type: "success"
236 | });
237 |
238 | this.props.closeModel()
239 |
240 | }, err => {
241 | notification.open({
242 | message: '新增主机失败',
243 | description: err["msg"],
244 | type: "error"
245 | });
246 | })
247 | }
248 | });
249 | }
250 |
251 | render() {
252 | return (
253 |
254 |
262 |
263 |
264 |
265 | );
266 | }
267 | }
268 |
--------------------------------------------------------------------------------
/src/view/plugin_manager/plugin_general.js:
--------------------------------------------------------------------------------
1 | import React, {Component} from 'react'
2 | import BaseView from '../../core/view.base'
3 |
4 | import {
5 | PluginListModel,
6 | TogglePluginEnableModel
7 | } from '../../models/plugin.models'
8 |
9 | import {PluginInfoStore} from '../../store/business.store'
10 |
11 | import {Button,Row,Col,Divider,Icon,Card,Switch,notification,Popconfirm} from 'antd'
12 |
13 |
14 | let pluginListModel = PluginListModel.getInstance(),
15 | togglePluginEnableModel = TogglePluginEnableModel.getInstance();
16 |
17 | const pluginInfoStore = PluginInfoStore.getInstance();
18 |
19 | class PluginGeneral extends BaseView {
20 |
21 | constructor(props) {
22 |
23 | super(props);
24 |
25 | this.state = {
26 | pluginList:[],
27 | pageStatus:'init',
28 | }
29 |
30 | }
31 |
32 | componentDidMount(){
33 | this.fetchPluginList();
34 |
35 | }
36 |
37 | fetchPluginList(){
38 |
39 | const self = this;
40 |
41 | pluginListModel.excute(res=>{
42 |
43 | const resData = res.data || [];
44 |
45 |
46 |
47 | const data = self.formatData(resData.plugins);
48 |
49 | const length = data.length;
50 | let visible = [];
51 | visible.length = length;
52 |
53 | //更新缓存
54 | let pluginStatue = {};
55 | resData.plugins.forEach(item=>{
56 | pluginStatue[item.plugin_name] = item.enable;
57 | })
58 |
59 | pluginInfoStore.setData(pluginStatue);
60 |
61 | self.setState({
62 | pluginList:data,
63 | pageStatus:'ready',
64 | visible:visible
65 | })
66 |
67 | self.updateSlider(pluginStatue)
68 |
69 |
70 | },err=>{
71 | //TODO
72 | })
73 |
74 | }
75 |
76 | formatData(data){
77 |
78 | let title = '',order = 0,urlPath = '';
79 |
80 | return data.map((item,idx)=>{
81 |
82 | title = item.plugin_name;
83 |
84 | switch (item.plugin_name) {
85 |
86 | case 'property_rate_limit' :
87 | title = '特征防刷器';
88 | order= 1;
89 | urlPath = 'plugin_manager/property_ratelimit';
90 | break;
91 | case 'waf' :
92 | title = '防火墙';
93 | order= 2;
94 | urlPath = 'plugin_manager/firewall';
95 | break;
96 | case 'anti_sql_injection':
97 | title = 'SQL注入防控器';
98 | order = 3;
99 | urlPath = 'plugin_manager/anti_sql_injection';
100 | break;
101 | }
102 |
103 | return {
104 | plugin_name:item.plugin_name,
105 | enable:item.enable,
106 | order:order,
107 | title:title,
108 | idx:idx,
109 | urlPath:urlPath
110 | }
111 |
112 | })
113 |
114 | }
115 |
116 | toggleEnable(item,idx){
117 | let self = this,
118 | pluginList = self.state.pluginList;
119 |
120 | const enable = item.enable == '1' ? 0 : 1;
121 | const message = enable == 0?'禁用':'启用';
122 |
123 | togglePluginEnableModel.url = item.plugin_name + '/enable';
124 |
125 | togglePluginEnableModel.setParam({
126 | enable:enable
127 | })
128 |
129 | togglePluginEnableModel.excute(res=>{
130 |
131 | pluginList[item.idx].enable = enable;
132 | self.hidePopConfirm(idx);
133 | self.setState({
134 | pluginList:pluginList,
135 | },()=>{
136 |
137 |
138 |
139 | let pluginInfo = pluginInfoStore.getData();
140 | pluginInfo[item.plugin_name] = enable;
141 |
142 | pluginInfoStore.setData(pluginInfo);
143 |
144 | console.log(pluginInfo)
145 |
146 | self.updateSlider(pluginInfo);
147 | })
148 |
149 |
150 | const noticeParam = {
151 | description:item.title + message + '成功',
152 | type:'success'
153 | }
154 | self.showNotification(noticeParam);
155 |
156 | //更新缓存
157 | let pluginInfo = pluginInfoStore.getData();
158 | pluginInfo[item.plugin_name] = enable;
159 | pluginInfoStore.setData(pluginInfo);
160 |
161 |
162 | },err=>{
163 |
164 | self.hidePopConfirm(idx);
165 |
166 | const noticeParam = {
167 | message:item.title + message + '失败',
168 | type:'success'
169 | }
170 |
171 | self.showNotification(noticeParam);
172 |
173 | });
174 |
175 | }
176 |
177 | showNotification(param){
178 |
179 | if(!param.description){return};
180 |
181 | if(param.type == 'success'){
182 |
183 | notification.success({
184 | message:param.message || '',
185 | description: param.description || '',
186 | duration:2
187 | });
188 |
189 | }else{
190 |
191 | notification.error({
192 | message:param.message || '',
193 | description: param.description || '',
194 | duration:2
195 | });
196 |
197 | }
198 | }
199 |
200 | showPopConfirm(idx){
201 | let visible = this.state.visible;
202 | visible[idx] = true;
203 | this.setState({
204 | visible:visible
205 | })
206 | }
207 |
208 | hidePopConfirm(idx){
209 | let visible = this.state.visible;
210 | visible[idx] = false;
211 | this.setState({
212 | visible:visible
213 | })
214 | }
215 |
216 | gotoPluginDetail(item){
217 |
218 | __mei_wei__.appHistory.reactHistory.push(__mei_wei__.env.basepath + item.urlPath);
219 | }
220 |
221 | renderCard(){
222 | const self = this;
223 | const pluginList = this.state.pluginList,visible = this.state.visible;
224 |
225 | let content = pluginList.map((item,idx)=>{
226 |
227 | const visibleItem = visible[idx];
228 |
229 | const enable = item.enable == '1'?true:false;
230 | const title = item.enable == '1'?"你确定要关闭插件吗?":"你确定要开启插件吗?";
231 | return (
232 |
233 | {item.title}}
235 | bordered={true}
236 | hoverable={true}
237 | activeTabKey='-'
238 | className= 'card_content'
239 | actions = {enable?[ ]:''}
240 | >
241 | {this.showPopConfirm(idx)}}/>
242 | self.toggleEnable(item,idx)}
245 | onCancel={() => self.hidePopConfirm(idx)}
246 | cancelText='取消'
247 | okText='确定'
248 | visible={visibleItem}
249 | >
250 |
251 |
252 |
253 |
254 | )
255 |
256 | })
257 |
258 | return (
259 |
260 | {content}
261 |
262 | )
263 |
264 |
265 | }
266 |
267 | renderPlugins(){
268 |
269 | return (
270 |
271 |
272 |
·插件概况
273 | {this.renderCard()}
274 |
275 |
276 | )
277 |
278 | }
279 |
280 | renderMain() {
281 |
282 | const pageStatus = this.state.pageStatus;
283 |
284 | if(pageStatus == 'ready'){
285 |
286 | return (
287 |
288 |
289 | {this.renderPlugins()}
290 |
291 |
292 | )
293 |
294 |
295 | }else{
296 |
297 | return ''
298 | }
299 |
300 | }
301 |
302 | }
303 |
304 | export default PluginGeneral;
--------------------------------------------------------------------------------
/src/ui/sidermenu.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 |
3 | import { Menu } from 'antd'
4 |
5 | const MenuItemGroup = Menu.ItemGroup;
6 | const SubMenu = Menu.SubMenu;
7 |
8 | import { PluginInfoStore, UserInfoStore } from '../store/business.store'
9 |
10 | import {
11 | PluginListModel
12 | } from '../models/plugin.models'
13 | import { UserListModel } from "../models/user_manage_models";
14 |
15 | const pluginInfoStore = PluginInfoStore.getInstance();
16 | const userInfoStore = UserInfoStore.getInstance();
17 |
18 | const pluginListModel = PluginListModel.getInstance();
19 | const userListModel = UserListModel.getInstance();
20 |
21 |
22 | class SideMenu extends Component {
23 |
24 | constructor(props) {
25 |
26 | super(props);
27 |
28 | // 菜单数据
29 | this.menuGroup = [
30 | {
31 | key: 'dashboard',
32 | name: '运行概况报表',
33 | urlpath: '/general/dashboard'
34 | }, {
35 | groupKey: 'router_manager',
36 | groupTitle: '路由管理',
37 | menuList: [
38 | {
39 | key: 'gateway_manage',
40 | name: '网关管理',
41 | urlpath: '/router_manager/gateway_manage'
42 | },
43 | {
44 | key: 'host_manage',
45 | name: '主机管理',
46 | urlpath: '/router_manager/host_manage'
47 | },
48 | {
49 | key: 'route_group',
50 | name: '路由规则管理',
51 | urlpath: '/router_manager/route_group'
52 | },
53 | {
54 | key: 'gray_divide',
55 | name: 'AB分流管理',
56 | urlpath: '/router_manager/gray_divide'
57 | }
58 | ]
59 | },
60 | {
61 | groupKey: 'plugin_manager',
62 | groupTitle: '插件管理',
63 | menuList: [
64 | {
65 | key: 'plugin_general',
66 | name: '插件列表',
67 | urlpath: '/plugin_manager/plugin_general'
68 | }, {
69 | subGroupKey: 'plugin_manager_property',
70 | subTitle: '特征防刷器',
71 | show: true,
72 | subMenuList: [
73 | {
74 | key: 'property_ratelimit',
75 | name: '特征防刷器配置',
76 | urlpath: '/plugin_manager/property_ratelimit'
77 | }, {
78 | key: 'property_isolated',
79 | name: '特征命中列表',
80 | urlpath: '/plugin_manager/property_isolated'
81 | }
82 | ]
83 | }, {
84 | subGroupKey: 'plugin_manager_firewall',
85 | subTitle: '防火墙',
86 | show: true,
87 | subMenuList: [
88 | {
89 | key: 'firewall',
90 | name: '防火墙配置',
91 | urlpath: '/plugin_manager/firewall'
92 | }, {
93 | key: 'firewall_isolated',
94 | name: '防火墙命中列表',
95 | urlpath: '/plugin_manager/firewall_isolated'
96 | }
97 | ]
98 | },
99 | {
100 | subGroupKey: 'plugin_manager_anti_slq',
101 | subTitle: 'SQL注入防控器',
102 | show: true,
103 | subMenuList: [
104 | {
105 | key: 'anti_sql_injection',
106 | name: 'SQL注入防控配置',
107 | urlpath: '/plugin_manager/anti_sql_injection'
108 | }
109 | ]
110 | }
111 | ]
112 | }
113 | ];
114 |
115 |
116 |
117 | this.state = {
118 | menuGroup: this.menuGroup,// 菜单数据
119 | pluginInfo: {},
120 | update: true, //
121 |
122 | selectedMenuKey: this.getSelectedMenuKey(),// 选中的菜单
123 | defaultOpenKeys: this.getDefaultOpenKeys() //默认展开的菜单组
124 |
125 | };
126 |
127 |
128 |
129 | }
130 |
131 |
132 |
133 | componentDidMount() {
134 | this.checkAuthority();
135 | this.getPluginsInfo();
136 | }
137 |
138 | //查看用户权限,并设置管理模块的显示
139 | checkAuthority() {
140 |
141 | const user_router = {
142 | groupKey: 'user_manager',
143 | groupTitle: '用户管理',
144 | menuList: [
145 | {
146 | key: 'user_list',
147 | name: '用户列表',
148 | urlpath: '/user_manager/user_list'
149 | },
150 | {
151 | key: 'operation_log',
152 | name: '操作日志',
153 | urlpath: '/user_manager/operation_log'
154 | }
155 | ]
156 | };
157 | const userInfo = userInfoStore.getData() || {};
158 |
159 | userListModel.setParam({
160 | username: userInfo.userName
161 | }, true)
162 |
163 |
164 | userListModel.excute(res => {
165 | if (res.data[0].is_admin == 1) {
166 | this.setState(prevState => {
167 | const old = prevState.menuGroup;
168 | return {
169 | menuGroup: [...old, user_router]
170 | }
171 | })
172 | }
173 | }, err => {
174 |
175 | })
176 |
177 | }
178 |
179 |
180 |
181 | getPluginsInfo() {
182 |
183 | let pluginInfo;
184 |
185 | const pluginLocalInfo = pluginInfoStore.getData();
186 |
187 | if (pluginLocalInfo) {
188 |
189 | pluginInfo = pluginLocalInfo;
190 |
191 | this.setMenuGroup(pluginInfo);
192 |
193 | } else {
194 |
195 | this.fetchPluginStatus(this.setMenuGroup.bind(this))
196 |
197 | }
198 |
199 |
200 |
201 | }
202 | //
203 | /*
204 | 设置插件管理动态菜单
205 | 根据插件的启用禁用状态动态显示侧边栏餐带
206 | */
207 | fetchPluginStatus(cb) {
208 |
209 | pluginListModel.excute(res => {
210 |
211 | const resData = res.data.plugins || [];
212 |
213 | let pluginStatus = {};
214 |
215 | resData.forEach(item => {
216 |
217 | pluginStatus[item.plugin_name] = item.enable;
218 |
219 | })
220 |
221 | pluginInfoStore.setData(pluginStatus);
222 |
223 | cb && cb(pluginStatus);
224 |
225 | }, err => {
226 |
227 | })
228 |
229 | }
230 |
231 | setMenuGroup(pluginInfo) {
232 |
233 | const pluginStatus = pluginInfo ? pluginInfo : this.props.pluginStatus;
234 |
235 | const udpate = this.state.update;
236 |
237 | let menuGroup = this.state.menuGroup, isUpdated = 0;
238 |
239 | if (!pluginStatus) { return menuGroup }
240 |
241 | let subMenuList, temp, subTemp, show;
242 |
243 | let newNemu = menuGroup.map(item => {
244 |
245 | temp = item;
246 |
247 | if (item.groupKey == 'plugin_manager') {
248 |
249 | subMenuList = item.menuList.map(subItem => {
250 |
251 | subTemp = subItem;
252 |
253 | if (subItem.subGroupKey == 'plugin_manager_property') {
254 | subTemp.show = pluginStatus.property_rate_limit == 1 ? 1 : 0;
255 |
256 | } else if (subItem.subGroupKey == 'plugin_manager_firewall') {
257 | subTemp.show = pluginStatus.waf == 1 ? 1 : 0;
258 | } else if (subItem.subGroupKey == 'plugin_manager_anti_slq') {
259 | subTemp.show = pluginStatus.anti_sql_injection == 1 ? 1 : 0;
260 | }
261 |
262 | if (subTemp.show !== subItem.show) {
263 | isUpdated++;
264 | }
265 |
266 | return subTemp
267 |
268 | })
269 |
270 | temp.menuList = subMenuList;
271 |
272 | }
273 |
274 |
275 | return temp
276 |
277 | })
278 |
279 | // return newNemu;
280 |
281 | this.setState({
282 | menuGroup: newNemu
283 | })
284 |
285 | }
286 |
287 | getSelectedMenuKey() {
288 |
289 | const routepath = this.props.routepath;
290 |
291 | const pathArr = routepath.split('/');
292 |
293 | const key = pathArr[pathArr.length - 1] || 'index';
294 |
295 | return [key];
296 |
297 | }
298 |
299 | getDefaultOpenKeys() {
300 |
301 | const routepath = this.props.routepath;
302 |
303 | const pathArr = routepath.split('/');
304 |
305 | const key = pathArr[pathArr.length - 2] || 'index';
306 |
307 | return [key];
308 |
309 | }
310 |
311 | onClickMenu(menuData) {
312 | var urlpath = menuData.item.props.data_urlpath;
313 |
314 | this.gotoMenuUrl(urlpath);
315 | }
316 |
317 | gotoMenuUrl(urlpath) {
318 |
319 | location.href = urlpath
320 |
321 | }
322 |
323 | renderMenuItems(menuList) {
324 |
325 | const self = this;
326 |
327 | return menuList.map(function (item, idx) {
328 |
329 | if (item.subMenuList) {
330 |
331 | if (item.show) {
332 | return (
333 |
334 |
335 | {self.renderMenuItems(item.subMenuList)}
336 |
337 |
338 | )
339 | }
340 |
341 | } else {
342 | return ({item.name} )
343 |
344 | }
345 |
346 |
347 | });
348 |
349 | }
350 |
351 | renderMenu() {
352 |
353 | var self = this;
354 |
355 | const menuGroup = this.state.menuGroup;
356 |
357 | return menuGroup.map(function (group, idx) {
358 |
359 | if (group.menuList && group.menuList.length > 0) {
360 |
361 | return (
362 |
363 | {self.renderMenuItems(group.menuList)}
364 |
365 | )
366 |
367 | } else {
368 |
369 | return (
370 | {group.name}
371 | )
372 |
373 | }
374 | });
375 |
376 | }
377 |
378 | render() {
379 |
380 | return (
381 |
387 | {this.renderMenu()}
388 |
389 | );
390 |
391 | }
392 |
393 | }
394 |
395 | export default SideMenu;
396 |
--------------------------------------------------------------------------------
/src/view/general/dashboard.js:
--------------------------------------------------------------------------------
1 | import React, {Component} from 'react'
2 | import BaseView from '../../core/view.base'
3 |
4 | import {getHttpAuth,gotoPage} from '../../util/util'
5 |
6 | import {
7 | DashboardListModel
8 | } from '../../models/general_models'
9 |
10 | import {Avatar, Button,Row,Col,Divider,Icon,Card,notification,Table, Spin} from 'antd'
11 |
12 | const { Meta } = Card;
13 |
14 | let dashboardListModel = DashboardListModel.getInstance();
15 |
16 | class Dashboard extends BaseView {
17 |
18 | constructor(props) {
19 |
20 | super(props);
21 |
22 | this.state = {
23 | baseInfo:{},
24 | requestInfo:{},
25 | baseInfoflag:true,
26 | pageStatus:'init'
27 | }
28 |
29 | this.leavePage = false;
30 |
31 | this.indata = {
32 | baseInfoTableColumns:[
33 | {
34 | title: '所属网关',
35 | dataIndex: 'service_name',
36 | align:'center'
37 | },
38 | {
39 | title: 'NgRouter版本',
40 | dataIndex: 'ngr_version',
41 | align:'center'
42 | },{
43 | title: 'NGINX版本',
44 | dataIndex: 'nginx_version',
45 | align:'center'
46 | },{
47 | title: 'LUA版本',
48 | dataIndex: 'ngx_lua_version',
49 | align:'center'
50 | },{
51 | title: '错误日志级别',
52 | dataIndex: 'error_log_level',
53 | align:'center'
54 | },{
55 | title: '最近启动时间',
56 | dataIndex: 'start_time',
57 | align:'center'
58 | },{
59 | title: '部署节点',
60 | dataIndex:'targets',
61 | align:'center'
62 | }
63 | ],
64 | requestInfoTableColumns:[
65 | {
66 | title: '所属网关',
67 | dataIndex: 'service_name',
68 | align:'center'
69 | },{
70 | title: '总请求次数',
71 | dataIndex: 'total_count',
72 | align:'center'
73 | },{
74 | title: '成功请求次数',
75 | dataIndex: 'total_success_count',
76 | align:'center'
77 | },{
78 | title: '请求总字节',
79 | dataIndex: 'traffic_write',
80 | align:'center'
81 | },{
82 | title: '响应总字节',
83 | dataIndex: 'traffic_read',
84 | align:'center'
85 | },
86 | {
87 | title: '200请求次数',
88 | dataIndex: 'request_2xx',
89 | align:'center'
90 | },
91 | {
92 | title: '300请求次数',
93 | dataIndex: 'request_3xx',
94 | align:'center'
95 | },
96 | {
97 | title: '400请求次数',
98 | dataIndex: 'request_4xx',
99 | align:'center'
100 | },
101 | {
102 | title: '500请求次数',
103 | dataIndex: 'request_5xx',
104 | align:'center'
105 | }
106 | ]
107 | }
108 | }
109 |
110 | cellTarget(text,record,index){
111 | let show_txt = '';
112 |
113 | let targets = record.targets;
114 | if(targets!=null) {
115 | for (let i = 0; i < targets.length; i++) {
116 | let ip = targets[i];
117 | show_txt += ip + "|";
118 | }
119 |
120 | if (show_txt.length > 0) {
121 | show_txt = show_txt.substr(0, show_txt.length - 1);
122 | }
123 | }
124 |
125 | return (
126 | {show_txt}
127 | )
128 | }
129 |
130 | componentDidMount(){
131 | this.fetchDashboardList();
132 | }
133 |
134 | componentWillUnmount(){
135 |
136 | this.leavePage = true;
137 | }
138 |
139 |
140 | fetchDashboardList(){
141 |
142 | const self = this;
143 | this.setState({
144 | baseInfoflag:true
145 | })
146 | dashboardListModel.excute(res=>{
147 |
148 | const resData = res.data || {};
149 |
150 | const data = self.formatData(resData);
151 | self.setState({
152 | baseInfo:data.baseInfo,
153 | requestInfo:data.requestInfo,
154 | pageStatus:'ready',
155 | baseInfoflag:false
156 | })
157 |
158 | const config = {
159 | description:'列表更新成功',
160 | type:'success'
161 | }
162 |
163 | self.showNotification(config);
164 |
165 | },err=>{
166 | console.log(err)
167 | })
168 |
169 | }
170 |
171 | getHttpAuth(){
172 |
173 | getHttpAuth(this.fetchDashboardList.bind(this));
174 |
175 | function faild(){
176 | gotoPage('/login');
177 | }
178 |
179 | }
180 |
181 | showNotification(param){
182 |
183 | if(!param.description){return};
184 |
185 | if(param.type == 'success'){
186 |
187 | notification.success({
188 | message:param.message || '',
189 | description: param.description || '',
190 | duration:2
191 | });
192 |
193 | }else{
194 |
195 | notification.error({
196 | message:param.message || '',
197 | description: param.description || '',
198 | duration:2
199 | });
200 |
201 | }
202 | }
203 |
204 | formatData(data){
205 |
206 | const baseInfo = data.base_infos || {},
207 | requestInfo = data.request_infos || {};
208 | return {
209 | baseInfo:this.formatBaseListData(baseInfo),
210 | requestInfo:this.formatRequestListData(requestInfo)
211 | }
212 |
213 | }
214 |
215 | formatBaseListData(listData){
216 |
217 | return listData.map((item,idx)=>{
218 | return {
219 | service_name:item.service_name || '',
220 | ngr_version:item.ngr_version || '',
221 | nginx_version:item.nginx_version || '',
222 | ngx_lua_version:item.ngx_lua_version,
223 | error_log_level:item.error_log_level,
224 | start_time:item.start_time,
225 | targets:item.targets,
226 | }
227 |
228 | })
229 | }
230 |
231 |
232 | formatRequestListData(listData){
233 |
234 | return listData.map((item,idx)=>{
235 | return {
236 | service_name:item.service_name || '',
237 | total_count:item.total_count,
238 | total_success_count:item.total_success_count,
239 | traffic_write:item.traffic_write,
240 | traffic_read:item.traffic_read,
241 | request_2xx:item.request_2xx,
242 | request_3xx:item.request_3xx,
243 | request_4xx:item.request_4xx,
244 | request_5xx:item.request_5xx
245 | }
246 |
247 | })
248 | }
249 |
250 | renderBaseInfo(){
251 |
252 | const baseInfo = this.state.baseInfo;
253 |
254 | return (
255 |
256 |
257 |
基础信息
258 |
259 |
266 |
267 |
268 |
269 | )
270 |
271 | }
272 |
273 | renderRequestInfo(){
274 |
275 | const requestInfo = this.state.requestInfo;
276 |
277 | return (
278 |
279 |
289 |
290 | )
291 |
292 | }
293 |
294 | renderMain() {
295 |
296 | const pageStatus = this.state.pageStatus;
297 |
298 | if(pageStatus == 'ready'){
299 |
300 | return (
301 |
302 |
303 | 刷新
304 |
305 | {this.renderBaseInfo()}
306 | {this.renderRequestInfo()}
307 |
308 |
309 | )
310 |
311 |
312 | }else{
313 |
314 | return ''
315 | }
316 |
317 | }
318 |
319 | }
320 |
321 | export default Dashboard;
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 |
2 | Apache License
3 | Version 2.0, January 2004
4 | http://www.apache.org/licenses/
5 |
6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7 |
8 | 1. Definitions.
9 |
10 | "License" shall mean the terms and conditions for use, reproduction,
11 | and distribution as defined by Sections 1 through 9 of this document.
12 |
13 | "Licensor" shall mean the copyright owner or entity authorized by
14 | the copyright owner that is granting the License.
15 |
16 | "Legal Entity" shall mean the union of the acting entity and all
17 | other entities that control, are controlled by, or are under common
18 | control with that entity. For the purposes of this definition,
19 | "control" means (i) the power, direct or indirect, to cause the
20 | direction or management of such entity, whether by contract or
21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
22 | outstanding shares, or (iii) beneficial ownership of such entity.
23 |
24 | "You" (or "Your") shall mean an individual or Legal Entity
25 | exercising permissions granted by this License.
26 |
27 | "Source" form shall mean the preferred form for making modifications,
28 | including but not limited to software source code, documentation
29 | source, and configuration files.
30 |
31 | "Object" form shall mean any form resulting from mechanical
32 | transformation or translation of a Source form, including but
33 | not limited to compiled object code, generated documentation,
34 | and conversions to other media types.
35 |
36 | "Work" shall mean the work of authorship, whether in Source or
37 | Object form, made available under the License, as indicated by a
38 | copyright notice that is included in or attached to the work
39 | (an example is provided in the Appendix below).
40 |
41 | "Derivative Works" shall mean any work, whether in Source or Object
42 | form, that is based on (or derived from) the Work and for which the
43 | editorial revisions, annotations, elaborations, or other modifications
44 | represent, as a whole, an original work of authorship. For the purposes
45 | of this License, Derivative Works shall not include works that remain
46 | separable from, or merely link (or bind by name) to the interfaces of,
47 | the Work and Derivative Works thereof.
48 |
49 | "Contribution" shall mean any work of authorship, including
50 | the original version of the Work and any modifications or additions
51 | to that Work or Derivative Works thereof, that is intentionally
52 | submitted to Licensor for inclusion in the Work by the copyright owner
53 | or by an individual or Legal Entity authorized to submit on behalf of
54 | the copyright owner. For the purposes of this definition, "submitted"
55 | means any form of electronic, verbal, or written communication sent
56 | to the Licensor or its representatives, including but not limited to
57 | communication on electronic mailing lists, source code control systems,
58 | and issue tracking systems that are managed by, or on behalf of, the
59 | Licensor for the purpose of discussing and improving the Work, but
60 | excluding communication that is conspicuously marked or otherwise
61 | designated in writing by the copyright owner as "Not a Contribution."
62 |
63 | "Contributor" shall mean Licensor and any individual or Legal Entity
64 | on behalf of whom a Contribution has been received by Licensor and
65 | subsequently incorporated within the Work.
66 |
67 | 2. Grant of Copyright License. Subject to the terms and conditions of
68 | this License, each Contributor hereby grants to You a perpetual,
69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70 | copyright license to reproduce, prepare Derivative Works of,
71 | publicly display, publicly perform, sublicense, and distribute the
72 | Work and such Derivative Works in Source or Object form.
73 |
74 | 3. Grant of Patent License. Subject to the terms and conditions of
75 | this License, each Contributor hereby grants to You a perpetual,
76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77 | (except as stated in this section) patent license to make, have made,
78 | use, offer to sell, sell, import, and otherwise transfer the Work,
79 | where such license applies only to those patent claims licensable
80 | by such Contributor that are necessarily infringed by their
81 | Contribution(s) alone or by combination of their Contribution(s)
82 | with the Work to which such Contribution(s) was submitted. If You
83 | institute patent litigation against any entity (including a
84 | cross-claim or counterclaim in a lawsuit) alleging that the Work
85 | or a Contribution incorporated within the Work constitutes direct
86 | or contributory patent infringement, then any patent licenses
87 | granted to You under this License for that Work shall terminate
88 | as of the date such litigation is filed.
89 |
90 | 4. Redistribution. You may reproduce and distribute copies of the
91 | Work or Derivative Works thereof in any medium, with or without
92 | modifications, and in Source or Object form, provided that You
93 | meet the following conditions:
94 |
95 | (a) You must give any other recipients of the Work or
96 | Derivative Works a copy of this License; and
97 |
98 | (b) You must cause any modified files to carry prominent notices
99 | stating that You changed the files; and
100 |
101 | (c) You must retain, in the Source form of any Derivative Works
102 | that You distribute, all copyright, patent, trademark, and
103 | attribution notices from the Source form of the Work,
104 | excluding those notices that do not pertain to any part of
105 | the Derivative Works; and
106 |
107 | (d) If the Work includes a "NOTICE" text file as part of its
108 | distribution, then any Derivative Works that You distribute must
109 | include a readable copy of the attribution notices contained
110 | within such NOTICE file, excluding those notices that do not
111 | pertain to any part of the Derivative Works, in at least one
112 | of the following places: within a NOTICE text file distributed
113 | as part of the Derivative Works; within the Source form or
114 | documentation, if provided along with the Derivative Works; or,
115 | within a display generated by the Derivative Works, if and
116 | wherever such third-party notices normally appear. The contents
117 | of the NOTICE file are for informational purposes only and
118 | do not modify the License. You may add Your own attribution
119 | notices within Derivative Works that You distribute, alongside
120 | or as an addendum to the NOTICE text from the Work, provided
121 | that such additional attribution notices cannot be construed
122 | as modifying the License.
123 |
124 | You may add Your own copyright statement to Your modifications and
125 | may provide additional or different license terms and conditions
126 | for use, reproduction, or distribution of Your modifications, or
127 | for any such Derivative Works as a whole, provided Your use,
128 | reproduction, and distribution of the Work otherwise complies with
129 | the conditions stated in this License.
130 |
131 | 5. Submission of Contributions. Unless You explicitly state otherwise,
132 | any Contribution intentionally submitted for inclusion in the Work
133 | by You to the Licensor shall be under the terms and conditions of
134 | this License, without any additional terms or conditions.
135 | Notwithstanding the above, nothing herein shall supersede or modify
136 | the terms of any separate license agreement you may have executed
137 | with Licensor regarding such Contributions.
138 |
139 | 6. Trademarks. This License does not grant permission to use the trade
140 | names, trademarks, service marks, or product names of the Licensor,
141 | except as required for reasonable and customary use in describing the
142 | origin of the Work and reproducing the content of the NOTICE file.
143 |
144 | 7. Disclaimer of Warranty. Unless required by applicable law or
145 | agreed to in writing, Licensor provides the Work (and each
146 | Contributor provides its Contributions) on an "AS IS" BASIS,
147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148 | implied, including, without limitation, any warranties or conditions
149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150 | PARTICULAR PURPOSE. You are solely responsible for determining the
151 | appropriateness of using or redistributing the Work and assume any
152 | risks associated with Your exercise of permissions under this License.
153 |
154 | 8. Limitation of Liability. In no event and under no legal theory,
155 | whether in tort (including negligence), contract, or otherwise,
156 | unless required by applicable law (such as deliberate and grossly
157 | negligent acts) or agreed to in writing, shall any Contributor be
158 | liable to You for damages, including any direct, indirect, special,
159 | incidental, or consequential damages of any character arising as a
160 | result of this License or out of the use or inability to use the
161 | Work (including but not limited to damages for loss of goodwill,
162 | work stoppage, computer failure or malfunction, or any and all
163 | other commercial damages or losses), even if such Contributor
164 | has been advised of the possibility of such damages.
165 |
166 | 9. Accepting Warranty or Additional Liability. While redistributing
167 | the Work or Derivative Works thereof, You may choose to offer,
168 | and charge a fee for, acceptance of support, warranty, indemnity,
169 | or other liability obligations and/or rights consistent with this
170 | License. However, in accepting such obligations, You may act only
171 | on Your own behalf and on Your sole responsibility, not on behalf
172 | of any other Contributor, and only if You agree to indemnify,
173 | defend, and hold each Contributor harmless for any liability
174 | incurred by, or claims asserted against, such Contributor by reason
175 | of your accepting any such warranty or additional liability.
176 |
177 | END OF TERMS AND CONDITIONS
178 |
179 | APPENDIX: How to apply the Apache License to your work.
180 |
181 | To apply the Apache License to your work, attach the following
182 | boilerplate notice, with the fields enclosed by brackets "[]"
183 | replaced with your own identifying information. (Don't include
184 | the brackets!) The text should be enclosed in the appropriate
185 | comment syntax for the file format. We also recommend that a
186 | file or class name and description of purpose be included on the
187 | same "printed page" as the copyright notice for easier
188 | identification within third-party archives.
189 |
190 | Copyright [2019] [GOGO-EASY TEAM]
191 |
192 | Licensed under the Apache License, Version 2.0 (the "License");
193 | you may not use this file except in compliance with the License.
194 | You may obtain a copy of the License at
195 |
196 | http://www.apache.org/licenses/LICENSE-2.0
197 |
198 | Unless required by applicable law or agreed to in writing, software
199 | distributed under the License is distributed on an "AS IS" BASIS,
200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201 | See the License for the specific language governing permissions and
202 | limitations under the License.
203 |
--------------------------------------------------------------------------------
/src/view/plugin_manager/property_isolated.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { Layout, Form, Button, Input, Radio, Row, Col, Divider, Popconfirm, Table, notification, Icon, Select, DatePicker } from 'antd'
3 |
4 | import BaseView from '../../core/view.base'
5 | import FieldsContent from '../../ui/ui.fiedld'
6 |
7 | import moment from 'moment';
8 |
9 | import {
10 | BlockedListModel,
11 | LimitListModel
12 | } from '../../models/proprety_ratelimit_models'
13 | import {GatewayListModel, HostListModel} from "../../models/host_manager_models";
14 |
15 |
16 | const { Header, Content } = Layout;
17 | const FormItem = Form.Item;
18 |
19 | let blockedListModel = BlockedListModel.getInstance(),
20 | limitListModel = LimitListModel.getInstance(),
21 | gatewayListModel = GatewayListModel.getInstance(),
22 | hostListModel = HostListModel.getInstance();
23 |
24 | class PropretyRateLimit extends BaseView {
25 |
26 | constructor(props) {
27 |
28 | super(props);
29 |
30 | this.state = {
31 |
32 | listData: [],
33 |
34 | searchData: {
35 | date: moment().format('YYYY-MM-DD'), //今天
36 | tableType: '0' //表类型 默认防阻止
37 | }
38 | }
39 |
40 | this.indata = {
41 |
42 | tableColumns: [
43 | {
44 | title: '序号',
45 | dataIndex: 'idx',
46 | width: '50px',
47 | align: 'center',
48 | render: this.renderCollapse.bind(this, 'idx')
49 | },
50 | {
51 | title: '特征防刷器名称',
52 | dataIndex: 'name',
53 | width: '150px',
54 | align: 'center',
55 | render: this.renderCollapse.bind(this, 'name')
56 | },
57 | {
58 | title: '所属网关编码',
59 | dataIndex: 'gateway_code',
60 | width: '150px',
61 | align: 'center'
62 | },
63 | {
64 | title: '所属主机',
65 | dataIndex: 'host',
66 | width: '150px',
67 | align: 'center'
68 | },
69 | {
70 | title: '特征详情',
71 | dataIndex: 'property_detail',
72 | align: 'center',
73 | children: [
74 | {
75 | title: '特征类型',
76 | dataIndex: 'property_type',
77 | key: 'property_type',
78 | width: '100px',
79 | align: 'center'
80 | },
81 | {
82 | title: '特征属性名称',
83 | dataIndex: 'property_name',
84 | key: 'property_name',
85 | width: '100px',
86 | align: 'center'
87 | },
88 | {
89 | title: '特征值',
90 | dataIndex: 'actual_value',
91 | key: 'actual_value',
92 | width: '100px',
93 | align: 'center'
94 | }
95 | ]
96 | },
97 | ],
98 |
99 | limitColumn: [
100 | {
101 | title: '命中时间',
102 | dataIndex: 'limit_time',
103 | width: '100px',
104 | align: 'center'
105 | },
106 | {
107 | title: '命中日期',
108 | dataIndex: 'limit_date',
109 | width: '100px',
110 | align: 'center'
111 | }
112 | ],
113 | blockedColumn: [
114 | {
115 | title: '命中时间',
116 | dataIndex: 'blocked_time',
117 | width: '100px',
118 | align: 'center',
119 | render: this.renderCollapse.bind(this, 'blocked_time')
120 | },
121 | {
122 | title: '命中日期',
123 | dataIndex: 'blocked_date',
124 | width: '100px',
125 | align: 'center',
126 | render: this.renderCollapse.bind(this, 'blocked_date')
127 | }
128 | ],
129 |
130 | searchFieldsArr: [
131 | {
132 | key: 'tableType',
133 | type: 'select',
134 | label: '命中类型',
135 | options: [
136 | {
137 | value: '0',
138 | desc: '限速且阻塞'
139 | }, {
140 | value: '1',
141 | desc: '仅限速'
142 | }
143 | ]
144 | },
145 | {
146 | key: 'date',
147 | type: 'select',
148 | label: '日期',
149 | options: this.getDateOptions()
150 | },
151 | {
152 | key:'gateway_code',
153 | label:'所属服务网关',
154 | type:'select',
155 | options:[
156 | {
157 | value:'defaultValue',
158 | desc:'请选择...'
159 | }
160 | ]
161 |
162 | },
163 | {
164 | key:'host_id',
165 | label:'所属主机',
166 | type:'select',
167 | options:[
168 | {
169 | value:'defaultValue',
170 | desc:'请选择...'
171 | }
172 | ]
173 |
174 | }
175 | ]
176 |
177 | }
178 |
179 | }
180 | renderCollapse (type, value, row) {
181 | const res = {
182 | children: value,
183 | props: {
184 | rowSpan: 1
185 | }
186 | }
187 | if ( /^idx|name|blocked_time|blocked_date$/.test(type) ) {
188 | res.props.rowSpan = row.rowSpan
189 | }
190 |
191 | return res
192 | }
193 |
194 | componentDidMount() {
195 | //this.fetchList();
196 | this.fetchGateway();
197 | }
198 |
199 | fetchGateway(){
200 | let self = this;
201 | let searchFieldsArr = this.indata.searchFieldsArr;
202 | return new Promise((resolve, reject) => {
203 | gatewayListModel.excute((res)=>{
204 | let listData = res.data;
205 | let option = {};
206 | option.gateway_codes = [];
207 | listData.forEach((item,idx)=>{
208 | option.gateway_codes.push({value:item.id,desc:item.gateway_code});
209 | })
210 |
211 | // 初始化查询条件gateway_code
212 | searchFieldsArr[2].options =searchFieldsArr[2].options.concat(option.gateway_codes);
213 | self.setState({searchFieldsArr:searchFieldsArr})
214 | resolve(res)
215 | },(err)=>{
216 | reject(err)
217 | })
218 | }).catch(err => {
219 |
220 | })
221 | }
222 |
223 | fetchSearchHost(gateway_id){
224 | let self = this;
225 | let searchFieldsArr = this.indata.searchFieldsArr;
226 | const param = {}
227 | param.gateway_id = gateway_id
228 | hostListModel.setParam(param)
229 | return new Promise((resolve, reject) => {
230 |
231 | hostListModel.excute((res)=>{
232 | let listData = res.data;
233 | let option = {};
234 | option.hosts = [];
235 | option.hosts.push({value:"defaultValue",desc:"请选择..."});
236 | listData.forEach((item,idx)=>{
237 | option.hosts.push({value:item.id,desc:item.host});
238 | })
239 |
240 | searchFieldsArr[3].options =option.hosts;
241 | self.setState({searchFieldsArr:searchFieldsArr})
242 | resolve(res)
243 | },(err)=>{
244 | reject(err)
245 | })
246 | }).catch(err => {
247 |
248 | })
249 | }
250 |
251 | getDateOptions() {
252 |
253 | let options = [], value = '', temp = '', desc = '';
254 |
255 | for (var i = 0; i < 5; i++) {
256 | value = moment().subtract(i, 'days').format('YYYY-MM-DD');
257 | temp = value.split('-');
258 | desc = `${temp[0]}年${temp[1]}月${temp[2]}日`;
259 | options.push({
260 | value: value,
261 | desc: desc
262 | })
263 | }
264 |
265 | return options
266 |
267 | }
268 |
269 | fetchList() {
270 | const searchData = this.state.searchData;
271 |
272 | const searchFn = searchData.tableType == '0' ? blockedListModel : limitListModel;
273 | const param = {}
274 | param.date = searchData.date
275 |
276 | if(searchData.gateway_code && searchData.gateway_code !== 'defaultValue'){
277 | param.gateway_id = searchData.gateway_code
278 | }else {
279 | const noticeConfig = {
280 | description:'所属网关编码不能为空',
281 | type:'faild'
282 | }
283 | this.showNotification(noticeConfig);
284 | return
285 | }
286 |
287 | if(searchData.host_id && searchData.host_id !== 'defaultValue'){
288 | param.host_id = searchData.host_id
289 | }else {
290 | const noticeConfig = {
291 | description:'所属主机不能为空',
292 | type:'faild'
293 | }
294 | this.showNotification(noticeConfig);
295 | return
296 | }
297 |
298 | searchFn.setParam(param, true);
299 |
300 | searchFn.excute(this.seachSuccess.bind(this), this.seachFaild.bind(this));
301 |
302 | }
303 |
304 | seachSuccess(res) {
305 |
306 | const resData = res.data || [];
307 |
308 | const listData = this.formatListData(resData);
309 |
310 | this.setState({
311 | listData
312 | })
313 |
314 | }
315 |
316 | seachFaild(err) {
317 |
318 | this.setState({
319 | listData: []
320 | })
321 |
322 | const noticeConfig = {
323 | description:err.msg || '查询失败',
324 | type:'faild'
325 | }
326 |
327 | this.showNotification(noticeConfig);
328 |
329 | }
330 |
331 | formatListData(listData) {
332 | const res = []
333 |
334 | listData.map((item, idx) => {
335 | return (item.property_detail || []).forEach((details, index) => {
336 | res.push({
337 | rowSpan: index === 0 ? item.property_detail.length : 0,
338 | key: res.length + 1,
339 | idx: idx + 1,
340 | name: item.name || '',
341 | gateway_code:item.gateway_code,
342 | host:item.host,
343 | id: item.id || '',
344 | blocked_time: item.blocked_time || '',
345 | limit_time: item.limit_time || '',
346 | limit_date: item.limit_date || '',
347 | blocked_date: item.blocked_date || '',
348 | property_type: details.property_type,
349 | property_name: details.property_name,
350 | actual_value: details.actual_value
351 | })
352 | })
353 |
354 | })
355 | return res
356 |
357 | }
358 |
359 | searchInputChange(key, e) {
360 |
361 | const target = e.target;
362 |
363 | const value = target ? target.value : e;
364 |
365 | let searchData = this.state.searchData;
366 |
367 | if(key =='gateway_code') {
368 |
369 | if(value =='defaultValue'){
370 | let searchFieldsArr = this.indata.searchFieldsArr;
371 | searchFieldsArr[3].options =[{value:"defaultValue",desc:"请选择..."}];
372 | searchData["host_id"]=''
373 | }
374 |
375 | this.fetchSearchHost(value)
376 | searchData["host_id"]=''
377 |
378 | }
379 | searchData[key] = value;
380 |
381 | this.setState({
382 | searchData: searchData
383 | })
384 |
385 | }
386 |
387 | handleSearch(e) {
388 |
389 | e.preventDefault();
390 |
391 | this.fetchList();
392 |
393 | }
394 |
395 | renderSearchBar() {
396 |
397 | const InputChangeHandle = this.searchInputChange.bind(this);
398 |
399 | const fieldsValue = this.state.searchData;
400 |
401 | let fieldsArr = this.indata.searchFieldsArr;
402 |
403 | const spanNum = 6;
404 |
405 | const fieldsConfig = { InputChangeHandle, fieldsValue, fieldsArr, spanNum };
406 |
407 | return (
408 |
412 |
413 |
414 |
415 |
416 |
417 |
422 | 查询
423 |
424 |
425 |
426 |
427 |
428 | )
429 |
430 | }
431 |
432 | renderMain() {
433 |
434 | const tableType = this.state.searchData.tableType,
435 | listData = this.state.listData;
436 |
437 | let tableColumns = this.indata.tableColumns;
438 |
439 | const blockedColumn = this.indata.blockedColumn,
440 | limitColumn = this.indata.limitColumn;
441 | const column = tableType == '0' ? blockedColumn : limitColumn;
442 |
443 | tableColumns = tableColumns.concat(column);
444 |
445 | const title = tableType == '0' ? '限速且阻塞命中列表' : '限速命中列表';
446 |
447 | return (
448 |
449 |
450 | {this.renderSearchBar()}
451 |
452 | { return ({title} ) }}
456 | bordered
457 | pagination={ false }
458 | expandRowByClick={true}
459 | pageSize={10}
460 | />
461 |
462 |
463 |
464 |
465 | );
466 |
467 | }
468 |
469 | showNotification(param){
470 |
471 | if(!param.description){return};
472 |
473 | if(param.type == 'warning'){
474 |
475 | notification.warning({
476 | message:param.message || '',
477 | description: param.description || '',
478 | duration:2
479 | });
480 |
481 |
482 | }else if(param.type == 'success'){
483 |
484 | notification.success({
485 | message:param.message || '',
486 | description: param.description || '',
487 | duration:2
488 | });
489 |
490 | }else{
491 |
492 | notification.error({
493 | message:param.message || '',
494 | description: param.description || '',
495 | duration:2
496 | });
497 |
498 | }
499 | }
500 |
501 | }
502 |
503 | export default PropretyRateLimit;
504 |
--------------------------------------------------------------------------------