├── 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 | 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 | [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://github.com/gogo-easy/ngrAdminPortal/blob/master/LICENSE) [![Version](https://img.shields.io/github/v/release/gogo-easy/ngrAdminPortal)](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 |
34 | 35 | 36 | {getFieldDecorator('gateway_code', { 37 | })( 38 | 39 | )} 40 | 41 | 42 | 43 | 44 | 45 | 48 | 49 | 50 | 51 |
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 |
66 |

操作记录日志

67 | 68 | 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 | 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 | 34 | 35 | 36 | 37 | {getFieldDecorator('userName', { 38 | })( 39 | 40 | )} 41 | 42 | 43 | {getFieldDecorator('phoneNumber', { 44 | })( 45 | 46 | )} 47 | 48 | 49 | 50 | 51 | 52 | 55 | 56 | 57 | 58 | 59 | 60 | 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 | 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 |
54 | 55 | 56 | 57 | 58 |
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 | 50 | 51 | 52 | {getFieldDecorator('user_name', { 53 | })( 54 | 55 | )} 56 | 57 | 61 | {getFieldDecorator('range_time', rangeConfig)( 62 | 63 | )} 64 | 65 | 66 | {getFieldDecorator('operation_type', { 67 | })( 68 | 69 | )} 70 | 71 | 72 | 73 | 74 | 75 | 76 | 79 | 80 | 81 | 82 | 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 | 77 | 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 | 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 | 51 | { 52 | this.props.editStatus ? 用户名:{this.props.username} 53 | : renderItem(this.props)("username", "用 户 名", true) 54 | } 55 | { 56 | this.props.editStatus ? null 57 | : ( 58 | 59 | {getFieldDecorator("password", { 60 | initialValue: this.props["password"], 61 | rules: [{ 62 | required: true, 63 | validator: this.checkPassWord 64 | }], 65 | })( 66 | 67 | )} 68 | 69 | ) 70 | } 71 | {renderItem(this.props)("superior", "上 级")} 72 | {renderItem(this.props)("mobile", "手 机")} 73 | {renderItem(this.props)("email", "邮 箱")} 74 | 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 | 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 | 41 |
NgRouter API网关管理控制台
42 | 43 | {getFieldDecorator('userName', { 44 | rules: [{ required: true, message: '请输入用户名' }], 45 | })( 46 | } placeholder="用户名" /> 47 | )} 48 | 49 | 50 | {getFieldDecorator('password', { 51 | rules: [{ required: true, message: '请输入密码' }], 52 | })( 53 | } type="password" placeholder="密码" /> 54 | )} 55 | 56 | 57 | {getFieldDecorator('remember', { 58 | valuePropName: 'checked', 59 | initialValue: true, 60 | })( 61 | 记住我 62 | )} 63 | Forgot password 64 | 67 | 68 |
Copyright @ 2019-2020 GoGo Easy Team
69 | 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 | 49 | {renderInput(props)("gateway_code", "网关编码:", false)} 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 | 98 | {getFieldDecorator('content_type', { 99 | initialValue: props["content_type"] ? props["content_type"] : undefined, 100 | })( 101 | 107 | )} 108 | 109 | 110 | 111 | 115 | {getFieldDecorator('message', { 116 | initialValue: props["message"], 117 | rules: [{ 118 | validator: this.checkMessge, 119 | }], 120 | 121 | })( 122 |