init
This commit is contained in:
2
visual/sentinel-dashboard-pro/src/main/webapp/resources/.gitignore
vendored
Normal file
2
visual/sentinel-dashboard-pro/src/main/webapp/resources/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
node_modules/
|
||||
tmp/
|
||||
@@ -0,0 +1,67 @@
|
||||
{
|
||||
/*
|
||||
* ENVIRONMENTS
|
||||
* =================
|
||||
*/
|
||||
|
||||
// Define globals exposed by modern browsers.
|
||||
"browser": true,
|
||||
|
||||
// Define globals exposed by jQuery.
|
||||
"jquery": true,
|
||||
|
||||
// Define globals exposed by Node.js.
|
||||
"node": true,
|
||||
|
||||
// Allow ES6.
|
||||
"esversion": 6,
|
||||
|
||||
/*
|
||||
* ENFORCING OPTIONS
|
||||
* =================
|
||||
*/
|
||||
|
||||
// Force all variable names to use either camelCase style or UPPER_CASE
|
||||
// with underscores.
|
||||
"camelcase": true,
|
||||
|
||||
// Prohibit use of == and != in favor of === and !==.
|
||||
"eqeqeq": true,
|
||||
|
||||
// Enforce tab width of 2 spaces.
|
||||
"indent": 2,
|
||||
|
||||
// Prohibit use of a variable before it is defined.
|
||||
"latedef": true,
|
||||
|
||||
// Enforce line length to 100 characters
|
||||
"maxlen": 100,
|
||||
|
||||
// Require capitalized names for constructor functions.
|
||||
"newcap": true,
|
||||
|
||||
// Enforce use of single quotation marks for strings.
|
||||
"quotmark": "single",
|
||||
|
||||
// Enforce placing 'use strict' at the top function scope
|
||||
// 前端项目中外层使用 strict 即可,覆盖此条规则
|
||||
"strict": false,
|
||||
|
||||
// Prohibit use of explicitly undeclared variables.
|
||||
"undef": true,
|
||||
|
||||
// Warn when variables are defined but never used.
|
||||
"unused": true,
|
||||
|
||||
/*
|
||||
* RELAXING OPTIONS
|
||||
* =================
|
||||
*/
|
||||
|
||||
// Suppress warnings about == null comparisons.
|
||||
"eqnull": true,
|
||||
"globals": {
|
||||
"$": false,
|
||||
"angular": false
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
# Sentinel Dashboard Frontend
|
||||
|
||||
## Env Requirement
|
||||
|
||||
- Node.js > 6.x
|
||||
|
||||
## Code Guide
|
||||
|
||||
- [Code Style Guide for HTML/CSS](https://codeguide.bootcss.com/)
|
||||
- [Airbnb JavaScript Style Guide](https://github.com/airbnb/javascript/treeConfig/es5-deprecated/es5)
|
||||
|
||||
## Install Packages
|
||||
|
||||
```
|
||||
npm install
|
||||
```
|
||||
|
||||
## Start Development
|
||||
|
||||
```
|
||||
npm start
|
||||
```
|
||||
|
||||
## Build for production
|
||||
|
||||
```
|
||||
npm run build
|
||||
```
|
||||
|
||||
## Credit
|
||||
|
||||
- [sb-admin-angular](https://github.com/start-angular/sb-admin-angular)
|
||||
@@ -0,0 +1,32 @@
|
||||
# Sentinel Dashboard Frontend
|
||||
|
||||
## 环境要求
|
||||
|
||||
- Node.js > 6.x
|
||||
|
||||
## 编码规范
|
||||
|
||||
- HTML/CSS 遵循 [Bootstrap 编码规范](https://codeguide.bootcss.com/)
|
||||
- JavaScript 遵循 [Airbnb JavaScript Style Guide](https://github.com/airbnb/javascript/treeConfig/es5-deprecated/es5) 以及最新的 ES 6 标准
|
||||
|
||||
## 安装依赖
|
||||
|
||||
```
|
||||
npm i
|
||||
```
|
||||
|
||||
## 开始本地开发
|
||||
|
||||
```
|
||||
npm start
|
||||
```
|
||||
|
||||
## 构建前端资源
|
||||
|
||||
```
|
||||
npm run build
|
||||
```
|
||||
|
||||
## Credit
|
||||
|
||||
- [sb-admin-angular](https://github.com/start-angular/sb-admin-angular)
|
||||
@@ -0,0 +1,370 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @ngdoc overview
|
||||
* @name sentinelDashboardApp
|
||||
* @description
|
||||
* # sentinelDashboardApp
|
||||
*
|
||||
* Main module of the application.
|
||||
*/
|
||||
|
||||
angular
|
||||
.module('sentinelDashboardApp', [
|
||||
'oc.lazyLoad',
|
||||
'ui.router',
|
||||
'ui.bootstrap',
|
||||
'angular-loading-bar',
|
||||
'ngDialog',
|
||||
'ui.bootstrap.datetimepicker',
|
||||
'ui-notification',
|
||||
'rzTable',
|
||||
'angular-clipboard',
|
||||
'selectize',
|
||||
'angularUtils.directives.dirPagination'
|
||||
])
|
||||
.factory('AuthInterceptor', ['$window', '$state', function ($window, $state) {
|
||||
var authInterceptor = {
|
||||
'responseError' : function(response) {
|
||||
if (response.status === 401) {
|
||||
// If not auth, clear session in localStorage and jump to the login page
|
||||
$window.localStorage.removeItem('session_sentinel_admin');
|
||||
$state.go('login');
|
||||
}
|
||||
|
||||
return response;
|
||||
},
|
||||
'response' : function(response) {
|
||||
return response;
|
||||
},
|
||||
'request' : function(config) {
|
||||
// Resolved resource loading failure after configuring ContextPath
|
||||
var baseUrl = $window.document.getElementsByTagName('base')[0].href;
|
||||
// config.url = baseUrl + config.url;
|
||||
console.log(baseUrl,"baseUrl");
|
||||
// config.url = config.url;
|
||||
return config;
|
||||
},
|
||||
'requestError' : function(config){
|
||||
return config;
|
||||
}
|
||||
};
|
||||
return authInterceptor;
|
||||
}])
|
||||
.config(['$stateProvider', '$urlRouterProvider', '$ocLazyLoadProvider', '$httpProvider',
|
||||
function ($stateProvider, $urlRouterProvider, $ocLazyLoadProvider, $httpProvider) {
|
||||
$httpProvider.interceptors.push('AuthInterceptor');
|
||||
|
||||
$ocLazyLoadProvider.config({
|
||||
debug: false,
|
||||
events: true,
|
||||
});
|
||||
|
||||
$urlRouterProvider.otherwise('/dashboard/home');
|
||||
|
||||
$stateProvider
|
||||
.state('login', {
|
||||
url: '/login',
|
||||
templateUrl: 'app/views/login.html',
|
||||
controller: 'LoginCtl',
|
||||
resolve: {
|
||||
loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) {
|
||||
return $ocLazyLoad.load({
|
||||
name: 'sentinelDashboardApp',
|
||||
files: [
|
||||
'app/scripts/controllers/login.js',
|
||||
]
|
||||
});
|
||||
}]
|
||||
}
|
||||
})
|
||||
|
||||
.state('dashboard', {
|
||||
url: '/dashboard',
|
||||
templateUrl: 'app/views/dashboard/main.html',
|
||||
resolve: {
|
||||
loadMyDirectives: ['$ocLazyLoad', function ($ocLazyLoad) {
|
||||
return $ocLazyLoad.load(
|
||||
{
|
||||
name: 'sentinelDashboardApp',
|
||||
files: [
|
||||
'app/scripts/directives/header/header.js',
|
||||
'app/scripts/directives/sidebar/sidebar.js',
|
||||
'app/scripts/directives/sidebar/sidebar-search/sidebar-search.js',
|
||||
]
|
||||
});
|
||||
}]
|
||||
}
|
||||
})
|
||||
|
||||
.state('dashboard.home', {
|
||||
url: '/home',
|
||||
templateUrl: 'app/views/dashboard/home.html',
|
||||
resolve: {
|
||||
loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) {
|
||||
return $ocLazyLoad.load({
|
||||
name: 'sentinelDashboardApp',
|
||||
files: [
|
||||
'app/scripts/controllers/main.js',
|
||||
]
|
||||
});
|
||||
}]
|
||||
}
|
||||
})
|
||||
|
||||
.state('dashboard.flowV1', {
|
||||
templateUrl: 'app/views/flow_v1.html',
|
||||
url: '/flow/:app',
|
||||
controller: 'FlowControllerV1',
|
||||
resolve: {
|
||||
loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) {
|
||||
return $ocLazyLoad.load({
|
||||
name: 'sentinelDashboardApp',
|
||||
files: [
|
||||
'app/scripts/controllers/flow_v1.js',
|
||||
]
|
||||
});
|
||||
}]
|
||||
}
|
||||
})
|
||||
|
||||
.state('dashboard.flow', {
|
||||
templateUrl: 'app/views/flow_v2.html',
|
||||
url: '/v2/flow/:app',
|
||||
controller: 'FlowControllerV2',
|
||||
resolve: {
|
||||
loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) {
|
||||
return $ocLazyLoad.load({
|
||||
name: 'sentinelDashboardApp',
|
||||
files: [
|
||||
'app/scripts/controllers/flow_v2.js',
|
||||
]
|
||||
});
|
||||
}]
|
||||
}
|
||||
})
|
||||
|
||||
.state('dashboard.paramFlow', {
|
||||
templateUrl: 'app/views/param_flow.html',
|
||||
url: '/paramFlow/:app',
|
||||
controller: 'ParamFlowController',
|
||||
resolve: {
|
||||
loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) {
|
||||
return $ocLazyLoad.load({
|
||||
name: 'sentinelDashboardApp',
|
||||
files: [
|
||||
'app/scripts/controllers/param_flow.js',
|
||||
]
|
||||
});
|
||||
}]
|
||||
}
|
||||
})
|
||||
|
||||
.state('dashboard.clusterAppAssignManage', {
|
||||
templateUrl: 'app/views/cluster_app_assign_manage.html',
|
||||
url: '/cluster/assign_manage/:app',
|
||||
controller: 'SentinelClusterAppAssignManageController',
|
||||
resolve: {
|
||||
loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) {
|
||||
return $ocLazyLoad.load({
|
||||
name: 'sentinelDashboardApp',
|
||||
files: [
|
||||
'app/scripts/controllers/cluster_app_assign_manage.js',
|
||||
]
|
||||
});
|
||||
}]
|
||||
}
|
||||
})
|
||||
|
||||
.state('dashboard.clusterAppServerList', {
|
||||
templateUrl: 'app/views/cluster_app_server_list.html',
|
||||
url: '/cluster/server/:app',
|
||||
controller: 'SentinelClusterAppServerListController',
|
||||
resolve: {
|
||||
loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) {
|
||||
return $ocLazyLoad.load({
|
||||
name: 'sentinelDashboardApp',
|
||||
files: [
|
||||
'app/scripts/controllers/cluster_app_server_list.js',
|
||||
]
|
||||
});
|
||||
}]
|
||||
}
|
||||
})
|
||||
|
||||
.state('dashboard.clusterAppClientList', {
|
||||
templateUrl: 'app/views/cluster_app_client_list.html',
|
||||
url: '/cluster/client/:app',
|
||||
controller: 'SentinelClusterAppTokenClientListController',
|
||||
resolve: {
|
||||
loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) {
|
||||
return $ocLazyLoad.load({
|
||||
name: 'sentinelDashboardApp',
|
||||
files: [
|
||||
'app/scripts/controllers/cluster_app_token_client_list.js',
|
||||
]
|
||||
});
|
||||
}]
|
||||
}
|
||||
})
|
||||
|
||||
.state('dashboard.clusterSingle', {
|
||||
templateUrl: 'app/views/cluster_single_config.html',
|
||||
url: '/cluster/single/:app',
|
||||
controller: 'SentinelClusterSingleController',
|
||||
resolve: {
|
||||
loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) {
|
||||
return $ocLazyLoad.load({
|
||||
name: 'sentinelDashboardApp',
|
||||
files: [
|
||||
'app/scripts/controllers/cluster_single.js',
|
||||
]
|
||||
});
|
||||
}]
|
||||
}
|
||||
})
|
||||
|
||||
.state('dashboard.authority', {
|
||||
templateUrl: 'app/views/authority.html',
|
||||
url: '/authority/:app',
|
||||
controller: 'AuthorityRuleController',
|
||||
resolve: {
|
||||
loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) {
|
||||
return $ocLazyLoad.load({
|
||||
name: 'sentinelDashboardApp',
|
||||
files: [
|
||||
'app/scripts/controllers/authority.js',
|
||||
]
|
||||
});
|
||||
}]
|
||||
}
|
||||
})
|
||||
|
||||
.state('dashboard.degrade', {
|
||||
templateUrl: 'app/views/degrade.html',
|
||||
url: '/degrade/:app',
|
||||
controller: 'DegradeCtl',
|
||||
resolve: {
|
||||
loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) {
|
||||
return $ocLazyLoad.load({
|
||||
name: 'sentinelDashboardApp',
|
||||
files: [
|
||||
'app/scripts/controllers/degrade.js',
|
||||
]
|
||||
});
|
||||
}]
|
||||
}
|
||||
})
|
||||
|
||||
.state('dashboard.system', {
|
||||
templateUrl: 'app/views/system.html',
|
||||
url: '/system/:app',
|
||||
controller: 'SystemCtl',
|
||||
resolve: {
|
||||
loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) {
|
||||
return $ocLazyLoad.load({
|
||||
name: 'sentinelDashboardApp',
|
||||
files: [
|
||||
'app/scripts/controllers/system.js',
|
||||
]
|
||||
});
|
||||
}]
|
||||
}
|
||||
})
|
||||
|
||||
.state('dashboard.machine', {
|
||||
templateUrl: 'app/views/machine.html',
|
||||
url: '/app/:app',
|
||||
controller: 'MachineCtl',
|
||||
resolve: {
|
||||
loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) {
|
||||
return $ocLazyLoad.load({
|
||||
name: 'sentinelDashboardApp',
|
||||
files: [
|
||||
'app/scripts/controllers/machine.js',
|
||||
]
|
||||
});
|
||||
}]
|
||||
}
|
||||
})
|
||||
|
||||
.state('dashboard.identity', {
|
||||
templateUrl: 'app/views/identity.html',
|
||||
url: '/identity/:app',
|
||||
controller: 'IdentityCtl',
|
||||
resolve: {
|
||||
loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) {
|
||||
return $ocLazyLoad.load({
|
||||
name: 'sentinelDashboardApp',
|
||||
files: [
|
||||
'app/scripts/controllers/identity.js',
|
||||
]
|
||||
});
|
||||
}]
|
||||
}
|
||||
})
|
||||
|
||||
.state('dashboard.gatewayIdentity', {
|
||||
templateUrl: 'app/views/gateway/identity.html',
|
||||
url: '/gateway/identity/:app',
|
||||
controller: 'GatewayIdentityCtl',
|
||||
resolve: {
|
||||
loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) {
|
||||
return $ocLazyLoad.load({
|
||||
name: 'sentinelDashboardApp',
|
||||
files: [
|
||||
'app/scripts/controllers/gateway/identity.js',
|
||||
]
|
||||
});
|
||||
}]
|
||||
}
|
||||
})
|
||||
|
||||
.state('dashboard.metric', {
|
||||
templateUrl: 'app/views/metric.html',
|
||||
url: '/metric/:app',
|
||||
controller: 'MetricCtl',
|
||||
resolve: {
|
||||
loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) {
|
||||
return $ocLazyLoad.load({
|
||||
name: 'sentinelDashboardApp',
|
||||
files: [
|
||||
'app/scripts/controllers/metric.js',
|
||||
]
|
||||
});
|
||||
}]
|
||||
}
|
||||
})
|
||||
|
||||
.state('dashboard.gatewayApi', {
|
||||
templateUrl: 'app/views/gateway/api.html',
|
||||
url: '/gateway/api/:app',
|
||||
controller: 'GatewayApiCtl',
|
||||
resolve: {
|
||||
loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) {
|
||||
return $ocLazyLoad.load({
|
||||
name: 'sentinelDashboardApp',
|
||||
files: [
|
||||
'app/scripts/controllers/gateway/api.js',
|
||||
]
|
||||
});
|
||||
}]
|
||||
}
|
||||
})
|
||||
|
||||
.state('dashboard.gatewayFlow', {
|
||||
templateUrl: 'app/views/gateway/flow.html',
|
||||
url: '/gateway/flow/:app',
|
||||
controller: 'GatewayFlowCtl',
|
||||
resolve: {
|
||||
loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) {
|
||||
return $ocLazyLoad.load({
|
||||
name: 'sentinelDashboardApp',
|
||||
files: [
|
||||
'app/scripts/controllers/gateway/flow.js',
|
||||
]
|
||||
});
|
||||
}]
|
||||
}
|
||||
});
|
||||
}]);
|
||||
@@ -0,0 +1,227 @@
|
||||
/**
|
||||
* Authority rule controller.
|
||||
*/
|
||||
angular.module('sentinelDashboardApp').controller('AuthorityRuleController', ['$scope', '$stateParams', 'AuthorityRuleService', 'ngDialog',
|
||||
'MachineService',
|
||||
function ($scope, $stateParams, AuthorityRuleService, ngDialog,
|
||||
MachineService) {
|
||||
$scope.app = $stateParams.app;
|
||||
|
||||
$scope.rulesPageConfig = {
|
||||
pageSize: 10,
|
||||
currentPageIndex: 1,
|
||||
totalPage: 1,
|
||||
totalCount: 0,
|
||||
};
|
||||
$scope.macsInputConfig = {
|
||||
searchField: ['text', 'value'],
|
||||
persist: true,
|
||||
create: false,
|
||||
maxItems: 1,
|
||||
render: {
|
||||
item: function (data, escape) {
|
||||
return '<div>' + escape(data.text) + '</div>';
|
||||
}
|
||||
},
|
||||
onChange: function (value, oldValue) {
|
||||
$scope.macInputModel = value;
|
||||
}
|
||||
};
|
||||
|
||||
function getMachineRules() {
|
||||
if (!$scope.macInputModel) {
|
||||
return;
|
||||
}
|
||||
let mac = $scope.macInputModel.split(':');
|
||||
AuthorityRuleService.queryMachineRules($scope.app, mac[0], mac[1])
|
||||
.success(function (data) {
|
||||
if (data.code === 0 && data.data) {
|
||||
$scope.loadError = undefined;
|
||||
$scope.rules = data.data;
|
||||
$scope.rulesPageConfig.totalCount = $scope.rules.length;
|
||||
} else {
|
||||
$scope.rules = [];
|
||||
$scope.rulesPageConfig.totalCount = 0;
|
||||
$scope.loadError = {message: data.msg};
|
||||
}
|
||||
})
|
||||
.error((data, header, config, status) => {
|
||||
$scope.loadError = {message: "未知错误"};
|
||||
});
|
||||
};
|
||||
$scope.getMachineRules = getMachineRules;
|
||||
getMachineRules();
|
||||
|
||||
var authorityRuleDialog;
|
||||
|
||||
$scope.editRule = function (rule) {
|
||||
$scope.currentRule = angular.copy(rule);
|
||||
$scope.authorityRuleDialog = {
|
||||
title: '编辑授权规则',
|
||||
type: 'edit',
|
||||
confirmBtnText: '保存',
|
||||
};
|
||||
authorityRuleDialog = ngDialog.open({
|
||||
template: '/app/views/dialog/authority-rule-dialog.html',
|
||||
width: 680,
|
||||
overlay: true,
|
||||
scope: $scope
|
||||
});
|
||||
};
|
||||
|
||||
$scope.addNewRule = function () {
|
||||
var mac = $scope.macInputModel.split(':');
|
||||
$scope.currentRule = {
|
||||
app: $scope.app,
|
||||
ip: mac[0],
|
||||
port: mac[1],
|
||||
rule: {
|
||||
strategy: 0,
|
||||
limitApp: '',
|
||||
}
|
||||
};
|
||||
$scope.authorityRuleDialog = {
|
||||
title: '新增授权规则',
|
||||
type: 'add',
|
||||
confirmBtnText: '新增',
|
||||
showAdvanceButton: true,
|
||||
};
|
||||
authorityRuleDialog = ngDialog.open({
|
||||
template: '/app/views/dialog/authority-rule-dialog.html',
|
||||
width: 680,
|
||||
overlay: true,
|
||||
scope: $scope
|
||||
});
|
||||
};
|
||||
|
||||
$scope.saveRule = function () {
|
||||
if (!AuthorityRuleService.checkRuleValid($scope.currentRule.rule)) {
|
||||
return;
|
||||
}
|
||||
if ($scope.authorityRuleDialog.type === 'add') {
|
||||
addNewRuleAndPush($scope.currentRule);
|
||||
} else if ($scope.authorityRuleDialog.type === 'edit') {
|
||||
saveRuleAndPush($scope.currentRule, true);
|
||||
}
|
||||
};
|
||||
|
||||
function addNewRuleAndPush(rule) {
|
||||
AuthorityRuleService.addNewRule(rule).success((data) => {
|
||||
if (data.success) {
|
||||
getMachineRules();
|
||||
authorityRuleDialog.close();
|
||||
} else {
|
||||
alert('添加规则失败:' + data.msg);
|
||||
}
|
||||
}).error((data) => {
|
||||
if (data) {
|
||||
alert('添加规则失败:' + data.msg);
|
||||
} else {
|
||||
alert("添加规则失败:未知错误");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function saveRuleAndPush(rule, edit) {
|
||||
AuthorityRuleService.saveRule(rule).success(function (data) {
|
||||
if (data.success) {
|
||||
alert("修改规则成功");
|
||||
getMachineRules();
|
||||
if (edit) {
|
||||
authorityRuleDialog.close();
|
||||
} else {
|
||||
confirmDialog.close();
|
||||
}
|
||||
} else {
|
||||
alert('修改规则失败:' + data.msg);
|
||||
}
|
||||
}).error((data) => {
|
||||
if (data) {
|
||||
alert('修改规则失败:' + data.msg);
|
||||
} else {
|
||||
alert("修改规则失败:未知错误");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function deleteRuleAndPush(entity) {
|
||||
if (entity.id === undefined || isNaN(entity.id)) {
|
||||
alert('规则 ID 不合法!');
|
||||
return;
|
||||
}
|
||||
AuthorityRuleService.deleteRule(entity).success((data) => {
|
||||
if (data.code == 0) {
|
||||
getMachineRules();
|
||||
confirmDialog.close();
|
||||
} else {
|
||||
alert('删除规则失败:' + data.msg);
|
||||
}
|
||||
}).error((data) => {
|
||||
if (data) {
|
||||
alert('删除规则失败:' + data.msg);
|
||||
} else {
|
||||
alert("删除规则失败:未知错误");
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
var confirmDialog;
|
||||
$scope.deleteRule = function (ruleEntity) {
|
||||
$scope.currentRule = ruleEntity;
|
||||
$scope.confirmDialog = {
|
||||
title: '删除授权规则',
|
||||
type: 'delete_rule',
|
||||
attentionTitle: '请确认是否删除如下授权限流规则',
|
||||
attention: '资源名: ' + ruleEntity.rule.resource + ', 流控应用: ' + ruleEntity.rule.limitApp +
|
||||
', 类型: ' + (ruleEntity.rule.strategy === 0 ? '白名单' : '黑名单'),
|
||||
confirmBtnText: '删除',
|
||||
};
|
||||
confirmDialog = ngDialog.open({
|
||||
template: '/app/views/dialog/confirm-dialog.html',
|
||||
scope: $scope,
|
||||
overlay: true
|
||||
});
|
||||
};
|
||||
|
||||
$scope.confirm = function () {
|
||||
if ($scope.confirmDialog.type === 'delete_rule') {
|
||||
deleteRuleAndPush($scope.currentRule);
|
||||
} else {
|
||||
console.error('error');
|
||||
}
|
||||
};
|
||||
|
||||
queryAppMachines();
|
||||
|
||||
function queryAppMachines() {
|
||||
MachineService.getAppMachines($scope.app).success(
|
||||
function (data) {
|
||||
if (data.code == 0) {
|
||||
// $scope.machines = data.data;
|
||||
if (data.data) {
|
||||
$scope.machines = [];
|
||||
$scope.macsInputOptions = [];
|
||||
data.data.forEach(function (item) {
|
||||
if (item.healthy) {
|
||||
$scope.macsInputOptions.push({
|
||||
text: item.ip + ':' + item.port,
|
||||
value: item.ip + ':' + item.port
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
if ($scope.macsInputOptions.length > 0) {
|
||||
$scope.macInputModel = $scope.macsInputOptions[0].value;
|
||||
}
|
||||
} else {
|
||||
$scope.macsInputOptions = [];
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
$scope.$watch('macInputModel', function () {
|
||||
if ($scope.macInputModel) {
|
||||
getMachineRules();
|
||||
}
|
||||
});
|
||||
}]);
|
||||
@@ -0,0 +1,283 @@
|
||||
var app = angular.module('sentinelDashboardApp');
|
||||
|
||||
app.controller('SentinelClusterAppAssignManageController', ['$scope', '$stateParams', 'ngDialog',
|
||||
'MachineService', 'ClusterStateService',
|
||||
function ($scope, $stateParams, ngDialog, MachineService, ClusterStateService) {
|
||||
$scope.app = $stateParams.app;
|
||||
const UNSUPPORTED_CODE = 4041;
|
||||
|
||||
const CLUSTER_MODE_CLIENT = 0;
|
||||
const CLUSTER_MODE_SERVER = 1;
|
||||
const DEFAULT_CLUSTER_SERVER_PORT = 18730;
|
||||
|
||||
$scope.tmp = {
|
||||
curClientChosen: [],
|
||||
curRemainingClientChosen: [],
|
||||
curChosenServer: {},
|
||||
};
|
||||
|
||||
function convertSetToString(set) {
|
||||
if (set === undefined) {
|
||||
return '';
|
||||
}
|
||||
let s = '';
|
||||
for (let i = 0; i < set.length; i++) {
|
||||
s = s + set[i];
|
||||
if (i < set.length - 1) {
|
||||
s = s + ',';
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
function convertStrToNamespaceSet(str) {
|
||||
if (str === undefined || str === '') {
|
||||
return [];
|
||||
}
|
||||
let arr = [];
|
||||
let spliced = str.split(',');
|
||||
spliced.forEach((v) => {
|
||||
arr.push(v.trim());
|
||||
});
|
||||
return arr;
|
||||
}
|
||||
|
||||
function processAppSingleData(data) {
|
||||
if (data.state.server && data.state.server.namespaceSet) {
|
||||
data.state.server.namespaceSetStr = convertSetToString(data.state.server.namespaceSet);
|
||||
data.mode = data.state.stateInfo.mode;
|
||||
}
|
||||
}
|
||||
|
||||
function removeFromArr(arr, v) {
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
if (arr[i] === v) {
|
||||
arr.splice(i, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function resetChosen() {
|
||||
$scope.tmp.curClientChosen = [];
|
||||
$scope.tmp.curRemainingClientChosen = [];
|
||||
}
|
||||
|
||||
function generateMachineId(e) {
|
||||
return e.ip + '@' + e.commandPort;
|
||||
}
|
||||
|
||||
function applyClusterMap(appClusterMachineList) {
|
||||
if (!appClusterMachineList) {
|
||||
return;
|
||||
}
|
||||
let tmpMap = new Map();
|
||||
$scope.clusterMap = [];
|
||||
$scope.remainingClientAddressList = [];
|
||||
let tmpServerList = [];
|
||||
let tmpClientList = [];
|
||||
appClusterMachineList.forEach((e) => {
|
||||
if (e.mode === CLUSTER_MODE_CLIENT) {
|
||||
tmpClientList.push(e);
|
||||
} else if (e.mode === CLUSTER_MODE_SERVER) {
|
||||
tmpServerList.push(e);
|
||||
} else {
|
||||
$scope.remainingClientAddressList.push(generateMachineId(e));
|
||||
}
|
||||
});
|
||||
tmpServerList.forEach((e) => {
|
||||
let ip = e.ip;
|
||||
let machineId = ip + '@' + e.commandPort;
|
||||
let group = {
|
||||
ip: ip,
|
||||
machineId: machineId,
|
||||
port: e.state.server.port,
|
||||
clientSet: [],
|
||||
namespaceSetStr: e.state.server.namespaceSetStr,
|
||||
belongToApp: true,
|
||||
};
|
||||
if (!tmpMap.has(ip)) {
|
||||
tmpMap.set(ip, group);
|
||||
}
|
||||
});
|
||||
tmpClientList.forEach((e) => {
|
||||
let ip = e.ip;
|
||||
let machineId = ip + '@' + e.commandPort;
|
||||
|
||||
let targetServer = e.state.client.clientConfig.serverHost;
|
||||
let targetPort = e.state.client.clientConfig.serverPort;
|
||||
if (targetServer === undefined || targetServer === '' ||
|
||||
targetPort === undefined || targetPort <= 0) {
|
||||
$scope.remainingClientAddressList.push(generateMachineId(e));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!tmpMap.has(targetServer)) {
|
||||
let group = {
|
||||
ip: targetServer,
|
||||
machineId: targetServer,
|
||||
port: targetPort,
|
||||
clientSet: [machineId],
|
||||
belongToApp: false,
|
||||
};
|
||||
tmpMap.set(targetServer, group);
|
||||
} else {
|
||||
let g = tmpMap.get(targetServer);
|
||||
g.clientSet.push(machineId);
|
||||
}
|
||||
});
|
||||
tmpMap.forEach((v) => {
|
||||
if (v !== undefined) {
|
||||
$scope.clusterMap.push(v);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$scope.onCurrentServerChange = () => {
|
||||
resetChosen();
|
||||
};
|
||||
|
||||
$scope.remainingClientAddressList = [];
|
||||
|
||||
$scope.moveToServerGroup = () => {
|
||||
let chosenServer = $scope.tmp.curChosenServer;
|
||||
if (!chosenServer || !chosenServer.machineId) {
|
||||
return;
|
||||
}
|
||||
$scope.tmp.curRemainingClientChosen.forEach(e => {
|
||||
chosenServer.clientSet.push(e);
|
||||
removeFromArr($scope.remainingClientAddressList, e);
|
||||
});
|
||||
resetChosen();
|
||||
};
|
||||
|
||||
$scope.moveToRemainingSharePool = () => {
|
||||
$scope.tmp.curClientChosen.forEach(e => {
|
||||
$scope.remainingClientAddressList.push(e);
|
||||
removeFromArr($scope.tmp.curChosenServer.clientSet, e);
|
||||
});
|
||||
resetChosen();
|
||||
};
|
||||
|
||||
function parseIpFromMachineId(machineId) {
|
||||
if (machineId.indexOf('@') === -1) {
|
||||
return machineId;
|
||||
}
|
||||
let arr = machineId.split('@');
|
||||
return arr[0];
|
||||
}
|
||||
|
||||
$scope.addToServerList = () => {
|
||||
let group;
|
||||
$scope.tmp.curRemainingClientChosen.forEach(e => {
|
||||
group = {
|
||||
machineId: e,
|
||||
ip: parseIpFromMachineId(e),
|
||||
port: DEFAULT_CLUSTER_SERVER_PORT,
|
||||
clientSet: [],
|
||||
namespaceSetStr: 'default,' + $scope.app,
|
||||
belongToApp: true,
|
||||
};
|
||||
$scope.clusterMap.push(group);
|
||||
removeFromArr($scope.remainingClientAddressList, e);
|
||||
$scope.tmp.curChosenServer = group;
|
||||
});
|
||||
resetChosen();
|
||||
};
|
||||
|
||||
$scope.removeFromServerList = () => {
|
||||
let chosenServer = $scope.tmp.curChosenServer;
|
||||
if (!chosenServer || !chosenServer.machineId) {
|
||||
return;
|
||||
}
|
||||
chosenServer.clientSet.forEach((e) => {
|
||||
if (e !== undefined) {
|
||||
$scope.remainingClientAddressList.push(e);
|
||||
}
|
||||
});
|
||||
|
||||
if (chosenServer.belongToApp || chosenServer.machineId.indexOf('@') !== -1) {
|
||||
$scope.remainingClientAddressList.push(chosenServer.machineId);
|
||||
} else {
|
||||
alert('提示:非本应用内机器将不会置回空闲列表中');
|
||||
}
|
||||
|
||||
removeFromArr($scope.clusterMap, chosenServer);
|
||||
|
||||
resetChosen();
|
||||
|
||||
if ($scope.clusterMap.length > 0) {
|
||||
$scope.tmp.curChosenServer = $scope.clusterMap[0];
|
||||
$scope.onCurrentServerChange();
|
||||
} else {
|
||||
$scope.tmp.curChosenServer = {};
|
||||
}
|
||||
};
|
||||
|
||||
function retrieveClusterAppInfo() {
|
||||
ClusterStateService.fetchClusterUniversalStateOfApp($scope.app).success(function (data) {
|
||||
if (data.code === 0 && data.data) {
|
||||
$scope.loadError = undefined;
|
||||
$scope.appClusterMachineList = data.data;
|
||||
$scope.appClusterMachineList.forEach(processAppSingleData);
|
||||
applyClusterMap($scope.appClusterMachineList);
|
||||
if ($scope.clusterMap.length > 0) {
|
||||
$scope.tmp.curChosenServer = $scope.clusterMap[0];
|
||||
$scope.onCurrentServerChange();
|
||||
}
|
||||
} else {
|
||||
$scope.appClusterMachineList = {};
|
||||
if (data.code === UNSUPPORTED_CODE) {
|
||||
$scope.loadError = {message: '该应用的 Sentinel 客户端不支持集群限流,请升级至 1.4.0 以上版本并引入相关依赖。'}
|
||||
} else {
|
||||
$scope.loadError = {message: data.msg};
|
||||
}
|
||||
}
|
||||
}).error(() => {
|
||||
$scope.loadError = {message: '未知错误'};
|
||||
});
|
||||
}
|
||||
|
||||
retrieveClusterAppInfo();
|
||||
|
||||
$scope.saveAndApplyAssign = () => {
|
||||
let ok = confirm('是否确认执行变更?');
|
||||
if (!ok) {
|
||||
return;
|
||||
}
|
||||
let cm = $scope.clusterMap;
|
||||
if (!cm) {
|
||||
cm = [];
|
||||
}
|
||||
cm.forEach((e) => {
|
||||
e.namespaceSet = convertStrToNamespaceSet(e.namespaceSetStr);
|
||||
});
|
||||
cm.namespaceSet = convertStrToNamespaceSet(cm.namespaceSetStr);
|
||||
let request = {
|
||||
clusterMap: cm,
|
||||
remainingList: $scope.remainingClientAddressList,
|
||||
};
|
||||
ClusterStateService.applyClusterFullAssignOfApp($scope.app, request).success((data) => {
|
||||
if (data.code === 0 && data.data) {
|
||||
let failedServerSet = data.data.failedServerSet;
|
||||
let failedClientSet = data.data.failedClientSet;
|
||||
if (failedClientSet.length === 0 && failedServerSet.length === 0) {
|
||||
alert('全部推送成功');
|
||||
} else {
|
||||
alert('推送完毕。token server 失败列表:' + JSON.stringify(failedServerSet) +
|
||||
'; token client 失败列表:' + JSON.stringify(failedClientSet));
|
||||
}
|
||||
|
||||
retrieveClusterAppInfo();
|
||||
} else {
|
||||
if (data.code === UNSUPPORTED_CODE) {
|
||||
alert('该应用的 Sentinel 客户端不支持集群限流,请升级至 1.4.0 以上版本并引入相关依赖。');
|
||||
} else {
|
||||
alert('推送失败:' + data.msg);
|
||||
}
|
||||
}
|
||||
}).error(() => {
|
||||
alert('未知错误');
|
||||
});
|
||||
};
|
||||
}]);
|
||||
@@ -0,0 +1,570 @@
|
||||
var app = angular.module('sentinelDashboardApp');
|
||||
|
||||
app.controller('SentinelClusterAppServerListController', ['$scope', '$stateParams', 'ngDialog',
|
||||
'MachineService', 'ClusterStateService',
|
||||
function ($scope, $stateParams, ngDialog, MachineService, ClusterStateService) {
|
||||
$scope.app = $stateParams.app;
|
||||
const UNSUPPORTED_CODE = 4041;
|
||||
|
||||
const CLUSTER_MODE_CLIENT = 0;
|
||||
const CLUSTER_MODE_SERVER = 1;
|
||||
const DEFAULT_CLUSTER_SERVER_PORT = 18730;
|
||||
const DEFAULT_NAMESPACE = 'default';
|
||||
const DEFAULT_MAX_ALLOWED_QPS = 20000;
|
||||
|
||||
// tmp for dialog temporary data.
|
||||
$scope.tmp = {
|
||||
curClientChosen: [],
|
||||
curRemainingClientChosen: [],
|
||||
curChosenServer: {},
|
||||
};
|
||||
|
||||
$scope.remainingMachineList = [];
|
||||
|
||||
function convertSetToString(set) {
|
||||
if (set === undefined) {
|
||||
return '';
|
||||
}
|
||||
if (set.length === 1 && set[0] === DEFAULT_NAMESPACE) {
|
||||
return DEFAULT_NAMESPACE;
|
||||
}
|
||||
let s = '';
|
||||
for (let i = 0; i < set.length; i++) {
|
||||
let ns = set[i];
|
||||
if (ns !== DEFAULT_NAMESPACE) {
|
||||
s = s + ns;
|
||||
if (i < set.length - 1) {
|
||||
s = s + ',';
|
||||
}
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
function convertStrToNamespaceSet(str) {
|
||||
if (str === undefined || str === '') {
|
||||
return [];
|
||||
}
|
||||
let arr = [];
|
||||
let spliced = str.split(',');
|
||||
spliced.forEach((v) => {
|
||||
arr.push(v.trim());
|
||||
});
|
||||
return arr;
|
||||
}
|
||||
|
||||
function processAppSingleData(data) {
|
||||
if (data.state.server && data.state.server.namespaceSet) {
|
||||
data.state.server.namespaceSetStr = convertSetToString(data.state.server.namespaceSet);
|
||||
data.mode = data.state.stateInfo.mode;
|
||||
}
|
||||
}
|
||||
|
||||
function removeFromArr(arr, v) {
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
if (arr[i] === v) {
|
||||
arr.splice(i, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function removeFromArrIf(arr, f) {
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
if (f(arr[i]) === true) {
|
||||
arr.splice(i, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function resetAssignDialogChosen() {
|
||||
$scope.tmp.curClientChosen = [];
|
||||
$scope.tmp.curRemainingClientChosen = [];
|
||||
}
|
||||
|
||||
function generateMachineId(e) {
|
||||
return e.ip + '@' + e.commandPort;
|
||||
}
|
||||
|
||||
function applyClusterMap(appClusterMachineList) {
|
||||
if (!appClusterMachineList) {
|
||||
return;
|
||||
}
|
||||
let tmpMap = new Map();
|
||||
let serverCommandPortMap = new Map();
|
||||
$scope.clusterMap = [];
|
||||
$scope.remainingMachineList = [];
|
||||
let tmpServerList = [];
|
||||
let tmpClientList = [];
|
||||
appClusterMachineList.forEach((e) => {
|
||||
if (e.mode === CLUSTER_MODE_CLIENT) {
|
||||
tmpClientList.push(e);
|
||||
} else if (e.mode === CLUSTER_MODE_SERVER) {
|
||||
tmpServerList.push(e);
|
||||
} else {
|
||||
$scope.remainingMachineList.push(generateMachineId(e));
|
||||
}
|
||||
});
|
||||
tmpServerList.forEach((e) => {
|
||||
let ip = e.ip;
|
||||
let machineId = ip + '@' + e.commandPort;
|
||||
let group = {
|
||||
ip: ip,
|
||||
machineId: machineId,
|
||||
port: e.state.server.port,
|
||||
clientSet: [],
|
||||
namespaceSetStr: e.state.server.namespaceSetStr,
|
||||
maxAllowedQps: e.state.server.flow.maxAllowedQps,
|
||||
belongToApp: true,
|
||||
};
|
||||
if (!tmpMap.has(machineId)) {
|
||||
tmpMap.set(machineId, group);
|
||||
}
|
||||
serverCommandPortMap.set(ip + ':' + e.state.server.port, e.commandPort);
|
||||
});
|
||||
tmpClientList.forEach((e) => {
|
||||
let ip = e.ip;
|
||||
let machineId = ip + '@' + e.commandPort;
|
||||
|
||||
let targetServer = e.state.client.clientConfig.serverHost;
|
||||
let targetPort = e.state.client.clientConfig.serverPort;
|
||||
if (targetServer === undefined || targetServer === '' ||
|
||||
targetPort === undefined || targetPort <= 0) {
|
||||
$scope.remainingMachineList.push(generateMachineId(e));
|
||||
return;
|
||||
}
|
||||
|
||||
let serverHostPort = targetServer + ':' + targetPort;
|
||||
|
||||
if (serverCommandPortMap.has(serverHostPort)) {
|
||||
let serverCommandPort = serverCommandPortMap.get(serverHostPort);
|
||||
let g;
|
||||
if (serverCommandPort < 0) {
|
||||
// Not belong to this app.
|
||||
g = tmpMap.get(serverHostPort);
|
||||
} else {
|
||||
// Belong to this app.
|
||||
g = tmpMap.get(targetServer + '@' + serverCommandPort);
|
||||
}
|
||||
g.clientSet.push(machineId);
|
||||
} else {
|
||||
let group = {
|
||||
ip: targetServer,
|
||||
machineId: serverHostPort,
|
||||
port: targetPort,
|
||||
clientSet: [machineId],
|
||||
belongToApp: false,
|
||||
};
|
||||
tmpMap.set(serverHostPort, group);
|
||||
// Indicates that it's not belonging to current app.
|
||||
serverCommandPortMap.set(serverHostPort, -1);
|
||||
}
|
||||
|
||||
// if (!tmpMap.has(serverHostPort)) {
|
||||
// let group = {
|
||||
// ip: targetServer,
|
||||
// machineId: targetServer,
|
||||
// port: targetPort,
|
||||
// clientSet: [machineId],
|
||||
// belongToApp: false,
|
||||
// };
|
||||
// tmpMap.set(targetServer, group);
|
||||
// } else {
|
||||
// let g = tmpMap.get(targetServer);
|
||||
// g.clientSet.push(machineId);
|
||||
// }
|
||||
});
|
||||
tmpMap.forEach((v) => {
|
||||
if (v !== undefined) {
|
||||
$scope.clusterMap.push(v);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$scope.notChosenServer = (id) => {
|
||||
return id !== $scope.serverAssignDialogData.serverData.currentServer;
|
||||
};
|
||||
|
||||
$scope.onCurrentServerChange = () => {
|
||||
resetAssignDialogChosen();
|
||||
};
|
||||
|
||||
$scope.moveToServerGroup = () => {
|
||||
$scope.tmp.curRemainingClientChosen.forEach(e => {
|
||||
$scope.serverAssignDialogData.serverData.clientSet.push(e);
|
||||
removeFromArr($scope.remainingMachineList, e);
|
||||
});
|
||||
resetAssignDialogChosen();
|
||||
};
|
||||
|
||||
$scope.moveToRemainingSharePool = () => {
|
||||
$scope.tmp.curClientChosen.forEach(e => {
|
||||
$scope.remainingMachineList.push(e);
|
||||
removeFromArr($scope.serverAssignDialogData.serverData.clientSet, e);
|
||||
});
|
||||
resetAssignDialogChosen();
|
||||
};
|
||||
|
||||
function parseIpFromMachineId(machineId) {
|
||||
if (machineId.indexOf(':') !== -1) {
|
||||
return machineId.split(':')[0];
|
||||
}
|
||||
if (machineId.indexOf('@') === -1) {
|
||||
return machineId;
|
||||
}
|
||||
let arr = machineId.split('@');
|
||||
return arr[0];
|
||||
}
|
||||
|
||||
function retrieveClusterAssignInfoOfApp() {
|
||||
ClusterStateService.fetchClusterUniversalStateOfApp($scope.app).success(function (data) {
|
||||
if (data.code === 0 && data.data) {
|
||||
$scope.loadError = undefined;
|
||||
$scope.appClusterMachineList = data.data;
|
||||
$scope.appClusterMachineList.forEach(processAppSingleData);
|
||||
applyClusterMap($scope.appClusterMachineList);
|
||||
} else {
|
||||
$scope.appClusterMachineList = {};
|
||||
if (data.code === UNSUPPORTED_CODE) {
|
||||
$scope.loadError = {message: '该应用的 Sentinel 客户端不支持集群限流,请升级至 1.4.0 以上版本并引入相关依赖。'}
|
||||
} else {
|
||||
$scope.loadError = {message: data.msg};
|
||||
}
|
||||
}
|
||||
}).error(() => {
|
||||
$scope.loadError = {message: '未知错误'};
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
$scope.newServerDialog = () => {
|
||||
retrieveClusterAssignInfoOfApp();
|
||||
$scope.serverAssignDialogData = {
|
||||
title: '新增 Token Server',
|
||||
type: 'add',
|
||||
confirmBtnText: '保存',
|
||||
serverData: {
|
||||
serverType: 0,
|
||||
clientSet: [],
|
||||
serverPort: DEFAULT_CLUSTER_SERVER_PORT,
|
||||
maxAllowedQps: DEFAULT_MAX_ALLOWED_QPS,
|
||||
}
|
||||
};
|
||||
$scope.serverAssignDialog = ngDialog.open({
|
||||
template: '/app/views/dialog/cluster/cluster-server-assign-dialog.html',
|
||||
width: 1000,
|
||||
overlay: true,
|
||||
scope: $scope
|
||||
});
|
||||
};
|
||||
|
||||
$scope.modifyServerAssignConfig = (serverVO) => {
|
||||
let id = serverVO.id;
|
||||
ClusterStateService.fetchClusterUniversalStateOfApp($scope.app).success(function (data) {
|
||||
if (data.code === 0 && data.data) {
|
||||
$scope.loadError = undefined;
|
||||
$scope.appClusterMachineList = data.data;
|
||||
$scope.appClusterMachineList.forEach(processAppSingleData);
|
||||
applyClusterMap($scope.appClusterMachineList);
|
||||
let clusterMap = $scope.clusterMap;
|
||||
let d;
|
||||
for (let i = 0; i < clusterMap.length; i++) {
|
||||
if (clusterMap[i].machineId === id) {
|
||||
d = clusterMap[i];
|
||||
}
|
||||
}
|
||||
if (!d) {
|
||||
alert('状态错误');
|
||||
return;
|
||||
}
|
||||
$scope.serverAssignDialogData = {
|
||||
title: 'Token Server 分配编辑',
|
||||
type: 'edit',
|
||||
confirmBtnText: '保存',
|
||||
serverData: {
|
||||
currentServer: d.machineId,
|
||||
belongToApp: serverVO.belongToApp,
|
||||
serverPort: d.port,
|
||||
clientSet: d.clientSet,
|
||||
}
|
||||
};
|
||||
if (d.maxAllowedQps !== undefined) {
|
||||
$scope.serverAssignDialogData.serverData.maxAllowedQps = d.maxAllowedQps;
|
||||
}
|
||||
$scope.serverAssignDialog = ngDialog.open({
|
||||
template: '/app/views/dialog/cluster/cluster-server-assign-dialog.html',
|
||||
width: 1000,
|
||||
overlay: true,
|
||||
scope: $scope
|
||||
});
|
||||
} else {
|
||||
if (data.code === UNSUPPORTED_CODE) {
|
||||
$scope.loadError = {message: '该应用的 Sentinel 客户端不支持集群限流,请升级至 1.4.0 以上版本并引入相关依赖。'}
|
||||
} else {
|
||||
$scope.loadError = {message: data.msg};
|
||||
}
|
||||
}
|
||||
}).error(() => {
|
||||
$scope.loadError = {message: '未知错误'};
|
||||
});
|
||||
};
|
||||
|
||||
function getRemainingMachineList() {
|
||||
return $scope.remainingMachineList.filter((e) => $scope.notChosenServer(e));
|
||||
}
|
||||
|
||||
function doApplyNewSingleServerAssign() {
|
||||
let ok = confirm('是否确认执行变更?');
|
||||
if (!ok) {
|
||||
return;
|
||||
}
|
||||
let serverData = $scope.serverAssignDialogData.serverData;
|
||||
let belongToApp = serverData.serverType == 0; // don't modify here!
|
||||
let machineId = serverData.currentServer;
|
||||
let request = {
|
||||
clusterMap: {
|
||||
machineId: machineId,
|
||||
ip: parseIpFromMachineId(machineId),
|
||||
port: serverData.serverPort,
|
||||
clientSet: serverData.clientSet,
|
||||
belongToApp: belongToApp,
|
||||
maxAllowedQps: serverData.maxAllowedQps,
|
||||
},
|
||||
remainingList: getRemainingMachineList(),
|
||||
};
|
||||
ClusterStateService.applyClusterSingleServerAssignOfApp($scope.app, request).success((data) => {
|
||||
if (data.code === 0 && data.data) {
|
||||
let failedServerSet = data.data.failedServerSet;
|
||||
let failedClientSet = data.data.failedClientSet;
|
||||
if (failedClientSet.length === 0 && failedServerSet.length === 0) {
|
||||
alert('全部推送成功');
|
||||
} else {
|
||||
let failedSet = [];
|
||||
if (failedServerSet) {
|
||||
failedServerSet.forEach((e) => {
|
||||
failedSet.push(e);
|
||||
});
|
||||
}
|
||||
if (failedClientSet) {
|
||||
failedClientSet.forEach((e) => {
|
||||
failedSet.push(e);
|
||||
});
|
||||
}
|
||||
|
||||
alert('推送完毕。失败机器列表:' + JSON.stringify(failedSet));
|
||||
}
|
||||
|
||||
location.reload();
|
||||
} else {
|
||||
if (data.code === UNSUPPORTED_CODE) {
|
||||
alert('该应用的 Sentinel 客户端不支持集群限流,请升级至 1.4.0 以上版本并引入相关依赖。');
|
||||
} else {
|
||||
alert('推送失败:' + data.msg);
|
||||
}
|
||||
}
|
||||
}).error(() => {
|
||||
alert('未知错误');
|
||||
});
|
||||
}
|
||||
|
||||
function doApplySingleServerAssignEdit() {
|
||||
let ok = confirm('是否确认执行变更?');
|
||||
if (!ok) {
|
||||
return;
|
||||
}
|
||||
let serverData = $scope.serverAssignDialogData.serverData;
|
||||
let machineId = serverData.currentServer;
|
||||
let request = {
|
||||
clusterMap: {
|
||||
machineId: machineId,
|
||||
ip: parseIpFromMachineId(machineId),
|
||||
port: serverData.serverPort,
|
||||
clientSet: serverData.clientSet,
|
||||
belongToApp: serverData.belongToApp,
|
||||
},
|
||||
remainingList: $scope.remainingMachineList,
|
||||
};
|
||||
if (serverData.maxAllowedQps !== undefined) {
|
||||
request.clusterMap.maxAllowedQps = serverData.maxAllowedQps;
|
||||
}
|
||||
ClusterStateService.applyClusterSingleServerAssignOfApp($scope.app, request).success((data) => {
|
||||
if (data.code === 0 && data.data) {
|
||||
let failedServerSet = data.data.failedServerSet;
|
||||
let failedClientSet = data.data.failedClientSet;
|
||||
if (failedClientSet.length === 0 && failedServerSet.length === 0) {
|
||||
alert('全部推送成功');
|
||||
} else {
|
||||
let failedSet = [];
|
||||
failedServerSet.forEach(failedSet.push);
|
||||
failedClientSet.forEach(failedSet.push);
|
||||
alert('推送完毕。失败机器列表:' + JSON.stringify(failedSet));
|
||||
}
|
||||
|
||||
location.reload();
|
||||
} else {
|
||||
if (data.code === UNSUPPORTED_CODE) {
|
||||
alert('该应用的 Sentinel 客户端不支持集群限流,请升级至 1.4.0 以上版本并引入相关依赖。');
|
||||
} else {
|
||||
alert('推送失败:' + data.msg);
|
||||
}
|
||||
}
|
||||
}).error(() => {
|
||||
alert('未知错误');
|
||||
});
|
||||
}
|
||||
|
||||
$scope.saveAssignForDialog = () => {
|
||||
if (!checkAssignDialogValid()) {
|
||||
return;
|
||||
}
|
||||
if ($scope.serverAssignDialogData.type === 'add') {
|
||||
doApplyNewSingleServerAssign();
|
||||
} else if ($scope.serverAssignDialogData.type === 'edit') {
|
||||
doApplySingleServerAssignEdit();
|
||||
} else {
|
||||
alert('未知的操作');
|
||||
}
|
||||
};
|
||||
|
||||
function checkAssignDialogValid() {
|
||||
let serverData = $scope.serverAssignDialogData.serverData;
|
||||
if (serverData.currentServer === undefined || serverData.currentServer === '') {
|
||||
alert('请指定有效的 Token Server');
|
||||
return false;
|
||||
}
|
||||
if (serverData.serverPort === undefined || serverData.serverPort <= 0 || serverData.serverPort > 65535) {
|
||||
alert('请输入合法的端口值');
|
||||
return false;
|
||||
}
|
||||
if (serverData.maxAllowedQps !== undefined && serverData.maxAllowedQps < 0) {
|
||||
alert('请输入合法的最大允许 QPS');
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
$scope.viewConnectionDetail = (serverVO) => {
|
||||
$scope.connectionDetailDialogData = {
|
||||
serverData: serverVO
|
||||
};
|
||||
$scope.connectionDetailDialog = ngDialog.open({
|
||||
template: '/app/views/dialog/cluster/cluster-server-connection-detail-dialog.html',
|
||||
width: 700,
|
||||
overlay: true,
|
||||
scope: $scope
|
||||
});
|
||||
};
|
||||
|
||||
function generateRequestLimitDataStr(limitData) {
|
||||
if (limitData.length === 1 && limitData[0].namespace === DEFAULT_NAMESPACE) {
|
||||
return 'default: ' + limitData[0].currentQps + ' / ' + limitData[0].maxAllowedQps;
|
||||
}
|
||||
for (let i = 0; i < limitData.length; i++) {
|
||||
let crl = limitData[i];
|
||||
if (crl.namespace === $scope.app) {
|
||||
return '' + crl.currentQps + ' / ' + crl.maxAllowedQps;
|
||||
}
|
||||
}
|
||||
return '0';
|
||||
}
|
||||
|
||||
function processServerListData(serverVO) {
|
||||
if (serverVO.state && serverVO.state.namespaceSet) {
|
||||
serverVO.state.namespaceSetStr = convertSetToString(serverVO.state.namespaceSet);
|
||||
}
|
||||
if (serverVO.state && serverVO.state.requestLimitData) {
|
||||
serverVO.state.requestLimitDataStr = generateRequestLimitDataStr(serverVO.state.requestLimitData);
|
||||
}
|
||||
}
|
||||
|
||||
$scope.generateConnectionSet = (data) => {
|
||||
let connectionSet = data;
|
||||
let s = '';
|
||||
if (connectionSet) {
|
||||
s = s + '[';
|
||||
for (let i = 0; i < connectionSet.length; i++) {
|
||||
s = s + connectionSet[i].address;
|
||||
if (i < connectionSet.length - 1) {
|
||||
s = s + ', ';
|
||||
}
|
||||
}
|
||||
s = s + ']';
|
||||
} else {
|
||||
s = '[]';
|
||||
}
|
||||
return s;
|
||||
};
|
||||
|
||||
function retrieveClusterServerInfo() {
|
||||
ClusterStateService.fetchClusterServerStateOfApp($scope.app).success(function (data) {
|
||||
if (data.code === 0 && data.data) {
|
||||
$scope.loadError = undefined;
|
||||
$scope.serverVOList = data.data;
|
||||
$scope.serverVOList.forEach(processServerListData);
|
||||
} else {
|
||||
$scope.serverVOList = {};
|
||||
if (data.code === UNSUPPORTED_CODE) {
|
||||
$scope.loadError = {message: '该应用的 Sentinel 客户端不支持集群限流,请升级至 1.4.0 以上版本并引入相关依赖。'}
|
||||
} else {
|
||||
$scope.loadError = {message: data.msg};
|
||||
}
|
||||
}
|
||||
}).error(() => {
|
||||
$scope.loadError = {message: '未知错误'};
|
||||
});
|
||||
}
|
||||
|
||||
retrieveClusterServerInfo();
|
||||
|
||||
let confirmUnbindServerDialog;
|
||||
$scope.unbindServer = (id) => {
|
||||
$scope.pendingUnbindIds = [id];
|
||||
$scope.confirmDialog = {
|
||||
title: '移除 Token Server',
|
||||
type: 'unbind_token_server',
|
||||
attentionTitle: '请确认是否移除以下 Token Server(该 server 下的 client 也会解除分配)',
|
||||
attention: id + '',
|
||||
confirmBtnText: '移除',
|
||||
};
|
||||
confirmUnbindServerDialog = ngDialog.open({
|
||||
template: '/app/views/dialog/confirm-dialog.html',
|
||||
scope: $scope,
|
||||
overlay: true
|
||||
});
|
||||
};
|
||||
|
||||
function apiUnbindServerAssign(ids) {
|
||||
ClusterStateService.applyClusterServerBatchUnbind($scope.app, ids).success((data) => {
|
||||
if (data.code === 0 && data.data) {
|
||||
let failedServerSet = data.data.failedServerSet;
|
||||
let failedClientSet = data.data.failedClientSet;
|
||||
if (failedClientSet.length === 0 && failedServerSet.length === 0) {
|
||||
alert('成功');
|
||||
} else {
|
||||
alert('操作推送完毕,部分失败机器列表:' + JSON.stringify(failedClientSet));
|
||||
}
|
||||
|
||||
location.reload();
|
||||
} else {
|
||||
if (data.code === UNSUPPORTED_CODE) {
|
||||
alert('该应用的 Sentinel 客户端不支持集群限流,请升级至 1.4.0 以上版本并引入相关依赖。');
|
||||
} else {
|
||||
alert('推送失败:' + data.msg);
|
||||
}
|
||||
}
|
||||
}).error(() => {
|
||||
alert('未知错误');
|
||||
});
|
||||
// confirmUnbindServerDialog.close();
|
||||
}
|
||||
|
||||
// Confirm function for confirm dialog.
|
||||
$scope.confirm = () => {
|
||||
if ($scope.confirmDialog.type === 'unbind_token_server') {
|
||||
apiUnbindServerAssign($scope.pendingUnbindIds);
|
||||
} else {
|
||||
console.error('Error dialog when unbinding token server');
|
||||
}
|
||||
};
|
||||
}]);
|
||||
@@ -0,0 +1,283 @@
|
||||
var app = angular.module('sentinelDashboardApp');
|
||||
|
||||
app.controller('SentinelClusterAppAssignManageController', ['$scope', '$stateParams', 'ngDialog',
|
||||
'MachineService', 'ClusterStateService',
|
||||
function ($scope, $stateParams, ngDialog, MachineService, ClusterStateService) {
|
||||
$scope.app = $stateParams.app;
|
||||
const UNSUPPORTED_CODE = 4041;
|
||||
|
||||
const CLUSTER_MODE_CLIENT = 0;
|
||||
const CLUSTER_MODE_SERVER = 1;
|
||||
const DEFAULT_CLUSTER_SERVER_PORT = 18730;
|
||||
|
||||
$scope.tmp = {
|
||||
curClientChosen: [],
|
||||
curRemainingClientChosen: [],
|
||||
curChosenServer: {},
|
||||
};
|
||||
|
||||
function convertSetToString(set) {
|
||||
if (set === undefined) {
|
||||
return '';
|
||||
}
|
||||
let s = '';
|
||||
for (let i = 0; i < set.length; i++) {
|
||||
s = s + set[i];
|
||||
if (i < set.length - 1) {
|
||||
s = s + ',';
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
function convertStrToNamespaceSet(str) {
|
||||
if (str === undefined || str === '') {
|
||||
return [];
|
||||
}
|
||||
let arr = [];
|
||||
let spliced = str.split(',');
|
||||
spliced.forEach((v) => {
|
||||
arr.push(v.trim());
|
||||
});
|
||||
return arr;
|
||||
}
|
||||
|
||||
function processAppSingleData(data) {
|
||||
if (data.state.server && data.state.server.namespaceSet) {
|
||||
data.state.server.namespaceSetStr = convertSetToString(data.state.server.namespaceSet);
|
||||
data.mode = data.state.stateInfo.mode;
|
||||
}
|
||||
}
|
||||
|
||||
function removeFromArr(arr, v) {
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
if (arr[i] === v) {
|
||||
arr.splice(i, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function resetChosen() {
|
||||
$scope.tmp.curClientChosen = [];
|
||||
$scope.tmp.curRemainingClientChosen = [];
|
||||
}
|
||||
|
||||
function generateMachineId(e) {
|
||||
return e.ip + '@' + e.commandPort;
|
||||
}
|
||||
|
||||
function applyClusterMap(appClusterMachineList) {
|
||||
if (!appClusterMachineList) {
|
||||
return;
|
||||
}
|
||||
let tmpMap = new Map();
|
||||
$scope.clusterMap = [];
|
||||
$scope.remainingClientAddressList = [];
|
||||
let tmpServerList = [];
|
||||
let tmpClientList = [];
|
||||
appClusterMachineList.forEach((e) => {
|
||||
if (e.mode === CLUSTER_MODE_CLIENT) {
|
||||
tmpClientList.push(e);
|
||||
} else if (e.mode === CLUSTER_MODE_SERVER) {
|
||||
tmpServerList.push(e);
|
||||
} else {
|
||||
$scope.remainingClientAddressList.push(generateMachineId(e));
|
||||
}
|
||||
});
|
||||
tmpServerList.forEach((e) => {
|
||||
let ip = e.ip;
|
||||
let machineId = ip + '@' + e.commandPort;
|
||||
let group = {
|
||||
ip: ip,
|
||||
machineId: machineId,
|
||||
port: e.state.server.port,
|
||||
clientSet: [],
|
||||
namespaceSetStr: e.state.server.namespaceSetStr,
|
||||
belongToApp: true,
|
||||
};
|
||||
if (!tmpMap.has(ip)) {
|
||||
tmpMap.set(ip, group);
|
||||
}
|
||||
});
|
||||
tmpClientList.forEach((e) => {
|
||||
let ip = e.ip;
|
||||
let machineId = ip + '@' + e.commandPort;
|
||||
|
||||
let targetServer = e.state.client.clientConfig.serverHost;
|
||||
let targetPort = e.state.client.clientConfig.serverPort;
|
||||
if (targetServer === undefined || targetServer === '' ||
|
||||
targetPort === undefined || targetPort <= 0) {
|
||||
$scope.remainingClientAddressList.push(generateMachineId(e));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!tmpMap.has(targetServer)) {
|
||||
let group = {
|
||||
ip: targetServer,
|
||||
machineId: targetServer,
|
||||
port: targetPort,
|
||||
clientSet: [machineId],
|
||||
belongToApp: false,
|
||||
};
|
||||
tmpMap.set(targetServer, group);
|
||||
} else {
|
||||
let g = tmpMap.get(targetServer);
|
||||
g.clientSet.push(machineId);
|
||||
}
|
||||
});
|
||||
tmpMap.forEach((v) => {
|
||||
if (v !== undefined) {
|
||||
$scope.clusterMap.push(v);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$scope.onCurrentServerChange = () => {
|
||||
resetChosen();
|
||||
};
|
||||
|
||||
$scope.remainingClientAddressList = [];
|
||||
|
||||
$scope.moveToServerGroup = () => {
|
||||
let chosenServer = $scope.tmp.curChosenServer;
|
||||
if (!chosenServer || !chosenServer.machineId) {
|
||||
return;
|
||||
}
|
||||
$scope.tmp.curRemainingClientChosen.forEach(e => {
|
||||
chosenServer.clientSet.push(e);
|
||||
removeFromArr($scope.remainingClientAddressList, e);
|
||||
});
|
||||
resetChosen();
|
||||
};
|
||||
|
||||
$scope.moveToRemainingSharePool = () => {
|
||||
$scope.tmp.curClientChosen.forEach(e => {
|
||||
$scope.remainingClientAddressList.push(e);
|
||||
removeFromArr($scope.tmp.curChosenServer.clientSet, e);
|
||||
});
|
||||
resetChosen();
|
||||
};
|
||||
|
||||
function parseIpFromMachineId(machineId) {
|
||||
if (machineId.indexOf('@') === -1) {
|
||||
return machineId;
|
||||
}
|
||||
let arr = machineId.split('@');
|
||||
return arr[0];
|
||||
}
|
||||
|
||||
$scope.addToServerList = () => {
|
||||
let group;
|
||||
$scope.tmp.curRemainingClientChosen.forEach(e => {
|
||||
group = {
|
||||
machineId: e,
|
||||
ip: parseIpFromMachineId(e),
|
||||
port: DEFAULT_CLUSTER_SERVER_PORT,
|
||||
clientSet: [],
|
||||
namespaceSetStr: 'default,' + $scope.app,
|
||||
belongToApp: true,
|
||||
};
|
||||
$scope.clusterMap.push(group);
|
||||
removeFromArr($scope.remainingClientAddressList, e);
|
||||
$scope.tmp.curChosenServer = group;
|
||||
});
|
||||
resetChosen();
|
||||
};
|
||||
|
||||
$scope.removeFromServerList = () => {
|
||||
let chosenServer = $scope.tmp.curChosenServer;
|
||||
if (!chosenServer || !chosenServer.machineId) {
|
||||
return;
|
||||
}
|
||||
chosenServer.clientSet.forEach((e) => {
|
||||
if (e !== undefined) {
|
||||
$scope.remainingClientAddressList.push(e);
|
||||
}
|
||||
});
|
||||
|
||||
if (chosenServer.belongToApp || chosenServer.machineId.indexOf('@') !== -1) {
|
||||
$scope.remainingClientAddressList.push(chosenServer.machineId);
|
||||
} else {
|
||||
alert('提示:非本应用内机器将不会置回空闲列表中');
|
||||
}
|
||||
|
||||
removeFromArr($scope.clusterMap, chosenServer);
|
||||
|
||||
resetChosen();
|
||||
|
||||
if ($scope.clusterMap.length > 0) {
|
||||
$scope.tmp.curChosenServer = $scope.clusterMap[0];
|
||||
$scope.onCurrentServerChange();
|
||||
} else {
|
||||
$scope.tmp.curChosenServer = {};
|
||||
}
|
||||
};
|
||||
|
||||
function retrieveClusterAppInfo() {
|
||||
ClusterStateService.fetchClusterUniversalStateOfApp($scope.app).success(function (data) {
|
||||
if (data.code === 0 && data.data) {
|
||||
$scope.loadError = undefined;
|
||||
$scope.appClusterMachineList = data.data;
|
||||
$scope.appClusterMachineList.forEach(processAppSingleData);
|
||||
applyClusterMap($scope.appClusterMachineList);
|
||||
if ($scope.clusterMap.length > 0) {
|
||||
$scope.tmp.curChosenServer = $scope.clusterMap[0];
|
||||
$scope.onCurrentServerChange();
|
||||
}
|
||||
} else {
|
||||
$scope.appClusterMachineList = {};
|
||||
if (data.code === UNSUPPORTED_CODE) {
|
||||
$scope.loadError = {message: '该应用的 Sentinel 客户端不支持集群限流,请升级至 1.4.0 以上版本并引入相关依赖。'}
|
||||
} else {
|
||||
$scope.loadError = {message: data.msg};
|
||||
}
|
||||
}
|
||||
}).error(() => {
|
||||
$scope.loadError = {message: '未知错误'};
|
||||
});
|
||||
}
|
||||
|
||||
retrieveClusterAppInfo();
|
||||
|
||||
$scope.saveAndApplyAssign = () => {
|
||||
let ok = confirm('是否确认执行变更?');
|
||||
if (!ok) {
|
||||
return;
|
||||
}
|
||||
let cm = $scope.clusterMap;
|
||||
if (!cm) {
|
||||
cm = [];
|
||||
}
|
||||
cm.forEach((e) => {
|
||||
e.namespaceSet = convertStrToNamespaceSet(e.namespaceSetStr);
|
||||
});
|
||||
cm.namespaceSet = convertStrToNamespaceSet(cm.namespaceSetStr);
|
||||
let request = {
|
||||
clusterMap: cm,
|
||||
remainingList: $scope.remainingClientAddressList,
|
||||
};
|
||||
ClusterStateService.applyClusterFullAssignOfApp($scope.app, request).success((data) => {
|
||||
if (data.code === 0 && data.data) {
|
||||
let failedServerSet = data.data.failedServerSet;
|
||||
let failedClientSet = data.data.failedClientSet;
|
||||
if (failedClientSet.length === 0 && failedServerSet.length === 0) {
|
||||
alert('全部推送成功');
|
||||
} else {
|
||||
alert('推送完毕。token server 失败列表:' + JSON.stringify(failedServerSet) +
|
||||
'; token client 失败列表:' + JSON.stringify(failedClientSet));
|
||||
}
|
||||
|
||||
retrieveClusterAppInfo();
|
||||
} else {
|
||||
if (data.code === UNSUPPORTED_CODE) {
|
||||
alert('该应用的 Sentinel 客户端不支持集群限流,请升级至 1.4.0 以上版本并引入相关依赖。');
|
||||
} else {
|
||||
alert('推送失败:' + data.msg);
|
||||
}
|
||||
}
|
||||
}).error(() => {
|
||||
alert('未知错误');
|
||||
});
|
||||
};
|
||||
}]);
|
||||
@@ -0,0 +1,97 @@
|
||||
var app = angular.module('sentinelDashboardApp');
|
||||
|
||||
app.controller('SentinelClusterAppServerMonitorController', ['$scope', '$stateParams', 'ngDialog',
|
||||
'MachineService', 'ClusterStateService',
|
||||
function ($scope, $stateParams, ngDialog, MachineService, ClusterStateService) {
|
||||
$scope.app = $stateParams.app;
|
||||
const UNSUPPORTED_CODE = 4041;
|
||||
|
||||
const CLUSTER_MODE_SERVER = 1;
|
||||
|
||||
$scope.tmp = {
|
||||
curChosenServer: {},
|
||||
};
|
||||
|
||||
function convertSetToString(set) {
|
||||
if (set === undefined) {
|
||||
return '';
|
||||
}
|
||||
let s = '';
|
||||
for (let i = 0; i < set.length; i++) {
|
||||
s = s + set[i];
|
||||
if (i < set.length - 1) {
|
||||
s = s + ',';
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
function processServerData(serverVO) {
|
||||
if (serverVO.state && serverVO.state.namespaceSet) {
|
||||
serverVO.state.namespaceSetStr = convertSetToString(serverVO.state.namespaceSet);
|
||||
}
|
||||
}
|
||||
|
||||
$scope.generateConnectionSet = (data) => {
|
||||
let connectionSet = data;
|
||||
let s = '';
|
||||
if (connectionSet) {
|
||||
s = s + '[';
|
||||
for (let i = 0; i < connectionSet.length; i++) {
|
||||
s = s + connectionSet[i].address;
|
||||
if (i < connectionSet.length - 1) {
|
||||
s = s + ', ';
|
||||
}
|
||||
}
|
||||
s = s + ']';
|
||||
} else {
|
||||
s = '[]';
|
||||
}
|
||||
return s;
|
||||
};
|
||||
|
||||
$scope.onChosenServerChange = () => {
|
||||
|
||||
};
|
||||
|
||||
function retrieveClusterServerInfo() {
|
||||
ClusterStateService.fetchClusterServerStateOfApp($scope.app).success(function (data) {
|
||||
if (data.code === 0 && data.data) {
|
||||
$scope.loadError = undefined;
|
||||
$scope.serverVOList = data.data;
|
||||
$scope.serverVOList.forEach(processServerData);
|
||||
|
||||
if ($scope.serverVOList.length > 0) {
|
||||
$scope.tmp.curChosenServer = $scope.serverVOList[0];
|
||||
$scope.onChosenServerChange();
|
||||
}
|
||||
} else {
|
||||
$scope.serverVOList = {};
|
||||
if (data.code === UNSUPPORTED_CODE) {
|
||||
$scope.loadError = {message: '该应用的 Sentinel 客户端不支持集群限流,请升级至 1.4.0 以上版本并引入相关依赖。'}
|
||||
} else {
|
||||
$scope.loadError = {message: data.msg};
|
||||
}
|
||||
}
|
||||
}).error(() => {
|
||||
$scope.loadError = {message: '未知错误'};
|
||||
});
|
||||
}
|
||||
|
||||
retrieveClusterServerInfo();
|
||||
|
||||
$scope.macsInputConfig = {
|
||||
searchField: ['text', 'value'],
|
||||
persist: true,
|
||||
create: false,
|
||||
maxItems: 1,
|
||||
render: {
|
||||
item: function (data, escape) {
|
||||
return '<div>' + escape(data.text) + '</div>';
|
||||
}
|
||||
},
|
||||
onChange: function (value, oldValue) {
|
||||
$scope.macInputModel = value;
|
||||
}
|
||||
};
|
||||
}]);
|
||||
@@ -0,0 +1,121 @@
|
||||
var app = angular.module('sentinelDashboardApp');
|
||||
|
||||
app.controller('SentinelClusterAppTokenClientListController', ['$scope', '$stateParams', 'ngDialog',
|
||||
'MachineService', 'ClusterStateService',
|
||||
function ($scope, $stateParams, ngDialog, MachineService, ClusterStateService) {
|
||||
$scope.app = $stateParams.app;
|
||||
|
||||
const UNSUPPORTED_CODE = 4041;
|
||||
const CLUSTER_MODE_CLIENT = 0;
|
||||
const CLUSTER_MODE_SERVER = 1;
|
||||
|
||||
function processClientData(clientVO) {
|
||||
|
||||
}
|
||||
|
||||
$scope.modifyClientConfigDialog = (clientVO) => {
|
||||
if (!clientVO) {
|
||||
return;
|
||||
}
|
||||
$scope.ccDialogData = {
|
||||
ip: clientVO.ip,
|
||||
commandPort: clientVO.commandPort,
|
||||
clientId: clientVO.id,
|
||||
serverHost: clientVO.state.clientConfig.serverHost,
|
||||
serverPort: clientVO.state.clientConfig.serverPort,
|
||||
requestTimeout: clientVO.state.clientConfig.requestTimeout,
|
||||
};
|
||||
$scope.ccDialog = ngDialog.open({
|
||||
template: '/app/views/dialog/cluster/cluster-client-config-dialog.html',
|
||||
width: 700,
|
||||
overlay: true,
|
||||
scope: $scope
|
||||
});
|
||||
};
|
||||
|
||||
function checkValidClientConfig(config) {
|
||||
if (!config.serverHost || config.serverHost.trim() == '') {
|
||||
alert('请输入有效的 Token Server IP');
|
||||
return false;
|
||||
}
|
||||
if (config.serverPort === undefined || config.serverPort <= 0 || config.serverPort > 65535) {
|
||||
alert('请输入有效的 Token Server 端口');
|
||||
return false;
|
||||
}
|
||||
if (config.requestTimeout === undefined || config.requestTimeout <= 0) {
|
||||
alert('请输入有效的请求超时时长');
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
$scope.doModifyClientConfig = () => {
|
||||
if (!checkValidClientConfig($scope.ccDialogData)) {
|
||||
return;
|
||||
}
|
||||
let id = $scope.ccDialogData.id;
|
||||
let request = {
|
||||
app: $scope.app,
|
||||
ip: $scope.ccDialogData.ip,
|
||||
port: $scope.ccDialogData.commandPort,
|
||||
mode: CLUSTER_MODE_CLIENT,
|
||||
clientConfig: {
|
||||
serverHost: $scope.ccDialogData.serverHost,
|
||||
serverPort: $scope.ccDialogData.serverPort,
|
||||
requestTimeout: $scope.ccDialogData.requestTimeout,
|
||||
}
|
||||
};
|
||||
ClusterStateService.modifyClusterConfig(request).success((data) => {
|
||||
if (data.code === 0 && data.data) {
|
||||
alert('修改 Token Client 配置成功');
|
||||
window.location.reload();
|
||||
} else {
|
||||
if (data.code === UNSUPPORTED_CODE) {
|
||||
alert('机器 ' + id + ' 的 Sentinel 没有引入集群限流客户端,请升级至 1.4.0 以上版本并引入相关依赖。');
|
||||
} else {
|
||||
alert('修改失败:' + data.msg);
|
||||
}
|
||||
}
|
||||
}).error((data, header, config, status) => {
|
||||
alert('未知错误');
|
||||
});
|
||||
};
|
||||
|
||||
function retrieveClusterTokenClientInfo() {
|
||||
ClusterStateService.fetchClusterClientStateOfApp($scope.app)
|
||||
.success((data) => {
|
||||
if (data.code === 0 && data.data) {
|
||||
$scope.loadError = undefined;
|
||||
$scope.clientVOList = data.data;
|
||||
$scope.clientVOList.forEach(processClientData);
|
||||
} else {
|
||||
$scope.clientVOList = [];
|
||||
if (data.code === UNSUPPORTED_CODE) {
|
||||
$scope.loadError = {message: '该应用的 Sentinel 客户端不支持集群限流,请升级至 1.4.0 以上版本并引入相关依赖。'}
|
||||
} else {
|
||||
$scope.loadError = {message: data.msg};
|
||||
}
|
||||
}
|
||||
})
|
||||
.error(() => {
|
||||
$scope.loadError = {message: '未知错误'};
|
||||
});
|
||||
}
|
||||
|
||||
retrieveClusterTokenClientInfo();
|
||||
|
||||
$scope.macsInputConfig = {
|
||||
searchField: ['text', 'value'],
|
||||
persist: true,
|
||||
create: false,
|
||||
maxItems: 1,
|
||||
render: {
|
||||
item: function (data, escape) {
|
||||
return '<div>' + escape(data.text) + '</div>';
|
||||
}
|
||||
},
|
||||
onChange: function (value, oldValue) {
|
||||
$scope.macInputModel = value;
|
||||
}
|
||||
};
|
||||
}]);
|
||||
@@ -0,0 +1,262 @@
|
||||
var app = angular.module('sentinelDashboardApp');
|
||||
|
||||
app.controller('SentinelClusterSingleController', ['$scope', '$stateParams', 'ngDialog',
|
||||
'MachineService', 'ClusterStateService',
|
||||
function ($scope, $stateParams, ngDialog, MachineService, ClusterStateService) {
|
||||
$scope.app = $stateParams.app;
|
||||
const UNSUPPORTED_CODE = 4041;
|
||||
|
||||
const CLUSTER_MODE_CLIENT = 0;
|
||||
const CLUSTER_MODE_SERVER = 1;
|
||||
|
||||
$scope.macsInputConfig = {
|
||||
searchField: ['text', 'value'],
|
||||
persist: true,
|
||||
create: false,
|
||||
maxItems: 1,
|
||||
render: {
|
||||
item: function (data, escape) {
|
||||
return '<div>' + escape(data.text) + '</div>';
|
||||
}
|
||||
},
|
||||
onChange: function (value, oldValue) {
|
||||
$scope.macInputModel = value;
|
||||
}
|
||||
};
|
||||
|
||||
function convertSetToString(set) {
|
||||
if (set === undefined) {
|
||||
return '';
|
||||
}
|
||||
let s = '';
|
||||
for (let i = 0; i < set.length; i++) {
|
||||
s = s + set[i];
|
||||
if (i < set.length - 1) {
|
||||
s = s + ',';
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
function convertStrToNamespaceSet(str) {
|
||||
if (str === undefined || str === '') {
|
||||
return [];
|
||||
}
|
||||
let arr = [];
|
||||
let spliced = str.split(',');
|
||||
spliced.forEach((v) => {
|
||||
arr.push(v.trim());
|
||||
});
|
||||
return arr;
|
||||
}
|
||||
|
||||
function fetchMachineClusterState() {
|
||||
if (!$scope.macInputModel || $scope.macInputModel === '') {
|
||||
return;
|
||||
}
|
||||
let mac = $scope.macInputModel.split(':');
|
||||
ClusterStateService.fetchClusterUniversalStateSingle($scope.app, mac[0], mac[1]).success(function (data) {
|
||||
if (data.code == 0 && data.data) {
|
||||
$scope.loadError = undefined;
|
||||
$scope.stateVO = data.data;
|
||||
$scope.stateVO.currentMode = $scope.stateVO.stateInfo.mode;
|
||||
if ($scope.stateVO.server && $scope.stateVO.server.namespaceSet) {
|
||||
$scope.stateVO.server.namespaceSetStr = convertSetToString($scope.stateVO.server.namespaceSet);
|
||||
}
|
||||
} else {
|
||||
$scope.stateVO = {};
|
||||
if (data.code === UNSUPPORTED_CODE) {
|
||||
$scope.loadError = {message: '机器 ' + mac[0] + ':' + mac[1] + ' 的 Sentinel 客户端版本不支持集群限流,请升级至 1.4.0 以上版本并引入相关依赖。'}
|
||||
} else {
|
||||
$scope.loadError = {message: data.msg};
|
||||
}
|
||||
}
|
||||
}).error((data, header, config, status) => {
|
||||
$scope.loadError = {message: '未知错误'};
|
||||
});
|
||||
}
|
||||
|
||||
fetchMachineClusterState();
|
||||
|
||||
function checkValidClientConfig(stateVO) {
|
||||
if (!stateVO.client || !stateVO.client.clientConfig) {
|
||||
alert('不合法的配置');
|
||||
return false;
|
||||
}
|
||||
let config = stateVO.client.clientConfig;
|
||||
if (!config.serverHost || config.serverHost.trim() == '') {
|
||||
alert('请输入有效的 Token Server IP');
|
||||
return false;
|
||||
}
|
||||
if (config.serverPort === undefined || config.serverPort <= 0 || config.serverPort > 65535) {
|
||||
alert('请输入有效的 Token Server 端口');
|
||||
return false;
|
||||
}
|
||||
if (config.requestTimeout === undefined || config.requestTimeout <= 0) {
|
||||
alert('请输入有效的请求超时时长');
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function sendClusterClientRequest(stateVO) {
|
||||
if (!checkValidClientConfig(stateVO)) {
|
||||
return;
|
||||
}
|
||||
if (!$scope.macInputModel) {
|
||||
return;
|
||||
}
|
||||
let mac = $scope.macInputModel.split(':');
|
||||
let request = {
|
||||
app: $scope.app,
|
||||
ip: mac[0],
|
||||
port: mac[1],
|
||||
};
|
||||
request.mode = CLUSTER_MODE_CLIENT;
|
||||
request.clientConfig = stateVO.client.clientConfig;
|
||||
ClusterStateService.modifyClusterConfig(request).success(function (data) {
|
||||
if (data.code == 0 && data.data) {
|
||||
alert('修改集群限流客户端配置成功');
|
||||
window.location.reload();
|
||||
} else {
|
||||
if (data.code === UNSUPPORTED_CODE) {
|
||||
alert('机器 ' + mac[0] + ':' + mac[1] + ' 的 Sentinel 客户端版本不支持集群限流客户端,请升级至 1.4.0 以上版本并引入相关依赖。');
|
||||
} else {
|
||||
alert('修改失败:' + data.msg);
|
||||
}
|
||||
}
|
||||
}).error((data, header, config, status) => {
|
||||
alert('未知错误');
|
||||
});
|
||||
}
|
||||
|
||||
function checkValidServerConfig(stateVO) {
|
||||
if (!stateVO.server || !stateVO.server.transport) {
|
||||
alert('不合法的配置');
|
||||
return false;
|
||||
}
|
||||
if (stateVO.server.namespaceSetStr === undefined || stateVO.server.namespaceSetStr == '') {
|
||||
alert('请输入有效的命名空间集合(多个 namespace 以 , 分隔)');
|
||||
return false;
|
||||
}
|
||||
let transportConfig = stateVO.server.transport;
|
||||
if (transportConfig.port === undefined || transportConfig.port <= 0 || transportConfig.port > 65535) {
|
||||
alert('请输入有效的 Token Server 端口');
|
||||
return false;
|
||||
}
|
||||
let flowConfig = stateVO.server.flow;
|
||||
if (flowConfig.maxAllowedQps === undefined || flowConfig.maxAllowedQps < 0) {
|
||||
alert('请输入有效的最大允许 QPS');
|
||||
return false;
|
||||
}
|
||||
// if (transportConfig.idleSeconds === undefined || transportConfig.idleSeconds <= 0) {
|
||||
// alert('请输入有效的连接清理时长 (idleSeconds)');
|
||||
// return false;
|
||||
// }
|
||||
return true;
|
||||
}
|
||||
|
||||
function sendClusterServerRequest(stateVO) {
|
||||
if (!checkValidServerConfig(stateVO)) {
|
||||
return;
|
||||
}
|
||||
if (!$scope.macInputModel) {
|
||||
return;
|
||||
}
|
||||
let mac = $scope.macInputModel.split(':');
|
||||
let request = {
|
||||
app: $scope.app,
|
||||
ip: mac[0],
|
||||
port: mac[1],
|
||||
};
|
||||
request.mode = CLUSTER_MODE_SERVER;
|
||||
request.flowConfig = stateVO.server.flow;
|
||||
request.transportConfig = stateVO.server.transport;
|
||||
request.namespaceSet = convertStrToNamespaceSet(stateVO.server.namespaceSetStr);
|
||||
ClusterStateService.modifyClusterConfig(request).success(function (data) {
|
||||
if (data.code == 0 && data.data) {
|
||||
alert('修改集群限流服务端配置成功');
|
||||
window.location.reload();
|
||||
} else {
|
||||
if (data.code === UNSUPPORTED_CODE) {
|
||||
alert('机器 ' + mac[0] + ':' + mac[1] + ' 的 Sentinel 客户端版本不支持集群限流服务端,请升级至 1.4.0 以上版本并引入相关依赖。');
|
||||
} else {
|
||||
alert('修改失败:' + data.msg);
|
||||
}
|
||||
}
|
||||
}).error((data, header, config, status) => {
|
||||
alert('未知错误');
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
$scope.saveConfig = () => {
|
||||
let ok = confirm('是否确定修改集群限流配置?');
|
||||
if (!ok) {
|
||||
return;
|
||||
}
|
||||
let mode = $scope.stateVO.stateInfo.mode;
|
||||
if (mode != 1 && mode != 0) {
|
||||
alert('未知的集群限流模式');
|
||||
return;
|
||||
}
|
||||
if (mode == 0) {
|
||||
sendClusterClientRequest($scope.stateVO);
|
||||
} else {
|
||||
sendClusterServerRequest($scope.stateVO);
|
||||
}
|
||||
};
|
||||
|
||||
function queryAppMachines() {
|
||||
MachineService.getAppMachines($scope.app).success(
|
||||
function (data) {
|
||||
if (data.code === 0) {
|
||||
// $scope.machines = data.data;
|
||||
if (data.data) {
|
||||
$scope.machines = [];
|
||||
$scope.macsInputOptionsOrigin = [];
|
||||
$scope.macsInputOptions = [];
|
||||
data.data.forEach(function (item) {
|
||||
if (item.healthy) {
|
||||
$scope.macsInputOptionsOrigin.push({
|
||||
text: item.ip + ':' + item.port,
|
||||
value: item.ip + ':' + item.port
|
||||
});
|
||||
}
|
||||
});
|
||||
$scope.macsInputOptions = $scope.macsInputOptionsOrigin;
|
||||
}
|
||||
if ($scope.macsInputOptions.length > 0) {
|
||||
$scope.macInputModel = $scope.macsInputOptions[0].value;
|
||||
}
|
||||
} else {
|
||||
$scope.macsInputOptions = [];
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
queryAppMachines();
|
||||
|
||||
$scope.$watch('searchKey', function () {
|
||||
if (!$scope.macsInputOptions) {
|
||||
return;
|
||||
}
|
||||
if ($scope.searchKey) {
|
||||
$scope.macsInputOptions = $scope.macsInputOptionsOrigin
|
||||
.filter((e) => e.value.indexOf($scope.searchKey) !== -1);
|
||||
} else {
|
||||
$scope.macsInputOptions = $scope.macsInputOptionsOrigin;
|
||||
}
|
||||
if ($scope.macsInputOptions.length > 0) {
|
||||
$scope.macInputModel = $scope.macsInputOptions[0].value;
|
||||
} else {
|
||||
$scope.macInputModel = '';
|
||||
}
|
||||
});
|
||||
|
||||
$scope.$watch('macInputModel', function () {
|
||||
if ($scope.macInputModel) {
|
||||
fetchMachineClusterState();
|
||||
}
|
||||
});
|
||||
}]);
|
||||
@@ -0,0 +1,204 @@
|
||||
var app = angular.module('sentinelDashboardApp');
|
||||
|
||||
app.controller('DegradeCtl', ['$scope', '$stateParams', 'DegradeService', 'ngDialog', 'MachineService',
|
||||
function ($scope, $stateParams, DegradeService, ngDialog, MachineService) {
|
||||
//初始化
|
||||
$scope.app = $stateParams.app;
|
||||
$scope.rulesPageConfig = {
|
||||
pageSize: 10,
|
||||
currentPageIndex: 1,
|
||||
totalPage: 1,
|
||||
totalCount: 0,
|
||||
};
|
||||
$scope.macsInputConfig = {
|
||||
searchField: ['text', 'value'],
|
||||
persist: true,
|
||||
create: false,
|
||||
maxItems: 1,
|
||||
render: {
|
||||
item: function (data, escape) {
|
||||
return '<div>' + escape(data.text) + '</div>';
|
||||
}
|
||||
},
|
||||
onChange: function (value, oldValue) {
|
||||
$scope.macInputModel = value;
|
||||
}
|
||||
};
|
||||
getMachineRules();
|
||||
function getMachineRules() {
|
||||
if (!$scope.macInputModel) {
|
||||
return;
|
||||
}
|
||||
var mac = $scope.macInputModel.split(':');
|
||||
DegradeService.queryMachineRules($scope.app, mac[0], mac[1]).success(
|
||||
function (data) {
|
||||
if (data.code == 0 && data.data) {
|
||||
$scope.rules = data.data;
|
||||
$scope.rulesPageConfig.totalCount = $scope.rules.length;
|
||||
} else {
|
||||
$scope.rules = [];
|
||||
$scope.rulesPageConfig.totalCount = 0;
|
||||
}
|
||||
});
|
||||
};
|
||||
$scope.getMachineRules = getMachineRules;
|
||||
|
||||
var degradeRuleDialog;
|
||||
$scope.editRule = function (rule) {
|
||||
$scope.currentRule = angular.copy(rule);
|
||||
$scope.degradeRuleDialog = {
|
||||
title: '编辑熔断规则',
|
||||
type: 'edit',
|
||||
confirmBtnText: '保存'
|
||||
};
|
||||
degradeRuleDialog = ngDialog.open({
|
||||
template: '/app/views/dialog/degrade-rule-dialog.html',
|
||||
width: 680,
|
||||
overlay: true,
|
||||
scope: $scope
|
||||
});
|
||||
};
|
||||
|
||||
$scope.addNewRule = function () {
|
||||
var mac = $scope.macInputModel.split(':');
|
||||
$scope.currentRule = {
|
||||
grade: 0,
|
||||
app: $scope.app,
|
||||
ip: mac[0],
|
||||
port: mac[1],
|
||||
limitApp: 'default',
|
||||
minRequestAmount: 5,
|
||||
statIntervalMs: 1000,
|
||||
};
|
||||
$scope.degradeRuleDialog = {
|
||||
title: '新增熔断规则',
|
||||
type: 'add',
|
||||
confirmBtnText: '新增'
|
||||
};
|
||||
degradeRuleDialog = ngDialog.open({
|
||||
template: '/app/views/dialog/degrade-rule-dialog.html',
|
||||
width: 680,
|
||||
overlay: true,
|
||||
scope: $scope
|
||||
});
|
||||
};
|
||||
|
||||
$scope.saveRule = function () {
|
||||
if (!DegradeService.checkRuleValid($scope.currentRule)) {
|
||||
return;
|
||||
}
|
||||
if ($scope.degradeRuleDialog.type === 'add') {
|
||||
addNewRule($scope.currentRule);
|
||||
} else if ($scope.degradeRuleDialog.type === 'edit') {
|
||||
saveRule($scope.currentRule, true);
|
||||
}
|
||||
};
|
||||
|
||||
function parseDegradeMode(grade) {
|
||||
switch (grade) {
|
||||
case 0:
|
||||
return '慢调用比例';
|
||||
case 1:
|
||||
return '异常比例';
|
||||
case 2:
|
||||
return '异常数';
|
||||
default:
|
||||
return '未知';
|
||||
}
|
||||
}
|
||||
|
||||
var confirmDialog;
|
||||
$scope.deleteRule = function (rule) {
|
||||
$scope.currentRule = rule;
|
||||
$scope.confirmDialog = {
|
||||
title: '删除熔断规则',
|
||||
type: 'delete_rule',
|
||||
attentionTitle: '请确认是否删除如下熔断规则',
|
||||
attention: '资源名: ' + rule.resource +
|
||||
', 熔断策略: ' + parseDegradeMode(rule.grade) + ', 阈值: ' + rule.count,
|
||||
confirmBtnText: '删除',
|
||||
};
|
||||
confirmDialog = ngDialog.open({
|
||||
template: '/app/views/dialog/confirm-dialog.html',
|
||||
scope: $scope,
|
||||
overlay: true
|
||||
});
|
||||
};
|
||||
|
||||
$scope.confirm = function () {
|
||||
if ($scope.confirmDialog.type == 'delete_rule') {
|
||||
deleteRule($scope.currentRule);
|
||||
} else {
|
||||
console.error('error');
|
||||
}
|
||||
};
|
||||
|
||||
function deleteRule(rule) {
|
||||
DegradeService.deleteRule(rule).success(function (data) {
|
||||
if (data.code == 0) {
|
||||
getMachineRules();
|
||||
confirmDialog.close();
|
||||
} else {
|
||||
alert('失败:' + data.msg);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
function addNewRule(rule) {
|
||||
DegradeService.newRule(rule).success(function (data) {
|
||||
if (data.code == 0) {
|
||||
getMachineRules();
|
||||
degradeRuleDialog.close();
|
||||
} else {
|
||||
alert('失败:' + data.msg);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
function saveRule(rule, edit) {
|
||||
DegradeService.saveRule(rule).success(function (data) {
|
||||
if (data.code == 0) {
|
||||
getMachineRules();
|
||||
if (edit) {
|
||||
degradeRuleDialog.close();
|
||||
} else {
|
||||
confirmDialog.close();
|
||||
}
|
||||
} else {
|
||||
alert('失败:' + data.msg);
|
||||
}
|
||||
});
|
||||
}
|
||||
queryAppMachines();
|
||||
function queryAppMachines() {
|
||||
MachineService.getAppMachines($scope.app).success(
|
||||
function (data) {
|
||||
if (data.code === 0) {
|
||||
// $scope.machines = data.data;
|
||||
if (data.data) {
|
||||
$scope.machines = [];
|
||||
$scope.macsInputOptions = [];
|
||||
data.data.forEach(function (item) {
|
||||
if (item.healthy) {
|
||||
$scope.macsInputOptions.push({
|
||||
text: item.ip + ':' + item.port,
|
||||
value: item.ip + ':' + item.port
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
if ($scope.macsInputOptions.length > 0) {
|
||||
$scope.macInputModel = $scope.macsInputOptions[0].value;
|
||||
}
|
||||
} else {
|
||||
$scope.macsInputOptions = [];
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
$scope.$watch('macInputModel', function () {
|
||||
if ($scope.macInputModel) {
|
||||
getMachineRules();
|
||||
}
|
||||
});
|
||||
}]);
|
||||
@@ -0,0 +1,220 @@
|
||||
var app = angular.module('sentinelDashboardApp');
|
||||
|
||||
app.controller('FlowControllerV1', ['$scope', '$stateParams', 'FlowServiceV1', 'ngDialog',
|
||||
'MachineService',
|
||||
function ($scope, $stateParams, FlowService, ngDialog,
|
||||
MachineService) {
|
||||
$scope.app = $stateParams.app;
|
||||
|
||||
$scope.rulesPageConfig = {
|
||||
pageSize: 10,
|
||||
currentPageIndex: 1,
|
||||
totalPage: 1,
|
||||
totalCount: 0,
|
||||
};
|
||||
$scope.macsInputConfig = {
|
||||
searchField: ['text', 'value'],
|
||||
persist: true,
|
||||
create: false,
|
||||
maxItems: 1,
|
||||
render: {
|
||||
item: function (data, escape) {
|
||||
return '<div>' + escape(data.text) + '</div>';
|
||||
}
|
||||
},
|
||||
onChange: function (value, oldValue) {
|
||||
$scope.macInputModel = value;
|
||||
}
|
||||
};
|
||||
|
||||
$scope.generateThresholdTypeShow = (rule) => {
|
||||
if (!rule.clusterMode) {
|
||||
return '单机';
|
||||
}
|
||||
if (rule.clusterConfig.thresholdType === 0) {
|
||||
return '集群均摊';
|
||||
} else if (rule.clusterConfig.thresholdType === 1) {
|
||||
return '集群总体';
|
||||
} else {
|
||||
return '集群';
|
||||
}
|
||||
};
|
||||
|
||||
getMachineRules();
|
||||
function getMachineRules() {
|
||||
if (!$scope.macInputModel) {
|
||||
return;
|
||||
}
|
||||
var mac = $scope.macInputModel.split(':');
|
||||
FlowService.queryMachineRules($scope.app, mac[0], mac[1]).success(
|
||||
function (data) {
|
||||
if (data.code == 0 && data.data) {
|
||||
$scope.rules = data.data;
|
||||
$scope.rulesPageConfig.totalCount = $scope.rules.length;
|
||||
} else {
|
||||
$scope.rules = [];
|
||||
$scope.rulesPageConfig.totalCount = 0;
|
||||
}
|
||||
});
|
||||
};
|
||||
$scope.getMachineRules = getMachineRules;
|
||||
|
||||
var flowRuleDialog;
|
||||
$scope.editRule = function (rule) {
|
||||
$scope.currentRule = angular.copy(rule);
|
||||
$scope.flowRuleDialog = {
|
||||
title: '编辑流控规则',
|
||||
type: 'edit',
|
||||
confirmBtnText: '保存',
|
||||
showAdvanceButton: rule.controlBehavior == 0 && rule.strategy == 0
|
||||
};
|
||||
flowRuleDialog = ngDialog.open({
|
||||
template: '/app/views/dialog/flow-rule-dialog.html',
|
||||
width: 680,
|
||||
overlay: true,
|
||||
scope: $scope
|
||||
});
|
||||
};
|
||||
|
||||
$scope.addNewRule = function () {
|
||||
var mac = $scope.macInputModel.split(':');
|
||||
$scope.currentRule = {
|
||||
grade: 1,
|
||||
strategy: 0,
|
||||
controlBehavior: 0,
|
||||
app: $scope.app,
|
||||
ip: mac[0],
|
||||
port: mac[1],
|
||||
limitApp: 'default',
|
||||
clusterMode: false,
|
||||
clusterConfig: {
|
||||
thresholdType: 0
|
||||
}
|
||||
};
|
||||
$scope.flowRuleDialog = {
|
||||
title: '新增流控规则',
|
||||
type: 'add',
|
||||
confirmBtnText: '新增',
|
||||
showAdvanceButton: true,
|
||||
};
|
||||
flowRuleDialog = ngDialog.open({
|
||||
template: '/app/views/dialog/flow-rule-dialog.html',
|
||||
width: 680,
|
||||
overlay: true,
|
||||
scope: $scope
|
||||
});
|
||||
};
|
||||
|
||||
$scope.saveRule = function () {
|
||||
if (!FlowService.checkRuleValid($scope.currentRule)) {
|
||||
return;
|
||||
}
|
||||
if ($scope.flowRuleDialog.type === 'add') {
|
||||
addNewRule($scope.currentRule);
|
||||
} else if ($scope.flowRuleDialog.type === 'edit') {
|
||||
saveRule($scope.currentRule, true);
|
||||
}
|
||||
};
|
||||
|
||||
var confirmDialog;
|
||||
$scope.deleteRule = function (rule) {
|
||||
$scope.currentRule = rule;
|
||||
$scope.confirmDialog = {
|
||||
title: '删除流控规则',
|
||||
type: 'delete_rule',
|
||||
attentionTitle: '请确认是否删除如下流控规则',
|
||||
attention: '资源名: ' + rule.resource + ', 流控应用: ' + rule.limitApp
|
||||
+ ', 阈值类型: ' + (rule.grade == 0 ? '线程数' : 'QPS') + ', 阈值: ' + rule.count,
|
||||
confirmBtnText: '删除',
|
||||
};
|
||||
confirmDialog = ngDialog.open({
|
||||
template: '/app/views/dialog/confirm-dialog.html',
|
||||
scope: $scope,
|
||||
overlay: true
|
||||
});
|
||||
};
|
||||
|
||||
$scope.confirm = function () {
|
||||
if ($scope.confirmDialog.type === 'delete_rule') {
|
||||
deleteRule($scope.currentRule);
|
||||
} else {
|
||||
console.error('error');
|
||||
}
|
||||
};
|
||||
|
||||
function deleteRule(rule) {
|
||||
FlowService.deleteRule(rule).success(function (data) {
|
||||
if (data.code == 0) {
|
||||
getMachineRules();
|
||||
confirmDialog.close();
|
||||
} else {
|
||||
alert('失败:' + data.msg);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
function addNewRule(rule) {
|
||||
FlowService.newRule(rule).success(function (data) {
|
||||
if (data.code === 0) {
|
||||
getMachineRules();
|
||||
flowRuleDialog.close();
|
||||
} else {
|
||||
alert('失败:' + data.msg);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$scope.onOpenAdvanceClick = function () {
|
||||
$scope.flowRuleDialog.showAdvanceButton = false;
|
||||
};
|
||||
$scope.onCloseAdvanceClick = function () {
|
||||
$scope.flowRuleDialog.showAdvanceButton = true;
|
||||
};
|
||||
|
||||
function saveRule(rule, edit) {
|
||||
FlowService.saveRule(rule).success(function (data) {
|
||||
if (data.code === 0) {
|
||||
getMachineRules();
|
||||
if (edit) {
|
||||
flowRuleDialog.close();
|
||||
} else {
|
||||
confirmDialog.close();
|
||||
}
|
||||
} else {
|
||||
alert('失败:' + data.msg);
|
||||
}
|
||||
});
|
||||
}
|
||||
queryAppMachines();
|
||||
function queryAppMachines() {
|
||||
MachineService.getAppMachines($scope.app).success(
|
||||
function (data) {
|
||||
if (data.code == 0) {
|
||||
// $scope.machines = data.data;
|
||||
if (data.data) {
|
||||
$scope.machines = [];
|
||||
$scope.macsInputOptions = [];
|
||||
data.data.forEach(function (item) {
|
||||
if (item.healthy) {
|
||||
$scope.macsInputOptions.push({
|
||||
text: item.ip + ':' + item.port,
|
||||
value: item.ip + ':' + item.port
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
if ($scope.macsInputOptions.length > 0) {
|
||||
$scope.macInputModel = $scope.macsInputOptions[0].value;
|
||||
}
|
||||
} else {
|
||||
$scope.macsInputOptions = [];
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
$scope.$watch('macInputModel', function () {
|
||||
if ($scope.macInputModel) {
|
||||
getMachineRules();
|
||||
}
|
||||
});
|
||||
}]);
|
||||
@@ -0,0 +1,221 @@
|
||||
var app = angular.module('sentinelDashboardApp');
|
||||
|
||||
app.controller('FlowControllerV2', ['$scope', '$stateParams', 'FlowServiceV2', 'ngDialog',
|
||||
'MachineService',
|
||||
function ($scope, $stateParams, FlowService, ngDialog,
|
||||
MachineService) {
|
||||
$scope.app = $stateParams.app;
|
||||
|
||||
$scope.rulesPageConfig = {
|
||||
pageSize: 10,
|
||||
currentPageIndex: 1,
|
||||
totalPage: 1,
|
||||
totalCount: 0,
|
||||
};
|
||||
$scope.macsInputConfig = {
|
||||
searchField: ['text', 'value'],
|
||||
persist: true,
|
||||
create: false,
|
||||
maxItems: 1,
|
||||
render: {
|
||||
item: function (data, escape) {
|
||||
return '<div>' + escape(data.text) + '</div>';
|
||||
}
|
||||
},
|
||||
onChange: function (value, oldValue) {
|
||||
$scope.macInputModel = value;
|
||||
}
|
||||
};
|
||||
|
||||
$scope.generateThresholdTypeShow = (rule) => {
|
||||
if (!rule.clusterMode) {
|
||||
return '单机';
|
||||
}
|
||||
if (rule.clusterConfig.thresholdType === 0) {
|
||||
return '集群均摊';
|
||||
} else if (rule.clusterConfig.thresholdType === 1) {
|
||||
return '集群总体';
|
||||
} else {
|
||||
return '集群';
|
||||
}
|
||||
};
|
||||
|
||||
getMachineRules();
|
||||
function getMachineRules() {
|
||||
if (!$scope.macInputModel) {
|
||||
return;
|
||||
}
|
||||
var mac = $scope.macInputModel.split(':');
|
||||
FlowService.queryMachineRules($scope.app, mac[0], mac[1]).success(
|
||||
function (data) {
|
||||
if (data.code == 0 && data.data) {
|
||||
$scope.rules = data.data;
|
||||
$scope.rulesPageConfig.totalCount = $scope.rules.length;
|
||||
} else {
|
||||
$scope.rules = [];
|
||||
$scope.rulesPageConfig.totalCount = 0;
|
||||
}
|
||||
});
|
||||
};
|
||||
$scope.getMachineRules = getMachineRules;
|
||||
|
||||
var flowRuleDialog;
|
||||
$scope.editRule = function (rule) {
|
||||
$scope.currentRule = angular.copy(rule);
|
||||
$scope.flowRuleDialog = {
|
||||
title: '编辑流控规则',
|
||||
type: 'edit',
|
||||
confirmBtnText: '保存',
|
||||
showAdvanceButton: rule.controlBehavior == 0 && rule.strategy == 0
|
||||
};
|
||||
flowRuleDialog = ngDialog.open({
|
||||
template: '/app/views/dialog/flow-rule-dialog.html',
|
||||
width: 680,
|
||||
overlay: true,
|
||||
scope: $scope
|
||||
});
|
||||
};
|
||||
|
||||
$scope.addNewRule = function () {
|
||||
var mac = $scope.macInputModel.split(':');
|
||||
$scope.currentRule = {
|
||||
grade: 1,
|
||||
strategy: 0,
|
||||
controlBehavior: 0,
|
||||
app: $scope.app,
|
||||
ip: mac[0],
|
||||
port: mac[1],
|
||||
limitApp: 'default',
|
||||
clusterMode: false,
|
||||
clusterConfig: {
|
||||
thresholdType: 0,
|
||||
fallbackToLocalWhenFail: true
|
||||
}
|
||||
};
|
||||
$scope.flowRuleDialog = {
|
||||
title: '新增流控规则',
|
||||
type: 'add',
|
||||
confirmBtnText: '新增',
|
||||
showAdvanceButton: true,
|
||||
};
|
||||
flowRuleDialog = ngDialog.open({
|
||||
template: '/app/views/dialog/flow-rule-dialog.html',
|
||||
width: 680,
|
||||
overlay: true,
|
||||
scope: $scope
|
||||
});
|
||||
};
|
||||
|
||||
$scope.saveRule = function () {
|
||||
if (!FlowService.checkRuleValid($scope.currentRule)) {
|
||||
return;
|
||||
}
|
||||
if ($scope.flowRuleDialog.type === 'add') {
|
||||
addNewRule($scope.currentRule);
|
||||
} else if ($scope.flowRuleDialog.type === 'edit') {
|
||||
saveRule($scope.currentRule, true);
|
||||
}
|
||||
};
|
||||
|
||||
var confirmDialog;
|
||||
$scope.deleteRule = function (rule) {
|
||||
$scope.currentRule = rule;
|
||||
$scope.confirmDialog = {
|
||||
title: '删除流控规则',
|
||||
type: 'delete_rule',
|
||||
attentionTitle: '请确认是否删除如下流控规则',
|
||||
attention: '资源名: ' + rule.resource + ', 流控应用: ' + rule.limitApp
|
||||
+ ', 阈值类型: ' + (rule.grade == 0 ? '线程数' : 'QPS') + ', 阈值: ' + rule.count,
|
||||
confirmBtnText: '删除',
|
||||
};
|
||||
confirmDialog = ngDialog.open({
|
||||
template: '/app/views/dialog/confirm-dialog.html',
|
||||
scope: $scope,
|
||||
overlay: true
|
||||
});
|
||||
};
|
||||
|
||||
$scope.confirm = function () {
|
||||
if ($scope.confirmDialog.type === 'delete_rule') {
|
||||
deleteRule($scope.currentRule);
|
||||
} else {
|
||||
console.error('error');
|
||||
}
|
||||
};
|
||||
|
||||
function deleteRule(rule) {
|
||||
FlowService.deleteRule(rule).success(function (data) {
|
||||
if (data.code == 0) {
|
||||
getMachineRules();
|
||||
confirmDialog.close();
|
||||
} else {
|
||||
alert('失败!');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
function addNewRule(rule) {
|
||||
FlowService.newRule(rule).success(function (data) {
|
||||
if (data.code == 0) {
|
||||
getMachineRules();
|
||||
flowRuleDialog.close();
|
||||
} else {
|
||||
alert('失败!');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$scope.onOpenAdvanceClick = function () {
|
||||
$scope.flowRuleDialog.showAdvanceButton = false;
|
||||
};
|
||||
$scope.onCloseAdvanceClick = function () {
|
||||
$scope.flowRuleDialog.showAdvanceButton = true;
|
||||
};
|
||||
|
||||
function saveRule(rule, edit) {
|
||||
FlowService.saveRule(rule).success(function (data) {
|
||||
if (data.code == 0) {
|
||||
getMachineRules();
|
||||
if (edit) {
|
||||
flowRuleDialog.close();
|
||||
} else {
|
||||
confirmDialog.close();
|
||||
}
|
||||
} else {
|
||||
alert('失败!');
|
||||
}
|
||||
});
|
||||
}
|
||||
queryAppMachines();
|
||||
function queryAppMachines() {
|
||||
MachineService.getAppMachines($scope.app).success(
|
||||
function (data) {
|
||||
if (data.code == 0) {
|
||||
// $scope.machines = data.data;
|
||||
if (data.data) {
|
||||
$scope.machines = [];
|
||||
$scope.macsInputOptions = [];
|
||||
data.data.forEach(function (item) {
|
||||
if (item.healthy) {
|
||||
$scope.macsInputOptions.push({
|
||||
text: item.ip + ':' + item.port,
|
||||
value: item.ip + ':' + item.port
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
if ($scope.macsInputOptions.length > 0) {
|
||||
$scope.macInputModel = $scope.macsInputOptions[0].value;
|
||||
}
|
||||
} else {
|
||||
$scope.macsInputOptions = [];
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
$scope.$watch('macInputModel', function () {
|
||||
if ($scope.macInputModel) {
|
||||
getMachineRules();
|
||||
}
|
||||
});
|
||||
}]);
|
||||
@@ -0,0 +1,245 @@
|
||||
var app = angular.module('sentinelDashboardApp');
|
||||
|
||||
app.controller('GatewayApiCtl', ['$scope', '$stateParams', 'GatewayApiService', 'ngDialog', 'MachineService',
|
||||
function ($scope, $stateParams, GatewayApiService, ngDialog, MachineService) {
|
||||
$scope.app = $stateParams.app;
|
||||
|
||||
$scope.apisPageConfig = {
|
||||
pageSize: 10,
|
||||
currentPageIndex: 1,
|
||||
totalPage: 1,
|
||||
totalCount: 0,
|
||||
};
|
||||
|
||||
$scope.macsInputConfig = {
|
||||
searchField: ['text', 'value'],
|
||||
persist: true,
|
||||
create: false,
|
||||
maxItems: 1,
|
||||
render: {
|
||||
item: function (data, escape) {
|
||||
return '<div>' + escape(data.text) + '</div>';
|
||||
}
|
||||
},
|
||||
onChange: function (value, oldValue) {
|
||||
$scope.macInputModel = value;
|
||||
}
|
||||
};
|
||||
|
||||
getApis();
|
||||
function getApis() {
|
||||
if (!$scope.macInputModel) {
|
||||
return;
|
||||
}
|
||||
|
||||
var mac = $scope.macInputModel.split(':');
|
||||
GatewayApiService.queryApis($scope.app, mac[0], mac[1]).success(
|
||||
function (data) {
|
||||
if (data.code == 0 && data.data) {
|
||||
// To merge rows for api who has more than one predicateItems, here we build data manually
|
||||
$scope.apis = [];
|
||||
|
||||
data.data.forEach(function(api) {
|
||||
api["predicateItems"].forEach(function (item, index) {
|
||||
var newItem = {};
|
||||
newItem["id"] = api["id"];
|
||||
newItem["app"] = api["app"];
|
||||
newItem["ip"] = api["ip"];
|
||||
newItem["port"] = api["port"];
|
||||
newItem["apiName"] = api["apiName"];
|
||||
newItem["pattern"] = item["pattern"];
|
||||
newItem["matchStrategy"] = item["matchStrategy"];
|
||||
// The itemSize indicates how many rows to merge, by using rowspan="{{api.itemSize}}" in <td> tag
|
||||
newItem["itemSize"] = api["predicateItems"].length;
|
||||
// Mark the flag of first item to zero, indicates the start row to merge
|
||||
newItem["firstFlag"] = index == 0 ? 0 : 1;
|
||||
// Still hold the data of predicateItems, in order to bind data in edit dialog html
|
||||
newItem["predicateItems"] = api["predicateItems"];
|
||||
$scope.apis.push(newItem);
|
||||
});
|
||||
});
|
||||
|
||||
$scope.apisPageConfig.totalCount = data.data.length;
|
||||
} else {
|
||||
$scope.apis = [];
|
||||
$scope.apisPageConfig.totalCount = 0;
|
||||
}
|
||||
});
|
||||
};
|
||||
$scope.getApis = getApis;
|
||||
|
||||
var gatewayApiDialog;
|
||||
$scope.editApi = function (api) {
|
||||
$scope.currentApi = angular.copy(api);
|
||||
$scope.gatewayApiDialog = {
|
||||
title: '编辑自定义 API',
|
||||
type: 'edit',
|
||||
confirmBtnText: '保存'
|
||||
};
|
||||
gatewayApiDialog = ngDialog.open({
|
||||
template: '/app/views/dialog/gateway/api-dialog.html',
|
||||
width: 900,
|
||||
overlay: true,
|
||||
scope: $scope
|
||||
});
|
||||
};
|
||||
|
||||
$scope.addNewApi = function () {
|
||||
var mac = $scope.macInputModel.split(':');
|
||||
$scope.currentApi = {
|
||||
grade: 0,
|
||||
app: $scope.app,
|
||||
ip: mac[0],
|
||||
port: mac[1],
|
||||
predicateItems: [{matchStrategy: 0, pattern: ''}]
|
||||
};
|
||||
$scope.gatewayApiDialog = {
|
||||
title: '新增自定义 API',
|
||||
type: 'add',
|
||||
confirmBtnText: '新增'
|
||||
};
|
||||
gatewayApiDialog = ngDialog.open({
|
||||
template: '/app/views/dialog/gateway/api-dialog.html',
|
||||
width: 900,
|
||||
overlay: true,
|
||||
scope: $scope
|
||||
});
|
||||
};
|
||||
|
||||
$scope.saveApi = function () {
|
||||
var apiNames = [];
|
||||
if ($scope.gatewayApiDialog.type === 'add') {
|
||||
apiNames = $scope.apis.map(function (item, index, array) {
|
||||
return item["apiName"];
|
||||
}).filter(function (item, index, array) {
|
||||
return array.indexOf(item) === index;
|
||||
});
|
||||
}
|
||||
|
||||
if (!GatewayApiService.checkApiValid($scope.currentApi, apiNames)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($scope.gatewayApiDialog.type === 'add') {
|
||||
addNewApi($scope.currentApi);
|
||||
} else if ($scope.gatewayApiDialog.type === 'edit') {
|
||||
saveApi($scope.currentApi, true);
|
||||
}
|
||||
};
|
||||
|
||||
function addNewApi(api) {
|
||||
GatewayApiService.newApi(api).success(function (data) {
|
||||
if (data.code == 0) {
|
||||
getApis();
|
||||
gatewayApiDialog.close();
|
||||
} else {
|
||||
alert('新增自定义API失败!' + data.msg);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
function saveApi(api, edit) {
|
||||
GatewayApiService.saveApi(api).success(function (data) {
|
||||
if (data.code == 0) {
|
||||
getApis();
|
||||
if (edit) {
|
||||
gatewayApiDialog.close();
|
||||
} else {
|
||||
confirmDialog.close();
|
||||
}
|
||||
} else {
|
||||
alert('修改自定义API失败!' + data.msg);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
var confirmDialog;
|
||||
$scope.deleteApi = function (api) {
|
||||
$scope.currentApi = api;
|
||||
$scope.confirmDialog = {
|
||||
title: '删除自定义API',
|
||||
type: 'delete_api',
|
||||
attentionTitle: '请确认是否删除如下自定义API',
|
||||
attention: 'API名称: ' + api.apiName,
|
||||
confirmBtnText: '删除',
|
||||
};
|
||||
confirmDialog = ngDialog.open({
|
||||
template: '/app/views/dialog/confirm-dialog.html',
|
||||
scope: $scope,
|
||||
overlay: true
|
||||
});
|
||||
};
|
||||
|
||||
$scope.confirm = function () {
|
||||
if ($scope.confirmDialog.type == 'delete_api') {
|
||||
deleteApi($scope.currentApi);
|
||||
} else {
|
||||
console.error('error');
|
||||
}
|
||||
};
|
||||
|
||||
function deleteApi(api) {
|
||||
GatewayApiService.deleteApi(api).success(function (data) {
|
||||
if (data.code == 0) {
|
||||
getApis();
|
||||
confirmDialog.close();
|
||||
} else {
|
||||
alert('删除自定义API失败!' + data.msg);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$scope.addNewMatchPattern = function() {
|
||||
var total;
|
||||
if ($scope.currentApi.predicateItems == null) {
|
||||
$scope.currentApi.predicateItems = [];
|
||||
total = 0;
|
||||
} else {
|
||||
total = $scope.currentApi.predicateItems.length;
|
||||
}
|
||||
$scope.currentApi.predicateItems.splice(total + 1, 0, {matchStrategy: 0, pattern: ''});
|
||||
};
|
||||
|
||||
$scope.removeMatchPattern = function($index) {
|
||||
if ($scope.currentApi.predicateItems.length <= 1) {
|
||||
// Should never happen since no remove button will display when only one predicateItem.
|
||||
alert('至少有一个匹配规则');
|
||||
return;
|
||||
}
|
||||
$scope.currentApi.predicateItems.splice($index, 1);
|
||||
};
|
||||
|
||||
queryAppMachines();
|
||||
function queryAppMachines() {
|
||||
MachineService.getAppMachines($scope.app).success(
|
||||
function (data) {
|
||||
if (data.code == 0) {
|
||||
// $scope.machines = data.data;
|
||||
if (data.data) {
|
||||
$scope.machines = [];
|
||||
$scope.macsInputOptions = [];
|
||||
data.data.forEach(function (item) {
|
||||
if (item.healthy) {
|
||||
$scope.macsInputOptions.push({
|
||||
text: item.ip + ':' + item.port,
|
||||
value: item.ip + ':' + item.port
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
if ($scope.macsInputOptions.length > 0) {
|
||||
$scope.macInputModel = $scope.macsInputOptions[0].value;
|
||||
}
|
||||
} else {
|
||||
$scope.macsInputOptions = [];
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
$scope.$watch('macInputModel', function () {
|
||||
if ($scope.macInputModel) {
|
||||
getApis();
|
||||
}
|
||||
});
|
||||
}]
|
||||
);
|
||||
@@ -0,0 +1,251 @@
|
||||
var app = angular.module('sentinelDashboardApp');
|
||||
|
||||
app.controller('GatewayFlowCtl', ['$scope', '$stateParams', 'GatewayFlowService', 'GatewayApiService', 'ngDialog', 'MachineService',
|
||||
function ($scope, $stateParams, GatewayFlowService, GatewayApiService, ngDialog, MachineService) {
|
||||
$scope.app = $stateParams.app;
|
||||
|
||||
$scope.rulesPageConfig = {
|
||||
pageSize: 10,
|
||||
currentPageIndex: 1,
|
||||
totalPage: 1,
|
||||
totalCount: 0,
|
||||
};
|
||||
|
||||
$scope.macsInputConfig = {
|
||||
searchField: ['text', 'value'],
|
||||
persist: true,
|
||||
create: false,
|
||||
maxItems: 1,
|
||||
render: {
|
||||
item: function (data, escape) {
|
||||
return '<div>' + escape(data.text) + '</div>';
|
||||
}
|
||||
},
|
||||
onChange: function (value, oldValue) {
|
||||
$scope.macInputModel = value;
|
||||
}
|
||||
};
|
||||
|
||||
getMachineRules();
|
||||
function getMachineRules() {
|
||||
if (!$scope.macInputModel) {
|
||||
return;
|
||||
}
|
||||
|
||||
var mac = $scope.macInputModel.split(':');
|
||||
GatewayFlowService.queryRules($scope.app, mac[0], mac[1]).success(
|
||||
function (data) {
|
||||
if (data.code == 0 && data.data) {
|
||||
$scope.rules = data.data;
|
||||
$scope.rulesPageConfig.totalCount = $scope.rules.length;
|
||||
} else {
|
||||
$scope.rules = [];
|
||||
$scope.rulesPageConfig.totalCount = 0;
|
||||
}
|
||||
});
|
||||
};
|
||||
$scope.getMachineRules = getMachineRules;
|
||||
|
||||
getApiNames();
|
||||
function getApiNames() {
|
||||
if (!$scope.macInputModel) {
|
||||
return;
|
||||
}
|
||||
|
||||
var mac = $scope.macInputModel.split(':');
|
||||
GatewayApiService.queryApis($scope.app, mac[0], mac[1]).success(
|
||||
function (data) {
|
||||
if (data.code == 0 && data.data) {
|
||||
$scope.apiNames = [];
|
||||
|
||||
data.data.forEach(function (api) {
|
||||
$scope.apiNames.push(api["apiName"]);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$scope.intervalUnits = [{val: 0, desc: '秒'}, {val: 1, desc: '分'}, {val: 2, desc: '时'}, {val: 3, desc: '天'}];
|
||||
|
||||
var gatewayFlowRuleDialog;
|
||||
$scope.editRule = function (rule) {
|
||||
$scope.currentRule = angular.copy(rule);
|
||||
$scope.gatewayFlowRuleDialog = {
|
||||
title: '编辑网关流控规则',
|
||||
type: 'edit',
|
||||
confirmBtnText: '保存'
|
||||
};
|
||||
gatewayFlowRuleDialog = ngDialog.open({
|
||||
template: '/app/views/dialog/gateway/flow-rule-dialog.html',
|
||||
width: 780,
|
||||
overlay: true,
|
||||
scope: $scope
|
||||
});
|
||||
};
|
||||
|
||||
$scope.addNewRule = function () {
|
||||
var mac = $scope.macInputModel.split(':');
|
||||
$scope.currentRule = {
|
||||
grade: 1,
|
||||
app: $scope.app,
|
||||
ip: mac[0],
|
||||
port: mac[1],
|
||||
resourceMode: 0,
|
||||
interval: 1,
|
||||
intervalUnit: 0,
|
||||
controlBehavior: 0,
|
||||
burst: 0,
|
||||
maxQueueingTimeoutMs: 0
|
||||
};
|
||||
|
||||
$scope.gatewayFlowRuleDialog = {
|
||||
title: '新增网关流控规则',
|
||||
type: 'add',
|
||||
confirmBtnText: '新增'
|
||||
};
|
||||
|
||||
gatewayFlowRuleDialog = ngDialog.open({
|
||||
template: '/app/views/dialog/gateway/flow-rule-dialog.html',
|
||||
width: 780,
|
||||
overlay: true,
|
||||
scope: $scope
|
||||
});
|
||||
};
|
||||
|
||||
$scope.saveRule = function () {
|
||||
if (!GatewayFlowService.checkRuleValid($scope.currentRule)) {
|
||||
return;
|
||||
}
|
||||
if ($scope.gatewayFlowRuleDialog.type === 'add') {
|
||||
addNewRule($scope.currentRule);
|
||||
} else if ($scope.gatewayFlowRuleDialog.type === 'edit') {
|
||||
saveRule($scope.currentRule, true);
|
||||
}
|
||||
};
|
||||
|
||||
$scope.useRouteID = function() {
|
||||
$scope.currentRule.resource = '';
|
||||
};
|
||||
|
||||
$scope.useCustormAPI = function() {
|
||||
$scope.currentRule.resource = '';
|
||||
};
|
||||
|
||||
$scope.useParamItem = function () {
|
||||
$scope.currentRule.paramItem = {
|
||||
parseStrategy: 0,
|
||||
matchStrategy: 0
|
||||
};
|
||||
};
|
||||
|
||||
$scope.notUseParamItem = function () {
|
||||
$scope.currentRule.paramItem = null;
|
||||
};
|
||||
|
||||
$scope.useParamItemVal = function() {
|
||||
$scope.currentRule.paramItem.pattern = "";
|
||||
$scope.currentRule.paramItem.matchStrategy = 0;
|
||||
};
|
||||
|
||||
$scope.notUseParamItemVal = function() {
|
||||
$scope.currentRule.paramItem.pattern = null;
|
||||
$scope.currentRule.paramItem.matchStrategy = null;
|
||||
};
|
||||
|
||||
function addNewRule(rule) {
|
||||
GatewayFlowService.newRule(rule).success(function (data) {
|
||||
if (data.code == 0) {
|
||||
getMachineRules();
|
||||
gatewayFlowRuleDialog.close();
|
||||
} else {
|
||||
alert('新增网关流控规则失败!' + data.msg);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
function saveRule(rule, edit) {
|
||||
GatewayFlowService.saveRule(rule).success(function (data) {
|
||||
if (data.code == 0) {
|
||||
getMachineRules();
|
||||
if (edit) {
|
||||
gatewayFlowRuleDialog.close();
|
||||
} else {
|
||||
confirmDialog.close();
|
||||
}
|
||||
} else {
|
||||
alert('修改网关流控规则失败!' + data.msg);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
var confirmDialog;
|
||||
$scope.deleteRule = function (rule) {
|
||||
$scope.currentRule = rule;
|
||||
$scope.confirmDialog = {
|
||||
title: '删除网关流控规则',
|
||||
type: 'delete_rule',
|
||||
attentionTitle: '请确认是否删除如下规则',
|
||||
attention: 'API名称: ' + rule.resource + ', ' + (rule.grade == 1 ? 'QPS阈值' : '线程数') + ': ' + rule.count,
|
||||
confirmBtnText: '删除',
|
||||
};
|
||||
confirmDialog = ngDialog.open({
|
||||
template: '/app/views/dialog/confirm-dialog.html',
|
||||
scope: $scope,
|
||||
overlay: true
|
||||
});
|
||||
};
|
||||
|
||||
$scope.confirm = function () {
|
||||
if ($scope.confirmDialog.type == 'delete_rule') {
|
||||
deleteRule($scope.currentRule);
|
||||
} else {
|
||||
console.error('error');
|
||||
}
|
||||
};
|
||||
|
||||
function deleteRule(rule) {
|
||||
GatewayFlowService.deleteRule(rule).success(function (data) {
|
||||
if (data.code == 0) {
|
||||
getMachineRules();
|
||||
confirmDialog.close();
|
||||
} else {
|
||||
alert('删除网关流控规则失败!' + data.msg);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
queryAppMachines();
|
||||
|
||||
function queryAppMachines() {
|
||||
MachineService.getAppMachines($scope.app).success(
|
||||
function (data) {
|
||||
if (data.code == 0) {
|
||||
if (data.data) {
|
||||
$scope.machines = [];
|
||||
$scope.macsInputOptions = [];
|
||||
data.data.forEach(function (item) {
|
||||
if (item.healthy) {
|
||||
$scope.macsInputOptions.push({
|
||||
text: item.ip + ':' + item.port,
|
||||
value: item.ip + ':' + item.port
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
if ($scope.macsInputOptions.length > 0) {
|
||||
$scope.macInputModel = $scope.macsInputOptions[0].value;
|
||||
}
|
||||
} else {
|
||||
$scope.macsInputOptions = [];
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
$scope.$watch('macInputModel', function () {
|
||||
if ($scope.macInputModel) {
|
||||
getMachineRules();
|
||||
getApiNames();
|
||||
}
|
||||
});
|
||||
}]
|
||||
);
|
||||
@@ -0,0 +1,299 @@
|
||||
var app = angular.module('sentinelDashboardApp');
|
||||
|
||||
app.controller('GatewayIdentityCtl', ['$scope', '$stateParams', 'IdentityService',
|
||||
'ngDialog', 'GatewayFlowService', 'GatewayApiService', 'DegradeService', 'MachineService',
|
||||
'$interval', '$location', '$timeout',
|
||||
function ($scope, $stateParams, IdentityService, ngDialog,
|
||||
GatewayFlowService, GatewayApiService, DegradeService, MachineService, $interval, $location, $timeout) {
|
||||
|
||||
$scope.app = $stateParams.app;
|
||||
|
||||
$scope.currentPage = 1;
|
||||
$scope.pageSize = 16;
|
||||
$scope.totalPage = 1;
|
||||
$scope.totalCount = 0;
|
||||
$scope.identities = [];
|
||||
|
||||
$scope.searchKey = '';
|
||||
|
||||
$scope.macsInputConfig = {
|
||||
searchField: ['text', 'value'],
|
||||
persist: true,
|
||||
create: false,
|
||||
maxItems: 1,
|
||||
render: {
|
||||
item: function (data, escape) {
|
||||
return '<div>' + escape(data.text) + '</div>';
|
||||
}
|
||||
},
|
||||
onChange: function (value, oldValue) {
|
||||
$scope.macInputModel = value;
|
||||
}
|
||||
};
|
||||
$scope.table = null;
|
||||
|
||||
getApiNames();
|
||||
function getApiNames() {
|
||||
if (!$scope.macInputModel) {
|
||||
return;
|
||||
}
|
||||
|
||||
var mac = $scope.macInputModel.split(':');
|
||||
GatewayApiService.queryApis($scope.app, mac[0], mac[1]).success(
|
||||
function (data) {
|
||||
if (data.code == 0 && data.data) {
|
||||
$scope.apiNames = [];
|
||||
|
||||
data.data.forEach(function (api) {
|
||||
$scope.apiNames.push(api["apiName"]);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var gatewayFlowRuleDialog;
|
||||
var gatewayFlowRuleDialogScope;
|
||||
$scope.addNewGatewayFlowRule = function (resource) {
|
||||
if (!$scope.macInputModel) {
|
||||
return;
|
||||
}
|
||||
var mac = $scope.macInputModel.split(':');
|
||||
gatewayFlowRuleDialogScope = $scope.$new(true);
|
||||
|
||||
gatewayFlowRuleDialogScope.apiNames = $scope.apiNames;
|
||||
|
||||
gatewayFlowRuleDialogScope.intervalUnits = [{val: 0, desc: '秒'}, {val: 1, desc: '分'}, {val: 2, desc: '时'}, {val: 3, desc: '天'}];
|
||||
|
||||
gatewayFlowRuleDialogScope.currentRule = {
|
||||
grade: 1,
|
||||
app: $scope.app,
|
||||
ip: mac[0],
|
||||
port: mac[1],
|
||||
resourceMode: gatewayFlowRuleDialogScope.apiNames.indexOf(resource) == -1 ? 0 : 1,
|
||||
resource: resource,
|
||||
interval: 1,
|
||||
intervalUnit: 0,
|
||||
controlBehavior: 0,
|
||||
burst: 0,
|
||||
maxQueueingTimeoutMs: 0
|
||||
};
|
||||
|
||||
gatewayFlowRuleDialogScope.gatewayFlowRuleDialog = {
|
||||
title: '新增网关流控规则',
|
||||
type: 'add',
|
||||
confirmBtnText: '新增',
|
||||
saveAndContinueBtnText: '新增并继续添加',
|
||||
showAdvanceButton: true
|
||||
};
|
||||
|
||||
gatewayFlowRuleDialogScope.useRouteID = function() {
|
||||
gatewayFlowRuleDialogScope.currentRule.resource = '';
|
||||
};
|
||||
|
||||
gatewayFlowRuleDialogScope.useCustormAPI = function() {
|
||||
gatewayFlowRuleDialogScope.currentRule.resource = '';
|
||||
};
|
||||
|
||||
gatewayFlowRuleDialogScope.useParamItem = function () {
|
||||
gatewayFlowRuleDialogScope.currentRule.paramItem = {
|
||||
parseStrategy: 0,
|
||||
matchStrategy: 0
|
||||
};
|
||||
};
|
||||
|
||||
gatewayFlowRuleDialogScope.notUseParamItem = function () {
|
||||
gatewayFlowRuleDialogScope.currentRule.paramItem = null;
|
||||
};
|
||||
|
||||
gatewayFlowRuleDialogScope.useParamItemVal = function() {
|
||||
gatewayFlowRuleDialogScope.currentRule.paramItem.pattern = "";
|
||||
};
|
||||
|
||||
gatewayFlowRuleDialogScope.notUseParamItemVal = function() {
|
||||
gatewayFlowRuleDialogScope.currentRule.paramItem.pattern = null;
|
||||
};
|
||||
|
||||
gatewayFlowRuleDialogScope.saveRule = saveGatewayFlowRule;
|
||||
gatewayFlowRuleDialogScope.saveRuleAndContinue = saveGatewayFlowRuleAndContinue;
|
||||
gatewayFlowRuleDialogScope.onOpenAdvanceClick = function () {
|
||||
gatewayFlowRuleDialogScope.gatewayFlowRuleDialog.showAdvanceButton = false;
|
||||
};
|
||||
gatewayFlowRuleDialogScope.onCloseAdvanceClick = function () {
|
||||
gatewayFlowRuleDialogScope.gatewayFlowRuleDialog.showAdvanceButton = true;
|
||||
};
|
||||
|
||||
gatewayFlowRuleDialog = ngDialog.open({
|
||||
template: '/app/views/dialog/gateway/flow-rule-dialog.html',
|
||||
width: 780,
|
||||
overlay: true,
|
||||
scope: gatewayFlowRuleDialogScope
|
||||
});
|
||||
};
|
||||
|
||||
function saveGatewayFlowRule() {
|
||||
if (!GatewayFlowService.checkRuleValid(gatewayFlowRuleDialogScope.currentRule)) {
|
||||
return;
|
||||
}
|
||||
GatewayFlowService.newRule(gatewayFlowRuleDialogScope.currentRule).success(function (data) {
|
||||
if (data.code === 0) {
|
||||
gatewayFlowRuleDialog.close();
|
||||
let url = '/dashboard/gateway/flow/' + $scope.app;
|
||||
$location.path(url);
|
||||
} else {
|
||||
alert('失败!');
|
||||
}
|
||||
}).error((data, header, config, status) => {
|
||||
alert('未知错误');
|
||||
});
|
||||
}
|
||||
|
||||
function saveGatewayFlowRuleAndContinue() {
|
||||
if (!GatewayFlowService.checkRuleValid(gatewayFlowRuleDialogScope.currentRule)) {
|
||||
return;
|
||||
}
|
||||
GatewayFlowService.newRule(gatewayFlowRuleDialogScope.currentRule).success(function (data) {
|
||||
if (data.code == 0) {
|
||||
gatewayFlowRuleDialog.close();
|
||||
} else {
|
||||
alert('失败!');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var degradeRuleDialog;
|
||||
$scope.addNewDegradeRule = function (resource) {
|
||||
if (!$scope.macInputModel) {
|
||||
return;
|
||||
}
|
||||
var mac = $scope.macInputModel.split(':');
|
||||
degradeRuleDialogScope = $scope.$new(true);
|
||||
degradeRuleDialogScope.currentRule = {
|
||||
enable: false,
|
||||
grade: 0,
|
||||
strategy: 0,
|
||||
resource: resource,
|
||||
limitApp: 'default',
|
||||
app: $scope.app,
|
||||
ip: mac[0],
|
||||
port: mac[1]
|
||||
};
|
||||
|
||||
degradeRuleDialogScope.degradeRuleDialog = {
|
||||
title: '新增降级规则',
|
||||
type: 'add',
|
||||
confirmBtnText: '新增',
|
||||
saveAndContinueBtnText: '新增并继续添加'
|
||||
};
|
||||
degradeRuleDialogScope.saveRule = saveDegradeRule;
|
||||
degradeRuleDialogScope.saveRuleAndContinue = saveDegradeRuleAndContinue;
|
||||
|
||||
degradeRuleDialog = ngDialog.open({
|
||||
template: '/app/views/dialog/degrade-rule-dialog.html',
|
||||
width: 680,
|
||||
overlay: true,
|
||||
scope: degradeRuleDialogScope
|
||||
});
|
||||
};
|
||||
|
||||
function saveDegradeRule() {
|
||||
if (!DegradeService.checkRuleValid(degradeRuleDialogScope.currentRule)) {
|
||||
return;
|
||||
}
|
||||
DegradeService.newRule(degradeRuleDialogScope.currentRule).success(function (data) {
|
||||
if (data.code == 0) {
|
||||
degradeRuleDialog.close();
|
||||
var url = '/dashboard/degrade/' + $scope.app;
|
||||
$location.path(url);
|
||||
} else {
|
||||
alert('失败!');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function saveDegradeRuleAndContinue() {
|
||||
if (!DegradeService.checkRuleValid(degradeRuleDialogScope.currentRule)) {
|
||||
return;
|
||||
}
|
||||
DegradeService.newRule(degradeRuleDialogScope.currentRule).success(function (data) {
|
||||
if (data.code == 0) {
|
||||
degradeRuleDialog.close();
|
||||
} else {
|
||||
alert('失败!');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var searchHandler;
|
||||
$scope.searchChange = function (searchKey) {
|
||||
$timeout.cancel(searchHandler);
|
||||
searchHandler = $timeout(function () {
|
||||
$scope.searchKey = searchKey;
|
||||
reInitIdentityDatas();
|
||||
}, 600);
|
||||
};
|
||||
|
||||
function queryAppMachines() {
|
||||
MachineService.getAppMachines($scope.app).success(
|
||||
function (data) {
|
||||
if (data.code === 0) {
|
||||
if (data.data) {
|
||||
$scope.machines = [];
|
||||
$scope.macsInputOptions = [];
|
||||
data.data.forEach(function (item) {
|
||||
if (item.healthy) {
|
||||
$scope.macsInputOptions.push({
|
||||
text: item.ip + ':' + item.port,
|
||||
value: item.ip + ':' + item.port
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
if ($scope.macsInputOptions.length > 0) {
|
||||
$scope.macInputModel = $scope.macsInputOptions[0].value;
|
||||
}
|
||||
} else {
|
||||
$scope.macsInputOptions = [];
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// Fetch all machines by current app name.
|
||||
queryAppMachines();
|
||||
|
||||
$scope.$watch('macInputModel', function () {
|
||||
if ($scope.macInputModel) {
|
||||
reInitIdentityDatas();
|
||||
}
|
||||
});
|
||||
|
||||
$scope.$on('$destroy', function () {
|
||||
$interval.cancel(intervalId);
|
||||
});
|
||||
|
||||
var intervalId;
|
||||
function reInitIdentityDatas() {
|
||||
getApiNames();
|
||||
queryIdentities();
|
||||
};
|
||||
|
||||
function queryIdentities() {
|
||||
var mac = $scope.macInputModel.split(':');
|
||||
if (mac == null || mac.length < 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
IdentityService.fetchClusterNodeOfMachine(mac[0], mac[1], $scope.searchKey).success(
|
||||
function (data) {
|
||||
if (data.code == 0 && data.data) {
|
||||
$scope.identities = data.data;
|
||||
$scope.totalCount = $scope.identities.length;
|
||||
} else {
|
||||
$scope.identities = [];
|
||||
$scope.totalCount = 0;
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
$scope.queryIdentities = queryIdentities;
|
||||
}]);
|
||||
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name sentinelDashboardApp.controller:MainCtrl
|
||||
* @description
|
||||
* # MainCtrl
|
||||
* Controller of the sentinelDashboardApp
|
||||
*/
|
||||
angular.module('sentinelDashboardApp')
|
||||
.controller('HomeCtrl', ['$scope', '$position', function ($scope, $position) {
|
||||
// do noting
|
||||
}]);
|
||||
@@ -0,0 +1,478 @@
|
||||
var app = angular.module('sentinelDashboardApp');
|
||||
|
||||
app.controller('IdentityCtl', ['$scope', '$stateParams', 'IdentityService',
|
||||
'ngDialog', 'FlowServiceV1', 'DegradeService', 'AuthorityRuleService', 'ParamFlowService', 'MachineService',
|
||||
'$interval', '$location', '$timeout',
|
||||
function ($scope, $stateParams, IdentityService, ngDialog,
|
||||
FlowService, DegradeService, AuthorityRuleService, ParamFlowService, MachineService, $interval, $location, $timeout) {
|
||||
|
||||
$scope.app = $stateParams.app;
|
||||
|
||||
$scope.currentPage = 1;
|
||||
$scope.pageSize = 16;
|
||||
$scope.totalPage = 1;
|
||||
$scope.totalCount = 0;
|
||||
$scope.identities = [];
|
||||
// 数据自动刷新频率, 默认10s
|
||||
var DATA_REFRESH_INTERVAL = 30;
|
||||
|
||||
$scope.isExpand = true;
|
||||
$scope.searchKey = '';
|
||||
$scope.firstExpandAll = false;
|
||||
$scope.isTreeView = true;
|
||||
|
||||
$scope.macsInputConfig = {
|
||||
searchField: ['text', 'value'],
|
||||
persist: true,
|
||||
create: false,
|
||||
maxItems: 1,
|
||||
render: {
|
||||
item: function (data, escape) {
|
||||
return '<div>' + escape(data.text) + '</div>';
|
||||
}
|
||||
},
|
||||
onChange: function (value, oldValue) {
|
||||
$scope.macInputModel = value;
|
||||
}
|
||||
};
|
||||
$scope.table = null;
|
||||
|
||||
var flowRuleDialog;
|
||||
var flowRuleDialogScope;
|
||||
$scope.addNewFlowRule = function (resource) {
|
||||
if (!$scope.macInputModel) {
|
||||
return;
|
||||
}
|
||||
var mac = $scope.macInputModel.split(':');
|
||||
flowRuleDialogScope = $scope.$new(true);
|
||||
flowRuleDialogScope.currentRule = {
|
||||
enable: false,
|
||||
strategy: 0,
|
||||
grade: 1,
|
||||
controlBehavior: 0,
|
||||
resource: resource,
|
||||
limitApp: 'default',
|
||||
clusterMode: false,
|
||||
clusterConfig: {
|
||||
thresholdType: 0
|
||||
},
|
||||
app: $scope.app,
|
||||
ip: mac[0],
|
||||
port: mac[1]
|
||||
};
|
||||
|
||||
flowRuleDialogScope.flowRuleDialog = {
|
||||
title: '新增流控规则',
|
||||
type: 'add',
|
||||
confirmBtnText: '新增',
|
||||
saveAndContinueBtnText: '新增并继续添加',
|
||||
showAdvanceButton: true
|
||||
};
|
||||
// $scope.flowRuleDialog = {
|
||||
// showAdvanceButton : true
|
||||
// };
|
||||
flowRuleDialogScope.saveRule = saveFlowRule;
|
||||
flowRuleDialogScope.saveRuleAndContinue = saveFlowRuleAndContinue;
|
||||
flowRuleDialogScope.onOpenAdvanceClick = function () {
|
||||
flowRuleDialogScope.flowRuleDialog.showAdvanceButton = false;
|
||||
};
|
||||
flowRuleDialogScope.onCloseAdvanceClick = function () {
|
||||
flowRuleDialogScope.flowRuleDialog.showAdvanceButton = true;
|
||||
};
|
||||
|
||||
flowRuleDialog = ngDialog.open({
|
||||
template: '/app/views/dialog/flow-rule-dialog.html',
|
||||
width: 680,
|
||||
overlay: true,
|
||||
scope: flowRuleDialogScope
|
||||
});
|
||||
};
|
||||
|
||||
function saveFlowRule() {
|
||||
if (!FlowService.checkRuleValid(flowRuleDialogScope.currentRule)) {
|
||||
return;
|
||||
}
|
||||
FlowService.newRule(flowRuleDialogScope.currentRule).success(function (data) {
|
||||
if (data.code === 0) {
|
||||
flowRuleDialog.close();
|
||||
let url = '/dashboard/flow/' + $scope.app;
|
||||
$location.path(url);
|
||||
} else {
|
||||
alert('失败:' + data.msg);
|
||||
}
|
||||
}).error((data, header, config, status) => {
|
||||
alert('未知错误');
|
||||
});
|
||||
}
|
||||
|
||||
function saveFlowRuleAndContinue() {
|
||||
if (!FlowService.checkRuleValid(flowRuleDialogScope.currentRule)) {
|
||||
return;
|
||||
}
|
||||
FlowService.newRule(flowRuleDialogScope.currentRule).success(function (data) {
|
||||
if (data.code === 0) {
|
||||
flowRuleDialog.close();
|
||||
} else {
|
||||
alert('失败:' + data.msg);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var degradeRuleDialog;
|
||||
var degradeRuleDialogScope;
|
||||
$scope.addNewDegradeRule = function (resource) {
|
||||
if (!$scope.macInputModel) {
|
||||
return;
|
||||
}
|
||||
var mac = $scope.macInputModel.split(':');
|
||||
degradeRuleDialogScope = $scope.$new(true);
|
||||
degradeRuleDialogScope.currentRule = {
|
||||
enable: false,
|
||||
grade: 0,
|
||||
strategy: 0,
|
||||
resource: resource,
|
||||
limitApp: 'default',
|
||||
minRequestAmount: 5,
|
||||
statIntervalMs: 1000,
|
||||
app: $scope.app,
|
||||
ip: mac[0],
|
||||
port: mac[1]
|
||||
};
|
||||
|
||||
degradeRuleDialogScope.degradeRuleDialog = {
|
||||
title: '新增熔断规则',
|
||||
type: 'add',
|
||||
confirmBtnText: '新增',
|
||||
saveAndContinueBtnText: '新增并继续添加'
|
||||
};
|
||||
degradeRuleDialogScope.saveRule = saveDegradeRule;
|
||||
degradeRuleDialogScope.saveRuleAndContinue = saveDegradeRuleAndContinue;
|
||||
|
||||
degradeRuleDialog = ngDialog.open({
|
||||
template: '/app/views/dialog/degrade-rule-dialog.html',
|
||||
width: 680,
|
||||
overlay: true,
|
||||
scope: degradeRuleDialogScope
|
||||
});
|
||||
};
|
||||
|
||||
function saveDegradeRule() {
|
||||
if (!DegradeService.checkRuleValid(degradeRuleDialogScope.currentRule)) {
|
||||
return;
|
||||
}
|
||||
DegradeService.newRule(degradeRuleDialogScope.currentRule).success(function (data) {
|
||||
if (data.code === 0) {
|
||||
degradeRuleDialog.close();
|
||||
var url = '/dashboard/degrade/' + $scope.app;
|
||||
$location.path(url);
|
||||
} else {
|
||||
alert('失败:' + data.msg);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function saveDegradeRuleAndContinue() {
|
||||
if (!DegradeService.checkRuleValid(degradeRuleDialogScope.currentRule)) {
|
||||
return;
|
||||
}
|
||||
DegradeService.newRule(degradeRuleDialogScope.currentRule).success(function (data) {
|
||||
if (data.code === 0) {
|
||||
degradeRuleDialog.close();
|
||||
} else {
|
||||
alert('失败:' + data.msg);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let authorityRuleDialog;
|
||||
let authorityRuleDialogScope;
|
||||
|
||||
function saveAuthorityRule() {
|
||||
let ruleEntity = authorityRuleDialogScope.currentRule;
|
||||
if (!AuthorityRuleService.checkRuleValid(ruleEntity.rule)) {
|
||||
return;
|
||||
}
|
||||
AuthorityRuleService.addNewRule(ruleEntity).success((data) => {
|
||||
if (data.success) {
|
||||
authorityRuleDialog.close();
|
||||
let url = '/dashboard/authority/' + $scope.app;
|
||||
$location.path(url);
|
||||
} else {
|
||||
alert('添加规则失败:' + data.msg);
|
||||
}
|
||||
}).error((data) => {
|
||||
if (data) {
|
||||
alert('添加规则失败:' + data.msg);
|
||||
} else {
|
||||
alert("添加规则失败:未知错误");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function saveAuthorityRuleAndContinue() {
|
||||
let ruleEntity = authorityRuleDialogScope.currentRule;
|
||||
if (!AuthorityRuleService.checkRuleValid(ruleEntity.rule)) {
|
||||
return;
|
||||
}
|
||||
AuthorityRuleService.addNewRule(ruleEntity).success((data) => {
|
||||
if (data.success) {
|
||||
authorityRuleDialog.close();
|
||||
} else {
|
||||
alert('添加规则失败:' + data.msg);
|
||||
}
|
||||
}).error((data) => {
|
||||
if (data) {
|
||||
alert('添加规则失败:' + data.msg);
|
||||
} else {
|
||||
alert("添加规则失败:未知错误");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$scope.addNewAuthorityRule = function (resource) {
|
||||
if (!$scope.macInputModel) {
|
||||
return;
|
||||
}
|
||||
let mac = $scope.macInputModel.split(':');
|
||||
authorityRuleDialogScope = $scope.$new(true);
|
||||
authorityRuleDialogScope.currentRule = {
|
||||
app: $scope.app,
|
||||
ip: mac[0],
|
||||
port: mac[1],
|
||||
rule: {
|
||||
resource: resource,
|
||||
strategy: 0,
|
||||
limitApp: '',
|
||||
}
|
||||
};
|
||||
|
||||
authorityRuleDialogScope.authorityRuleDialog = {
|
||||
title: '新增授权规则',
|
||||
type: 'add',
|
||||
confirmBtnText: '新增',
|
||||
saveAndContinueBtnText: '新增并继续添加'
|
||||
};
|
||||
authorityRuleDialogScope.saveRule = saveAuthorityRule;
|
||||
authorityRuleDialogScope.saveRuleAndContinue = saveAuthorityRuleAndContinue;
|
||||
|
||||
authorityRuleDialog = ngDialog.open({
|
||||
template: '/app/views/dialog/authority-rule-dialog.html',
|
||||
width: 680,
|
||||
overlay: true,
|
||||
scope: authorityRuleDialogScope
|
||||
});
|
||||
};
|
||||
|
||||
let paramFlowRuleDialog;
|
||||
let paramFlowRuleDialogScope;
|
||||
|
||||
function saveParamFlowRule() {
|
||||
let ruleEntity = paramFlowRuleDialogScope.currentRule;
|
||||
if (!ParamFlowService.checkRuleValid(ruleEntity.rule)) {
|
||||
return;
|
||||
}
|
||||
ParamFlowService.addNewRule(ruleEntity).success((data) => {
|
||||
if (data.success) {
|
||||
paramFlowRuleDialog.close();
|
||||
let url = '/dashboard/paramFlow/' + $scope.app;
|
||||
$location.path(url);
|
||||
} else {
|
||||
alert('添加热点规则失败:' + data.msg);
|
||||
}
|
||||
}).error((data) => {
|
||||
if (data) {
|
||||
alert('添加热点规则失败:' + data.msg);
|
||||
} else {
|
||||
alert("添加热点规则失败:未知错误");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function saveParamFlowRuleAndContinue() {
|
||||
let ruleEntity = paramFlowRuleDialogScope.currentRule;
|
||||
if (!ParamFlowService.checkRuleValid(ruleEntity.rule)) {
|
||||
return;
|
||||
}
|
||||
ParamFlowService.addNewRule(ruleEntity).success((data) => {
|
||||
if (data.success) {
|
||||
paramFlowRuleDialog.close();
|
||||
} else {
|
||||
alert('添加热点规则失败:' + data.msg);
|
||||
}
|
||||
}).error((data) => {
|
||||
if (data) {
|
||||
alert('添加热点规则失败:' + data.msg);
|
||||
} else {
|
||||
alert("添加热点规则失败:未知错误");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$scope.addNewParamFlowRule = function (resource) {
|
||||
if (!$scope.macInputModel) {
|
||||
return;
|
||||
}
|
||||
let mac = $scope.macInputModel.split(':');
|
||||
paramFlowRuleDialogScope = $scope.$new(true);
|
||||
paramFlowRuleDialogScope.currentRule = {
|
||||
app: $scope.app,
|
||||
ip: mac[0],
|
||||
port: mac[1],
|
||||
rule: {
|
||||
resource: resource,
|
||||
grade: 1,
|
||||
paramFlowItemList: [],
|
||||
count: 0,
|
||||
limitApp: 'default',
|
||||
controlBehavior: 0,
|
||||
durationInSec: 1,
|
||||
burstCount: 0,
|
||||
maxQueueingTimeMs: 0,
|
||||
clusterMode: false,
|
||||
clusterConfig: {
|
||||
thresholdType: 0,
|
||||
fallbackToLocalWhenFail: true,
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
paramFlowRuleDialogScope.paramFlowRuleDialog = {
|
||||
title: '新增热点规则',
|
||||
type: 'add',
|
||||
confirmBtnText: '新增',
|
||||
saveAndContinueBtnText: '新增并继续添加',
|
||||
supportAdvanced: false,
|
||||
showAdvanceButton: true
|
||||
};
|
||||
paramFlowRuleDialogScope.saveRule = saveParamFlowRule;
|
||||
paramFlowRuleDialogScope.saveRuleAndContinue = saveParamFlowRuleAndContinue;
|
||||
// paramFlowRuleDialogScope.onOpenAdvanceClick = function () {
|
||||
// paramFlowRuleDialogScope.paramFlowRuleDialog.showAdvanceButton = false;
|
||||
// };
|
||||
// paramFlowRuleDialogScope.onCloseAdvanceClick = function () {
|
||||
// paramFlowRuleDialogScope.paramFlowRuleDialog.showAdvanceButton = true;
|
||||
// };
|
||||
|
||||
paramFlowRuleDialog = ngDialog.open({
|
||||
template: '/app/views/dialog/param-flow-rule-dialog.html',
|
||||
width: 680,
|
||||
overlay: true,
|
||||
scope: paramFlowRuleDialogScope
|
||||
});
|
||||
};
|
||||
|
||||
var searchHandler;
|
||||
$scope.searchChange = function (searchKey) {
|
||||
$timeout.cancel(searchHandler);
|
||||
searchHandler = $timeout(function () {
|
||||
$scope.searchKey = searchKey;
|
||||
$scope.isExpand = true;
|
||||
$scope.firstExpandAll = true;
|
||||
reInitIdentityDatas();
|
||||
$scope.firstExpandAll = false;
|
||||
}, 600);
|
||||
};
|
||||
|
||||
$scope.initTreeTable = function () {
|
||||
// if (!$scope.table) {
|
||||
com_github_culmat_jsTreeTable.register(window);
|
||||
$scope.table = window.treeTable($('#identities'));
|
||||
// }
|
||||
};
|
||||
|
||||
$scope.expandAll = function () {
|
||||
$scope.isExpand = true;
|
||||
};
|
||||
$scope.collapseAll = function () {
|
||||
$scope.isExpand = false;
|
||||
};
|
||||
$scope.treeView = function () {
|
||||
$scope.isTreeView = true;
|
||||
queryIdentities();
|
||||
};
|
||||
$scope.listView = function () {
|
||||
$scope.isTreeView = false;
|
||||
queryIdentities();
|
||||
};
|
||||
|
||||
function queryAppMachines() {
|
||||
MachineService.getAppMachines($scope.app).success(
|
||||
function (data) {
|
||||
if (data.code === 0) {
|
||||
if (data.data) {
|
||||
$scope.machines = [];
|
||||
$scope.macsInputOptions = [];
|
||||
data.data.forEach(function (item) {
|
||||
if (item.healthy) {
|
||||
$scope.macsInputOptions.push({
|
||||
text: item.ip + ':' + item.port,
|
||||
value: item.ip + ':' + item.port
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
if ($scope.macsInputOptions.length > 0) {
|
||||
$scope.macInputModel = $scope.macsInputOptions[0].value;
|
||||
}
|
||||
} else {
|
||||
$scope.macsInputOptions = [];
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// Fetch all machines by current app name.
|
||||
queryAppMachines();
|
||||
|
||||
$scope.$watch('macInputModel', function () {
|
||||
if ($scope.macInputModel) {
|
||||
reInitIdentityDatas();
|
||||
}
|
||||
});
|
||||
|
||||
$scope.$on('$destroy', function () {
|
||||
$interval.cancel(intervalId);
|
||||
});
|
||||
|
||||
var intervalId;
|
||||
function reInitIdentityDatas() {
|
||||
// $interval.cancel(intervalId);
|
||||
queryIdentities();
|
||||
// intervalId = $interval(function () {
|
||||
// queryIdentities();
|
||||
// }, DATA_REFRESH_INTERVAL * 1000);
|
||||
};
|
||||
|
||||
function queryIdentities() {
|
||||
var mac = $scope.macInputModel.split(':');
|
||||
if (mac == null || mac.length < 2) {
|
||||
return;
|
||||
}
|
||||
if ($scope.isTreeView) {
|
||||
IdentityService.fetchIdentityOfMachine(mac[0], mac[1], $scope.searchKey).success(
|
||||
function (data) {
|
||||
if (data.code == 0 && data.data) {
|
||||
$scope.identities = data.data;
|
||||
$scope.totalCount = $scope.identities.length;
|
||||
} else {
|
||||
$scope.identities = [];
|
||||
$scope.totalCount = 0;
|
||||
}
|
||||
}
|
||||
);
|
||||
} else {
|
||||
IdentityService.fetchClusterNodeOfMachine(mac[0], mac[1], $scope.searchKey).success(
|
||||
function (data) {
|
||||
if (data.code == 0 && data.data) {
|
||||
$scope.identities = data.data;
|
||||
$scope.totalCount = $scope.identities.length;
|
||||
} else {
|
||||
$scope.identities = [];
|
||||
$scope.totalCount = 0;
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
$scope.queryIdentities = queryIdentities;
|
||||
}]);
|
||||
@@ -0,0 +1,33 @@
|
||||
var app = angular.module('sentinelDashboardApp');
|
||||
|
||||
app.controller('LoginCtl', ['$scope', '$state', '$window', 'AuthService',
|
||||
function ($scope, $state, $window, AuthService) {
|
||||
// If auth passed, jump to the index page directly
|
||||
if ($window.localStorage.getItem('session_sentinel_admin')) {
|
||||
$state.go('dashboard');
|
||||
}
|
||||
|
||||
$scope.login = function () {
|
||||
if (!$scope.username) {
|
||||
alert('请输入用户名');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$scope.password) {
|
||||
alert('请输入密码');
|
||||
return;
|
||||
}
|
||||
|
||||
var param = {"username": $scope.username, "password": $scope.password};
|
||||
|
||||
AuthService.login(param).success(function (data) {
|
||||
if (data.code == 0) {
|
||||
$window.localStorage.setItem('session_sentinel_admin', JSON.stringify(data.data));
|
||||
$state.go('dashboard');
|
||||
} else {
|
||||
alert(data.msg);
|
||||
}
|
||||
});
|
||||
};
|
||||
}]
|
||||
);
|
||||
@@ -0,0 +1,65 @@
|
||||
var app = angular.module('sentinelDashboardApp');
|
||||
|
||||
app.controller('MachineCtl', ['$scope', '$stateParams', 'MachineService',
|
||||
function ($scope, $stateParams, MachineService) {
|
||||
$scope.app = $stateParams.app;
|
||||
$scope.propertyName = '';
|
||||
$scope.reverse = false;
|
||||
$scope.currentPage = 1;
|
||||
$scope.machines = [];
|
||||
$scope.machinesPageConfig = {
|
||||
pageSize: 10,
|
||||
currentPageIndex: 1,
|
||||
totalPage: 1,
|
||||
totalCount: 0,
|
||||
};
|
||||
|
||||
$scope.sortBy = function (propertyName) {
|
||||
// console.log('machine sortBy ' + propertyName);
|
||||
$scope.reverse = ($scope.propertyName === propertyName) ? !$scope.reverse : false;
|
||||
$scope.propertyName = propertyName;
|
||||
};
|
||||
|
||||
$scope.reloadMachines = function() {
|
||||
MachineService.getAppMachines($scope.app).success(
|
||||
function (data) {
|
||||
// console.log('get machines: ' + data.data[0].hostname)
|
||||
if (data.code == 0 && data.data) {
|
||||
$scope.machines = data.data;
|
||||
var healthy = 0;
|
||||
$scope.machines.forEach(function (item) {
|
||||
if (item.healthy) {
|
||||
healthy++;
|
||||
}
|
||||
if (!item.hostname) {
|
||||
item.hostname = '未知'
|
||||
}
|
||||
})
|
||||
$scope.healthyCount = healthy;
|
||||
$scope.machinesPageConfig.totalCount = $scope.machines.length;
|
||||
} else {
|
||||
$scope.machines = [];
|
||||
$scope.healthyCount = 0;
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
$scope.removeMachine = function(ip, port) {
|
||||
if (!confirm("confirm to remove machine [" + ip + ":" + port + "]?")) {
|
||||
return;
|
||||
}
|
||||
MachineService.removeAppMachine($scope.app, ip, port).success(
|
||||
function(data) {
|
||||
if (data.code == 0) {
|
||||
$scope.reloadMachines();
|
||||
} else {
|
||||
alert("remove failed");
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
$scope.reloadMachines();
|
||||
|
||||
}]);
|
||||
@@ -0,0 +1,10 @@
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name sentinelDashboardApp.controller:MainCtrl
|
||||
* @description
|
||||
* # MainCtrl
|
||||
* Controller of the sentinelDashboardApp
|
||||
*/
|
||||
angular.module('sentinelDashboardApp')
|
||||
.controller('DashboardCtrl', ['$scope', '$position', function ($scope, $position) {
|
||||
}]);
|
||||
@@ -0,0 +1,269 @@
|
||||
var app = angular.module('sentinelDashboardApp');
|
||||
|
||||
app.controller('MetricCtl', ['$scope', '$stateParams', 'MetricService', '$interval', '$timeout',
|
||||
function ($scope, $stateParams, MetricService, $interval, $timeout) {
|
||||
$scope.charts = [];
|
||||
$scope.endTime = new Date();
|
||||
$scope.startTime = new Date();
|
||||
$scope.startTime.setMinutes($scope.endTime.getMinutes() - 30);
|
||||
$scope.startTimeFmt = formatDate($scope.startTime);
|
||||
$scope.endTimeFmt = formatDate($scope.endTime);
|
||||
function formatDate(date) {
|
||||
return moment(date).format('YYYY/MM/DD HH:mm:ss');
|
||||
}
|
||||
$scope.changeStartTime = function (startTime) {
|
||||
$scope.startTime = new Date(startTime);
|
||||
$scope.startTimeFmt = formatDate(startTime);
|
||||
};
|
||||
$scope.changeEndTime = function (endTime) {
|
||||
$scope.endTime = new Date(endTime);
|
||||
$scope.endTimeFmt = formatDate(endTime);
|
||||
};
|
||||
|
||||
$scope.app = $stateParams.app;
|
||||
// 数据自动刷新频率
|
||||
var DATA_REFRESH_INTERVAL = 1000 * 10;
|
||||
|
||||
$scope.servicePageConfig = {
|
||||
pageSize: 6,
|
||||
currentPageIndex: 1,
|
||||
totalPage: 1,
|
||||
totalCount: 0,
|
||||
};
|
||||
$scope.servicesChartConfigs = [];
|
||||
|
||||
$scope.pageChanged = function (newPageNumber) {
|
||||
$scope.servicePageConfig.currentPageIndex = newPageNumber;
|
||||
reInitIdentityDatas();
|
||||
};
|
||||
|
||||
var searchT;
|
||||
$scope.searchService = function () {
|
||||
$timeout.cancel(searchT);
|
||||
searchT = $timeout(function () {
|
||||
reInitIdentityDatas();
|
||||
}, 600);
|
||||
}
|
||||
|
||||
var intervalId;
|
||||
reInitIdentityDatas();
|
||||
function reInitIdentityDatas() {
|
||||
$interval.cancel(intervalId);
|
||||
queryIdentityDatas();
|
||||
intervalId = $interval(function () {
|
||||
queryIdentityDatas();
|
||||
}, DATA_REFRESH_INTERVAL);
|
||||
};
|
||||
|
||||
$scope.$on('$destroy', function () {
|
||||
$interval.cancel(intervalId);
|
||||
});
|
||||
$scope.initAllChart = function () {
|
||||
//revoke useless charts positively
|
||||
while($scope.charts.length > 0) {
|
||||
let chart = $scope.charts.pop();
|
||||
chart.destroy();
|
||||
}
|
||||
$.each($scope.metrics, function (idx, metric) {
|
||||
if (idx == $scope.metrics.length - 1) {
|
||||
return;
|
||||
}
|
||||
const chart = new G2.Chart({
|
||||
container: 'chart' + idx,
|
||||
forceFit: true,
|
||||
width: 100,
|
||||
height: 250,
|
||||
padding: [10, 30, 70, 50]
|
||||
});
|
||||
$scope.charts.push(chart);
|
||||
var maxQps = 0;
|
||||
for (var i in metric.data) {
|
||||
var item = metric.data[i];
|
||||
if (item.passQps > maxQps) {
|
||||
maxQps = item.passQps;
|
||||
}
|
||||
if (item.blockQps > maxQps) {
|
||||
maxQps = item.blockQps;
|
||||
}
|
||||
}
|
||||
chart.source(metric.data);
|
||||
chart.scale('timestamp', {
|
||||
type: 'time',
|
||||
mask: 'YYYY-MM-DD HH:mm:ss'
|
||||
});
|
||||
chart.scale('passQps', {
|
||||
min: 0,
|
||||
max: maxQps,
|
||||
fine: true,
|
||||
alias: '通过 QPS'
|
||||
// max: 10
|
||||
});
|
||||
chart.scale('blockQps', {
|
||||
min: 0,
|
||||
max: maxQps,
|
||||
fine: true,
|
||||
alias: '拒绝 QPS',
|
||||
});
|
||||
chart.scale('rt', {
|
||||
min: 0,
|
||||
fine: true,
|
||||
});
|
||||
chart.axis('rt', {
|
||||
grid: null,
|
||||
label: null
|
||||
});
|
||||
chart.axis('blockQps', {
|
||||
grid: null,
|
||||
label: null
|
||||
});
|
||||
|
||||
chart.axis('timestamp', {
|
||||
label: {
|
||||
textStyle: {
|
||||
textAlign: 'center', // 文本对齐方向,可取值为: start center end
|
||||
fill: '#404040', // 文本的颜色
|
||||
fontSize: '11', // 文本大小
|
||||
//textBaseline: 'top', // 文本基准线,可取 top middle bottom,默认为middle
|
||||
},
|
||||
autoRotate: false,
|
||||
formatter: function (text, item, index) {
|
||||
return text.substring(11, 11 + 5);
|
||||
}
|
||||
}
|
||||
});
|
||||
chart.legend({
|
||||
custom: true,
|
||||
position: 'bottom',
|
||||
allowAllCanceled: true,
|
||||
itemFormatter: function (val) {
|
||||
if ('passQps' === val) {
|
||||
return '通过 QPS';
|
||||
}
|
||||
if ('blockQps' === val) {
|
||||
return '拒绝 QPS';
|
||||
}
|
||||
return val;
|
||||
},
|
||||
items: [
|
||||
{ value: 'passQps', marker: { symbol: 'hyphen', stroke: 'green', radius: 5, lineWidth: 2 } },
|
||||
{ value: 'blockQps', marker: { symbol: 'hyphen', stroke: 'blue', radius: 5, lineWidth: 2 } },
|
||||
//{ value: 'rt', marker: {symbol: 'hyphen', stroke: 'gray', radius: 5, lineWidth: 2} },
|
||||
],
|
||||
onClick: function (ev) {
|
||||
const item = ev.item;
|
||||
const value = item.value;
|
||||
const checked = ev.checked;
|
||||
const geoms = chart.getAllGeoms();
|
||||
for (var i = 0; i < geoms.length; i++) {
|
||||
const geom = geoms[i];
|
||||
if (geom.getYScale().field === value) {
|
||||
if (checked) {
|
||||
geom.show();
|
||||
} else {
|
||||
geom.hide();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
chart.line().position('timestamp*passQps').size(1).color('green').shape('smooth');
|
||||
chart.line().position('timestamp*blockQps').size(1).color('blue').shape('smooth');
|
||||
//chart.line().position('timestamp*rt').size(1).color('gray').shape('smooth');
|
||||
G2.track(false);
|
||||
chart.render();
|
||||
});
|
||||
};
|
||||
|
||||
$scope.metrics = [];
|
||||
$scope.emptyObjs = [];
|
||||
function queryIdentityDatas() {
|
||||
var params = {
|
||||
app: $scope.app,
|
||||
pageIndex: $scope.servicePageConfig.currentPageIndex,
|
||||
pageSize: $scope.servicePageConfig.pageSize,
|
||||
desc: $scope.isDescOrder,
|
||||
searchKey: $scope.serviceQuery
|
||||
};
|
||||
MetricService.queryAppSortedIdentities(params).success(function (data) {
|
||||
$scope.metrics = [];
|
||||
$scope.emptyObjs = [];
|
||||
if (data.code === 0 && data.data) {
|
||||
var metricsObj = data.data.metric;
|
||||
var identityNames = Object.keys(metricsObj);
|
||||
if (identityNames.length < 1) {
|
||||
$scope.emptyServices = true;
|
||||
} else {
|
||||
$scope.emptyServices = false;
|
||||
}
|
||||
$scope.servicePageConfig.totalPage = data.data.totalPage;
|
||||
$scope.servicePageConfig.pageSize = data.data.pageSize;
|
||||
var totalCount = data.data.totalCount;
|
||||
$scope.servicePageConfig.totalCount = totalCount;
|
||||
for (i = 0; i < totalCount; i++) {
|
||||
$scope.emptyObjs.push({});
|
||||
}
|
||||
$.each(identityNames, function (idx, identityName) {
|
||||
var identityDatas = metricsObj[identityName];
|
||||
var metrics = {};
|
||||
metrics.resource = identityName;
|
||||
// metrics.data = identityDatas;
|
||||
metrics.data = fillZeros(identityDatas);
|
||||
metrics.shortData = lastOfArray(identityDatas, 6);
|
||||
$scope.metrics.push(metrics);
|
||||
});
|
||||
// push an empty element in the last, for ng-init reasons.
|
||||
$scope.metrics.push([]);
|
||||
} else {
|
||||
$scope.emptyServices = true;
|
||||
console.log(data.msg);
|
||||
}
|
||||
});
|
||||
};
|
||||
function fillZeros(metricData) {
|
||||
if (!metricData || metricData.length == 0) {
|
||||
return [];
|
||||
}
|
||||
var filledData = [];
|
||||
filledData.push(metricData[0]);
|
||||
var lastTime = metricData[0].timestamp / 1000;
|
||||
for (var i = 1; i < metricData.length; i++) {
|
||||
var curTime = metricData[i].timestamp / 1000;
|
||||
if (curTime > lastTime + 1) {
|
||||
for (var j = lastTime + 1; j < curTime; j++) {
|
||||
filledData.push({
|
||||
"timestamp": j * 1000,
|
||||
"passQps": 0,
|
||||
"blockQps": 0,
|
||||
"successQps": 0,
|
||||
"exception": 0,
|
||||
"rt": 0,
|
||||
"count": 0
|
||||
})
|
||||
}
|
||||
}
|
||||
filledData.push(metricData[i]);
|
||||
lastTime = curTime;
|
||||
}
|
||||
return filledData;
|
||||
}
|
||||
function lastOfArray(arr, n) {
|
||||
if (!arr.length) {
|
||||
return [];
|
||||
}
|
||||
var rs = [];
|
||||
for (i = 0; i < n && i < arr.length; i++) {
|
||||
rs.push(arr[arr.length - 1 - i]);
|
||||
}
|
||||
return rs;
|
||||
}
|
||||
|
||||
$scope.isDescOrder = true;
|
||||
$scope.setDescOrder = function () {
|
||||
$scope.isDescOrder = true;
|
||||
reInitIdentityDatas();
|
||||
}
|
||||
$scope.setAscOrder = function () {
|
||||
$scope.isDescOrder = false;
|
||||
reInitIdentityDatas();
|
||||
}
|
||||
}]);
|
||||
@@ -0,0 +1,328 @@
|
||||
/**
|
||||
* Parameter flow control controller.
|
||||
*
|
||||
* @author Eric Zhao
|
||||
*/
|
||||
angular.module('sentinelDashboardApp').controller('ParamFlowController', ['$scope', '$stateParams', 'ParamFlowService', 'ngDialog',
|
||||
'MachineService',
|
||||
function ($scope, $stateParams, ParamFlowService, ngDialog,
|
||||
MachineService) {
|
||||
const UNSUPPORTED_CODE = 4041;
|
||||
$scope.app = $stateParams.app;
|
||||
$scope.curExItem = {};
|
||||
|
||||
$scope.paramItemClassTypeList = [
|
||||
'int', 'double', 'java.lang.String', 'long', 'float', 'char', 'byte'
|
||||
];
|
||||
|
||||
$scope.rulesPageConfig = {
|
||||
pageSize: 10,
|
||||
currentPageIndex: 1,
|
||||
totalPage: 1,
|
||||
totalCount: 0,
|
||||
};
|
||||
$scope.macsInputConfig = {
|
||||
searchField: ['text', 'value'],
|
||||
persist: true,
|
||||
create: false,
|
||||
maxItems: 1,
|
||||
render: {
|
||||
item: function (data, escape) {
|
||||
return '<div>' + escape(data.text) + '</div>';
|
||||
}
|
||||
},
|
||||
onChange: function (value, oldValue) {
|
||||
$scope.macInputModel = value;
|
||||
}
|
||||
};
|
||||
|
||||
function updateSingleParamItem(arr, v, t, c) {
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
if (arr[i].object === v && arr[i].classType === t) {
|
||||
arr[i].count = c;
|
||||
return;
|
||||
}
|
||||
}
|
||||
arr.push({object: v, classType: t, count: c});
|
||||
}
|
||||
|
||||
function removeSingleParamItem(arr, v, t) {
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
if (arr[i].object === v && arr[i].classType === t) {
|
||||
arr.splice(i, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function isNumberClass(classType) {
|
||||
return classType === 'int' || classType === 'double' ||
|
||||
classType === 'float' || classType === 'long' || classType === 'short';
|
||||
}
|
||||
|
||||
function isByteClass(classType) {
|
||||
return classType === 'byte';
|
||||
}
|
||||
|
||||
function notNumberAtLeastZero(num) {
|
||||
return num === undefined || num === '' || isNaN(num) || num < 0;
|
||||
}
|
||||
|
||||
function notGoodNumber(num) {
|
||||
return num === undefined || num === '' || isNaN(num);
|
||||
}
|
||||
|
||||
function notGoodNumberBetweenExclusive(num, l ,r) {
|
||||
return num === undefined || num === '' || isNaN(num) || num < l || num > r;
|
||||
}
|
||||
|
||||
$scope.notValidParamItem = (curExItem) => {
|
||||
if (isNumberClass(curExItem.classType) && notGoodNumber(curExItem.object)) {
|
||||
return true;
|
||||
}
|
||||
if (isByteClass(curExItem.classType) && notGoodNumberBetweenExclusive(curExItem.object, -128, 127)) {
|
||||
return true;
|
||||
}
|
||||
return curExItem.object === undefined || curExItem.classType === undefined ||
|
||||
notNumberAtLeastZero(curExItem.count);
|
||||
};
|
||||
|
||||
$scope.addParamItem = () => {
|
||||
updateSingleParamItem($scope.currentRule.rule.paramFlowItemList,
|
||||
$scope.curExItem.object, $scope.curExItem.classType, $scope.curExItem.count);
|
||||
let oldItem = $scope.curExItem;
|
||||
$scope.curExItem = {classType: oldItem.classType};
|
||||
};
|
||||
|
||||
$scope.removeParamItem = (v, t) => {
|
||||
removeSingleParamItem($scope.currentRule.rule.paramFlowItemList, v, t);
|
||||
};
|
||||
|
||||
function getMachineRules() {
|
||||
if (!$scope.macInputModel) {
|
||||
return;
|
||||
}
|
||||
let mac = $scope.macInputModel.split(':');
|
||||
ParamFlowService.queryMachineRules($scope.app, mac[0], mac[1])
|
||||
.success(function (data) {
|
||||
if (data.code === 0 && data.data) {
|
||||
$scope.loadError = undefined;
|
||||
$scope.rules = data.data;
|
||||
$scope.rulesPageConfig.totalCount = $scope.rules.length;
|
||||
} else {
|
||||
$scope.rules = [];
|
||||
$scope.rulesPageConfig.totalCount = 0;
|
||||
if (data.code === UNSUPPORTED_CODE) {
|
||||
$scope.loadError = {message: "机器 " + mac[0] + ":" + mac[1] + " 的 Sentinel 客户端版本不支持热点参数限流功能,请升级至 0.2.0 以上版本并引入 sentinel-parameter-flow-control 依赖。"}
|
||||
} else {
|
||||
$scope.loadError = {message: data.msg}
|
||||
}
|
||||
}
|
||||
})
|
||||
.error((data, header, config, status) => {
|
||||
$scope.loadError = {message: "未知错误"}
|
||||
});
|
||||
}
|
||||
$scope.getMachineRules = getMachineRules;
|
||||
getMachineRules();
|
||||
|
||||
var paramFlowRuleDialog;
|
||||
|
||||
$scope.editRule = function (rule) {
|
||||
$scope.currentRule = angular.copy(rule);
|
||||
if ($scope.currentRule.rule && $scope.currentRule.rule.durationInSec === undefined) {
|
||||
$scope.currentRule.rule.durationInSec = 1;
|
||||
}
|
||||
$scope.paramFlowRuleDialog = {
|
||||
title: '编辑热点规则',
|
||||
type: 'edit',
|
||||
confirmBtnText: '保存',
|
||||
supportAdvanced: true,
|
||||
showAdvanceButton: rule.rule.paramFlowItemList === undefined || rule.rule.paramFlowItemList.length <= 0
|
||||
};
|
||||
paramFlowRuleDialog = ngDialog.open({
|
||||
template: '/app/views/dialog/param-flow-rule-dialog.html',
|
||||
width: 680,
|
||||
overlay: true,
|
||||
scope: $scope
|
||||
});
|
||||
$scope.curExItem = {};
|
||||
};
|
||||
|
||||
$scope.addNewRule = function () {
|
||||
var mac = $scope.macInputModel.split(':');
|
||||
$scope.currentRule = {
|
||||
app: $scope.app,
|
||||
ip: mac[0],
|
||||
port: mac[1],
|
||||
rule: {
|
||||
grade: 1,
|
||||
paramFlowItemList: [],
|
||||
count: 0,
|
||||
limitApp: 'default',
|
||||
controlBehavior: 0,
|
||||
durationInSec: 1,
|
||||
burstCount: 0,
|
||||
maxQueueingTimeMs: 0,
|
||||
clusterMode: false,
|
||||
clusterConfig: {
|
||||
thresholdType: 0,
|
||||
fallbackToLocalWhenFail: true,
|
||||
}
|
||||
}
|
||||
};
|
||||
$scope.paramFlowRuleDialog = {
|
||||
title: '新增热点规则',
|
||||
type: 'add',
|
||||
confirmBtnText: '新增',
|
||||
supportAdvanced: true,
|
||||
showAdvanceButton: true,
|
||||
};
|
||||
paramFlowRuleDialog = ngDialog.open({
|
||||
template: '/app/views/dialog/param-flow-rule-dialog.html',
|
||||
width: 680,
|
||||
overlay: true,
|
||||
scope: $scope
|
||||
});
|
||||
$scope.curExItem = {};
|
||||
};
|
||||
|
||||
$scope.onOpenAdvanceClick = function () {
|
||||
$scope.paramFlowRuleDialog.showAdvanceButton = false;
|
||||
};
|
||||
$scope.onCloseAdvanceClick = function () {
|
||||
$scope.paramFlowRuleDialog.showAdvanceButton = true;
|
||||
};
|
||||
|
||||
$scope.saveRule = function () {
|
||||
if (!ParamFlowService.checkRuleValid($scope.currentRule.rule)) {
|
||||
return;
|
||||
}
|
||||
if ($scope.paramFlowRuleDialog.type === 'add') {
|
||||
addNewRuleAndPush($scope.currentRule);
|
||||
} else if ($scope.paramFlowRuleDialog.type === 'edit') {
|
||||
saveRuleAndPush($scope.currentRule, true);
|
||||
}
|
||||
};
|
||||
|
||||
function addNewRuleAndPush(rule) {
|
||||
ParamFlowService.addNewRule(rule).success((data) => {
|
||||
if (data.success) {
|
||||
getMachineRules();
|
||||
paramFlowRuleDialog.close();
|
||||
} else {
|
||||
alert('添加规则失败:' + data.msg);
|
||||
}
|
||||
}).error((data) => {
|
||||
if (data) {
|
||||
alert('添加规则失败:' + data.msg);
|
||||
} else {
|
||||
alert("添加规则失败:未知错误");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function saveRuleAndPush(rule, edit) {
|
||||
ParamFlowService.saveRule(rule).success(function (data) {
|
||||
if (data.success) {
|
||||
alert("修改规则成功");
|
||||
getMachineRules();
|
||||
if (edit) {
|
||||
paramFlowRuleDialog.close();
|
||||
} else {
|
||||
confirmDialog.close();
|
||||
}
|
||||
} else {
|
||||
alert('修改规则失败:' + data.msg);
|
||||
}
|
||||
}).error((data) => {
|
||||
if (data) {
|
||||
alert('修改规则失败:' + data.msg);
|
||||
} else {
|
||||
alert("修改规则失败:未知错误");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function deleteRuleAndPush(entity) {
|
||||
if (entity.id === undefined || isNaN(entity.id)) {
|
||||
alert('规则 ID 不合法!');
|
||||
return;
|
||||
}
|
||||
ParamFlowService.deleteRule(entity).success((data) => {
|
||||
if (data.code == 0) {
|
||||
getMachineRules();
|
||||
confirmDialog.close();
|
||||
} else {
|
||||
alert('删除规则失败:' + data.msg);
|
||||
}
|
||||
}).error((data) => {
|
||||
if (data) {
|
||||
alert('删除规则失败:' + data.msg);
|
||||
} else {
|
||||
alert("删除规则失败:未知错误");
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
var confirmDialog;
|
||||
$scope.deleteRule = function (ruleEntity) {
|
||||
$scope.currentRule = ruleEntity;
|
||||
console.log('deleting: ' + ruleEntity);
|
||||
$scope.confirmDialog = {
|
||||
title: '删除热点规则',
|
||||
type: 'delete_rule',
|
||||
attentionTitle: '请确认是否删除如下热点参数限流规则',
|
||||
attention: '资源名: ' + ruleEntity.rule.resource + ', 热点参数索引: ' + ruleEntity.rule.paramIdx +
|
||||
', 限流模式: ' + (ruleEntity.rule.grade === 1 ? 'QPS' : '未知') + ', 限流阈值: ' + ruleEntity.rule.count,
|
||||
confirmBtnText: '删除',
|
||||
};
|
||||
confirmDialog = ngDialog.open({
|
||||
template: '/app/views/dialog/confirm-dialog.html',
|
||||
scope: $scope,
|
||||
overlay: true
|
||||
});
|
||||
};
|
||||
|
||||
$scope.confirm = function () {
|
||||
if ($scope.confirmDialog.type === 'delete_rule') {
|
||||
deleteRuleAndPush($scope.currentRule);
|
||||
} else {
|
||||
console.error('error');
|
||||
}
|
||||
};
|
||||
|
||||
queryAppMachines();
|
||||
|
||||
function queryAppMachines() {
|
||||
MachineService.getAppMachines($scope.app).success(
|
||||
function (data) {
|
||||
if (data.code == 0) {
|
||||
// $scope.machines = data.data;
|
||||
if (data.data) {
|
||||
$scope.machines = [];
|
||||
$scope.macsInputOptions = [];
|
||||
data.data.forEach(function (item) {
|
||||
if (item.healthy) {
|
||||
$scope.macsInputOptions.push({
|
||||
text: item.ip + ':' + item.port,
|
||||
value: item.ip + ':' + item.port
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
if ($scope.macsInputOptions.length > 0) {
|
||||
$scope.macInputModel = $scope.macsInputOptions[0].value;
|
||||
}
|
||||
} else {
|
||||
$scope.macsInputOptions = [];
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
$scope.$watch('macInputModel', function () {
|
||||
if ($scope.macInputModel) {
|
||||
getMachineRules();
|
||||
}
|
||||
});
|
||||
}]);
|
||||
@@ -0,0 +1,239 @@
|
||||
var app = angular.module('sentinelDashboardApp');
|
||||
|
||||
app.controller('SystemCtl', ['$scope', '$stateParams', 'SystemService', 'ngDialog', 'MachineService',
|
||||
function ($scope, $stateParams, SystemService,
|
||||
ngDialog, MachineService) {
|
||||
//初始化
|
||||
$scope.app = $stateParams.app;
|
||||
$scope.rulesPageConfig = {
|
||||
pageSize: 10,
|
||||
currentPageIndex: 1,
|
||||
totalPage: 1,
|
||||
totalCount: 0,
|
||||
};
|
||||
$scope.macsInputConfig = {
|
||||
searchField: ['text', 'value'],
|
||||
persist: true,
|
||||
create: false,
|
||||
maxItems: 1,
|
||||
render: {
|
||||
item: function (data, escape) {
|
||||
return '<div>' + escape(data.text) + '</div>';
|
||||
}
|
||||
},
|
||||
onChange: function (value, oldValue) {
|
||||
$scope.macInputModel = value;
|
||||
}
|
||||
};
|
||||
|
||||
getMachineRules();
|
||||
function getMachineRules() {
|
||||
if (!$scope.macInputModel) {
|
||||
return;
|
||||
}
|
||||
let mac = $scope.macInputModel.split(':');
|
||||
SystemService.queryMachineRules($scope.app, mac[0], mac[1]).success(
|
||||
function (data) {
|
||||
if (data.code === 0 && data.data) {
|
||||
$scope.rules = data.data;
|
||||
$.each($scope.rules, function (idx, rule) {
|
||||
if (rule.highestSystemLoad >= 0) {
|
||||
rule.grade = 0;
|
||||
} else if (rule.avgRt >= 0) {
|
||||
rule.grade = 1;
|
||||
} else if (rule.maxThread >= 0) {
|
||||
rule.grade = 2;
|
||||
} else if (rule.qps >= 0) {
|
||||
rule.grade = 3;
|
||||
} else if (rule.highestCpuUsage >= 0) {
|
||||
rule.grade = 4;
|
||||
}
|
||||
});
|
||||
$scope.rulesPageConfig.totalCount = $scope.rules.length;
|
||||
} else {
|
||||
$scope.rules = [];
|
||||
$scope.rulesPageConfig.totalCount = 0;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$scope.getMachineRules = getMachineRules;
|
||||
var systemRuleDialog;
|
||||
$scope.editRule = function (rule) {
|
||||
$scope.currentRule = angular.copy(rule);
|
||||
$scope.systemRuleDialog = {
|
||||
title: '编辑系统保护规则',
|
||||
type: 'edit',
|
||||
confirmBtnText: '保存'
|
||||
};
|
||||
systemRuleDialog = ngDialog.open({
|
||||
template: '/app/views/dialog/system-rule-dialog.html',
|
||||
width: 680,
|
||||
overlay: true,
|
||||
scope: $scope
|
||||
});
|
||||
};
|
||||
|
||||
$scope.addNewRule = function () {
|
||||
var mac = $scope.macInputModel.split(':');
|
||||
$scope.currentRule = {
|
||||
grade: 0,
|
||||
app: $scope.app,
|
||||
ip: mac[0],
|
||||
port: mac[1],
|
||||
};
|
||||
$scope.systemRuleDialog = {
|
||||
title: '新增系统保护规则',
|
||||
type: 'add',
|
||||
confirmBtnText: '新增'
|
||||
};
|
||||
systemRuleDialog = ngDialog.open({
|
||||
template: '/app/views/dialog/system-rule-dialog.html',
|
||||
width: 680,
|
||||
overlay: true,
|
||||
scope: $scope
|
||||
});
|
||||
};
|
||||
|
||||
$scope.saveRule = function () {
|
||||
if ($scope.systemRuleDialog.type === 'add') {
|
||||
addNewRule($scope.currentRule);
|
||||
} else if ($scope.systemRuleDialog.type === 'edit') {
|
||||
saveRule($scope.currentRule, true);
|
||||
}
|
||||
};
|
||||
|
||||
var confirmDialog;
|
||||
$scope.deleteRule = function (rule) {
|
||||
$scope.currentRule = rule;
|
||||
var ruleTypeDesc = '';
|
||||
var ruleTypeCount = null;
|
||||
if (rule.highestSystemLoad != -1) {
|
||||
ruleTypeDesc = 'LOAD';
|
||||
ruleTypeCount = rule.highestSystemLoad;
|
||||
} else if (rule.avgRt != -1) {
|
||||
ruleTypeDesc = 'RT';
|
||||
ruleTypeCount = rule.avgRt;
|
||||
} else if (rule.maxThread != -1) {
|
||||
ruleTypeDesc = '线程数';
|
||||
ruleTypeCount = rule.maxThread;
|
||||
} else if (rule.qps != -1) {
|
||||
ruleTypeDesc = 'QPS';
|
||||
ruleTypeCount = rule.qps;
|
||||
}else if (rule.highestCpuUsage != -1) {
|
||||
ruleTypeDesc = 'CPU 使用率';
|
||||
ruleTypeCount = rule.highestCpuUsage;
|
||||
}
|
||||
|
||||
$scope.confirmDialog = {
|
||||
title: '删除系统保护规则',
|
||||
type: 'delete_rule',
|
||||
attentionTitle: '请确认是否删除如下系统保护规则',
|
||||
attention: '阈值类型: ' + ruleTypeDesc + ', 阈值: ' + ruleTypeCount,
|
||||
confirmBtnText: '删除',
|
||||
};
|
||||
confirmDialog = ngDialog.open({
|
||||
template: '/app/views/dialog/confirm-dialog.html',
|
||||
scope: $scope,
|
||||
overlay: true
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
$scope.confirm = function () {
|
||||
if ($scope.confirmDialog.type === 'delete_rule') {
|
||||
deleteRule($scope.currentRule);
|
||||
// } else if ($scope.confirmDialog.type == 'enable_rule') {
|
||||
// $scope.currentRule.enable = true;
|
||||
// saveRule($scope.currentRule);
|
||||
// } else if ($scope.confirmDialog.type == 'disable_rule') {
|
||||
// $scope.currentRule.enable = false;
|
||||
// saveRule($scope.currentRule);
|
||||
// } else if ($scope.confirmDialog.type == 'enable_all') {
|
||||
// enableAll($scope.app);
|
||||
// } else if ($scope.confirmDialog.type == 'disable_all') {
|
||||
// disableAll($scope.app);
|
||||
} else {
|
||||
console.error('error');
|
||||
}
|
||||
};
|
||||
|
||||
function deleteRule(rule) {
|
||||
SystemService.deleteRule(rule).success(function (data) {
|
||||
if (data.code === 0) {
|
||||
getMachineRules();
|
||||
confirmDialog.close();
|
||||
} else if (data.msg != null) {
|
||||
alert('失败:' + data.msg);
|
||||
} else {
|
||||
alert('失败:未知错误');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function addNewRule(rule) {
|
||||
if (rule.grade == 4 && (rule.highestCpuUsage < 0 || rule.highestCpuUsage > 1)) {
|
||||
alert('CPU 使用率模式的取值范围应为 [0.0, 1.0],对应 0% - 100%');
|
||||
return;
|
||||
}
|
||||
SystemService.newRule(rule).success(function (data) {
|
||||
if (data.code === 0) {
|
||||
getMachineRules();
|
||||
systemRuleDialog.close();
|
||||
} else if (data.msg != null) {
|
||||
alert('失败:' + data.msg);
|
||||
} else {
|
||||
alert('失败:未知错误');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function saveRule(rule, edit) {
|
||||
SystemService.saveRule(rule).success(function (data) {
|
||||
if (data.code === 0) {
|
||||
getMachineRules();
|
||||
if (edit) {
|
||||
systemRuleDialog.close();
|
||||
} else {
|
||||
confirmDialog.close();
|
||||
}
|
||||
} else if (data.msg != null) {
|
||||
alert('失败:' + data.msg);
|
||||
} else {
|
||||
alert('失败:未知错误');
|
||||
}
|
||||
});
|
||||
}
|
||||
queryAppMachines();
|
||||
function queryAppMachines() {
|
||||
MachineService.getAppMachines($scope.app).success(
|
||||
function (data) {
|
||||
if (data.code === 0) {
|
||||
// $scope.machines = data.data;
|
||||
if (data.data) {
|
||||
$scope.machines = [];
|
||||
$scope.macsInputOptions = [];
|
||||
data.data.forEach(function (item) {
|
||||
if (item.healthy) {
|
||||
$scope.macsInputOptions.push({
|
||||
text: item.ip + ':' + item.port,
|
||||
value: item.ip + ':' + item.port
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
if ($scope.macsInputOptions.length > 0) {
|
||||
$scope.macInputModel = $scope.macsInputOptions[0].value;
|
||||
}
|
||||
} else {
|
||||
$scope.macsInputOptions = [];
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
$scope.$watch('macInputModel', function () {
|
||||
if ($scope.macInputModel) {
|
||||
getMachineRules();
|
||||
}
|
||||
});
|
||||
}]);
|
||||
@@ -0,0 +1,15 @@
|
||||
<div class="header">
|
||||
<nav class="navbar navbar-fixed-top navbar-inverse" style="" role="navigation">
|
||||
<div class="navbar-brand">
|
||||
<span style="color: #fff;font-size: 26px;" ng-model="dashboardVersion">Sentinel 控制台 {{dashboardVersion}}</span>
|
||||
</div>
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li ng-show="showLogout">
|
||||
<a href="javascript:void(0);" ng-click="logout()"
|
||||
style="margin: 3px 15px 0 0;"><span class="glyphicon glyphicon-log-out"></span> 注销</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
<!-- end nav -->
|
||||
<sidebar></sidebar>
|
||||
</div>
|
||||
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* @ngdoc directive
|
||||
* @name izzyposWebApp.directive:adminPosHeader
|
||||
* @description
|
||||
* # adminPosHeader
|
||||
*/
|
||||
angular.module('sentinelDashboardApp')
|
||||
.directive('header', ['VersionService', 'AuthService', function () {
|
||||
return {
|
||||
templateUrl: 'app/scripts/directives/header/header.html',
|
||||
restrict: 'E',
|
||||
replace: true,
|
||||
controller: function ($scope, $state, $window, VersionService, AuthService) {
|
||||
VersionService.version().success(function (data) {
|
||||
if (data.code == 0) {
|
||||
$scope.dashboardVersion = data.data;
|
||||
}
|
||||
});
|
||||
|
||||
if (!$window.localStorage.getItem("session_sentinel_admin")) {
|
||||
AuthService.check().success(function (data) {
|
||||
if (data.code == 0) {
|
||||
$window.localStorage.setItem('session_sentinel_admin', JSON.stringify(data.data));
|
||||
handleLogout($scope, data.data.id)
|
||||
} else {
|
||||
$state.go('login');
|
||||
}
|
||||
});
|
||||
} else {
|
||||
try {
|
||||
var id = JSON.parse($window.localStorage.getItem("session_sentinel_admin")).id;
|
||||
handleLogout($scope, id);
|
||||
} catch (e) {
|
||||
// Historical version compatibility processing, fixes issue-1449
|
||||
// If error happens while parsing, remove item in localStorage and redirect to login page.
|
||||
$window.localStorage.removeItem("session_sentinel_admin");
|
||||
$state.go('login');
|
||||
}
|
||||
}
|
||||
|
||||
function handleLogout($scope, id) {
|
||||
if (id == 'FAKE_EMP_ID') {
|
||||
$scope.showLogout = false;
|
||||
} else {
|
||||
$scope.showLogout = true;
|
||||
}
|
||||
}
|
||||
|
||||
$scope.logout = function () {
|
||||
AuthService.logout().success(function (data) {
|
||||
if (data.code == 0) {
|
||||
$window.localStorage.removeItem("session_sentinel_admin");
|
||||
$state.go('login');
|
||||
} else {
|
||||
alert('logout error');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}]);
|
||||
@@ -0,0 +1,10 @@
|
||||
<li class="sidebar-search">
|
||||
<div class="input-group custom-search-form">
|
||||
<input type="text" class="form-control" ng-model="searchApp" placeholder="Search...">
|
||||
<span class="input-group-btn">
|
||||
<button class="btn btn-default" type="button">
|
||||
<i class="fa fa-search"></i>
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
</li>
|
||||
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* @ngdoc directive
|
||||
* @name izzyposWebApp.directive:adminPosHeader
|
||||
* @description
|
||||
* # adminPosHeader
|
||||
*/
|
||||
|
||||
angular.module('sentinelDashboardApp')
|
||||
.directive('sidebarSearch', function () {
|
||||
return {
|
||||
templateUrl: 'app/scripts/directives/sidebar/sidebar-search/sidebar-search.html',
|
||||
restrict: 'E',
|
||||
replace: true,
|
||||
scope: {
|
||||
},
|
||||
controller: function ($scope) {
|
||||
$scope.selectedMenu = 'home';
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,91 @@
|
||||
<div class="navbar-default sidebar" role="navigation" style="overflow-y: auto;">
|
||||
<div class="sidebar-nav navbar-collapse">
|
||||
<ul class="nav in" id="side-menu">
|
||||
<li class="sidebar-search">
|
||||
<div class="input-group" style="">
|
||||
<input type="text" class="form-control highlight-border" placeholder="应用名" ng-model="searchApp">
|
||||
<span class="input-group-btn">
|
||||
<button class="btn btn-secondary btn-default-inverse" type="button">搜索</button>
|
||||
</span>
|
||||
</div>
|
||||
</li>
|
||||
<li ui-sref-active="active">
|
||||
<a ui-sref="dashboard.home" style="font-size:16px;">
|
||||
<span class="glyphicon glyphicon-dashboard"></span>
|
||||
首页</a>
|
||||
</li>
|
||||
|
||||
<li ng-class="{active: true}" ng-repeat="entry in apps | filter: { app: searchApp }">{{dropDown}}
|
||||
<a href="javascript:void(0);" ng-click="click($event)" collapse="{{collpaseall == 1}}" style="font-size: 16px;word-break: break-word;">
|
||||
{{entry.app}}
|
||||
<span class="fa arrow"></span>
|
||||
<span class="arrow">({{entry.healthyCount}}/{{entry.machines.length}})</span>
|
||||
</a>
|
||||
|
||||
<!--<ul class="nav nav-second-level" collapse="{{entry.active}}" style="display: none;">-->
|
||||
<ul class="nav nav-second-level" ng-show="entry.active">
|
||||
<li ui-sref-active="active">
|
||||
<a ui-sref="dashboard.metric({app: entry.app})">
|
||||
<i class="fa fa-bar-chart"></i> 实时监控</a>
|
||||
</li>
|
||||
|
||||
<li ui-sref-active="active" ng-if="!entry.isGateway">
|
||||
<a ui-sref="dashboard.identity({app: entry.app})">
|
||||
<i class="glyphicon glyphicon-list-alt"></i> 簇点链路</a>
|
||||
</li>
|
||||
|
||||
<li ui-sref-active="active" ng-if="entry.isGateway">
|
||||
<a ui-sref="dashboard.gatewayIdentity({app: entry.app})">
|
||||
<i class="glyphicon glyphicon-filter"></i> 请求链路</a>
|
||||
</li>
|
||||
|
||||
<li ui-sref-active="active" ng-if="entry.appType==0">
|
||||
<a ui-sref="dashboard.flowV1({app: entry.app})">
|
||||
<i class="glyphicon glyphicon-filter"></i> 流控规则</a>
|
||||
</li>
|
||||
|
||||
<li ui-sref-active="active" ng-if="entry.isGateway">
|
||||
<a ui-sref="dashboard.gatewayApi({app: entry.app})">
|
||||
<i class="glyphicon glyphicon-tags"></i> API 管理</a>
|
||||
</li>
|
||||
<li ui-sref-active="active" ng-if="entry.isGateway">
|
||||
<a ui-sref="dashboard.gatewayFlow({app: entry.app})">
|
||||
<i class="glyphicon glyphicon-filter"></i> 流控规则</a>
|
||||
</li>
|
||||
|
||||
<!-- <li ui-sref-active="active" ng-if="!entry.isGateway">-->
|
||||
<!-- <a ui-sref="dashboard.flow({app: entry.app})">-->
|
||||
<!-- <i class="glyphicon glyphicon-filter"></i> 流控规则 V2</a>-->
|
||||
<!-- </li>-->
|
||||
|
||||
<li ui-sref-active="active">
|
||||
<a ui-sref="dashboard.degrade({app: entry.app})">
|
||||
<i class="glyphicon glyphicon-flash"></i> 熔断规则</a>
|
||||
</li>
|
||||
<li ui-sref-active="active" ng-if="!entry.isGateway">
|
||||
<a ui-sref="dashboard.paramFlow({app: entry.app})">
|
||||
<i class="glyphicon glyphicon-fire"></i> 热点规则</a>
|
||||
</li>
|
||||
<li ui-sref-active="active">
|
||||
<a ui-sref="dashboard.system({app: entry.app})">
|
||||
<i class="glyphicon glyphicon-lock"></i> 系统规则</a>
|
||||
</li>
|
||||
<li ui-sref-active="active" ng-if="!entry.isGateway">
|
||||
<a ui-sref="dashboard.authority({app: entry.app})">
|
||||
<i class="glyphicon glyphicon-check"></i> 授权规则</a>
|
||||
</li>
|
||||
<li ui-sref-active="active" ng-if="!entry.isGateway">
|
||||
<a ui-sref="dashboard.clusterAppServerList({app: entry.app})">
|
||||
<i class="glyphicon glyphicon-cloud"></i> 集群流控</a>
|
||||
</li>
|
||||
|
||||
<li ui-sref-active="active">
|
||||
<a ui-sref="dashboard.machine({app: entry.app})">
|
||||
<i class="glyphicon glyphicon-th-list"></i> 机器列表</a>
|
||||
</li>
|
||||
</ul>
|
||||
<!-- /.nav-second-level -->
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,71 @@
|
||||
angular.module('sentinelDashboardApp')
|
||||
.directive('sidebar', ['$location', '$stateParams', 'AppService', function () {
|
||||
return {
|
||||
templateUrl: 'app/scripts/directives/sidebar/sidebar.html',
|
||||
restrict: 'E',
|
||||
replace: true,
|
||||
scope: {
|
||||
},
|
||||
controller: function ($scope, $stateParams, $location, AppService) {
|
||||
$scope.app = $stateParams.app;
|
||||
$scope.collapseVar = 0;
|
||||
|
||||
// app
|
||||
AppService.getApps().success(
|
||||
function (data) {
|
||||
if (data.code === 0) {
|
||||
let path = $location.path().split('/');
|
||||
let initHashApp = path[path.length - 1];
|
||||
$scope.apps = data.data;
|
||||
$scope.apps = $scope.apps.map(function (item) {
|
||||
if (item.app === initHashApp) {
|
||||
item.active = true;
|
||||
}
|
||||
let healthyCount = 0;
|
||||
for (let i in item.machines) {
|
||||
if (item.machines[i].healthy) {
|
||||
healthyCount++;
|
||||
}
|
||||
}
|
||||
item.healthyCount = healthyCount;
|
||||
// Handle appType
|
||||
item.isGateway = item.appType === 1 || item.appType === 11 || item.appType === 12;
|
||||
|
||||
if (item.shown) {
|
||||
return item;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// toggle side bar
|
||||
$scope.click = function ($event) {
|
||||
let entry = angular.element($event.target).scope().entry;
|
||||
entry.active = !entry.active;// toggle this clicked app bar
|
||||
|
||||
$scope.apps.forEach(function (item) { // collapse other app bars
|
||||
if (item !== entry) {
|
||||
item.active = false;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
$scope.addSearchApp = function () {
|
||||
let findApp = false;
|
||||
for (let i = 0; i < $scope.apps.length; i++) {
|
||||
if ($scope.apps[i].app === $scope.searchApp) {
|
||||
findApp = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!findApp) {
|
||||
$scope.apps.push({ app: $scope.searchApp });
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}]);
|
||||
@@ -0,0 +1,17 @@
|
||||
var app = angular.module('sentinelDashboardApp');
|
||||
|
||||
app.filter('range', [function () {
|
||||
return function (input, length) {
|
||||
if (isNaN(length) || length <= 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
input = [];
|
||||
for (var index = 1; index <= length; index++) {
|
||||
input.push(index);
|
||||
}
|
||||
|
||||
return input;
|
||||
};
|
||||
|
||||
}]);
|
||||
@@ -0,0 +1,292 @@
|
||||
var com_github_culmat_jsTreeTable = (function(){
|
||||
|
||||
function depthFirst(tree, func, childrenAttr) {
|
||||
childrenAttr = childrenAttr || 'children'
|
||||
function i_depthFirst(node) {
|
||||
if (node[childrenAttr]) {
|
||||
$.each(node[childrenAttr], function(i, child) {
|
||||
i_depthFirst(child)
|
||||
})
|
||||
}
|
||||
func(node)
|
||||
}
|
||||
$.each(tree, function(i, root) {
|
||||
i_depthFirst(root)
|
||||
})
|
||||
return tree
|
||||
}
|
||||
|
||||
/*
|
||||
* make a deep copy of the object
|
||||
*/
|
||||
function copy(data){
|
||||
return JSON.parse(JSON.stringify(data))
|
||||
}
|
||||
|
||||
function makeTree (data, idAttr, refAttr, childrenAttr) {
|
||||
var data_tmp = data
|
||||
idAttr = idAttr || 'id'
|
||||
refAttr = refAttr || 'parent'
|
||||
childrenAttr = childrenAttr || 'children'
|
||||
|
||||
var byName = []
|
||||
$.each(data_tmp, function(i, entry) {
|
||||
byName[entry[idAttr]] = entry
|
||||
})
|
||||
var tree = []
|
||||
$.each(data_tmp, function(i, entry) {
|
||||
var parents = entry[refAttr]
|
||||
if(!$.isArray(parents)){
|
||||
parents = [parents]
|
||||
}
|
||||
if(parents.length == 0){
|
||||
tree.push(entry)
|
||||
} else {
|
||||
var inTree = false;
|
||||
$.each(parents, function(i,parentID){
|
||||
var parent = byName[parentID]
|
||||
if (parent) {
|
||||
if (!parent[childrenAttr]) {
|
||||
parent[childrenAttr] = []
|
||||
}
|
||||
if($.inArray(entry, parent[childrenAttr])< 0)
|
||||
parent[childrenAttr].push(entry)
|
||||
inTree = true
|
||||
}
|
||||
})
|
||||
if(!inTree){
|
||||
tree.push(entry)
|
||||
}
|
||||
}
|
||||
})
|
||||
return tree
|
||||
}
|
||||
|
||||
function renderTree(tree, childrenAttr, idAttr, attrs, renderer, tableAttributes) {
|
||||
childrenAttr = childrenAttr || 'children'
|
||||
idAttr = idAttr || 'id'
|
||||
tableAttributes = tableAttributes || {}
|
||||
var maxLevel = 0;
|
||||
var ret = []
|
||||
|
||||
var table = $("<table>")
|
||||
$.each(tableAttributes, function(key, value){
|
||||
if(key == 'class' && value != 'jsTT') {
|
||||
table.addClass(value)
|
||||
} else {
|
||||
table.attr(key, value)
|
||||
}
|
||||
})
|
||||
var thead = $("<thead>")
|
||||
var tr = $("<tr>")
|
||||
var tbody = $("<tbody>")
|
||||
|
||||
table.append(thead)
|
||||
thead.append(tr)
|
||||
table.append(tbody)
|
||||
if (attrs) {
|
||||
$.each(attrs, function(attr, desc) {
|
||||
$(tr).append($('<th>' + desc + '</th>'))
|
||||
})
|
||||
} else {
|
||||
$(tr).append($('<th>' + idAttr + '</th>'))
|
||||
$.each(tree[0], function(key, value) {
|
||||
if (key != childrenAttr && key != idAttr)
|
||||
$(tr).append($('<th>' + key + '</th>'))
|
||||
})
|
||||
}
|
||||
|
||||
function render(node, parent) {
|
||||
var tr = $("<tr>")
|
||||
$(tr).attr('data-tt-id', node[idAttr])
|
||||
$(tr).attr('data-tt-level', node['data-tt-level'])
|
||||
if(!node[childrenAttr] || node[childrenAttr].length == 0)
|
||||
$(tr).attr('data-tt-isleaf', true)
|
||||
else
|
||||
$(tr).attr('data-tt-isnode', true)
|
||||
if (parent) {
|
||||
$(tr).attr('data-tt-parent-id', parent[idAttr])
|
||||
}
|
||||
if (renderer) {
|
||||
renderer($(tr), node)
|
||||
}else if (attrs) {
|
||||
$.each(attrs, function(attr, desc) {
|
||||
$(tr).append($('<td>' + node[attr] + '</td>'))
|
||||
})
|
||||
} else {
|
||||
$(tr).append($('<td>' + node[idAttr] + '</td>'))
|
||||
$.each(node, function(key, value) {
|
||||
if (key != childrenAttr && key != idAttr && key != 'data-tt-level')
|
||||
$(tr).append($('<td>' + value + '</td>'))
|
||||
})
|
||||
}
|
||||
tbody.append(tr)
|
||||
}
|
||||
|
||||
function i_renderTree(subTree, childrenAttr, level, parent) {
|
||||
maxLevel = Math.max(maxLevel, level)
|
||||
$.each(subTree, function(i, node) {
|
||||
node['data-tt-level'] = level
|
||||
render(node, parent)
|
||||
if (node[childrenAttr]) {
|
||||
$.each(node[childrenAttr], function(i, child) {
|
||||
i_renderTree([ child ], childrenAttr, level + 1, node)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
i_renderTree(tree, childrenAttr, 1)
|
||||
if (tree[0])
|
||||
tree[0].maxLevel = maxLevel
|
||||
return table
|
||||
}
|
||||
|
||||
function attr2attr(nodes, attrs){
|
||||
$.each(nodes, function(i, node) {
|
||||
$.each(attrs, function(j, at) {
|
||||
node[at] = $(node).attr(at)
|
||||
})
|
||||
})
|
||||
return nodes
|
||||
}
|
||||
|
||||
function treeTable(table){
|
||||
table.addClass('jsTT')
|
||||
table.expandLevel = function (n) {
|
||||
$("tr[data-tt-level]", table).each(function(index) {
|
||||
var level = parseInt($(this).attr('data-tt-level'))
|
||||
if (level > n-1) {
|
||||
this.trCollapse(true)
|
||||
} else if (level == n-1){
|
||||
this.trExpand(true)
|
||||
}
|
||||
})
|
||||
}
|
||||
function getLevel(node){
|
||||
var level = node.attr('data-tt-level')
|
||||
if(level != undefined ) return parseInt(level)
|
||||
var parentID = node.attr('data-tt-parent-id')
|
||||
if( parentID == undefined){
|
||||
return 0
|
||||
} else {
|
||||
return getLevel($('tr[data-tt-id="'+parentID+'"]', table).first()) + 1
|
||||
}
|
||||
}
|
||||
$("tr[data-tt-id]", table).each(function(i,node){
|
||||
node = $(node)
|
||||
node.attr('data-tt-level', getLevel(node))
|
||||
})
|
||||
var dat = $("tr[data-tt-level]", table).get()
|
||||
$.each(dat, function(j, d) {
|
||||
d.trChildrenVisible = true
|
||||
d.trChildren = []
|
||||
})
|
||||
dat = attr2attr(dat, ['data-tt-id', 'data-tt-parent-id'])
|
||||
dat = makeTree(dat, 'data-tt-id', 'data-tt-parent-id', 'trChildren')
|
||||
|
||||
var imgExpand = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAsTAAALEwEAmpwYAAAKT2lDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVNnVFPpFj333vRCS4iAlEtvUhUIIFJCi4AUkSYqIQkQSoghodkVUcERRUUEG8igiAOOjoCMFVEsDIoK2AfkIaKOg6OIisr74Xuja9a89+bN/rXXPues852zzwfACAyWSDNRNYAMqUIeEeCDx8TG4eQuQIEKJHAAEAizZCFz/SMBAPh+PDwrIsAHvgABeNMLCADATZvAMByH/w/qQplcAYCEAcB0kThLCIAUAEB6jkKmAEBGAYCdmCZTAKAEAGDLY2LjAFAtAGAnf+bTAICd+Jl7AQBblCEVAaCRACATZYhEAGg7AKzPVopFAFgwABRmS8Q5ANgtADBJV2ZIALC3AMDOEAuyAAgMADBRiIUpAAR7AGDIIyN4AISZABRG8lc88SuuEOcqAAB4mbI8uSQ5RYFbCC1xB1dXLh4ozkkXKxQ2YQJhmkAuwnmZGTKBNA/g88wAAKCRFRHgg/P9eM4Ors7ONo62Dl8t6r8G/yJiYuP+5c+rcEAAAOF0ftH+LC+zGoA7BoBt/qIl7gRoXgugdfeLZrIPQLUAoOnaV/Nw+H48PEWhkLnZ2eXk5NhKxEJbYcpXff5nwl/AV/1s+X48/Pf14L7iJIEyXYFHBPjgwsz0TKUcz5IJhGLc5o9H/LcL//wd0yLESWK5WCoU41EScY5EmozzMqUiiUKSKcUl0v9k4t8s+wM+3zUAsGo+AXuRLahdYwP2SycQWHTA4vcAAPK7b8HUKAgDgGiD4c93/+8//UegJQCAZkmScQAAXkQkLlTKsz/HCAAARKCBKrBBG/TBGCzABhzBBdzBC/xgNoRCJMTCQhBCCmSAHHJgKayCQiiGzbAdKmAv1EAdNMBRaIaTcA4uwlW4Dj1wD/phCJ7BKLyBCQRByAgTYSHaiAFiilgjjggXmYX4IcFIBBKLJCDJiBRRIkuRNUgxUopUIFVIHfI9cgI5h1xGupE7yAAygvyGvEcxlIGyUT3UDLVDuag3GoRGogvQZHQxmo8WoJvQcrQaPYw2oefQq2gP2o8+Q8cwwOgYBzPEbDAuxsNCsTgsCZNjy7EirAyrxhqwVqwDu4n1Y8+xdwQSgUXACTYEd0IgYR5BSFhMWE7YSKggHCQ0EdoJNwkDhFHCJyKTqEu0JroR+cQYYjIxh1hILCPWEo8TLxB7iEPENyQSiUMyJ7mQAkmxpFTSEtJG0m5SI+ksqZs0SBojk8naZGuyBzmULCAryIXkneTD5DPkG+Qh8lsKnWJAcaT4U+IoUspqShnlEOU05QZlmDJBVaOaUt2ooVQRNY9aQq2htlKvUYeoEzR1mjnNgxZJS6WtopXTGmgXaPdpr+h0uhHdlR5Ol9BX0svpR+iX6AP0dwwNhhWDx4hnKBmbGAcYZxl3GK+YTKYZ04sZx1QwNzHrmOeZD5lvVVgqtip8FZHKCpVKlSaVGyovVKmqpqreqgtV81XLVI+pXlN9rkZVM1PjqQnUlqtVqp1Q61MbU2epO6iHqmeob1Q/pH5Z/YkGWcNMw09DpFGgsV/jvMYgC2MZs3gsIWsNq4Z1gTXEJrHN2Xx2KruY/R27iz2qqaE5QzNKM1ezUvOUZj8H45hx+Jx0TgnnKKeX836K3hTvKeIpG6Y0TLkxZVxrqpaXllirSKtRq0frvTau7aedpr1Fu1n7gQ5Bx0onXCdHZ4/OBZ3nU9lT3acKpxZNPTr1ri6qa6UbobtEd79up+6Ynr5egJ5Mb6feeb3n+hx9L/1U/W36p/VHDFgGswwkBtsMzhg8xTVxbzwdL8fb8VFDXcNAQ6VhlWGX4YSRudE8o9VGjUYPjGnGXOMk423GbcajJgYmISZLTepN7ppSTbmmKaY7TDtMx83MzaLN1pk1mz0x1zLnm+eb15vft2BaeFostqi2uGVJsuRaplnutrxuhVo5WaVYVVpds0atna0l1rutu6cRp7lOk06rntZnw7Dxtsm2qbcZsOXYBtuutm22fWFnYhdnt8Wuw+6TvZN9un2N/T0HDYfZDqsdWh1+c7RyFDpWOt6azpzuP33F9JbpL2dYzxDP2DPjthPLKcRpnVOb00dnF2e5c4PziIuJS4LLLpc+Lpsbxt3IveRKdPVxXeF60vWdm7Obwu2o26/uNu5p7ofcn8w0nymeWTNz0MPIQ+BR5dE/C5+VMGvfrH5PQ0+BZ7XnIy9jL5FXrdewt6V3qvdh7xc+9j5yn+M+4zw33jLeWV/MN8C3yLfLT8Nvnl+F30N/I/9k/3r/0QCngCUBZwOJgUGBWwL7+Hp8Ib+OPzrbZfay2e1BjKC5QRVBj4KtguXBrSFoyOyQrSH355jOkc5pDoVQfujW0Adh5mGLw34MJ4WHhVeGP45wiFga0TGXNXfR3ENz30T6RJZE3ptnMU85ry1KNSo+qi5qPNo3ujS6P8YuZlnM1VidWElsSxw5LiquNm5svt/87fOH4p3iC+N7F5gvyF1weaHOwvSFpxapLhIsOpZATIhOOJTwQRAqqBaMJfITdyWOCnnCHcJnIi/RNtGI2ENcKh5O8kgqTXqS7JG8NXkkxTOlLOW5hCepkLxMDUzdmzqeFpp2IG0yPTq9MYOSkZBxQqohTZO2Z+pn5mZ2y6xlhbL+xW6Lty8elQfJa7OQrAVZLQq2QqboVFoo1yoHsmdlV2a/zYnKOZarnivN7cyzytuQN5zvn//tEsIS4ZK2pYZLVy0dWOa9rGo5sjxxedsK4xUFK4ZWBqw8uIq2Km3VT6vtV5eufr0mek1rgV7ByoLBtQFr6wtVCuWFfevc1+1dT1gvWd+1YfqGnRs+FYmKrhTbF5cVf9go3HjlG4dvyr+Z3JS0qavEuWTPZtJm6ebeLZ5bDpaql+aXDm4N2dq0Dd9WtO319kXbL5fNKNu7g7ZDuaO/PLi8ZafJzs07P1SkVPRU+lQ27tLdtWHX+G7R7ht7vPY07NXbW7z3/T7JvttVAVVN1WbVZftJ+7P3P66Jqun4lvttXa1ObXHtxwPSA/0HIw6217nU1R3SPVRSj9Yr60cOxx++/p3vdy0NNg1VjZzG4iNwRHnk6fcJ3/ceDTradox7rOEH0x92HWcdL2pCmvKaRptTmvtbYlu6T8w+0dbq3nr8R9sfD5w0PFl5SvNUyWna6YLTk2fyz4ydlZ19fi753GDborZ752PO32oPb++6EHTh0kX/i+c7vDvOXPK4dPKy2+UTV7hXmq86X23qdOo8/pPTT8e7nLuarrlca7nuer21e2b36RueN87d9L158Rb/1tWeOT3dvfN6b/fF9/XfFt1+cif9zsu72Xcn7q28T7xf9EDtQdlD3YfVP1v+3Njv3H9qwHeg89HcR/cGhYPP/pH1jw9DBY+Zj8uGDYbrnjg+OTniP3L96fynQ89kzyaeF/6i/suuFxYvfvjV69fO0ZjRoZfyl5O/bXyl/erA6xmv28bCxh6+yXgzMV70VvvtwXfcdx3vo98PT+R8IH8o/2j5sfVT0Kf7kxmTk/8EA5jz/GMzLdsAAAAgY0hSTQAAeiUAAICDAAD5/wAAgOkAAHUwAADqYAAAOpgAABdvkl/FRgAAAHlJREFUeNrcU1sNgDAQ6wgmcAM2MICGGlg1gJnNzWQcvwQGy1j4oUl/7tH0mpwzM7SgQyO+EZAUWh2MkkzSWhJwuRAlHYsJwEwyvs1gABDuzqoJcTw5qxaIJN0bgQRgIjnlmn1heSO5PE6Y2YXe+5Cr5+h++gs12AcAS6FS+7YOsj4AAAAASUVORK5CYII="
|
||||
var imgCollapse = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAsTAAALEwEAmpwYAAAKT2lDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVNnVFPpFj333vRCS4iAlEtvUhUIIFJCi4AUkSYqIQkQSoghodkVUcERRUUEG8igiAOOjoCMFVEsDIoK2AfkIaKOg6OIisr74Xuja9a89+bN/rXXPues852zzwfACAyWSDNRNYAMqUIeEeCDx8TG4eQuQIEKJHAAEAizZCFz/SMBAPh+PDwrIsAHvgABeNMLCADATZvAMByH/w/qQplcAYCEAcB0kThLCIAUAEB6jkKmAEBGAYCdmCZTAKAEAGDLY2LjAFAtAGAnf+bTAICd+Jl7AQBblCEVAaCRACATZYhEAGg7AKzPVopFAFgwABRmS8Q5ANgtADBJV2ZIALC3AMDOEAuyAAgMADBRiIUpAAR7AGDIIyN4AISZABRG8lc88SuuEOcqAAB4mbI8uSQ5RYFbCC1xB1dXLh4ozkkXKxQ2YQJhmkAuwnmZGTKBNA/g88wAAKCRFRHgg/P9eM4Ors7ONo62Dl8t6r8G/yJiYuP+5c+rcEAAAOF0ftH+LC+zGoA7BoBt/qIl7gRoXgugdfeLZrIPQLUAoOnaV/Nw+H48PEWhkLnZ2eXk5NhKxEJbYcpXff5nwl/AV/1s+X48/Pf14L7iJIEyXYFHBPjgwsz0TKUcz5IJhGLc5o9H/LcL//wd0yLESWK5WCoU41EScY5EmozzMqUiiUKSKcUl0v9k4t8s+wM+3zUAsGo+AXuRLahdYwP2SycQWHTA4vcAAPK7b8HUKAgDgGiD4c93/+8//UegJQCAZkmScQAAXkQkLlTKsz/HCAAARKCBKrBBG/TBGCzABhzBBdzBC/xgNoRCJMTCQhBCCmSAHHJgKayCQiiGzbAdKmAv1EAdNMBRaIaTcA4uwlW4Dj1wD/phCJ7BKLyBCQRByAgTYSHaiAFiilgjjggXmYX4IcFIBBKLJCDJiBRRIkuRNUgxUopUIFVIHfI9cgI5h1xGupE7yAAygvyGvEcxlIGyUT3UDLVDuag3GoRGogvQZHQxmo8WoJvQcrQaPYw2oefQq2gP2o8+Q8cwwOgYBzPEbDAuxsNCsTgsCZNjy7EirAyrxhqwVqwDu4n1Y8+xdwQSgUXACTYEd0IgYR5BSFhMWE7YSKggHCQ0EdoJNwkDhFHCJyKTqEu0JroR+cQYYjIxh1hILCPWEo8TLxB7iEPENyQSiUMyJ7mQAkmxpFTSEtJG0m5SI+ksqZs0SBojk8naZGuyBzmULCAryIXkneTD5DPkG+Qh8lsKnWJAcaT4U+IoUspqShnlEOU05QZlmDJBVaOaUt2ooVQRNY9aQq2htlKvUYeoEzR1mjnNgxZJS6WtopXTGmgXaPdpr+h0uhHdlR5Ol9BX0svpR+iX6AP0dwwNhhWDx4hnKBmbGAcYZxl3GK+YTKYZ04sZx1QwNzHrmOeZD5lvVVgqtip8FZHKCpVKlSaVGyovVKmqpqreqgtV81XLVI+pXlN9rkZVM1PjqQnUlqtVqp1Q61MbU2epO6iHqmeob1Q/pH5Z/YkGWcNMw09DpFGgsV/jvMYgC2MZs3gsIWsNq4Z1gTXEJrHN2Xx2KruY/R27iz2qqaE5QzNKM1ezUvOUZj8H45hx+Jx0TgnnKKeX836K3hTvKeIpG6Y0TLkxZVxrqpaXllirSKtRq0frvTau7aedpr1Fu1n7gQ5Bx0onXCdHZ4/OBZ3nU9lT3acKpxZNPTr1ri6qa6UbobtEd79up+6Ynr5egJ5Mb6feeb3n+hx9L/1U/W36p/VHDFgGswwkBtsMzhg8xTVxbzwdL8fb8VFDXcNAQ6VhlWGX4YSRudE8o9VGjUYPjGnGXOMk423GbcajJgYmISZLTepN7ppSTbmmKaY7TDtMx83MzaLN1pk1mz0x1zLnm+eb15vft2BaeFostqi2uGVJsuRaplnutrxuhVo5WaVYVVpds0atna0l1rutu6cRp7lOk06rntZnw7Dxtsm2qbcZsOXYBtuutm22fWFnYhdnt8Wuw+6TvZN9un2N/T0HDYfZDqsdWh1+c7RyFDpWOt6azpzuP33F9JbpL2dYzxDP2DPjthPLKcRpnVOb00dnF2e5c4PziIuJS4LLLpc+Lpsbxt3IveRKdPVxXeF60vWdm7Obwu2o26/uNu5p7ofcn8w0nymeWTNz0MPIQ+BR5dE/C5+VMGvfrH5PQ0+BZ7XnIy9jL5FXrdewt6V3qvdh7xc+9j5yn+M+4zw33jLeWV/MN8C3yLfLT8Nvnl+F30N/I/9k/3r/0QCngCUBZwOJgUGBWwL7+Hp8Ib+OPzrbZfay2e1BjKC5QRVBj4KtguXBrSFoyOyQrSH355jOkc5pDoVQfujW0Adh5mGLw34MJ4WHhVeGP45wiFga0TGXNXfR3ENz30T6RJZE3ptnMU85ry1KNSo+qi5qPNo3ujS6P8YuZlnM1VidWElsSxw5LiquNm5svt/87fOH4p3iC+N7F5gvyF1weaHOwvSFpxapLhIsOpZATIhOOJTwQRAqqBaMJfITdyWOCnnCHcJnIi/RNtGI2ENcKh5O8kgqTXqS7JG8NXkkxTOlLOW5hCepkLxMDUzdmzqeFpp2IG0yPTq9MYOSkZBxQqohTZO2Z+pn5mZ2y6xlhbL+xW6Lty8elQfJa7OQrAVZLQq2QqboVFoo1yoHsmdlV2a/zYnKOZarnivN7cyzytuQN5zvn//tEsIS4ZK2pYZLVy0dWOa9rGo5sjxxedsK4xUFK4ZWBqw8uIq2Km3VT6vtV5eufr0mek1rgV7ByoLBtQFr6wtVCuWFfevc1+1dT1gvWd+1YfqGnRs+FYmKrhTbF5cVf9go3HjlG4dvyr+Z3JS0qavEuWTPZtJm6ebeLZ5bDpaql+aXDm4N2dq0Dd9WtO319kXbL5fNKNu7g7ZDuaO/PLi8ZafJzs07P1SkVPRU+lQ27tLdtWHX+G7R7ht7vPY07NXbW7z3/T7JvttVAVVN1WbVZftJ+7P3P66Jqun4lvttXa1ObXHtxwPSA/0HIw6217nU1R3SPVRSj9Yr60cOxx++/p3vdy0NNg1VjZzG4iNwRHnk6fcJ3/ceDTradox7rOEH0x92HWcdL2pCmvKaRptTmvtbYlu6T8w+0dbq3nr8R9sfD5w0PFl5SvNUyWna6YLTk2fyz4ydlZ19fi753GDborZ752PO32oPb++6EHTh0kX/i+c7vDvOXPK4dPKy2+UTV7hXmq86X23qdOo8/pPTT8e7nLuarrlca7nuer21e2b36RueN87d9L158Rb/1tWeOT3dvfN6b/fF9/XfFt1+cif9zsu72Xcn7q28T7xf9EDtQdlD3YfVP1v+3Njv3H9qwHeg89HcR/cGhYPP/pH1jw9DBY+Zj8uGDYbrnjg+OTniP3L96fynQ89kzyaeF/6i/suuFxYvfvjV69fO0ZjRoZfyl5O/bXyl/erA6xmv28bCxh6+yXgzMV70VvvtwXfcdx3vo98PT+R8IH8o/2j5sfVT0Kf7kxmTk/8EA5jz/GMzLdsAAAAgY0hSTQAAeiUAAICDAAD5/wAAgOkAAHUwAADqYAAAOpgAABdvkl/FRgAAAHFJREFUeNpi/P//PwMlgImBQsA44C6gvhfa29v3MzAwOODRc6CystIRbxi0t7fjDJjKykpGYrwwi1hxnLHQ3t7+jIGBQRJJ6HllZaUUKYEYRYBPOB0gBShKwKGA////48VtbW3/8clTnBIH3gCKkzJgAGvBX0dDm0sCAAAAAElFTkSuQmCC"
|
||||
$("tr[data-tt-level]", table).each(function(index, tr) {
|
||||
var level = $(tr).attr('data-tt-level')
|
||||
var td = $("td",tr).first()
|
||||
if(tr.trChildren.length>0){
|
||||
td.prepend($('<img id="state" style="cursor:pointer" src="'+imgCollapse+'"/>'))
|
||||
} else {
|
||||
td.prepend($('<span style="padding-left:16px;" /></span>'))
|
||||
}
|
||||
td.prepend($('<span style="padding-left:'+(15*parseInt(level-1))+'px;" /></span>'))
|
||||
// td.css('white-space','nowrap')
|
||||
tr.trExpand = function(changeState){
|
||||
if(this.trChildren.length < 1) return
|
||||
if(changeState) {
|
||||
this.trChildrenVisible = true
|
||||
$('#state', this).get(0).src= imgCollapse
|
||||
}
|
||||
var doit = changeState || this.trChildrenVisible
|
||||
$.each(this.trChildren, function(i, ctr) {
|
||||
if(doit) $(ctr).css('display', 'table-row')
|
||||
ctr.trExpand()
|
||||
})
|
||||
}
|
||||
tr.trCollapse = function(changeState){
|
||||
if(this.trChildren.length < 1) return
|
||||
if(changeState) {
|
||||
this.trChildrenVisible = false
|
||||
$('#state', this).get(0).src= imgExpand
|
||||
}
|
||||
$.each(this.trChildren, function(i, ctr) {
|
||||
$(ctr).css('display', 'none')
|
||||
ctr.trCollapse()
|
||||
})
|
||||
}
|
||||
$(tr).click(function() {
|
||||
this.trChildrenVisible ? this.trCollapse(true) : this.trExpand(true)
|
||||
})
|
||||
})
|
||||
return table
|
||||
}
|
||||
|
||||
function appendTreetable(tree, options) {
|
||||
function inALine(nodes) {
|
||||
var tr = $('<tr>')
|
||||
$.each(nodes, function(i, node){
|
||||
tr.append($('<td style="padding-right: 20px;">').append(node))
|
||||
})
|
||||
return $('<table border="0"/>').append(tr)
|
||||
|
||||
}
|
||||
options = options || {}
|
||||
options.idAttr = (options.idAttr || 'id')
|
||||
options.childrenAttr = (options.childrenAttr || 'children')
|
||||
var controls = (options.controls || [])
|
||||
|
||||
if (!options.mountPoint)
|
||||
options.mountPoint = $('body')
|
||||
|
||||
if (options.depthFirst)
|
||||
depthFirst(tree, options.depthFirst, options.childrenAttr)
|
||||
var rendered = renderTree(tree, options.childrenAttr, options.idAttr,
|
||||
options.renderedAttr, options.renderer, options.tableAttributes)
|
||||
|
||||
treeTable(rendered)
|
||||
if (options.replaceContent) {
|
||||
options.mountPoint.html('')
|
||||
}
|
||||
var initialExpandLevel = options.initialExpandLevel ? parseInt(options.initialExpandLevel) : -1
|
||||
initialExpandLevel = Math.min(initialExpandLevel, tree[0].maxLevel)
|
||||
rendered.expandLevel(initialExpandLevel)
|
||||
if(options.slider){
|
||||
var slider = $('<div style="margin-right: 15px;">')
|
||||
slider.width('200px')
|
||||
slider.slider({
|
||||
min : 1,
|
||||
max : tree[0].maxLevel,
|
||||
range : "min",
|
||||
value : initialExpandLevel,
|
||||
slide : function(event, ui) {
|
||||
rendered.expandLevel(ui.value)
|
||||
}
|
||||
})
|
||||
controls = [slider].concat(options.controls)
|
||||
}
|
||||
|
||||
if(controls.length >0){
|
||||
options.mountPoint.append(inALine(controls))
|
||||
}
|
||||
options.mountPoint.append(rendered)
|
||||
return rendered
|
||||
}
|
||||
|
||||
return {
|
||||
depthFirst : depthFirst,
|
||||
makeTree : makeTree,
|
||||
renderTree : renderTree,
|
||||
attr2attr : attr2attr,
|
||||
treeTable : treeTable,
|
||||
appendTreetable : appendTreetable,
|
||||
jsTreeTable : '1.0',
|
||||
register : function(target){
|
||||
$.each(this, function(key, value){ if(key != 'register') target[key] = value})
|
||||
}
|
||||
}
|
||||
})();
|
||||
@@ -0,0 +1,12 @@
|
||||
|
||||
var app = angular.module('sentinelDashboardApp');
|
||||
|
||||
app.service('AppService', ['$http', function ($http) {
|
||||
this.getApps = function () {
|
||||
return $http({
|
||||
// url: 'app/mock_infos',
|
||||
url: 'app/briefinfos.json',
|
||||
method: 'GET'
|
||||
});
|
||||
};
|
||||
}]);
|
||||
@@ -0,0 +1,25 @@
|
||||
var app = angular.module('sentinelDashboardApp');
|
||||
|
||||
app.service('AuthService', ['$http', function ($http) {
|
||||
this.check = function () {
|
||||
return $http({
|
||||
url: '/auth/check',
|
||||
method: 'POST'
|
||||
});
|
||||
};
|
||||
|
||||
this.login = function (param) {
|
||||
return $http({
|
||||
url: '/auth/login',
|
||||
params: param,
|
||||
method: 'POST'
|
||||
});
|
||||
};
|
||||
|
||||
this.logout = function () {
|
||||
return $http({
|
||||
url: '/auth/logout',
|
||||
method: 'POST'
|
||||
});
|
||||
};
|
||||
}]);
|
||||
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
* Authority rule service.
|
||||
*/
|
||||
angular.module('sentinelDashboardApp').service('AuthorityRuleService', ['$http', function ($http) {
|
||||
this.queryMachineRules = function(app, ip, port) {
|
||||
var param = {
|
||||
app: app,
|
||||
ip: ip,
|
||||
port: port
|
||||
};
|
||||
return $http({
|
||||
url: '/authority/rules',
|
||||
params: param,
|
||||
method: 'GET'
|
||||
});
|
||||
};
|
||||
|
||||
this.addNewRule = function(rule) {
|
||||
return $http({
|
||||
url: '/authority/rule',
|
||||
data: rule,
|
||||
method: 'POST'
|
||||
});
|
||||
};
|
||||
|
||||
this.saveRule = function (entity) {
|
||||
return $http({
|
||||
url: '/authority/rule/' + entity.id,
|
||||
data: entity,
|
||||
method: 'PUT'
|
||||
});
|
||||
};
|
||||
|
||||
this.deleteRule = function (entity) {
|
||||
return $http({
|
||||
url: '/authority/rule/' + entity.id,
|
||||
method: 'DELETE'
|
||||
});
|
||||
};
|
||||
|
||||
this.checkRuleValid = function checkRuleValid(rule) {
|
||||
if (rule.resource === undefined || rule.resource === '') {
|
||||
alert('资源名称不能为空');
|
||||
return false;
|
||||
}
|
||||
if (rule.limitApp === undefined || rule.limitApp === '') {
|
||||
alert('流控针对应用不能为空');
|
||||
return false;
|
||||
}
|
||||
if (rule.strategy === undefined) {
|
||||
alert('必须选择黑白名单模式');
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
}]);
|
||||
@@ -0,0 +1,73 @@
|
||||
/**
|
||||
* Cluster state control service.
|
||||
*
|
||||
* @author Eric Zhao
|
||||
*/
|
||||
angular.module('sentinelDashboardApp').service('ClusterStateService', ['$http', function ($http) {
|
||||
|
||||
this.fetchClusterUniversalStateSingle = function(app, ip, port) {
|
||||
var param = {
|
||||
app: app,
|
||||
ip: ip,
|
||||
port: port
|
||||
};
|
||||
return $http({
|
||||
url: '/cluster/state_single',
|
||||
params: param,
|
||||
method: 'GET'
|
||||
});
|
||||
};
|
||||
|
||||
this.fetchClusterUniversalStateOfApp = function(app) {
|
||||
return $http({
|
||||
url: '/cluster/state/' + app,
|
||||
method: 'GET'
|
||||
});
|
||||
};
|
||||
|
||||
this.fetchClusterServerStateOfApp = function(app) {
|
||||
return $http({
|
||||
url: '/cluster/server_state/' + app,
|
||||
method: 'GET'
|
||||
});
|
||||
};
|
||||
|
||||
this.fetchClusterClientStateOfApp = function(app) {
|
||||
return $http({
|
||||
url: '/cluster/client_state/' + app,
|
||||
method: 'GET'
|
||||
});
|
||||
};
|
||||
|
||||
this.modifyClusterConfig = function(config) {
|
||||
return $http({
|
||||
url: '/cluster/config/modify_single',
|
||||
data: config,
|
||||
method: 'POST'
|
||||
});
|
||||
};
|
||||
|
||||
this.applyClusterFullAssignOfApp = function(app, clusterMap) {
|
||||
return $http({
|
||||
url: '/cluster/assign/all_server/' + app,
|
||||
data: clusterMap,
|
||||
method: 'POST'
|
||||
});
|
||||
};
|
||||
|
||||
this.applyClusterSingleServerAssignOfApp = function(app, request) {
|
||||
return $http({
|
||||
url: '/cluster/assign/single_server/' + app,
|
||||
data: request,
|
||||
method: 'POST'
|
||||
});
|
||||
};
|
||||
|
||||
this.applyClusterServerBatchUnbind = function(app, machineSet) {
|
||||
return $http({
|
||||
url: '/cluster/assign/unbind_server/' + app,
|
||||
data: machineSet,
|
||||
method: 'POST'
|
||||
});
|
||||
};
|
||||
}]);
|
||||
@@ -0,0 +1,97 @@
|
||||
var app = angular.module('sentinelDashboardApp');
|
||||
|
||||
app.service('DegradeService', ['$http', function ($http) {
|
||||
this.queryMachineRules = function (app, ip, port) {
|
||||
var param = {
|
||||
app: app,
|
||||
ip: ip,
|
||||
port: port
|
||||
};
|
||||
return $http({
|
||||
url: 'degrade/rules.json',
|
||||
params: param,
|
||||
method: 'GET'
|
||||
});
|
||||
};
|
||||
|
||||
this.newRule = function (rule) {
|
||||
return $http({
|
||||
url: '/degrade/rule',
|
||||
data: rule,
|
||||
method: 'POST'
|
||||
});
|
||||
};
|
||||
|
||||
this.saveRule = function (rule) {
|
||||
var param = {
|
||||
id: rule.id,
|
||||
resource: rule.resource,
|
||||
limitApp: rule.limitApp,
|
||||
grade: rule.grade,
|
||||
count: rule.count,
|
||||
timeWindow: rule.timeWindow,
|
||||
statIntervalMs: rule.statIntervalMs,
|
||||
minRequestAmount: rule.minRequestAmount,
|
||||
slowRatioThreshold: rule.slowRatioThreshold,
|
||||
};
|
||||
return $http({
|
||||
url: '/degrade/rule/' + rule.id,
|
||||
data: param,
|
||||
method: 'PUT'
|
||||
});
|
||||
};
|
||||
|
||||
this.deleteRule = function (rule) {
|
||||
return $http({
|
||||
url: '/degrade/rule/' + rule.id,
|
||||
method: 'DELETE'
|
||||
});
|
||||
};
|
||||
|
||||
this.checkRuleValid = function (rule) {
|
||||
if (rule.resource === undefined || rule.resource === '') {
|
||||
alert('资源名称不能为空');
|
||||
return false;
|
||||
}
|
||||
if (rule.grade === undefined || rule.grade < 0) {
|
||||
alert('未知的降级策略');
|
||||
return false;
|
||||
}
|
||||
if (rule.count === undefined || rule.count === '' || rule.count < 0) {
|
||||
alert('降级阈值不能为空或小于 0');
|
||||
return false;
|
||||
}
|
||||
if (rule.timeWindow == undefined || rule.timeWindow === '' || rule.timeWindow <= 0) {
|
||||
alert('熔断时长必须大于 0s');
|
||||
return false;
|
||||
}
|
||||
if (rule.minRequestAmount == undefined || rule.minRequestAmount <= 0) {
|
||||
alert('最小请求数目需大于 0');
|
||||
return false;
|
||||
}
|
||||
if (rule.statIntervalMs == undefined || rule.statIntervalMs <= 0) {
|
||||
alert('统计窗口时长需大于 0s');
|
||||
return false;
|
||||
}
|
||||
if (rule.statIntervalMs !== undefined && rule.statIntervalMs > 60 * 1000 * 2) {
|
||||
alert('统计窗口时长不能超过 120 分钟');
|
||||
return false;
|
||||
}
|
||||
// 异常比率类型.
|
||||
if (rule.grade == 1 && rule.count > 1) {
|
||||
alert('异常比率超出范围:[0.0 - 1.0]');
|
||||
return false;
|
||||
}
|
||||
if (rule.grade == 0) {
|
||||
if (rule.slowRatioThreshold == undefined) {
|
||||
alert('慢调用比率不能为空');
|
||||
return false;
|
||||
}
|
||||
if (rule.slowRatioThreshold < 0 || rule.slowRatioThreshold > 1) {
|
||||
alert('慢调用比率超出范围:[0.0 - 1.0]');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
}]);
|
||||
@@ -0,0 +1,119 @@
|
||||
var app = angular.module('sentinelDashboardApp');
|
||||
|
||||
app.service('FlowServiceV1', ['$http', function ($http) {
|
||||
this.queryMachineRules = function (app, ip, port) {
|
||||
var param = {
|
||||
app: app,
|
||||
ip: ip,
|
||||
port: port
|
||||
};
|
||||
return $http({
|
||||
url: '/v1/flow/rules',
|
||||
params: param,
|
||||
method: 'GET'
|
||||
});
|
||||
};
|
||||
|
||||
this.newRule = function (rule) {
|
||||
var param = {
|
||||
resource: rule.resource,
|
||||
limitApp: rule.limitApp,
|
||||
grade: rule.grade,
|
||||
count: rule.count,
|
||||
strategy: rule.strategy,
|
||||
refResource: rule.refResource,
|
||||
controlBehavior: rule.controlBehavior,
|
||||
warmUpPeriodSec: rule.warmUpPeriodSec,
|
||||
maxQueueingTimeMs: rule.maxQueueingTimeMs,
|
||||
app: rule.app,
|
||||
ip: rule.ip,
|
||||
port: rule.port
|
||||
};
|
||||
|
||||
return $http({
|
||||
url: '/v1/flow/rule',
|
||||
data: rule,
|
||||
method: 'POST'
|
||||
});
|
||||
};
|
||||
|
||||
this.saveRule = function (rule) {
|
||||
var param = {
|
||||
id: rule.id,
|
||||
resource: rule.resource,
|
||||
limitApp: rule.limitApp,
|
||||
grade: rule.grade,
|
||||
count: rule.count,
|
||||
strategy: rule.strategy,
|
||||
refResource: rule.refResource,
|
||||
controlBehavior: rule.controlBehavior,
|
||||
warmUpPeriodSec: rule.warmUpPeriodSec,
|
||||
maxQueueingTimeMs: rule.maxQueueingTimeMs,
|
||||
};
|
||||
|
||||
return $http({
|
||||
url: '/v1/flow/save.json',
|
||||
params: param,
|
||||
method: 'PUT'
|
||||
});
|
||||
};
|
||||
|
||||
this.deleteRule = function (rule) {
|
||||
var param = {
|
||||
id: rule.id,
|
||||
app: rule.app
|
||||
};
|
||||
|
||||
return $http({
|
||||
url: '/v1/flow/delete.json',
|
||||
params: param,
|
||||
method: 'DELETE'
|
||||
});
|
||||
};
|
||||
|
||||
function notNumberAtLeastZero(num) {
|
||||
return num === undefined || num === '' || isNaN(num) || num < 0;
|
||||
}
|
||||
|
||||
function notNumberGreaterThanZero(num) {
|
||||
return num === undefined || num === '' || isNaN(num) || num <= 0;
|
||||
}
|
||||
|
||||
this.checkRuleValid = function (rule) {
|
||||
if (rule.resource === undefined || rule.resource === '') {
|
||||
alert('资源名称不能为空');
|
||||
return false;
|
||||
}
|
||||
if (rule.count === undefined || rule.count < 0) {
|
||||
alert('限流阈值必须大于等于 0');
|
||||
return false;
|
||||
}
|
||||
if (rule.strategy === undefined || rule.strategy < 0) {
|
||||
alert('无效的流控模式');
|
||||
return false;
|
||||
}
|
||||
if (rule.strategy == 1 || rule.strategy == 2) {
|
||||
if (rule.refResource === undefined || rule.refResource == '') {
|
||||
alert('请填写关联资源或入口');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (rule.controlBehavior === undefined || rule.controlBehavior < 0) {
|
||||
alert('无效的流控整形方式');
|
||||
return false;
|
||||
}
|
||||
if (rule.controlBehavior == 1 && notNumberGreaterThanZero(rule.warmUpPeriodSec)) {
|
||||
alert('预热时长必须大于 0');
|
||||
return false;
|
||||
}
|
||||
if (rule.controlBehavior == 2 && notNumberGreaterThanZero(rule.maxQueueingTimeMs)) {
|
||||
alert('排队超时时间必须大于 0');
|
||||
return false;
|
||||
}
|
||||
if (rule.clusterMode && (rule.clusterConfig === undefined || rule.clusterConfig.thresholdType === undefined)) {
|
||||
alert('集群限流配置不正确');
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
}]);
|
||||
@@ -0,0 +1,85 @@
|
||||
var app = angular.module('sentinelDashboardApp');
|
||||
|
||||
app.service('FlowServiceV2', ['$http', function ($http) {
|
||||
this.queryMachineRules = function (app, ip, port) {
|
||||
var param = {
|
||||
app: app,
|
||||
ip: ip,
|
||||
port: port
|
||||
};
|
||||
return $http({
|
||||
url: '/v2/flow/rules',
|
||||
params: param,
|
||||
method: 'GET'
|
||||
});
|
||||
};
|
||||
|
||||
this.newRule = function (rule) {
|
||||
return $http({
|
||||
url: '/v2/flow/rule',
|
||||
data: rule,
|
||||
method: 'POST'
|
||||
});
|
||||
};
|
||||
|
||||
this.saveRule = function (rule) {
|
||||
return $http({
|
||||
url: '/v2/flow/rule/' + rule.id,
|
||||
data: rule,
|
||||
method: 'PUT'
|
||||
});
|
||||
};
|
||||
|
||||
this.deleteRule = function (rule) {
|
||||
return $http({
|
||||
url: '/v2/flow/rule/' + rule.id,
|
||||
method: 'DELETE'
|
||||
});
|
||||
};
|
||||
|
||||
function notNumberAtLeastZero(num) {
|
||||
return num === undefined || num === '' || isNaN(num) || num < 0;
|
||||
}
|
||||
|
||||
function notNumberGreaterThanZero(num) {
|
||||
return num === undefined || num === '' || isNaN(num) || num <= 0;
|
||||
}
|
||||
|
||||
this.checkRuleValid = function (rule) {
|
||||
if (rule.resource === undefined || rule.resource === '') {
|
||||
alert('资源名称不能为空');
|
||||
return false;
|
||||
}
|
||||
if (rule.count === undefined || rule.count < 0) {
|
||||
alert('限流阈值必须大于等于 0');
|
||||
return false;
|
||||
}
|
||||
if (rule.strategy === undefined || rule.strategy < 0) {
|
||||
alert('无效的流控模式');
|
||||
return false;
|
||||
}
|
||||
if (rule.strategy == 1 || rule.strategy == 2) {
|
||||
if (rule.refResource === undefined || rule.refResource == '') {
|
||||
alert('请填写关联资源或入口');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (rule.controlBehavior === undefined || rule.controlBehavior < 0) {
|
||||
alert('无效的流控整形方式');
|
||||
return false;
|
||||
}
|
||||
if (rule.controlBehavior == 1 && notNumberGreaterThanZero(rule.warmUpPeriodSec)) {
|
||||
alert('预热时长必须大于 0');
|
||||
return false;
|
||||
}
|
||||
if (rule.controlBehavior == 2 && notNumberGreaterThanZero(rule.maxQueueingTimeMs)) {
|
||||
alert('排队超时时间必须大于 0');
|
||||
return false;
|
||||
}
|
||||
if (rule.clusterMode && (rule.clusterConfig === undefined || rule.clusterConfig.thresholdType === undefined)) {
|
||||
alert('集群限流配置不正确');
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
}]);
|
||||
@@ -0,0 +1,73 @@
|
||||
var app = angular.module('sentinelDashboardApp');
|
||||
|
||||
app.service('GatewayApiService', ['$http', function ($http) {
|
||||
this.queryApis = function (app, ip, port) {
|
||||
var param = {
|
||||
app: app,
|
||||
ip: ip,
|
||||
port: port
|
||||
};
|
||||
return $http({
|
||||
url: '/gateway/api/list.json',
|
||||
params: param,
|
||||
method: 'GET'
|
||||
});
|
||||
};
|
||||
|
||||
this.newApi = function (api) {
|
||||
return $http({
|
||||
url: '/gateway/api/new.json',
|
||||
data: api,
|
||||
method: 'POST'
|
||||
});
|
||||
};
|
||||
|
||||
this.saveApi = function (api) {
|
||||
return $http({
|
||||
url: '/gateway/api/save.json',
|
||||
data: api,
|
||||
method: 'POST'
|
||||
});
|
||||
};
|
||||
|
||||
this.deleteApi = function (api) {
|
||||
var param = {
|
||||
id: api.id,
|
||||
app: api.app
|
||||
};
|
||||
return $http({
|
||||
url: '/gateway/api/delete.json',
|
||||
params: param,
|
||||
method: 'POST'
|
||||
});
|
||||
};
|
||||
|
||||
this.checkApiValid = function (api, apiNames) {
|
||||
if (api.apiName === undefined || api.apiName === '') {
|
||||
alert('API名称不能为空');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (api.predicateItems == null || api.predicateItems.length === 0) {
|
||||
// Should never happen since no remove button will display when only one predicateItem.
|
||||
alert('至少有一个匹配规则');
|
||||
return false;
|
||||
}
|
||||
|
||||
for (var i = 0; i < api.predicateItems.length; i++) {
|
||||
var predicateItem = api.predicateItems[i];
|
||||
var pattern = predicateItem.pattern;
|
||||
if (pattern === undefined || pattern === '') {
|
||||
alert('匹配串不能为空,请检查');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (apiNames.indexOf(api.apiName) !== -1) {
|
||||
alert('API名称(' + api.apiName + ')已存在');
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
}]);
|
||||
@@ -0,0 +1,76 @@
|
||||
var app = angular.module('sentinelDashboardApp');
|
||||
|
||||
app.service('GatewayFlowService', ['$http', function ($http) {
|
||||
this.queryRules = function (app, ip, port) {
|
||||
var param = {
|
||||
app: app,
|
||||
ip: ip,
|
||||
port: port
|
||||
};
|
||||
|
||||
return $http({
|
||||
url: '/gateway/flow/list.json',
|
||||
params: param,
|
||||
method: 'GET'
|
||||
});
|
||||
};
|
||||
|
||||
this.newRule = function (rule) {
|
||||
return $http({
|
||||
url: '/gateway/flow/new.json',
|
||||
data: rule,
|
||||
method: 'POST'
|
||||
});
|
||||
};
|
||||
|
||||
this.saveRule = function (rule) {
|
||||
return $http({
|
||||
url: '/gateway/flow/save.json',
|
||||
data: rule,
|
||||
method: 'POST'
|
||||
});
|
||||
};
|
||||
|
||||
this.deleteRule = function (rule) {
|
||||
var param = {
|
||||
id: rule.id,
|
||||
app: rule.app
|
||||
};
|
||||
|
||||
return $http({
|
||||
url: '/gateway/flow/delete.json',
|
||||
params: param,
|
||||
method: 'POST'
|
||||
});
|
||||
};
|
||||
|
||||
this.checkRuleValid = function (rule) {
|
||||
if (rule.resource === undefined || rule.resource === '') {
|
||||
alert('API名称不能为空');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (rule.paramItem != null) {
|
||||
if (rule.paramItem.parseStrategy == 2 ||
|
||||
rule.paramItem.parseStrategy == 3 ||
|
||||
rule.paramItem.parseStrategy == 4) {
|
||||
if (rule.paramItem.fieldName === undefined || rule.paramItem.fieldName === '') {
|
||||
alert('当参数属性为Header、URL参数、Cookie时,参数名称不能为空');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (rule.paramItem.pattern === '') {
|
||||
alert('匹配串不能为空');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (rule.count === undefined || rule.count < 0) {
|
||||
alert((rule.grade === 1 ? 'QPS阈值' : '线程数') + '必须大于等于 0');
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
}]);
|
||||
@@ -0,0 +1,30 @@
|
||||
var app = angular.module('sentinelDashboardApp');
|
||||
|
||||
app.service('IdentityService', ['$http', function ($http) {
|
||||
|
||||
this.fetchIdentityOfMachine = function (ip, port, searchKey) {
|
||||
var param = {
|
||||
ip: ip,
|
||||
port: port,
|
||||
searchKey: searchKey
|
||||
};
|
||||
return $http({
|
||||
url: 'resource/machineResource.json',
|
||||
params: param,
|
||||
method: 'GET'
|
||||
});
|
||||
};
|
||||
this.fetchClusterNodeOfMachine = function (ip, port, searchKey) {
|
||||
var param = {
|
||||
ip: ip,
|
||||
port: port,
|
||||
type: 'cluster',
|
||||
searchKey: searchKey
|
||||
};
|
||||
return $http({
|
||||
url: 'resource/machineResource.json',
|
||||
params: param,
|
||||
method: 'GET'
|
||||
});
|
||||
};
|
||||
}]);
|
||||
@@ -0,0 +1,25 @@
|
||||
var app = angular.module('sentinelDashboardApp');
|
||||
|
||||
app.service('MachineService', ['$http', '$httpParamSerializerJQLike',
|
||||
function ($http, $httpParamSerializerJQLike) {
|
||||
this.getAppMachines = function (app) {
|
||||
return $http({
|
||||
url: 'app/' + app + '/machines.json',
|
||||
method: 'GET'
|
||||
});
|
||||
};
|
||||
this.removeAppMachine = function (app, ip, port) {
|
||||
return $http({
|
||||
url: 'app/' + app + '/machine/remove.json',
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-type': 'application/x-www-form-urlencoded; charset=UTF-8'
|
||||
},
|
||||
data: $httpParamSerializerJQLike({
|
||||
ip: ip,
|
||||
port: port
|
||||
})
|
||||
});
|
||||
};
|
||||
}]
|
||||
);
|
||||
@@ -0,0 +1,36 @@
|
||||
var app = angular.module('sentinelDashboardApp');
|
||||
|
||||
app.service('MetricService', ['$http', function ($http) {
|
||||
|
||||
this.queryAppSortedIdentities = function (params) {
|
||||
return $http({
|
||||
url: '/metric/queryTopResourceMetric.json',
|
||||
params: params,
|
||||
method: 'GET'
|
||||
});
|
||||
};
|
||||
|
||||
this.queryByAppAndIdentity = function (params) {
|
||||
return $http({
|
||||
url: '/metric/queryByAppAndResource.json',
|
||||
params: params,
|
||||
method: 'GET'
|
||||
});
|
||||
};
|
||||
|
||||
this.queryByMachineAndIdentity = function (ip, port, identity, startTime, endTime) {
|
||||
var param = {
|
||||
ip: ip,
|
||||
port: port,
|
||||
identity: identity,
|
||||
startTime: startTime.getTime(),
|
||||
endTime: endTime.getTime()
|
||||
};
|
||||
|
||||
return $http({
|
||||
url: '/metric/queryByAppAndResource.json',
|
||||
params: param,
|
||||
method: 'GET'
|
||||
});
|
||||
};
|
||||
}]);
|
||||
@@ -0,0 +1,104 @@
|
||||
/**
|
||||
* Parameter flow control service.
|
||||
*
|
||||
* @author Eric Zhao
|
||||
*/
|
||||
angular.module('sentinelDashboardApp').service('ParamFlowService', ['$http', function ($http) {
|
||||
this.queryMachineRules = function(app, ip, port) {
|
||||
var param = {
|
||||
app: app,
|
||||
ip: ip,
|
||||
port: port
|
||||
};
|
||||
return $http({
|
||||
url: '/paramFlow/rules',
|
||||
params: param,
|
||||
method: 'GET'
|
||||
});
|
||||
};
|
||||
|
||||
this.addNewRule = function(rule) {
|
||||
return $http({
|
||||
url: '/paramFlow/rule',
|
||||
data: rule,
|
||||
method: 'POST'
|
||||
});
|
||||
};
|
||||
|
||||
this.saveRule = function (entity) {
|
||||
return $http({
|
||||
url: '/paramFlow/rule/' + entity.id,
|
||||
data: entity,
|
||||
method: 'PUT'
|
||||
});
|
||||
};
|
||||
|
||||
this.deleteRule = function (entity) {
|
||||
return $http({
|
||||
url: '/paramFlow/rule/' + entity.id,
|
||||
method: 'DELETE'
|
||||
});
|
||||
};
|
||||
|
||||
function isNumberClass(classType) {
|
||||
return classType === 'int' || classType === 'double' ||
|
||||
classType === 'float' || classType === 'long' || classType === 'short';
|
||||
}
|
||||
|
||||
function isByteClass(classType) {
|
||||
return classType === 'byte';
|
||||
}
|
||||
|
||||
function notNumberAtLeastZero(num) {
|
||||
return num === undefined || num === '' || isNaN(num) || num < 0;
|
||||
}
|
||||
|
||||
function notGoodNumber(num) {
|
||||
return num === undefined || num === '' || isNaN(num);
|
||||
}
|
||||
|
||||
function notGoodNumberBetweenExclusive(num, l ,r) {
|
||||
return num === undefined || num === '' || isNaN(num) || num < l || num > r;
|
||||
}
|
||||
|
||||
function notValidParamItem(curExItem) {
|
||||
if (isNumberClass(curExItem.classType) && notGoodNumber(curExItem.object)) {
|
||||
return true;
|
||||
}
|
||||
if (isByteClass(curExItem.classType) && notGoodNumberBetweenExclusive(curExItem.object, -128, 127)) {
|
||||
return true;
|
||||
}
|
||||
return curExItem.object === undefined || curExItem.classType === undefined ||
|
||||
notNumberAtLeastZero(curExItem.count);
|
||||
}
|
||||
|
||||
this.checkRuleValid = function (rule) {
|
||||
if (!rule.resource || rule.resource === '') {
|
||||
alert('资源名称不能为空');
|
||||
return false;
|
||||
}
|
||||
if (rule.grade != 1) {
|
||||
alert('未知的限流模式');
|
||||
return false;
|
||||
}
|
||||
if (rule.count < 0) {
|
||||
alert('限流阈值必须大于等于 0');
|
||||
return false;
|
||||
}
|
||||
if (rule.paramIdx === undefined || rule.paramIdx === '' || isNaN(rule.paramIdx) || rule.paramIdx < 0) {
|
||||
alert('热点参数索引必须大于等于 0');
|
||||
return false;
|
||||
}
|
||||
if (rule.paramFlowItemList !== undefined) {
|
||||
for (var i = 0; i < rule.paramFlowItemList.length; i++) {
|
||||
var item = rule.paramFlowItemList[i];
|
||||
if (notValidParamItem(item)) {
|
||||
alert('热点参数例外项不合法,请检查值和类型是否正确:参数为 ' + item.object + ', 类型为 ' +
|
||||
item.classType + ', 限流阈值为 ' + item.count);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
}]);
|
||||
@@ -0,0 +1,77 @@
|
||||
var app = angular.module('sentinelDashboardApp');
|
||||
|
||||
app.service('SystemService', ['$http', function ($http) {
|
||||
this.queryMachineRules = function (app, ip, port) {
|
||||
var param = {
|
||||
app: app,
|
||||
ip: ip,
|
||||
port: port
|
||||
};
|
||||
return $http({
|
||||
url: 'system/rules.json',
|
||||
params: param,
|
||||
method: 'GET'
|
||||
});
|
||||
};
|
||||
|
||||
this.newRule = function (rule) {
|
||||
var param = {
|
||||
app: rule.app,
|
||||
ip: rule.ip,
|
||||
port: rule.port
|
||||
};
|
||||
if (rule.grade == 0) {// avgLoad
|
||||
param.highestSystemLoad = rule.highestSystemLoad;
|
||||
} else if (rule.grade == 1) {// avgRt
|
||||
param.avgRt = rule.avgRt;
|
||||
} else if (rule.grade == 2) {// maxThread
|
||||
param.maxThread = rule.maxThread;
|
||||
} else if (rule.grade == 3) {// qps
|
||||
param.qps = rule.qps;
|
||||
} else if (rule.grade == 4) {// cpu
|
||||
param.highestCpuUsage = rule.highestCpuUsage;
|
||||
}
|
||||
|
||||
return $http({
|
||||
url: '/system/new.json',
|
||||
params: param,
|
||||
method: 'GET'
|
||||
});
|
||||
};
|
||||
|
||||
this.saveRule = function (rule) {
|
||||
var param = {
|
||||
id: rule.id,
|
||||
};
|
||||
if (rule.grade == 0) {// avgLoad
|
||||
param.highestSystemLoad = rule.highestSystemLoad;
|
||||
} else if (rule.grade == 1) {// avgRt
|
||||
param.avgRt = rule.avgRt;
|
||||
} else if (rule.grade == 2) {// maxThread
|
||||
param.maxThread = rule.maxThread;
|
||||
} else if (rule.grade == 3) {// qps
|
||||
param.qps = rule.qps;
|
||||
} else if (rule.grade == 4) {// cpu
|
||||
param.highestCpuUsage = rule.highestCpuUsage;
|
||||
}
|
||||
|
||||
return $http({
|
||||
url: '/system/save.json',
|
||||
params: param,
|
||||
method: 'GET'
|
||||
});
|
||||
};
|
||||
|
||||
this.deleteRule = function (rule) {
|
||||
var param = {
|
||||
id: rule.id,
|
||||
app: rule.app
|
||||
};
|
||||
|
||||
return $http({
|
||||
url: '/system/delete.json',
|
||||
params: param,
|
||||
method: 'GET'
|
||||
});
|
||||
};
|
||||
}]);
|
||||
@@ -0,0 +1,10 @@
|
||||
var app = angular.module('sentinelDashboardApp');
|
||||
|
||||
app.service('VersionService', ['$http', function ($http) {
|
||||
this.version = function () {
|
||||
return $http({
|
||||
url: '/version',
|
||||
method: 'GET'
|
||||
});
|
||||
};
|
||||
}]);
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,399 @@
|
||||
/*!
|
||||
* Start Bootstrap - SB Admin 2 Bootstrap Admin Theme (http://startbootstrap.com)
|
||||
* Code licensed under the Apache License v2.0.
|
||||
* For details, see http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*/
|
||||
|
||||
body {
|
||||
background-color: #f8f8f8;
|
||||
}
|
||||
|
||||
.example {
|
||||
padding: .625rem 1.825rem .625rem 2.5rem;
|
||||
border: 1px #ccc dashed;
|
||||
position: relative;
|
||||
margin: 0 0 .625rem 0;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
dl dt,
|
||||
dl dd {
|
||||
line-height: 1.25rem;
|
||||
}
|
||||
dl dt {
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
}
|
||||
dl dd {
|
||||
margin-left: .9375rem;
|
||||
}
|
||||
dl.horizontal dt {
|
||||
float: left;
|
||||
width: 10rem;
|
||||
overflow: hidden;
|
||||
clear: left;
|
||||
text-align: right;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
dl.horizontal dd {
|
||||
margin-left: 11.25rem;
|
||||
}
|
||||
|
||||
#wrapper {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#page-wrapper {
|
||||
padding: 0 15px;
|
||||
min-height: 568px;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
@media(min-width:768px) {
|
||||
#page-wrapper {
|
||||
position: inherit;
|
||||
margin: 0 0 0 250px;
|
||||
padding: 0 30px;
|
||||
border-left: 1px solid #e7e7e7;
|
||||
}
|
||||
}
|
||||
|
||||
.navbar-top-links {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.navbar-top-links li {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.navbar-top-links li:last-child {
|
||||
margin-right: 15px;
|
||||
}
|
||||
|
||||
.navbar-top-links li a {
|
||||
padding: 15px;
|
||||
min-height: 50px;
|
||||
}
|
||||
|
||||
.navbar-top-links .dropdown-menu li {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.navbar-top-links .dropdown-menu li:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.navbar-top-links .dropdown-menu li a {
|
||||
padding: 3px 20px;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.navbar-top-links .dropdown-menu li a div {
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
.navbar-top-links .dropdown-messages,
|
||||
.navbar-top-links .dropdown-tasks,
|
||||
.navbar-top-links .dropdown-alerts {
|
||||
width: 310px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.navbar-top-links .dropdown-messages {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.navbar-top-links .dropdown-tasks {
|
||||
margin-left: -59px;
|
||||
}
|
||||
|
||||
.navbar-top-links .dropdown-alerts {
|
||||
margin-left: -123px;
|
||||
}
|
||||
|
||||
.navbar-top-links .dropdown-user {
|
||||
right: 0;
|
||||
left: auto;
|
||||
}
|
||||
|
||||
.sidebar .sidebar-nav.navbar-collapse {
|
||||
padding-right: 0;
|
||||
padding-left: 0;
|
||||
background-color: #71b1d1;
|
||||
color: #ffffff;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
list-style: none inside none;
|
||||
}
|
||||
|
||||
.sidebar .sidebar-search {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.sidebar ul li {
|
||||
border-bottom: 1px solid #e7e7e7;
|
||||
}
|
||||
|
||||
.sidebar ul li a.active {
|
||||
background-color: #ffffff;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.sidebar a{
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.sidebar .arrow {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.sidebar .fa.arrow:before {
|
||||
content: "\f104";
|
||||
}
|
||||
|
||||
.sidebar .active>a>.fa.arrow:before {
|
||||
content: "\f107";
|
||||
}
|
||||
|
||||
.sidebar .nav-second-level li,
|
||||
.sidebar .nav-third-level li {
|
||||
border-bottom: 0!important;
|
||||
}
|
||||
|
||||
.sidebar .nav-second-level li a {
|
||||
padding-left: 37px;
|
||||
}
|
||||
|
||||
.sidebar .nav-third-level li a {
|
||||
padding-left: 52px;
|
||||
}
|
||||
|
||||
@media(min-width:768px) {
|
||||
.sidebar {
|
||||
z-index: 1;
|
||||
position: absolute;
|
||||
width: 250px;
|
||||
margin-top: 51px;
|
||||
}
|
||||
|
||||
.navbar-top-links .dropdown-messages,
|
||||
.navbar-top-links .dropdown-tasks,
|
||||
.navbar-top-links .dropdown-alerts {
|
||||
margin-left: auto;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.btn-outline {
|
||||
color: inherit;
|
||||
background-color: transparent;
|
||||
transition: all .5s;
|
||||
}
|
||||
|
||||
.btn-primary.btn-outline {
|
||||
color: #428bca;
|
||||
}
|
||||
|
||||
.btn-success.btn-outline {
|
||||
color: #5cb85c;
|
||||
}
|
||||
|
||||
.btn-info.btn-outline {
|
||||
color: #5bc0de;
|
||||
}
|
||||
|
||||
.btn-warning.btn-outline {
|
||||
color: #f0ad4e;
|
||||
}
|
||||
|
||||
.btn-danger.btn-outline {
|
||||
color: #d9534f;
|
||||
}
|
||||
|
||||
.btn-primary.btn-outline:hover,
|
||||
.btn-success.btn-outline:hover,
|
||||
.btn-info.btn-outline:hover,
|
||||
.btn-warning.btn-outline:hover,
|
||||
.btn-danger.btn-outline:hover {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.chat {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.chat li {
|
||||
margin-bottom: 10px;
|
||||
padding-bottom: 5px;
|
||||
border-bottom: 1px dotted #999;
|
||||
}
|
||||
|
||||
.chat li.left .chat-body {
|
||||
margin-left: 60px;
|
||||
}
|
||||
|
||||
.chat li.right .chat-body {
|
||||
margin-right: 60px;
|
||||
}
|
||||
|
||||
.chat li .chat-body p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.panel .slidedown .glyphicon,
|
||||
.chat .glyphicon {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.chat-panel .panel-body {
|
||||
height: 350px;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
.login-panel {
|
||||
margin-top: 25%;
|
||||
}
|
||||
|
||||
.flot-chart {
|
||||
display: block;
|
||||
height: 400px;
|
||||
}
|
||||
|
||||
.flot-chart-content {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.dataTables_wrapper {
|
||||
position: relative;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
table.dataTable thead .sorting,
|
||||
table.dataTable thead .sorting_asc,
|
||||
table.dataTable thead .sorting_desc,
|
||||
table.dataTable thead .sorting_asc_disabled,
|
||||
table.dataTable thead .sorting_desc_disabled {
|
||||
background: 0 0;
|
||||
}
|
||||
|
||||
table.dataTable thead .sorting_asc:after {
|
||||
content: "\f0de";
|
||||
float: right;
|
||||
font-family: fontawesome;
|
||||
}
|
||||
|
||||
table.dataTable thead .sorting_desc:after {
|
||||
content: "\f0dd";
|
||||
float: right;
|
||||
font-family: fontawesome;
|
||||
}
|
||||
|
||||
table.dataTable thead .sorting:after {
|
||||
content: "\f0dc";
|
||||
float: right;
|
||||
font-family: fontawesome;
|
||||
color: rgba(50,50,50,.5);
|
||||
}
|
||||
|
||||
.btn-circle {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
padding: 6px 0;
|
||||
border-radius: 15px;
|
||||
text-align: center;
|
||||
font-size: 12px;
|
||||
line-height: 1.428571429;
|
||||
}
|
||||
|
||||
.btn-circle.btn-lg {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
padding: 10px 16px;
|
||||
border-radius: 25px;
|
||||
font-size: 18px;
|
||||
line-height: 1.33;
|
||||
}
|
||||
|
||||
.btn-circle.btn-xl {
|
||||
width: 70px;
|
||||
height: 70px;
|
||||
padding: 10px 16px;
|
||||
border-radius: 35px;
|
||||
font-size: 24px;
|
||||
line-height: 1.33;
|
||||
}
|
||||
|
||||
.show-grid [class^=col-] {
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
border: 1px solid #ddd;
|
||||
background-color: #eee!important;
|
||||
}
|
||||
|
||||
.show-grid {
|
||||
margin: 15px 0;
|
||||
}
|
||||
|
||||
.huge {
|
||||
font-size: 40px;
|
||||
}
|
||||
|
||||
.panel-green {
|
||||
border-color: #5cb85c;
|
||||
}
|
||||
|
||||
.panel-green .panel-heading {
|
||||
border-color: #5cb85c;
|
||||
color: #fff;
|
||||
background-color: #5cb85c;
|
||||
}
|
||||
|
||||
.panel-green a {
|
||||
color: #5cb85c;
|
||||
}
|
||||
|
||||
.panel-green a:hover {
|
||||
color: #3d8b3d;
|
||||
}
|
||||
|
||||
.panel-red {
|
||||
border-color: #d9534f;
|
||||
}
|
||||
|
||||
.panel-red .panel-heading {
|
||||
border-color: #d9534f;
|
||||
color: #fff;
|
||||
background-color: #d9534f;
|
||||
}
|
||||
|
||||
.panel-red a {
|
||||
color: #d9534f;
|
||||
}
|
||||
|
||||
.panel-red a:hover {
|
||||
color: #b52b27;
|
||||
}
|
||||
|
||||
.panel-yellow {
|
||||
border-color: #f0ad4e;
|
||||
}
|
||||
|
||||
.panel-yellow .panel-heading {
|
||||
border-color: #f0ad4e;
|
||||
color: #fff;
|
||||
background-color: #f0ad4e;
|
||||
}
|
||||
|
||||
.panel-yellow a {
|
||||
color: #f0ad4e;
|
||||
}
|
||||
|
||||
.panel-yellow a:hover {
|
||||
color: #df8a13;
|
||||
}
|
||||
@@ -0,0 +1,180 @@
|
||||
.timeline {
|
||||
position: relative;
|
||||
padding: 20px 0 20px;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.timeline:before {
|
||||
content: " ";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 50%;
|
||||
width: 3px;
|
||||
margin-left: -1.5px;
|
||||
background-color: #eeeeee;
|
||||
}
|
||||
|
||||
.timeline > li {
|
||||
position: relative;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.timeline > li:before,
|
||||
.timeline > li:after {
|
||||
content: " ";
|
||||
display: table;
|
||||
}
|
||||
|
||||
.timeline > li:after {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.timeline > li:before,
|
||||
.timeline > li:after {
|
||||
content: " ";
|
||||
display: table;
|
||||
}
|
||||
|
||||
.timeline > li:after {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.timeline > li > .timeline-panel {
|
||||
float: left;
|
||||
position: relative;
|
||||
width: 46%;
|
||||
padding: 20px;
|
||||
border: 1px solid #d4d4d4;
|
||||
border-radius: 2px;
|
||||
-webkit-box-shadow: 0 1px 6px rgba(0,0,0,0.175);
|
||||
box-shadow: 0 1px 6px rgba(0,0,0,0.175);
|
||||
}
|
||||
|
||||
.timeline > li > .timeline-panel:before {
|
||||
content: " ";
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
top: 26px;
|
||||
right: -15px;
|
||||
border-top: 15px solid transparent;
|
||||
border-right: 0 solid #ccc;
|
||||
border-bottom: 15px solid transparent;
|
||||
border-left: 15px solid #ccc;
|
||||
}
|
||||
|
||||
.timeline > li > .timeline-panel:after {
|
||||
content: " ";
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
top: 27px;
|
||||
right: -14px;
|
||||
border-top: 14px solid transparent;
|
||||
border-right: 0 solid #fff;
|
||||
border-bottom: 14px solid transparent;
|
||||
border-left: 14px solid #fff;
|
||||
}
|
||||
|
||||
.timeline > li > .timeline-badge {
|
||||
z-index: 100;
|
||||
position: absolute;
|
||||
top: 16px;
|
||||
left: 50%;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
margin-left: -25px;
|
||||
border-radius: 50% 50% 50% 50%;
|
||||
text-align: center;
|
||||
font-size: 1.4em;
|
||||
line-height: 50px;
|
||||
color: #fff;
|
||||
background-color: #999999;
|
||||
}
|
||||
|
||||
.timeline > li.timeline-inverted > .timeline-panel {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.timeline > li.timeline-inverted > .timeline-panel:before {
|
||||
right: auto;
|
||||
left: -15px;
|
||||
border-right-width: 15px;
|
||||
border-left-width: 0;
|
||||
}
|
||||
|
||||
.timeline > li.timeline-inverted > .timeline-panel:after {
|
||||
right: auto;
|
||||
left: -14px;
|
||||
border-right-width: 14px;
|
||||
border-left-width: 0;
|
||||
}
|
||||
|
||||
.timeline-badge.primary {
|
||||
background-color: #2e6da4 !important;
|
||||
}
|
||||
|
||||
.timeline-badge.success {
|
||||
background-color: #3f903f !important;
|
||||
}
|
||||
|
||||
.timeline-badge.warning {
|
||||
background-color: #f0ad4e !important;
|
||||
}
|
||||
|
||||
.timeline-badge.danger {
|
||||
background-color: #d9534f !important;
|
||||
}
|
||||
|
||||
.timeline-badge.info {
|
||||
background-color: #5bc0de !important;
|
||||
}
|
||||
|
||||
.timeline-title {
|
||||
margin-top: 0;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.timeline-body > p,
|
||||
.timeline-body > ul {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.timeline-body > p + p {
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
@media(max-width:767px) {
|
||||
ul.timeline:before {
|
||||
left: 40px;
|
||||
}
|
||||
|
||||
ul.timeline > li > .timeline-panel {
|
||||
width: calc(100% - 90px);
|
||||
width: -moz-calc(100% - 90px);
|
||||
width: -webkit-calc(100% - 90px);
|
||||
}
|
||||
|
||||
ul.timeline > li > .timeline-badge {
|
||||
top: 16px;
|
||||
left: 15px;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
ul.timeline > li > .timeline-panel {
|
||||
float: right;
|
||||
}
|
||||
|
||||
ul.timeline > li > .timeline-panel:before {
|
||||
right: auto;
|
||||
left: -15px;
|
||||
border-right-width: 15px;
|
||||
border-left-width: 0;
|
||||
}
|
||||
|
||||
ul.timeline > li > .timeline-panel:after {
|
||||
right: auto;
|
||||
left: -14px;
|
||||
border-right-width: 14px;
|
||||
border-left-width: 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
<div class="row" style="margin-left: 1px; margin-top:10px; height: 50px;">
|
||||
<div class="col-md-6" style="margin-bottom: 10px;">
|
||||
<span style="font-size: 30px;font-weight: bold;">{{app}}</span>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<button class="btn btn-default-inverse" style="float: right; margin-right: 10px;" ng-disabled="!macInputModel" ng-click="addNewRule()">
|
||||
<i class="fa fa-plus"></i> 新增授权规则</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="separator"></div>
|
||||
|
||||
<div class="container-fluid">
|
||||
<div class="row" style="margin-top: 20px; margin-bottom: 20px;">
|
||||
<div class="col-md-12">
|
||||
<div class="card">
|
||||
<div class="inputs-header">
|
||||
<span class="brand" style="font-size: 13px;">授权规则</span>
|
||||
<button class="btn btn-primary" style="float: right; margin-right: 10px; height: 30px;font-size: 12px;" ng-click="getMachineRules()">刷新</button>
|
||||
<input class="form-control witdh-200" placeholder="关键字" ng-model="searchKey">
|
||||
<div class="control-group" style="float:right;margin-right: 10px;margin-bottom: -10px;">
|
||||
<selectize id="gsInput" class="selectize-input-200" config="macsInputConfig" options="macsInputOptions" ng-model="macInputModel"
|
||||
placeholder="机器"></selectize>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!--.tools-header -->
|
||||
<div class="card-body" style="padding: 0px 0px;">
|
||||
<table class="table" style="border-left: none; border-right:none;margin-top: 10px;">
|
||||
<thead>
|
||||
<tr style="background: #F3F5F7;">
|
||||
<td style="width: 40%">
|
||||
资源名
|
||||
</td>
|
||||
<td style="width: 10%;">
|
||||
流控应用
|
||||
</td>
|
||||
<td style="width: 10%;">
|
||||
授权类型
|
||||
</td>
|
||||
<td style="width: 12%;">
|
||||
操作
|
||||
</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr dir-paginate="ruleEntity in rules | filter: searchKey | itemsPerPage: rulesPageConfig.pageSize " current-page="rulesPageConfig.currentPageIndex"
|
||||
pagination-id="entriesPagination">
|
||||
<td style="word-wrap:break-word;word-break:break-all;">{{ruleEntity.rule.resource}}</td>
|
||||
<td style="word-wrap:break-word;word-break:break-all;">{{ruleEntity.rule.limitApp }}</td>
|
||||
<td>
|
||||
<span ng-if="ruleEntity.rule.strategy == 0">白名单</span>
|
||||
<span ng-if="ruleEntity.rule.strategy == 1">黑名单</span>
|
||||
</td>
|
||||
<td>
|
||||
<button class="btn btn-xs btn-default" type="button" ng-click="editRule(ruleEntity)" style="font-size: 12px; height:25px;">编辑</button>
|
||||
<button class="btn btn-xs btn-default" type="button" ng-click="deleteRule(ruleEntity)" style="font-size: 12px; height:25px;">删除</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!-- .card-body -->
|
||||
<div class="pagination-footer">
|
||||
<dir-pagination-controls boundary-links="true" template-url="app/views/pagination.tpl.html" pagination-id="entriesPagination"
|
||||
on-page-change="">
|
||||
</dir-pagination-controls>
|
||||
<div class="tools" style="">
|
||||
<span>共 {{rulesPageConfig.totalCount}} 条记录, </span>
|
||||
<span>
|
||||
每页
|
||||
<input class="form-control" ng-model="rulesPageConfig.pageSize"> 条记录
|
||||
</span>
|
||||
</div>
|
||||
<!-- .tools -->
|
||||
</div>
|
||||
<!-- pagination-footer -->
|
||||
</div>
|
||||
<!-- .card -->
|
||||
</div>
|
||||
<!-- .col-md-12 -->
|
||||
</div>
|
||||
<!-- -->
|
||||
</div>
|
||||
<!-- .container-fluid -->
|
||||
@@ -0,0 +1,30 @@
|
||||
<div class="row clearfix">
|
||||
<form role="form" class="form-horizontal">
|
||||
<div class="form-group" ng-if="stateVO.currentMode == 0">
|
||||
<label class="col-sm-2 control-label">连接状态</label>
|
||||
<div class="col-sm-4">
|
||||
<p class="form-control-static text-danger" ng-if="stateVO.client.clientConfig.clientState === 0">未连接</p>
|
||||
<p class="form-control-static" ng-if="stateVO.client.clientConfig.clientState === 1">连接中</p>
|
||||
<p class="form-control-static text-success" ng-if="stateVO.client.clientConfig.clientState === 2">已连接</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label">Token Server IP</label>
|
||||
<div class="col-sm-4">
|
||||
<input type="text" class="form-control highlight-border" ng-model='stateVO.client.clientConfig.serverHost' placeholder='请指定 Token Server IP' />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label">Token Server 端口</label>
|
||||
<div class="col-sm-4">
|
||||
<input type="number" min="0" max="65535" required class="form-control highlight-border" ng-model='stateVO.client.clientConfig.serverPort' placeholder='请指定 Token Server 端口' />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label">请求超时时间(ms)</label>
|
||||
<div class="col-sm-4">
|
||||
<input type="number" min="0" required class="form-control highlight-border" ng-model='stateVO.client.clientConfig.requestTimeout' placeholder='请指定请求超时时间(ms)' />
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@@ -0,0 +1,29 @@
|
||||
<div class="row clearfix">
|
||||
<form role="form" class="form-horizontal">
|
||||
<div class="form-group" ng-if="stateVO.currentMode == 1">
|
||||
<label class="col-sm-2 control-label">Token Server 模式</label>
|
||||
<div class="col-sm-4">
|
||||
<p class="form-control-static" ng-if="!stateVO.server.embedded">独立模式 (Alone)</p>
|
||||
<p class="form-control-static" ng-if="stateVO.server.embedded">嵌入模式 (Embedded)</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label">Token Server 端口</label>
|
||||
<div class="col-sm-4">
|
||||
<input type="number" min="1" max="65535" required class="form-control highlight-border" ng-model='stateVO.server.transport.port' placeholder='请指定 Token Server 端口' />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label">命名空间集合</label>
|
||||
<div class="col-sm-4">
|
||||
<input type="text" required class="form-control highlight-border" ng-model='stateVO.server.namespaceSetStr' placeholder='请指定服务端服务的命名空间集合(以,分隔)' />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label">最大全局 QPS</label>
|
||||
<div class="col-sm-4">
|
||||
<input type="number" min="0" max="100000" required class="form-control highlight-border" ng-model='stateVO.server.flow.maxAllowedQps' placeholder='请指定服务端最大全局 QPS' />
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@@ -0,0 +1,118 @@
|
||||
<div class="row" style="margin-left: 1px; margin-top:10px; height: 50px;">
|
||||
<div class="col-md-6" style="margin-bottom: 10px;">
|
||||
<span style="font-size: 30px;font-weight: bold;">{{app}}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="separator"></div>
|
||||
<div class="container-fluid">
|
||||
<div class="row" style="margin-top: 20px; margin-bottom: 20px;">
|
||||
<div class="col-md-12">
|
||||
<div class="card">
|
||||
<div class="inputs-header">
|
||||
<span class="brand" style="font-size: 13px;">集群限流 - 机器分配/管控</span>
|
||||
</div>
|
||||
|
||||
<!-- error panel -->
|
||||
<div class="row clearfix" ng-if="loadError">
|
||||
<div class="col-md-6 col-md-offset-3">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-body">
|
||||
<center>
|
||||
<p>{{loadError.message}}</p>
|
||||
</center>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!--.tools-header -->
|
||||
<div class="card-body" style="padding: 0px 0px;" ng-if="!loadError">
|
||||
<form role="form" class="form-horizontal">
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label">Server 列表</label>
|
||||
<div class="col-sm-4">
|
||||
<select ng-model="tmp.curChosenServer" ng-change="onCurrentServerChange()" size="8"
|
||||
ng-options="serverGroup.machineId for serverGroup in clusterMap"
|
||||
class="form-control"></select>
|
||||
</div>
|
||||
<button type="button" class="btn btn-outline-warning" ng-click="removeFromServerList()">移除
|
||||
</button>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label">Token Server 端口</label>
|
||||
<div class="col-sm-4">
|
||||
<input type="number" class="form-control highlight-border"
|
||||
ng-disabled="!tmp.curChosenServer.belongToApp"
|
||||
ng-model='tmp.curChosenServer.port' placeholder='port' min="1" max="65535"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label">命名空间集合</label>
|
||||
<div class="col-sm-4">
|
||||
<input type="text" required class="form-control highlight-border"
|
||||
ng-disabled="!tmp.curChosenServer.belongToApp"
|
||||
ng-model='tmp.curChosenServer.namespaceSetStr'
|
||||
placeholder='请指定服务端服务的命名空间集合(以,分隔)'/>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<form role="form" class="form-inline" style="margin-top: 30px; margin-left: 20px;">
|
||||
<div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12">
|
||||
<label class="control-label" style="width: 200px; text-align: center;">当前对应客户端列表</label>
|
||||
<select size="8" multiple="multiple" ng-model="tmp.curClientChosen"
|
||||
ng-options="ip for ip in tmp.curChosenServer.clientSet"
|
||||
class="form-control" style="width: 100%;"></select>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12">
|
||||
<button type="button" class="btn btn-outline-primary"
|
||||
ng-disabled="!tmp.curChosenServer || !tmp.curChosenServer.machineId"
|
||||
ng-click="moveToServerGroup()">←
|
||||
</button>
|
||||
<button type="button" class="btn btn-outline-primary"
|
||||
ng-click="moveToRemainingSharePool()">→
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12">
|
||||
<label class="control-label" style="width: 220px; text-align: center;">未分配机器列表</label>
|
||||
<div>
|
||||
<select size="8" multiple="multiple" ng-model="tmp.curRemainingClientChosen"
|
||||
ng-options="ip for ip in remainingClientAddressList"
|
||||
class="form-control" style="width: 100%;">
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-6">
|
||||
<button type="button" class="btn btn-outline-primary"
|
||||
ng-click="addToServerList()">添加为 server
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<div class="separator"></div>
|
||||
<div style="margin-top: 20px;">
|
||||
<button type="button" style="margin: 0 10px 10px 10px;" class="btn btn-outline-success"
|
||||
ng-click="saveAndApplyAssign()">保存并执行分配
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- .card-body -->
|
||||
</div>
|
||||
<!-- .card -->
|
||||
</div>
|
||||
<!-- .col-md-12 -->
|
||||
</div>
|
||||
<!-- -->
|
||||
</div>
|
||||
<!-- .container-fluid -->
|
||||
@@ -0,0 +1,73 @@
|
||||
<div class="row" style="margin-left: 1px; margin-top:10px; height: 50px;">
|
||||
<div class="col-md-6" style="margin-bottom: 10px;">
|
||||
<span style="font-size: 30px;font-weight: bold;">{{app}}</span>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<a class="btn btn-default-inverse" style="float: right; margin-right: 10px;" ui-sref="dashboard.clusterAppServerList({app: app})">
|
||||
Token Server 列表
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="separator"></div>
|
||||
<div class="container-fluid">
|
||||
<div class="row" style="margin-top: 20px; margin-bottom: 20px;">
|
||||
<div class="col-md-12">
|
||||
<div class="card">
|
||||
<div class="inputs-header">
|
||||
<span class="brand" style="font-size: 13px;">集群限流 - Token Client 列表</span>
|
||||
</div>
|
||||
|
||||
<!-- error panel -->
|
||||
<div class="row clearfix" ng-if="loadError">
|
||||
<div class="col-md-6 col-md-offset-3">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-body">
|
||||
<center>
|
||||
<p>{{loadError.message}}</p>
|
||||
</center>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!--.tools-header -->
|
||||
<div class="card-body" style="padding: 0px 0px;" ng-if="!loadError">
|
||||
<!-- table start -->
|
||||
<table class="table" style="border-left: none; border-right:none;margin-top: 10px;">
|
||||
<thead>
|
||||
<tr style="background: #F3F5F7;">
|
||||
<td style="min-width: 12%;">Client ID</td>
|
||||
<td>Server IP</td>
|
||||
<td>Server 端口</td>
|
||||
<td>连接状态</td>
|
||||
<td style="min-width: 15%;">操作</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="clientVO in clientVOList">
|
||||
<td style="word-wrap:break-word;word-break:break-all;">{{clientVO.id}}</td>
|
||||
<td style="word-wrap:break-word;word-break:break-all;">{{clientVO.state.clientConfig.serverHost}}</td>
|
||||
<td>{{clientVO.state.clientConfig.serverPort}}</td>
|
||||
<td>
|
||||
<span class="form-control-static text-danger" ng-if="clientVO.state.clientConfig.clientState === 0">未连接</span>
|
||||
<span class="form-control-static" ng-if="clientVO.state.clientConfig.clientState === 1">连接中</span>
|
||||
<span class="form-control-static text-success" ng-if="clientVO.state.clientConfig.clientState === 2">已连接</span>
|
||||
</td>
|
||||
<td>
|
||||
<button class="btn btn-xs btn-outline-primary" type="button"
|
||||
ng-click="modifyClientConfigDialog(clientVO)" style="font-size: 12px; height:25px;">编辑配置</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!-- .card-body -->
|
||||
</div>
|
||||
<!-- .card -->
|
||||
</div>
|
||||
<!-- .col-md-12 -->
|
||||
</div>
|
||||
<!-- -->
|
||||
</div>
|
||||
<!-- .container-fluid -->
|
||||
@@ -0,0 +1,96 @@
|
||||
<div class="row" style="margin-left: 1px; margin-top:10px; height: 50px;">
|
||||
<div class="col-md-6" style="margin-bottom: 10px;">
|
||||
<span style="font-size: 30px;font-weight: bold;">{{app}}</span>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<button class="btn btn-default-inverse" style="float: right; margin-right: 10px;" ng-if="!loadError" ng-click="newServerDialog()">
|
||||
<i class="fa fa-plus"></i> 新增 Token Server</button>
|
||||
<a class="btn btn-default-inverse" style="float: right; margin-right: 10px;" ui-sref="dashboard.clusterAppClientList({app: app})">
|
||||
Token Client 列表
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="separator"></div>
|
||||
<div class="container-fluid">
|
||||
<div class="row" style="margin-top: 20px; margin-bottom: 20px;">
|
||||
<div class="col-md-12">
|
||||
<div class="card">
|
||||
<div class="inputs-header">
|
||||
<span class="brand" style="font-size: 13px;">集群限流 - Token Server 列表</span>
|
||||
<input class="form-control width-200" placeholder="搜索 server..." ng-model="searchKey">
|
||||
</div>
|
||||
|
||||
<!-- error panel -->
|
||||
<div class="row clearfix" ng-if="loadError">
|
||||
<div class="col-md-6 col-md-offset-3">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-body">
|
||||
<center>
|
||||
<p>{{loadError.message}}</p>
|
||||
</center>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!--.tools-header -->
|
||||
<div class="card-body" style="padding: 0px 0px;" ng-if="!loadError">
|
||||
<!-- table start -->
|
||||
<table class="table" style="border-left: none; border-right:none;margin-top: 10px;">
|
||||
<thead>
|
||||
<tr style="background: #F3F5F7;">
|
||||
<td style="width: 15%;">Server ID</td>
|
||||
<td style="width: 10%;">Port</td>
|
||||
<td style="width: 15%;">命名空间集合</td>
|
||||
<td style="width: 10%;">运行模式</td>
|
||||
<td>总连接数</td>
|
||||
<td>QPS 总览</td>
|
||||
<td style="width: 20%;">操作</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="serverVO in serverVOList | filter: {id: searchKey}">
|
||||
<td style="word-wrap:break-word;word-break:break-all;">
|
||||
<span ng-if="serverVO.belongToApp">{{serverVO.id}}</span>
|
||||
<span ng-if="!serverVO.belongToApp">{{serverVO.id}}(自主指定)</span>
|
||||
</td>
|
||||
<td>{{serverVO.port}}</td>
|
||||
<td style="word-wrap:break-word;word-break:break-all;">
|
||||
<span ng-if="serverVO.belongToApp">{{serverVO.state.namespaceSetStr}}</span>
|
||||
<span ng-if="!serverVO.belongToApp">未知</span>
|
||||
</td>
|
||||
<td style="word-wrap:break-word;word-break:break-all;">
|
||||
<span ng-if="!serverVO.belongToApp">未知</span>
|
||||
<span ng-if="serverVO.belongToApp && serverVO.state.embedded">嵌入模式</span>
|
||||
<span ng-if="serverVO.belongToApp && !serverVO.state.embedded">独立模式</span>
|
||||
</td>
|
||||
<td style="word-wrap:break-word;word-break:break-all;">
|
||||
<span ng-if="serverVO.belongToApp">{{serverVO.connectedCount}}</span>
|
||||
<span ng-if="!serverVO.belongToApp">未知</span>
|
||||
</td>
|
||||
<td>
|
||||
<span ng-if="serverVO.belongToApp">{{serverVO.state.requestLimitDataStr}}</span>
|
||||
<span ng-if="!serverVO.belongToApp">未知</span>
|
||||
</td>
|
||||
<td>
|
||||
<button class="btn btn-xs btn-outline-primary" type="button" ng-if="serverVO.belongToApp"
|
||||
ng-click="viewConnectionDetail(serverVO)" style="font-size: 12px; height:25px;">连接详情</button>
|
||||
<button class="btn btn-xs btn-outline-primary" type="button"
|
||||
ng-click="modifyServerAssignConfig(serverVO)" style="font-size: 12px; height:25px;">管理</button>
|
||||
<button class="btn btn-xs btn-outline-danger" type="button"
|
||||
ng-click="unbindServer(serverVO.id)" style="font-size: 12px; height:25px;">移除</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!-- .card-body -->
|
||||
</div>
|
||||
<!-- .card -->
|
||||
</div>
|
||||
<!-- .col-md-12 -->
|
||||
</div>
|
||||
<!-- -->
|
||||
</div>
|
||||
<!-- .container-fluid -->
|
||||
@@ -0,0 +1,88 @@
|
||||
<div class="row" style="margin-left: 1px; margin-top:10px; height: 50px;">
|
||||
<div class="col-md-6" style="margin-bottom: 10px;">
|
||||
<span style="font-size: 30px;font-weight: bold;">{{app}}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="separator"></div>
|
||||
<div class="container-fluid">
|
||||
<div class="row" style="margin-top: 20px; margin-bottom: 20px;">
|
||||
<div class="col-md-12">
|
||||
<div class="card">
|
||||
<div class="inputs-header">
|
||||
<span class="brand" style="font-size: 13px;">集群限流 - Token Server 总览</span>
|
||||
</div>
|
||||
|
||||
<!-- error panel -->
|
||||
<div class="row clearfix" ng-if="loadError">
|
||||
<div class="col-md-6 col-md-offset-3">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-body">
|
||||
<center>
|
||||
<p>{{loadError.message}}</p>
|
||||
</center>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!--.tools-header -->
|
||||
<div class="card-body" style="padding: 0px 0px;" ng-if="!loadError">
|
||||
<form role="form" class="form-horizontal">
|
||||
<div class="form-group" hidden>
|
||||
<label class="col-sm-2 control-label">Token Server 列表</label>
|
||||
<div class="col-sm-4">
|
||||
<select ng-model="tmp.curChosenServer" ng-change="onChosenServerChange()"
|
||||
ng-options="serverEntity.id for serverEntity in serverVOList"
|
||||
class="form-control"></select>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<!-- table start -->
|
||||
<table class="table" style="border-left: none; border-right:none;margin-top: 10px;">
|
||||
<thead>
|
||||
<tr style="background: #F3F5F7;">
|
||||
<td style="width: 12%;">Server ID</td>
|
||||
<td style="width: 5%;">Port</td>
|
||||
<td style="width: 10%;">命名空间集合</td>
|
||||
<td>总连接数</td>
|
||||
<td>连接情况</td>
|
||||
<td>QPS 总览</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="serverVO in clientVOList">
|
||||
<td style="word-wrap:break-word;word-break:break-all;">{{serverVO.id}}</td>
|
||||
<td>{{serverVO.port}}</td>
|
||||
<td style="word-wrap:break-word;word-break:break-all;">
|
||||
{{serverVO.state.namespaceSetStr}}
|
||||
</td>
|
||||
<td style="word-wrap:break-word;word-break:break-all;">
|
||||
{{serverVO.connectedCount}}
|
||||
</td>
|
||||
<td>
|
||||
<p ng-repeat="cg in serverVO.state.connection">
|
||||
namespace: {{cg.namespace}}, 连接数: {{cg.connectedCount}}, clients:
|
||||
{{generateConnectionSet(cg.connectionSet)}}
|
||||
</p>
|
||||
</td>
|
||||
<td>
|
||||
<p ng-repeat="crl in serverVO.state.requestLimitData">
|
||||
namespace: {{crl.namespace}}, 当前 QPS: {{crl.currentQps}}, 最大允许 QPS:
|
||||
{{crl.maxAllowedQps}}
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!-- .card-body -->
|
||||
</div>
|
||||
<!-- .card -->
|
||||
</div>
|
||||
<!-- .col-md-12 -->
|
||||
</div>
|
||||
<!-- -->
|
||||
</div>
|
||||
<!-- .container-fluid -->
|
||||
@@ -0,0 +1,95 @@
|
||||
|
||||
<div class="row" style="margin-left: 1px; margin-top:10px; height: 50px;">
|
||||
<div class="col-md-6" style="margin-bottom: 10px;">
|
||||
<span style="font-size: 30px;font-weight: bold;">{{app}}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="separator"></div>
|
||||
<div class="container-fluid">
|
||||
<div class="row" style="margin-top: 20px; margin-bottom: 20px;">
|
||||
<div class="col-md-12">
|
||||
<div class="card">
|
||||
<div class="inputs-header">
|
||||
<span class="brand" style="font-size: 13px;">集群限流</span>
|
||||
<input class="form-control witdh-200" placeholder="机器搜索" ng-model="searchKey">
|
||||
<div class="control-group" style="float:right;margin-right: 10px;margin-bottom: -10px;">
|
||||
<selectize id="gsInput" class="selectize-input-200" config="macsInputConfig" options="macsInputOptions" ng-model="macInputModel"
|
||||
placeholder="机器"></selectize>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- error panel -->
|
||||
<div class="row clearfix" ng-if="loadError">
|
||||
<div class="col-md-6 col-md-offset-3">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-body">
|
||||
<center>
|
||||
<p>{{loadError.message}}</p>
|
||||
</center>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!--.tools-header -->
|
||||
<div class="card-body" style="padding: 0px 0px;" ng-if="!loadError">
|
||||
<form role="form" class="form-horizontal">
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label">当前模式</label>
|
||||
<p class="col-sm-6 control-label" style="text-align: left; font-weight: normal;" ng-if="stateVO.currentMode == 0">Client</p>
|
||||
<p class="col-sm-6 control-label" style="text-align: left; font-weight: normal;" ng-if="stateVO.currentMode == 1">Server</p>
|
||||
<p class="col-sm-6 control-label" style="text-align: left; font-weight: normal;" ng-if="stateVO.currentMode == -1">未开启</p>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label">集群限流模式变换</label>
|
||||
<div class="col-sm-4">
|
||||
<div class="form-control highlight-border" align="center">
|
||||
<input type="radio" name="mode" value="0" ng-model='stateVO.stateInfo.mode' ng-disabled="!stateVO.stateInfo.clientAvailable" /> Client
|
||||
<input type="radio" name="mode" value="1" ng-model='stateVO.stateInfo.mode' ng-disabled="!stateVO.stateInfo.serverAvailable" /> Server
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<!-- no-cluster-mode-available-panel.start -->
|
||||
<div ng-if="!stateVO.stateInfo.clientAvailable && !stateVO.stateInfo.serverAvailable">
|
||||
<!-- error panel -->
|
||||
<div class="row clearfix">
|
||||
<div class="col-md-6 col-md-offset-3">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-body">
|
||||
<center>
|
||||
<p>该机器未引入 Sentinel 集群限流客户端或服务端的相关依赖,请引入相关依赖。</p>
|
||||
</center>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- no-cluster-mode-available-panel.stop -->
|
||||
|
||||
<div ng-if="stateVO.stateInfo.clientAvailable || stateVO.stateInfo.serverAvailable">
|
||||
<div ng-if="stateVO.stateInfo.clientAvailable && stateVO.stateInfo.mode == 0">
|
||||
<div ng-include="'app/views/cluster/client.html'"></div>
|
||||
</div>
|
||||
<div ng-if="stateVO.stateInfo.serverAvailable && stateVO.stateInfo.mode == 1">
|
||||
<div ng-include="'app/views/cluster/server.html'"></div>
|
||||
</div>
|
||||
|
||||
<div class="separator"></div>
|
||||
<div clss="row" style="margin-top: 20px;">
|
||||
<button style="margin: 0 10px 10px 10px;" class="btn btn-outline-success"
|
||||
ng-click="saveConfig()">保存配置</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<!-- .card-body -->
|
||||
</div>
|
||||
<!-- .card -->
|
||||
</div>
|
||||
<!-- .col-md-12 -->
|
||||
</div>
|
||||
<!-- -->
|
||||
</div>
|
||||
<!-- .container-fluid -->
|
||||
@@ -0,0 +1,13 @@
|
||||
<div>
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<h1 class="page-header">欢迎使用 Sentinel 控制台</h1>
|
||||
</div>
|
||||
<!-- /.col-lg-12 -->
|
||||
</div>
|
||||
<!-- /.row -->
|
||||
<!-- /.panel -->
|
||||
<div class="row">
|
||||
</div>
|
||||
<!-- /.row -->
|
||||
</div>
|
||||
@@ -0,0 +1,10 @@
|
||||
<div id="wrapper">
|
||||
<!-- Navigation -->
|
||||
<header></header>
|
||||
<!-- /.navbar-top-links -->
|
||||
<!-- /.navbar-static-side -->
|
||||
<div id="page-wrapper">
|
||||
<div ui-view></div>
|
||||
</div>
|
||||
<!-- /#page-wrapper -->
|
||||
</div>
|
||||
@@ -0,0 +1,98 @@
|
||||
<div class="row" style="margin-left: 1px; margin-top:10px; height: 50px;">
|
||||
<div class="col-md-6" style="margin-bottom: 10px;">
|
||||
<span style="font-size: 30px;font-weight: bold;">{{app}}</span>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<button class="btn btn-default-inverse" style="float: right; margin-right: 10px;" ng-disabled="!macInputModel" ng-click="addNewRule()">
|
||||
<i class="fa fa-plus"></i> 新增熔断规则</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="separator"></div>
|
||||
|
||||
<div class="container-fluid">
|
||||
<div class="row" style="margin-top: 20px; margin-bottom: 20px;">
|
||||
<div class="col-md-12">
|
||||
<div class="card">
|
||||
<div class="inputs-header">
|
||||
<span class="brand" style="font-size: 13px;">熔断规则</span>
|
||||
<!--<button class="btn btn-danger" style="float: right;margin-right: 10px;height: 30px;font-size: 12px;" ng-click="disableAll()">全部禁用</button>-->
|
||||
<button class="btn btn-primary" style="float: right; margin-right: 10px; height: 30px;font-size: 12px;" ng-click="getMachineRules()">刷新</button>
|
||||
<input class="form-control witdh-200" placeholder="关键字" ng-model="searchKey">
|
||||
<div class="control-group" style="float:right;margin-right: 10px;margin-bottom: -10px;">
|
||||
<selectize id="gsInput" class="selectize-input-200" config="macsInputConfig" options="macsInputOptions" ng-model="macInputModel"
|
||||
placeholder="机器"></selectize>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!--.tools-header -->
|
||||
<div class="card-body" style="padding: 0px 0px;">
|
||||
<table class="table" style="border-left: none; border-right:none;margin-top: 10px;">
|
||||
<thead>
|
||||
<tr style="background: #F3F5F7;">
|
||||
<td style="width: 40%">
|
||||
资源名
|
||||
</td>
|
||||
<td style="width: 10%;">
|
||||
熔断策略
|
||||
</td>
|
||||
<td style="width: 10%;">
|
||||
阈值
|
||||
</td>
|
||||
<td style="width: 10%;">
|
||||
熔断时长(s)
|
||||
</td>
|
||||
<td style="width: 12%;">
|
||||
操作
|
||||
</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr dir-paginate="rule in rules | filter : searchKey | itemsPerPage: rulesPageConfig.pageSize " current-page="rulesPageConfig.currentPageIndex"
|
||||
pagination-id="entriesPagination">
|
||||
<td style="word-wrap:break-word;word-break:break-all;">{{rule.resource}}</td>
|
||||
<!--<td style="word-wrap:break-word;word-break:break-all;">{{rule.limitApp }}</td>-->
|
||||
<td>
|
||||
<span ng-if="rule.grade == 0">慢调用比例</span>
|
||||
<span ng-if="rule.grade == 1" title="异常比例">异常比例</span>
|
||||
<span ng-if="rule.grade == 2" title="异常数">异常数</span>
|
||||
</td>
|
||||
<td style="word-wrap:break-word;word-break:break-all;">
|
||||
{{rule.count}}
|
||||
</td>
|
||||
<td style="word-wrap:break-word;word-break:break-all;">
|
||||
{{rule.timeWindow}}s
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<button class="btn btn-xs btn-default" type="button" ng-click="editRule(rule)" style="font-size: 12px; height:25px;">编辑</button>
|
||||
<button class="btn btn-xs btn-default" type="button" ng-click="deleteRule(rule)" style="font-size: 12px; height:25px;">删除</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!-- .card-body -->
|
||||
<div class="pagination-footer">
|
||||
<dir-pagination-controls boundary-links="true" template-url="app/views/pagination.tpl.html" pagination-id="entriesPagination"
|
||||
on-page-change="">
|
||||
</dir-pagination-controls>
|
||||
<div class="tools" style="">
|
||||
<span>共 {{rulesPageConfig.totalCount}} 条记录, </span>
|
||||
<span>
|
||||
每页
|
||||
<input class="form-control" ng-model="rulesPageConfig.pageSize"> 条记录,
|
||||
</span>
|
||||
<span>第 {{rulesPageConfig.currentPageIndex}} / {{rulesPageConfig.totalPage}} 页</span>
|
||||
</div>
|
||||
<!-- .tools -->
|
||||
</div>
|
||||
<!-- pagination-footer -->
|
||||
</div>
|
||||
<!-- .card -->
|
||||
</div>
|
||||
<!-- .col-md-12 -->
|
||||
</div>
|
||||
<!-- -->
|
||||
</div>
|
||||
<!-- .container-fluid -->
|
||||
@@ -0,0 +1,46 @@
|
||||
<div>
|
||||
<span class="brand" style="font-weight:bold;">{{authorityRuleDialog.title}}</span>
|
||||
<div class="card" style="margin-top: 20px;margin-bottom: 10px;">
|
||||
<div class="panel-body">
|
||||
<div class="row">
|
||||
<form role="form" class="form-horizontal">
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label">资源名</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" ng-if="authorityRuleDialog.type == 'edit'" class="form-control" placeholder="资源名" ng-model='currentRule.rule.resource'
|
||||
disabled="" />
|
||||
<input type="text" ng-if="authorityRuleDialog.type == 'add'" class="form-control highlight-border" placeholder="资源名称" ng-model='currentRule.rule.resource'
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label">流控应用</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" class="form-control highlight-border" ng-model='currentRule.rule.limitApp' placeholder='指调用方,多个调用方名称用半角英文逗号(,)分隔'
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label">授权类型</label>
|
||||
<div class="col-sm-4">
|
||||
<div class="form-control highlight-border" align="center">
|
||||
<input type="radio" name="strategy" value="0" checked ng-model='currentRule.rule.strategy' /> 白名单
|
||||
<input type="radio" name="strategy" value="1" ng-model='currentRule.rule.strategy' /> 黑名单
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
<div class="separator"></div>
|
||||
<div clss="row" style="margin-top: 20px;">
|
||||
<button class="btn btn-outline-danger" style="float:right; height: 30px;font-size: 12px;margin-left: 10px;" ng-click="closeThisDialog()">取消</button>
|
||||
<button class="btn btn-outline-success" style="float:right; height: 30px;font-size: 12px;margin-left: 10px;" ng-click="saveRule()">{{authorityRuleDialog.confirmBtnText}}</button>
|
||||
<button ng-if="authorityRuleDialog.saveAndContinueBtnText" class="btn btn-default" style="float:right; height: 30px;font-size: 12px;"
|
||||
ng-click="saveRuleAndContinue()">{{authorityRuleDialog.saveAndContinueBtnText}}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,40 @@
|
||||
<div>
|
||||
<span class="brand" style="font-weight:bold;">修改 Token Client 配置</span>
|
||||
<div class="card" style="margin-top: 20px;margin-bottom: 10px;">
|
||||
<div class="panel-body">
|
||||
<div class="row">
|
||||
<form role="form" class="form-horizontal">
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">Client ID</label>
|
||||
<div class="col-sm-4">
|
||||
<p class="form-control-static">{{ccDialogData.clientId}}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">Token Server IP</label>
|
||||
<div class="col-sm-4">
|
||||
<input type="text" class="form-control highlight-border" ng-model='ccDialogData.serverHost' placeholder='请指定 Token Server IP' />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">Token Server 端口</label>
|
||||
<div class="col-sm-4">
|
||||
<input type="number" min="0" max="65535" required class="form-control highlight-border" ng-model='ccDialogData.serverPort' placeholder='请指定 Token Server 端口' />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">请求超时时间(ms)</label>
|
||||
<div class="col-sm-4">
|
||||
<input type="number" min="0" required class="form-control highlight-border" ng-model='ccDialogData.requestTimeout' placeholder='请指定请求超时时间(ms)' />
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="separator"></div>
|
||||
<div clss="row" style="margin-top: 20px;">
|
||||
<button class="btn btn-outline-danger" style="float:right; height: 30px;font-size: 12px;margin-left: 10px;" ng-click="ccDialog.close()">取消</button>
|
||||
<button class="btn btn-outline-success" style="float:right; height: 30px;font-size: 12px;margin-left: 10px;" ng-click="doModifyClientConfig()">保存</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,139 @@
|
||||
<div>
|
||||
<span class="brand" style="font-weight:bold;">{{serverAssignDialogData.title}}</span>
|
||||
<div class="card" style="margin-top: 20px;margin-bottom: 10px;">
|
||||
<div class="panel-body">
|
||||
<div class="row">
|
||||
<form role="form" class="form-horizontal">
|
||||
<div ng-if="serverAssignDialogData.type == 'edit'">
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label">Token Server</label>
|
||||
<div class="col-sm-4">
|
||||
<p class="form-control-static">{{serverAssignDialogData.serverData.currentServer}}</p>
|
||||
</div>
|
||||
|
||||
<label class="col-sm-2 control-label">Server 端口</label>
|
||||
<div class="col-sm-3">
|
||||
<input type="number" min="1" max="65535" class="form-control highlight-border"
|
||||
ng-disabled="!serverAssignDialogData.serverData.belongToApp"
|
||||
ng-model='serverAssignDialogData.serverData.serverPort' placeholder='请输入 Token Server 端口'/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" ng-if="serverAssignDialogData.serverData.belongToApp">
|
||||
<label class="col-sm-2 control-label"
|
||||
title="server 最大允许的总 QPS,注意 embedded 模式下不要设的太大">最大允许 QPS</label>
|
||||
<div class="col-sm-3">
|
||||
<input type="number" min="0" max="200000" class="form-control highlight-border"
|
||||
ng-model='serverAssignDialogData.serverData.maxAllowedQps' placeholder='请输入 server 最大允许 QPS'/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-if="serverAssignDialogData.type == 'add'">
|
||||
<div class="form-group" >
|
||||
<label class="col-sm-2 control-label">机器类型</label>
|
||||
<div class="col-sm-4">
|
||||
<div class="form-control highlight-border" align="center">
|
||||
<input type="radio" name="strategy" value="0" checked ng-model='serverAssignDialogData.serverData.serverType' /> 应用内机器
|
||||
<input type="radio" name="strategy" value="1" ng-model='serverAssignDialogData.serverData.serverType' /> 外部指定机器
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-if="serverAssignDialogData.serverData.serverType == 1">
|
||||
<div class="col-sm-6">
|
||||
<p class="form-control-static text-primary" style="font-size: x-small;">若指定外部 server,请先在相应页面对外部 server 进行配置,然后在此页面指定。</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-if="serverAssignDialogData.serverData.serverType == 0">
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label">选择机器</label>
|
||||
<div class="col-sm-4">
|
||||
<select ng-model="serverAssignDialogData.serverData.currentServer" ng-change="onCurrentServerChange()"
|
||||
ng-options="machineId for machineId in remainingMachineList"
|
||||
class="form-control"></select>
|
||||
</div>
|
||||
|
||||
<label class="col-sm-2 control-label">Server 端口</label>
|
||||
<div class="col-sm-3">
|
||||
<input type="number" min="1" max="65535" class="form-control highlight-border"
|
||||
ng-model='serverAssignDialogData.serverData.serverPort' placeholder='请输入 Token Server 端口号'/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label"
|
||||
title="server 最大允许的总 QPS,注意 embedded 模式下不要设的太大">最大允许 QPS</label>
|
||||
<div class="col-sm-3">
|
||||
<input type="number" min="0" max="200000" class="form-control highlight-border"
|
||||
ng-model='serverAssignDialogData.serverData.maxAllowedQps' placeholder='请输入 server 最大允许 QPS'/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-if="serverAssignDialogData.serverData.serverType == 1">
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label">Server IP</label>
|
||||
<div class="col-sm-4">
|
||||
<input type="text" class="form-control highlight-border"
|
||||
ng-model='serverAssignDialogData.serverData.currentServer' placeholder='请输入独立的 Token Server IP'/>
|
||||
</div>
|
||||
|
||||
<label class="col-sm-2 control-label">Server 端口</label>
|
||||
<div class="col-sm-3">
|
||||
<input type="number" min="1" max="65535" class="form-control highlight-border"
|
||||
ng-model='serverAssignDialogData.serverData.serverPort' placeholder='请输入 Token Server 端口号'/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<!-- assign form start -->
|
||||
<form role="form" class="form-inline" ng-if="serverAssignDialogData.serverData.currentServer"
|
||||
style="margin-top: 30px; margin-left: 20px; text-align: center;">
|
||||
<div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12">
|
||||
<label class="control-label" style="width: 220px; text-align: center;">请从中选取 client:</label>
|
||||
<div>
|
||||
<select size="8" multiple="multiple" ng-model="tmp.curRemainingClientChosen"
|
||||
ng-options="ip for ip in remainingMachineList | filter: notChosenServer"
|
||||
class="form-control" style="width: 100%;">
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12">
|
||||
|
||||
<button type="button" class="btn btn-outline-primary"
|
||||
ng-click="moveToRemainingSharePool()">←
|
||||
</button>
|
||||
<button type="button" class="btn btn-outline-primary"
|
||||
ng-click="moveToServerGroup()">→
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12">
|
||||
<label class="control-label" style="width: 200px; text-align: center;">已选取的 client 列表</label>
|
||||
<div>
|
||||
<select size="8" multiple="multiple" ng-model="tmp.curClientChosen"
|
||||
ng-options="ip for ip in serverAssignDialogData.serverData.clientSet"
|
||||
class="form-control" style="width: 100%;"></select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="separator"></div>
|
||||
<div clss="row" style="margin-top: 20px;">
|
||||
<button class="btn btn-outline-danger" style="float:right; height: 30px;font-size: 12px;margin-left: 10px;" ng-click="serverAssignDialog.close()">取消</button>
|
||||
<button class="btn btn-outline-success" style="float:right; height: 30px;font-size: 12px;margin-left: 10px;" ng-click="saveAssignForDialog()">{{serverAssignDialogData.confirmBtnText}}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,37 @@
|
||||
<div>
|
||||
<span class="brand" style="font-weight:bold;">连接详情</span>
|
||||
<div class="card" style="margin-top: 20px;margin-bottom: 10px;">
|
||||
<div class="panel-body">
|
||||
<div class="row">
|
||||
<form role="form" class="form-horizontal">
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">Token Server</label>
|
||||
<div class="col-sm-4">
|
||||
<p class="form-control-static">{{connectionDetailDialogData.serverData.id}}</p>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="col-md-12">
|
||||
<!-- table start -->
|
||||
<table class="table" style="border-left: none; border-right:none;margin-top: 10px;">
|
||||
<thead>
|
||||
<tr style="background: #F3F5F7;">
|
||||
<td style="min-width: 15%;" class="text-center">命名空间</td>
|
||||
<td class="text-center">连接数</td>
|
||||
<td class="text-center">连接详情</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="cg in connectionDetailDialogData.serverData.state.connection">
|
||||
<td style="word-wrap:break-word;word-break:break-all;" class="text-center">{{cg.namespace}}</td>
|
||||
<td style="word-wrap:break-word;word-break:break-all;" class="text-center">{{cg.connectedCount}}</td>
|
||||
<td class="text-center">{{generateConnectionSet(cg.connectionSet)}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,20 @@
|
||||
<div>
|
||||
<span class="brand" style="font-weight:bold;">{{confirmDialog.title}}</span>
|
||||
<div class="card" style="margin-top: 20px;margin-bottom: 10px;">
|
||||
<div class="panel-body">
|
||||
<div class="row">
|
||||
<p style="margin-left: 20px; margin-right: 20px;">
|
||||
{{confirmDialog.attentionTitle}}:
|
||||
<br/>
|
||||
<br/>
|
||||
<code style="word-wrap: break-word; white-space:normal">{{confirmDialog.attention}}</code>
|
||||
</p>
|
||||
</div>
|
||||
<div class="separator"></div>
|
||||
<div clss="row" style="margin-top: 20px;">
|
||||
<button class="btn btn-default-inverse" style="float:right; height: 30px;font-size: 12px;margin-left: 10px;" ng-click="closeThisDialog()">取消</button>
|
||||
<button class="btn btn-outline-danger" style="float:right; height: 30px;font-size: 12px;" ng-click="confirm()">{{confirmDialog.confirmBtnText}}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,93 @@
|
||||
<div>
|
||||
<span class="brand" style="font-weight:bold;">{{degradeRuleDialog.title}}</span>
|
||||
<div class="card" style="margin-top: 20px;margin-bottom: 10px;">
|
||||
<div class="panel-body">
|
||||
<div class="row">
|
||||
<form role="form" class="form-horizontal">
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label">资源名</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" ng-if="degradeRuleDialog.type == 'edit'" class="form-control" placeholder="资源名" ng-model='currentRule.resource'
|
||||
disabled="" />
|
||||
<input type="text" ng-if="degradeRuleDialog.type == 'add'" class="form-control highlight-border" placeholder="资源名" ng-model='currentRule.resource'
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!--<div class="form-group">-->
|
||||
<!--<label class="col-sm-2 control-label">流控应用</label>-->
|
||||
<!--<div class="col-sm-9">-->
|
||||
<!--<input type="text" class="form-control highlight-border" ng-model='currentRule.limitApp' placeholder='"default"表示所有应用。' />-->
|
||||
<!--</div>-->
|
||||
<!--</div>-->
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label">熔断策略</label>
|
||||
<div class="col-sm-9">
|
||||
<div class="form-control highlight-border" align="center">
|
||||
<input type="radio" name="grade" value="0" checked ng-model='currentRule.grade' title="慢调用比例(1.8.0+ 版本生效)" /> 慢调用比例
|
||||
<input type="radio" name="grade" value="1" ng-model='currentRule.grade' title="异常比例" /> 异常比例
|
||||
<input type="radio" name="grade" value="2" ng-model='currentRule.grade' title="异常数" /> 异常数
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label ng-if="currentRule.grade == 0" class="col-sm-2 control-label" title="最大 RT,超过该值则计为慢调用">最大 RT</label>
|
||||
<label ng-if="currentRule.grade == 1" class="col-sm-2 control-label">比例阈值</label>
|
||||
<label ng-if="currentRule.grade == 2" class="col-sm-2 control-label">异常数</label>
|
||||
<div class="col-sm-4">
|
||||
<input type='number' class="form-control highlight-border" ng-model='currentRule.count' ng-if="currentRule.grade == 0" placeholder="RT (毫秒)"/>
|
||||
<input type='number' class="form-control highlight-border" ng-model='currentRule.count' ng-if="currentRule.grade == 1" placeholder="取值范围 [0.0,1.0]"/>
|
||||
<input type='number' class="form-control highlight-border" ng-model='currentRule.count' ng-if="currentRule.grade == 2" placeholder="异常数"/>
|
||||
</div>
|
||||
<div ng-if="currentRule.grade == 0">
|
||||
<label class="col-sm-2 control-label">比例阈值</label>
|
||||
<div class="col-sm-3">
|
||||
<input type='number' min="0" class="form-control highlight-border" ng-model='currentRule.slowRatioThreshold'
|
||||
placeholder="取值 [0.0, 1.0]" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label">熔断时长</label>
|
||||
<div class="col-sm-4">
|
||||
<div class="input-group">
|
||||
<input type='number' min="0" class="form-control highlight-border" ng-model='currentRule.timeWindow'
|
||||
placeholder="熔断时长(s)" />
|
||||
<span class="input-group-addon">s</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<label class="col-sm-2 control-label" style="text-align: center; padding-right: 5px;"
|
||||
title="触发熔断的最小请求数目,若当前统计窗口内的请求数小于此值,即使达到熔断条件规则也不会触发">最小请求数</label>
|
||||
<div class="col-sm-3">
|
||||
<input type='number' min="1" class="form-control highlight-border" ng-model='currentRule.minRequestAmount'
|
||||
placeholder="最小请求数目" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label">统计时长</label>
|
||||
<div class="col-sm-4">
|
||||
<div class="input-group">
|
||||
<input type='number' min="1" class="form-control highlight-border" ng-model='currentRule.statIntervalMs'
|
||||
placeholder="统计时长(ms)" />
|
||||
<span class="input-group-addon">ms</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
<div class="separator"></div>
|
||||
<div clss="row" style="margin-top: 20px;">
|
||||
<button class="btn btn-outline-danger" style="float:right; height: 30px;font-size: 12px;margin-left: 10px;" ng-click="closeThisDialog()">取消</button>
|
||||
<button class="btn btn-outline-success" style="float:right; height: 30px;font-size: 12px;margin-left: 10px;" ng-click="saveRule()">{{degradeRuleDialog.confirmBtnText}}</button>
|
||||
<button ng-if="degradeRuleDialog.saveAndContinueBtnText" class="btn btn-default" style="float:right; height: 30px;font-size: 12px;"
|
||||
ng-click="saveRuleAndContinue()">{{degradeRuleDialog.saveAndContinueBtnText}}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,148 @@
|
||||
<div>
|
||||
<span class="brand" style="font-weight:bold;">{{flowRuleDialog.title}}</span>
|
||||
<div class="card" style="margin-top: 20px;margin-bottom: 10px;">
|
||||
<div class="panel-body">
|
||||
<div class="row">
|
||||
<form role="form" class="form-horizontal">
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label">资源名</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" ng-if="flowRuleDialog.type == 'edit'" class="form-control" placeholder="资源名" ng-model='currentRule.resource'
|
||||
disabled="" />
|
||||
<input type="text" ng-if="flowRuleDialog.type == 'add'" class="form-control highlight-border" placeholder="资源名" ng-model='currentRule.resource'
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label" data-toggle="tooltip" title="流控针对的来源,即流量入口的调用来源(origin)">针对来源</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" class="form-control highlight-border" ng-model='currentRule.limitApp' placeholder='调用来源,"default"表示所有应用'
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label">阈值类型</label>
|
||||
<div class="col-sm-4">
|
||||
<div class="form-control highlight-border" align="center">
|
||||
<input type="radio" name="grade" value="1" checked ng-model='currentRule.grade' /> QPS
|
||||
<input type="radio" name="grade" value="0" ng-model='currentRule.grade' /> 并发线程数
|
||||
</div>
|
||||
</div>
|
||||
<div ng-if="!currentRule.clusterMode">
|
||||
<label class="col-sm-2 control-label">单机阈值</label>
|
||||
<div class="col-sm-3">
|
||||
<input type='number' min="0" class="form-control highlight-border" ng-model='currentRule.count' placeholder="单机阈值" />
|
||||
</div>
|
||||
</div>
|
||||
<div ng-if="currentRule.clusterMode && currentRule.clusterConfig.thresholdType == 0">
|
||||
<label class="col-sm-2 control-label">均摊阈值</label>
|
||||
<div class="col-sm-3">
|
||||
<input type='number' min="0" class="form-control highlight-border" ng-model='currentRule.count' placeholder="单机均摊阈值" />
|
||||
</div>
|
||||
</div>
|
||||
<div ng-if="currentRule.clusterMode && currentRule.clusterConfig.thresholdType == 1">
|
||||
<label class="col-sm-2 control-label">集群阈值</label>
|
||||
<div class="col-sm-3">
|
||||
<input type='number' min="0" class="form-control highlight-border" ng-model='currentRule.count' placeholder="集群总体阈值" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label">是否集群</label>
|
||||
<div class="col-sm-2">
|
||||
<label class="checkbox-inline">
|
||||
<input type="checkbox" name="clusterMode" ng-model="currentRule.clusterMode">
|
||||
</label>
|
||||
</div>
|
||||
<div ng-if="currentRule.clusterMode">
|
||||
<label class="col-sm-3 control-label">集群阈值模式</label>
|
||||
<div class="col-sm-4">
|
||||
<div class="form-control highlight-border" align="center">
|
||||
<input type="radio" name="clusterThresholdType" value="0" ng-model='currentRule.clusterConfig.thresholdType' /> 单机均摊
|
||||
<input type="radio" name="clusterThresholdType" value="1" ng-model='currentRule.clusterConfig.thresholdType' /> 总体阈值
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group" ng-if="currentRule.clusterMode">
|
||||
<label class="col-sm-2 control-label">失败退化</label>
|
||||
<div class="col-sm-8">
|
||||
<label class="checkbox-inline">
|
||||
<input type="checkbox" name="fallbackToLocalWhenFail" ng-model="currentRule.clusterConfig.fallbackToLocalWhenFail">
|
||||
<i class="glyphicon glyphicon-info-sign"></i> 如果 Token Server 不可用是否退化到单机限流
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-if="!flowRuleDialog.showAdvanceButton && !currentRule.clusterMode">
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label" title="调用关系流控模式">流控模式</label>
|
||||
<div class="col-sm-9">
|
||||
<div class="form-control highlight-border" align="center">
|
||||
<input type="radio" name="strategy" value="0" ng-model='currentRule.strategy' /> 直接
|
||||
<input type="radio" name="strategy" value="1" ng-model='currentRule.strategy' /> 关联
|
||||
<input type="radio" name="strategy" value="2" ng-model='currentRule.strategy' /> 链路
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group" ng-show="currentRule.strategy==1">
|
||||
<label class="col-sm-2 control-label">关联资源</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" class="form-control highlight-border" placeholder="关联资源" ng-model='currentRule.refResource' />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" ng-show="currentRule.strategy==2">
|
||||
<label class="col-sm-2 control-label">入口资源</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" class="form-control highlight-border" placeholder="入口资源" ng-model='currentRule.refResource' />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-if="currentRule.grade==1 && !flowRuleDialog.showAdvanceButton && !currentRule.clusterMode">
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label">流控效果</label>
|
||||
<div class="col-sm-9">
|
||||
<div class="form-control highlight-border" align="center">
|
||||
<input type="radio" name="controlBehavior" value="0" checked ng-model='currentRule.controlBehavior' /> 快速失败
|
||||
<input type="radio" name="controlBehavior" value="1" ng-model='currentRule.controlBehavior' /> Warm Up
|
||||
<input type="radio" name="controlBehavior" value="2" ng-model='currentRule.controlBehavior' /> 排队等待
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div ng-if="currentRule.controlBehavior==1">
|
||||
<label class="col-sm-2 control-label">预热时长</label>
|
||||
<div class="col-sm-9">
|
||||
<input type='number' class="form-control highlight-border" ng-model='currentRule.warmUpPeriodSec' placeholder="秒" />
|
||||
</div>
|
||||
</div>
|
||||
<div ng-if="currentRule.controlBehavior==2">
|
||||
<label class="col-sm-2 control-label">超时时间</label>
|
||||
<div class="col-sm-9">
|
||||
<input type='number' class="form-control highlight-border" ng-model='currentRule.maxQueueingTimeMs' placeholder="毫秒" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group text-center" ng-if="!currentRule.clusterMode">
|
||||
<a ng-click="onOpenAdvanceClick()" ng-if="flowRuleDialog.showAdvanceButton" style="cursor: pointer;">高级选项</a>
|
||||
<a ng-click="onCloseAdvanceClick()" ng-if="!flowRuleDialog.showAdvanceButton" style="cursor: pointer;">关闭高级选项</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="separator"></div>
|
||||
<div clss="row" style="margin-top: 20px;">
|
||||
<button class="btn btn-outline-danger" style="float:right; height: 30px;font-size: 12px;margin-left: 10px;" ng-click="closeThisDialog()">取消</button>
|
||||
<button class="btn btn-outline-success" style="float:right; height: 30px;font-size: 12px;margin-left: 10px;" ng-click="saveRule()">{{flowRuleDialog.confirmBtnText}}</button>
|
||||
<button ng-if="flowRuleDialog.saveAndContinueBtnText" class="btn btn-default" style="float:right; height: 30px;font-size: 12px;"
|
||||
ng-click="saveRuleAndContinue()">{{flowRuleDialog.saveAndContinueBtnText}}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,49 @@
|
||||
<div>
|
||||
<span class="brand" style="font-weight:bold;">{{gatewayApiDialog.title}}</span>
|
||||
<div class="card" style="margin-top: 20px;margin-bottom: 10px;">
|
||||
<div class="panel-body">
|
||||
<form role="form" class="form-horizontal">
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label">API 名称</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" ng-if="gatewayApiDialog.type == 'edit'" class="form-control" placeholder="请输入" ng-model='currentApi.apiName'
|
||||
disabled="" />
|
||||
<input type="text" ng-if="gatewayApiDialog.type == 'add'" class="form-control highlight-border" placeholder="请输入" ng-model='currentApi.apiName' />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group" ng-repeat="predicateItem in currentApi.predicateItems track by $index">
|
||||
<label class="col-sm-2 control-label">匹配模式</label>
|
||||
<div class="col-sm-4 control-label">
|
||||
<div class="form-control highlight-border" align="center">
|
||||
<input type="radio" value="0" checked ng-model="predicateItem.matchStrategy" title="精确" /> 精确
|
||||
<input type="radio" value="1" ng-model="predicateItem.matchStrategy" title="前缀" /> 前缀 
|
||||
<input type="radio" value="2" ng-model="predicateItem.matchStrategy" title="正则" /> 正则 
|
||||
</div>
|
||||
</div>
|
||||
<label class="col-sm-2 control-label">匹配串</label>
|
||||
<div class="col-sm-3 control-label">
|
||||
<input type='text' ng-model="predicateItem.pattern" class="form-control highlight-border" placeholder="请输入" />
|
||||
</div>
|
||||
<div class="col-sm-1 control-label" align="center">
|
||||
<button class="btn btn-outline-danger" ng-click="removeMatchPattern($index)"
|
||||
align="center" ng-if="currentApi.predicateItems.length > 1">X</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group" style="text-align: center">
|
||||
<button class="btn btn-outline-primary" ng-click="addNewMatchPattern()">
|
||||
<i class="fa fa-plus"></i> 新增匹配规则
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
<div class="separator"></div>
|
||||
<div clss="row" style="margin-top: 20px;">
|
||||
<button class="btn btn-outline-danger" style="float:right; height: 30px;font-size: 12px;margin-left: 10px;" ng-click="closeThisDialog()">取消</button>
|
||||
<button class="btn btn-outline-success" style="float:right; height: 30px;font-size: 12px;margin-left: 10px;" ng-click="saveApi()">{{gatewayApiDialog.confirmBtnText}}</button>
|
||||
<button ng-if="gatewayApiDialog.saveAndContinueBtnText" class="btn btn-default" style="float:right; height: 30px;font-size: 12px;"
|
||||
ng-click="saveApiAndContinue()">{{gatewayApiDialog.saveAndContinueBtnText}}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,172 @@
|
||||
<div>
|
||||
<span class="brand" style="font-weight:bold;">{{gatewayFlowRuleDialog.title}}</span>
|
||||
<div class="card" style="margin-top: 20px;margin-bottom: 10px;">
|
||||
<div class="panel-body">
|
||||
<div class="row">
|
||||
<form role="form" class="form-horizontal">
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label">API 类型</label>
|
||||
<div class="col-sm-9">
|
||||
<div class="form-control highlight-border" align="center">
|
||||
<input type="radio" ng-if="gatewayFlowRuleDialog.type == 'edit'" value="0" checked ng-model='currentRule.resourceMode'
|
||||
disabled="" title="Route ID" /><span ng-if="gatewayFlowRuleDialog.type == 'edit'"> Route ID </span>
|
||||
<input type="radio" ng-if="gatewayFlowRuleDialog.type == 'add'" value="0" checked ng-model='currentRule.resourceMode'
|
||||
title="Route ID" ng-click="useRouteID()"/><span ng-if="gatewayFlowRuleDialog.type == 'add'"> Route ID </span>
|
||||
<input type="radio" ng-if="gatewayFlowRuleDialog.type == 'edit'" value="1" ng-model='currentRule.resourceMode'
|
||||
disabled="" title="API分组" /><span ng-if="gatewayFlowRuleDialog.type == 'edit'"> API 分组  </span>
|
||||
<input type="radio" ng-if="gatewayFlowRuleDialog.type == 'add'" value="1" ng-model='currentRule.resourceMode'
|
||||
title="API分组" ng-click="useCustormAPI()"/><span ng-if="gatewayFlowRuleDialog.type == 'add'"> API 分组  </span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label">API 名称</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" ng-if="currentRule.resourceMode == 0 && gatewayFlowRuleDialog.type == 'edit'" class="form-control" placeholder="请输入 Route ID" ng-model='currentRule.resource'
|
||||
disabled="" />
|
||||
<input type="text" ng-if="currentRule.resourceMode == 0 && gatewayFlowRuleDialog.type == 'add'" class="form-control highlight-border" placeholder="请输入 Route ID" ng-model='currentRule.resource' />
|
||||
|
||||
<select ng-if="currentRule.resourceMode == 1 && gatewayFlowRuleDialog.type == 'edit'" ng-model="currentRule.resource" ng-init="selectedName = currentRule.resource"
|
||||
disabled="" ng-options="name for name in apiNames" class="form-control">
|
||||
</select>
|
||||
<select ng-if="currentRule.resourceMode == 1 && gatewayFlowRuleDialog.type == 'add'" ng-model="currentRule.resource" ng-init="currentRule.resource"
|
||||
ng-options="name for name in apiNames" class="form-control">
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label">针对请求属性</label>
|
||||
<div class="col-sm-2">
|
||||
<label class="checkbox-inline">
|
||||
<input type="checkbox" ng-if="currentRule.paramItem != null" checked ng-click="notUseParamItem()" />
|
||||
<input type="checkbox" ng-if="currentRule.paramItem == null" ng-click="useParamItem()" />
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group" ng-if="currentRule.paramItem != null">
|
||||
<label class="col-sm-2 control-label">参数属性</label>
|
||||
<div class="col-sm-9">
|
||||
<div class="form-control highlight-border" align="center">
|
||||
<input type="radio" name="parseStrategy" value="0" checked ng-model='currentRule.paramItem.parseStrategy' title="Client IP" /> Client IP
|
||||
<input type="radio" name="parseStrategy" value="1" ng-model='currentRule.paramItem.parseStrategy' title="Remote Host" /> Remote Host
|
||||
<input type="radio" name="parseStrategy" value="2" ng-model='currentRule.paramItem.parseStrategy' title="Header" /> Header
|
||||
<input type="radio" name="parseStrategy" value="3" ng-model='currentRule.paramItem.parseStrategy' title="URL 参数" /> URL 参数
|
||||
<input type="radio" name="parseStrategy" value="4" ng-model='currentRule.paramItem.parseStrategy' title="Cookie" /> Cookie
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group" ng-if="currentRule.paramItem != null && (currentRule.paramItem.parseStrategy == 2 || currentRule.paramItem.parseStrategy == 3 || currentRule.paramItem.parseStrategy == 4)">
|
||||
<label class="col-sm-2 control-label">
|
||||
<span ng-if="currentRule.paramItem.parseStrategy==2">Header 名称</span>
|
||||
<span ng-if="currentRule.paramItem.parseStrategy==3">URL 参数名称</span>
|
||||
<span ng-if="currentRule.paramItem.parseStrategy==4">Cookie 名称</span>
|
||||
</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" name="fieldName" class="form-control highlight-border" placeholder="请输入" ng-model='currentRule.paramItem.fieldName' />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group" ng-if="currentRule.paramItem != null">
|
||||
<label class="col-sm-2 control-label">属性值匹配</label>
|
||||
<div class="col-sm-2">
|
||||
<label class="checkbox-inline">
|
||||
<input type="checkbox" ng-if="currentRule.paramItem.pattern != null" checked ng-click="notUseParamItemVal()"/>
|
||||
<input type="checkbox" ng-if="currentRule.paramItem.pattern == null" ng-click="useParamItemVal()"/>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group" ng-if="currentRule.paramItem.pattern != null">
|
||||
<label class="col-sm-2 control-label">匹配模式</label>
|
||||
<div class="col-sm-4 control-label">
|
||||
<div class="form-control highlight-border" align="center">
|
||||
<input type="radio" value="0" checked ng-model="currentRule.paramItem.matchStrategy" title="精确" /> 精确
|
||||
<input type="radio" value="3" ng-model="currentRule.paramItem.matchStrategy" title="子串" /> 子串
|
||||
<input type="radio" value="2" ng-model="currentRule.paramItem.matchStrategy" title="正则" /> 正则
|
||||
</div>
|
||||
</div>
|
||||
<label class="col-sm-2 control-label">匹配串</label>
|
||||
<div class="col-sm-3 control-label">
|
||||
<input type='text' ng-model="currentRule.paramItem.pattern" class="form-control highlight-border" placeholder="匹配串" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label">阈值类型</label>
|
||||
<div class="col-sm-9">
|
||||
<div class="form-control highlight-border" align="center">
|
||||
<input type="radio" name="grade" value="1" checked ng-model="currentRule.grade" title="QPS" /> QPS
|
||||
<input type="radio" name="grade" value="0" ng-model="currentRule.grade" title="线程数" /> 线程数
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div>
|
||||
<label class="col-sm-2 control-label">
|
||||
<span ng-if="currentRule.grade==1">QPS 阈值</span>
|
||||
<span ng-if="currentRule.grade==0">线程数</span>
|
||||
</label>
|
||||
<div class="col-sm-3">
|
||||
<input type='number' min="0" class="form-control highlight-border" ng-model='currentRule.count' placeholder="阈值" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group" ng-if="currentRule.grade==1">
|
||||
<div>
|
||||
<label class="col-sm-2 control-label">间隔</label>
|
||||
<div class="col-sm-3">
|
||||
<input type='number' id="txtInterval" min="1" class="form-control highlight-border" ng-model='currentRule.interval' placeholder="统计窗口时间长度" />
|
||||
</div>
|
||||
<div class="col-sm-2">
|
||||
<select ng-model="currentRule.intervalUnit" ng-init="currentRule.intervalUnit"
|
||||
ng-options="intervalUnit.val as intervalUnit.desc for intervalUnit in intervalUnits" class="form-control" ng-click="changeIntervalUnit()">
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group" ng-if="currentRule.grade==1">
|
||||
<label class="col-sm-2 control-label">流控方式</label>
|
||||
<div class="col-sm-9">
|
||||
<div class="form-control highlight-border" align="center">
|
||||
<input type="radio" name="controlBehavior" value="0" checked ng-model='currentRule.controlBehavior' title="快速失败" /> 快速失败
|
||||
<input type="radio" name="controlBehavior" value="2" ng-model='currentRule.controlBehavior' title="匀速排队" /> 匀速排队 
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group" ng-if="currentRule.grade==1 && currentRule.controlBehavior==0">
|
||||
<div>
|
||||
<label class="col-sm-2 control-label">Burst size</label>
|
||||
<div class="col-sm-3">
|
||||
<input type='number' min="0" class="form-control highlight-border" ng-model='currentRule.burst' placeholder="突发请求额外允许数" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group" ng-if="currentRule.grade==1 && currentRule.controlBehavior==2">
|
||||
<div>
|
||||
<label class="col-sm-2 control-label">超时时间</label>
|
||||
<div class="col-sm-3">
|
||||
<input type='number' min="0" class="form-control highlight-border" ng-model='currentRule.maxQueueingTimeoutMs' placeholder="排队等待时间(ms)" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="separator"></div>
|
||||
<div clss="row" style="margin-top: 20px;">
|
||||
<button class="btn btn-outline-danger" style="float:right; height: 30px;font-size: 12px;margin-left: 10px;" ng-click="closeThisDialog()">取消</button>
|
||||
<button class="btn btn-outline-success" style="float:right; height: 30px;font-size: 12px;margin-left: 10px;" ng-click="saveRule()">{{gatewayFlowRuleDialog.confirmBtnText}}</button>
|
||||
<button ng-if="gatewayFlowRuleDialog.saveAndContinueBtnText" class="btn btn-default" style="float:right; height: 30px;font-size: 12px;"
|
||||
ng-click="saveRuleAndContinue()">{{gatewayFlowRuleDialog.saveAndContinueBtnText}}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,166 @@
|
||||
<div>
|
||||
<span class="brand" style="font-weight:bold;">{{paramFlowRuleDialog.title}}</span>
|
||||
<div class="card" style="margin-top: 20px;margin-bottom: 10px;">
|
||||
<div class="panel-body">
|
||||
<div class="clearfix">
|
||||
<form role="form" class="form-horizontal">
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label">资源名</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" ng-if="paramFlowRuleDialog.type == 'edit'" class="form-control" placeholder="资源名" ng-model='currentRule.rule.resource' disabled="" />
|
||||
<input type="text" ng-if="paramFlowRuleDialog.type == 'add'" class="form-control highlight-border" placeholder="资源名" ng-model='currentRule.rule.resource' required />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label">限流模式</label>
|
||||
<p class="col-sm-9 control-label" style="text-align: left; font-weight: normal;">QPS 模式</p>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label">参数索引</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="number" class="form-control highlight-border" ng-model='currentRule.rule.paramIdx' placeholder='请填入传入的热点参数的索引(从 0 开始)' />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div ng-if="!currentRule.rule.clusterMode">
|
||||
<label class="col-sm-2 control-label">单机阈值</label>
|
||||
<div class="col-sm-3">
|
||||
<input type="number" class="form-control highlight-border" ng-model='currentRule.rule.count' placeholder='单机阈值' />
|
||||
</div>
|
||||
<label class="col-sm-3 control-label" title="统计窗口时间长度,单位为 s">统计窗口时长</label>
|
||||
<div class="input-group col-sm-3">
|
||||
<input type="number" class="form-control highlight-border"
|
||||
ng-model='currentRule.rule.durationInSec' min="1"
|
||||
placeholder='请填入统计窗口时长(单位为 秒)' />
|
||||
<span class="input-group-addon">秒</span>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-if="currentRule.rule.clusterMode && currentRule.rule.clusterConfig.thresholdType == 0">
|
||||
<label class="col-sm-2 control-label">均摊阈值</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="number" class="form-control highlight-border" ng-model='currentRule.rule.count' placeholder='集群均摊阈值' />
|
||||
</div>
|
||||
</div>
|
||||
<div ng-if="currentRule.rule.clusterMode && currentRule.rule.clusterConfig.thresholdType == 1">
|
||||
<label class="col-sm-2 control-label">集群阈值</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="number" class="form-control highlight-border" ng-model='currentRule.rule.count' placeholder='集群总体阈值' />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label">是否集群</label>
|
||||
<div class="col-sm-2">
|
||||
<input type="checkbox" name="clusterMode" ng-model="currentRule.rule.clusterMode">
|
||||
</div>
|
||||
<div ng-if="currentRule.rule.clusterMode">
|
||||
<label class="col-sm-3 control-label">集群阈值模式</label>
|
||||
<div class="col-sm-4">
|
||||
<div class="form-control highlight-border" align="center">
|
||||
<input type="radio" name="clusterThresholdType" value="0" ng-model='currentRule.rule.clusterConfig.thresholdType' /> 单机均摊
|
||||
<input type="radio" name="clusterThresholdType" value="1" ng-model='currentRule.rule.clusterConfig.thresholdType' /> 总体阈值
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group" ng-if="currentRule.rule.clusterMode">
|
||||
<label class="col-sm-2 control-label">失败退化</label>
|
||||
<div class="col-sm-8">
|
||||
<div class="checkbox-inline">
|
||||
<input type="checkbox" name="fallbackToLocalWhenFail"
|
||||
ng-model="currentRule.rule.clusterConfig.fallbackToLocalWhenFail">
|
||||
<i class="glyphicon glyphicon-info-sign"></i> 若选择,则 Token Server 不可用时将退化到单机限流
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- exclusion item part start -->
|
||||
<div ng-if="!paramFlowRuleDialog.showAdvanceButton">
|
||||
<hr />
|
||||
<div class="form-group">
|
||||
<div class="form-group" style="text-align: center">
|
||||
<label class="control-label">参数例外项</label>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label">参数类型</label>
|
||||
<div class="col-md-9">
|
||||
<select ng-model="curExItem.classType" ng-options="classType for classType in paramItemClassTypeList" class="form-control" placeholder="请选择参数类型">
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label">参数值</label>
|
||||
<div class="col-md-3">
|
||||
<input ng-model="curExItem.object" type="text" class="form-control" placeholder="例外项参数值">
|
||||
</div>
|
||||
|
||||
<label class="col-sm-2 control-label">限流阈值</label>
|
||||
<div class="col-md-3">
|
||||
<input type="number" ng-model="curExItem.count" class="form-control" placeholder="限流阈值">
|
||||
</div>
|
||||
|
||||
<div class="col-md-2">
|
||||
<button type="button" class="btn btn-success"
|
||||
ng-disabled="notValidParamItem(curExItem)"
|
||||
ng-click="addParamItem()">
|
||||
<span class="fa fa-plus"> 添加</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="col-md-12">
|
||||
<table class="table table-condensed table-hover">
|
||||
<thead>
|
||||
<th>参数值</th>
|
||||
<th>参数类型</th>
|
||||
<th>限流阈值</th>
|
||||
<th>操作</th>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="paramItem in currentRule.rule.paramFlowItemList">
|
||||
<td><input ng-model="paramItem.object" type="text" class="form-control" placeholder="例外项参数"></td>
|
||||
<td>
|
||||
<p>{{paramItem.classType}}</p>
|
||||
</td>
|
||||
<td>
|
||||
<input type="number" ng-model="paramItem.count" class="form-control" placeholder="限流阈值">
|
||||
</td>
|
||||
<td>
|
||||
<button type="button" class="btn btn-danger"
|
||||
ng-click="removeParamItem(paramItem.object, paramItem.classType)"><span
|
||||
class="fa fa-trash-o"> 删除</span></button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- exclusion item part end -->
|
||||
<div class="form-group text-center" ng-if="paramFlowRuleDialog.supportAdvanced">
|
||||
<a ng-click="onOpenAdvanceClick()" ng-if="paramFlowRuleDialog.showAdvanceButton" style="cursor: pointer;">高级选项</a>
|
||||
<a ng-click="onCloseAdvanceClick()" ng-if="!paramFlowRuleDialog.showAdvanceButton" style="cursor: pointer;">关闭高级选项</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="separator"></div>
|
||||
<div clss="row" style="margin-top: 20px;">
|
||||
<button class="btn btn-outline-danger" style="float:right; height: 30px;font-size: 12px;margin-left: 10px;" ng-click="closeThisDialog()">取消</button>
|
||||
<button class="btn btn-outline-success" style="float:right; height: 30px;font-size: 12px;margin-left: 10px;" ng-click="saveRule()">{{paramFlowRuleDialog.confirmBtnText}}</button>
|
||||
<button ng-if="paramFlowRuleDialog.saveAndContinueBtnText" class="btn btn-default" style="float:right; height: 30px;font-size: 12px;"
|
||||
ng-click="saveRuleAndContinue()">{{paramFlowRuleDialog.saveAndContinueBtnText}}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,58 @@
|
||||
<div>
|
||||
<span class="brand" style="font-weight:bold;">{{systemRuleDialog.title}}</span>
|
||||
<div class="card" style="margin-top: 20px;margin-bottom: 10px;">
|
||||
<div class="panel-body">
|
||||
<div class="row">
|
||||
<form role="form" class="form-horizontal">
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label">阈值类型</label>
|
||||
<div class="col-sm-9">
|
||||
<div class="form-control" ng-if="systemRuleDialog.type == 'edit'" align="center">
|
||||
<!--highestSystemLoad -->
|
||||
<input type="radio" name="grade" value="0" checked ng-model='currentRule.grade' ng-disabled="systemRuleDialog.type == 'edit'" /> LOAD
|
||||
<!--avgRt -->
|
||||
<input type="radio" name="grade" value="1" ng-model='currentRule.grade' ng-disabled="systemRuleDialog.type == 'edit'" /> RT
|
||||
<!--maxThread -->
|
||||
<input type="radio" name="grade" value="2" ng-model='currentRule.grade' ng-disabled="systemRuleDialog.type == 'edit'" /> 线程数
|
||||
<!--qps -->
|
||||
<input type="radio" name="grade" value="3" ng-model='currentRule.grade' ng-disabled="systemRuleDialog.type == 'edit'"/> 入口 QPS
|
||||
<!--highestCpuUsage -->
|
||||
<input type="radio" name="grade" value="4" ng-model='currentRule.grade' ng-disabled="systemRuleDialog.type == 'edit'" /> CPU 使用率
|
||||
|
||||
</div>
|
||||
<div class="form-control highlight-border" ng-if="systemRuleDialog.type == 'add'" align="center">
|
||||
<!--highestSystemLoad -->
|
||||
<input type="radio" name="grade" value="0" ng-model='currentRule.grade' ng-disabled="systemRuleDialog.type == 'edit'" /> LOAD
|
||||
<!--avgRt -->
|
||||
<input type="radio" name="grade" value="1" ng-model='currentRule.grade' ng-disabled="systemRuleDialog.type == 'edit'" /> RT
|
||||
<!--maxThread -->
|
||||
<input type="radio" name="grade" value="2" ng-model='currentRule.grade' ng-disabled="systemRuleDialog.type == 'edit'" /> 线程数
|
||||
<!--qps -->
|
||||
<input type="radio" name="grade" value="3" ng-model='currentRule.grade' ng-disabled="systemRuleDialog.type == 'edit'"/> 入口 QPS
|
||||
<!--highestCpuUsage -->
|
||||
<input type="radio" name="grade" value="4" checked ng-model='currentRule.grade' ng-disabled="systemRuleDialog.type == 'edit'" /> CPU 使用率
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label">阈值</label>
|
||||
<div class="col-sm-9">
|
||||
<input type='number' min="0" class="form-control highlight-border" ng-model='currentRule.highestSystemLoad' placeholder="[0, ~)的正整数" ng-if="currentRule.grade == 0"/>
|
||||
<input type='number' min="0" class="form-control highlight-border" ng-model='currentRule.avgRt' placeholder="[0, ~)的正整数" ng-if="currentRule.grade == 1"/>
|
||||
<input type='number' min="0" class="form-control highlight-border" ng-model='currentRule.maxThread' placeholder="[0, ~)的正整数" ng-if="currentRule.grade == 2"/>
|
||||
<input type='number' min="0" class="form-control highlight-border" ng-model='currentRule.qps' placeholder="[0, ~)的正整数" ng-if="currentRule.grade == 3"/>
|
||||
<input type='number' min="0" class="form-control highlight-border" ng-model='currentRule.highestCpuUsage' placeholder="[0, 1]的小数,代表百分比" ng-if="currentRule.grade == 4"/>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="separator"></div>
|
||||
<div clss="row" style="margin-top: 20px;">
|
||||
<button class="btn btn-outline-danger" style="float:right; height: 30px;font-size: 12px;margin-left: 10px;" ng-click="closeThisDialog()">取消</button>
|
||||
<button class="btn btn-outline-success" style="float:right; height: 30px;font-size: 12px;" ng-click="saveRule()">{{systemRuleDialog.confirmBtnText}}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,117 @@
|
||||
<div class="row" style="margin-left: 1px; margin-top:10px; height: 50px;">
|
||||
<div class="col-md-6" style="margin-bottom: 10px;">
|
||||
<span style="font-size: 30px;font-weight: bold;">{{app}}</span>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<button class="btn btn-default-inverse" style="float: right; margin-right: 10px;" ng-disabled="!macInputModel" ng-click="addNewRule()">
|
||||
<i class="fa fa-plus"></i> 新增流控规则</button>
|
||||
<!--<a class="btn btn-outline-success" style="float: right; margin-right: 10px;" ui-sref="dashboard.flow({app: app})">-->
|
||||
<!--回到集群页面</a>-->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="separator"></div>
|
||||
|
||||
<div class="container-fluid">
|
||||
<div class="row" style="margin-top: 20px; margin-bottom: 20px;">
|
||||
<div class="col-md-12">
|
||||
<div class="card">
|
||||
<div class="inputs-header">
|
||||
<span class="brand" style="font-size: 13px;">流控规则</span>
|
||||
<button class="btn btn-danger" style="float: right;margin-right: 10px;height: 30px;font-size: 12px;" ng-click="disableAll()">全部禁用</button>
|
||||
<button class="btn btn-primary" style="float: right; margin-right: 10px; height: 30px;font-size: 12px;" ng-click="getMachineRules()">刷新</button>
|
||||
<input class="form-control witdh-200" placeholder="关键字" ng-model="searchKey">
|
||||
<div class="control-group" style="float:right;margin-right: 10px;margin-bottom: -10px;">
|
||||
<selectize id="gsInput" class="selectize-input-200" config="macsInputConfig" options="macsInputOptions" ng-model="macInputModel"
|
||||
placeholder="机器"></selectize>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!--.tools-header -->
|
||||
<div class="card-body" style="padding: 0px 0px;">
|
||||
<table class="table" style="border-left: none; border-right:none;margin-top: 10px;">
|
||||
<thead>
|
||||
<tr style="background: #F3F5F7;">
|
||||
<td style="width: 40%">
|
||||
资源名
|
||||
</td>
|
||||
<td style="width: 8%;">
|
||||
来源应用
|
||||
</td>
|
||||
<td style="width: 8%;">
|
||||
流控模式
|
||||
</td>
|
||||
<td style="width: 8%;">
|
||||
阈值类型
|
||||
</td>
|
||||
<td style="width: 6%;">
|
||||
阈值
|
||||
</td>
|
||||
<td style="width: 8%;">
|
||||
阈值模式
|
||||
</td>
|
||||
<td style="width: 10%;">
|
||||
流控效果
|
||||
</td>
|
||||
<td style="width: 12%;">
|
||||
操作
|
||||
</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr dir-paginate="rule in rules | filter: searchKey | itemsPerPage: rulesPageConfig.pageSize " current-page="rulesPageConfig.currentPageIndex"
|
||||
pagination-id="entriesPagination">
|
||||
<td style="word-wrap:break-word;word-break:break-all;">{{rule.resource}}</td>
|
||||
<td style="word-wrap:break-word;word-break:break-all;">{{rule.limitApp }}</td>
|
||||
<td>
|
||||
<span ng-if="rule.strategy == 0">直接</span>
|
||||
<span ng-if="rule.strategy == 1">关联</span>
|
||||
<span ng-if="rule.strategy == 2">链路</span>
|
||||
</td>
|
||||
<td>
|
||||
{{rule.grade==0 ? '线程数' : 'QPS'}}
|
||||
</td>
|
||||
<td style="word-wrap:break-word;word-break:break-all;">
|
||||
{{rule.count}}
|
||||
</td>
|
||||
<td>
|
||||
<span>{{generateThresholdTypeShow(rule)}}</span>
|
||||
</td>
|
||||
<td>
|
||||
<span ng-if="rule.controlBehavior == 0">快速失败</span>
|
||||
<span ng-if="rule.controlBehavior == 1">Warm Up</span>
|
||||
<span ng-if="rule.controlBehavior == 2">排队等待</span>
|
||||
<span ng-if="rule.controlBehavior == 3">预热排队</span>
|
||||
</td>
|
||||
<td>
|
||||
<button class="btn btn-xs btn-default" type="button" ng-click="editRule(rule)" style="font-size: 12px; height:25px;">编辑</button>
|
||||
<button class="btn btn-xs btn-default" type="button" ng-click="deleteRule(rule)" style="font-size: 12px; height:25px;">删除</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!-- .card-body -->
|
||||
<div class="pagination-footer">
|
||||
<dir-pagination-controls boundary-links="true" template-url="app/views/pagination.tpl.html" pagination-id="entriesPagination"
|
||||
on-page-change="">
|
||||
</dir-pagination-controls>
|
||||
<div class="tools" style="">
|
||||
<span>共 {{rulesPageConfig.totalCount}} 条记录, </span>
|
||||
<span>
|
||||
每页
|
||||
<input class="form-control" ng-model="rulesPageConfig.pageSize"> 条记录
|
||||
</span>
|
||||
<span>第 {{rulesPageConfig.currentPageIndex}} / {{rulesPageConfig.totalPage}} 页</span>
|
||||
</div>
|
||||
<!-- .tools -->
|
||||
</div>
|
||||
<!-- pagination-footer -->
|
||||
</div>
|
||||
<!-- .card -->
|
||||
</div>
|
||||
<!-- .col-md-12 -->
|
||||
</div>
|
||||
<!-- -->
|
||||
</div>
|
||||
<!-- .container-fluid -->
|
||||
@@ -0,0 +1,113 @@
|
||||
<div class="row" style="margin-left: 1px; margin-top:10px; height: 50px;">
|
||||
<div class="col-md-6" style="margin-bottom: 10px;">
|
||||
<span style="font-size: 30px;font-weight: bold;">{{app}}</span>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<button class="btn btn-default-inverse" style="float: right; margin-right: 10px;" ng-click="addNewRule()">
|
||||
<i class="fa fa-plus"></i> 新增流控规则
|
||||
</button>
|
||||
<a class="btn btn-default-inverse" style="float: right; margin-right: 10px;" ui-sref="dashboard.flowV1({app: app})">
|
||||
回到单机页面
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="separator"></div>
|
||||
|
||||
<div class="container-fluid">
|
||||
<div class="row" style="margin-top: 20px; margin-bottom: 20px;">
|
||||
<div class="col-md-12">
|
||||
<div class="card">
|
||||
<div class="inputs-header">
|
||||
<span class="brand" style="font-size: 13px;">流控规则</span>
|
||||
<input class="form-control witdh-200" placeholder="关键字" ng-model="searchKey">
|
||||
</div>
|
||||
|
||||
<!--.tools-header -->
|
||||
<div class="card-body" style="padding: 0px 0px;">
|
||||
<table class="table" style="border-left: none; border-right:none;margin-top: 10px;">
|
||||
<thead>
|
||||
<tr style="background: #F3F5F7;">
|
||||
<td style="width: 40%">
|
||||
资源名
|
||||
</td>
|
||||
<td style="width: 10%;">
|
||||
来源应用
|
||||
</td>
|
||||
<td style="width: 8%;">
|
||||
流控模式
|
||||
</td>
|
||||
<td style="width: 8%;">
|
||||
阈值类型
|
||||
</td>
|
||||
<td style="width: 8%;">
|
||||
阈值
|
||||
</td>
|
||||
<td style="width: 8%;">
|
||||
阈值模式
|
||||
</td>
|
||||
<td style="width: 8%;">
|
||||
流控效果
|
||||
</td>
|
||||
<td style="width: 12%;">
|
||||
操作
|
||||
</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr dir-paginate="rule in rules | filter: searchKey | itemsPerPage: rulesPageConfig.pageSize " current-page="rulesPageConfig.currentPageIndex"
|
||||
pagination-id="entriesPagination">
|
||||
<td style="word-wrap:break-word;word-break:break-all;">{{rule.resource}}</td>
|
||||
<td style="word-wrap:break-word;word-break:break-all;">{{rule.limitApp }}</td>
|
||||
<td>
|
||||
<span ng-if="rule.strategy == 0">直接</span>
|
||||
<span ng-if="rule.strategy == 1">关联</span>
|
||||
<span ng-if="rule.strategy == 2">链路</span>
|
||||
</td>
|
||||
<td>
|
||||
{{rule.grade == 0 ? '线程数' : 'QPS'}}
|
||||
</td>
|
||||
<td style="word-wrap:break-word;word-break:break-all;">
|
||||
{{rule.count}}
|
||||
</td>
|
||||
<td>
|
||||
<span>{{generateThresholdTypeShow(rule)}}</span>
|
||||
</td>
|
||||
<td>
|
||||
<span ng-if="rule.controlBehavior == 0">快速失败</span>
|
||||
<span ng-if="rule.controlBehavior == 1">Warm Up</span>
|
||||
<span ng-if="rule.controlBehavior == 2">排队等待</span>
|
||||
<span ng-if="rule.controlBehavior == 3">预热排队</span>
|
||||
</td>
|
||||
<td>
|
||||
<button class="btn btn-xs btn-default" type="button" ng-click="editRule(rule)" style="font-size: 12px; height:25px;">编辑</button>
|
||||
<button class="btn btn-xs btn-default" type="button" ng-click="deleteRule(rule)" style="font-size: 12px; height:25px;">删除</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!-- .card-body -->
|
||||
<div class="pagination-footer">
|
||||
<dir-pagination-controls boundary-links="true" template-url="app/views/pagination.tpl.html" pagination-id="entriesPagination"
|
||||
on-page-change="">
|
||||
</dir-pagination-controls>
|
||||
<div class="tools" style="">
|
||||
<span>共 {{rulesPageConfig.totalCount}} 条记录, </span>
|
||||
<span>
|
||||
每页
|
||||
<input class="form-control" ng-model="rulesPageConfig.pageSize"> 条记录
|
||||
</span>
|
||||
<!--<span>第 {{rulesPageConfig.currentPageIndex}} / {{rulesPageConfig.totalPage}} 页</span>-->
|
||||
</div>
|
||||
<!-- .tools -->
|
||||
</div>
|
||||
<!-- pagination-footer -->
|
||||
</div>
|
||||
<!-- .card -->
|
||||
</div>
|
||||
<!-- .col-md-12 -->
|
||||
</div>
|
||||
<!-- -->
|
||||
</div>
|
||||
<!-- .container-fluid -->
|
||||
@@ -0,0 +1,87 @@
|
||||
<div class="row" style="margin-left: 1px; margin-top:10px; height: 50px;">
|
||||
<div class="col-md-6" style="margin-bottom: 10px;">
|
||||
<span style="font-size: 30px;font-weight: bold;">{{app}}</span>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<button class="btn btn-outline-primary" style="float: right; margin-right: 10px;" ng-disabled="!macInputModel" ng-click="addNewApi()">
|
||||
<i class="fa fa-plus"></i> 新增 API 分组</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="separator"></div>
|
||||
|
||||
<div class="container-fluid">
|
||||
<div class="row" style="margin-top: 20px; margin-bottom: 20px;">
|
||||
<div class="col-md-12">
|
||||
<div class="card">
|
||||
<div class="inputs-header">
|
||||
<span class="brand" style="font-size: 13px;">API 分组管理</span>
|
||||
<button class="btn btn-primary" style="float: right; margin-right: 10px; height: 30px;font-size: 12px;" ng-click="getApis()">刷新</button>
|
||||
<input class="form-control witdh-200" placeholder="关键字" ng-model="searchKey">
|
||||
<div class="control-group" style="float:right;margin-right: 10px;margin-bottom: -10px;">
|
||||
<selectize id="gsInput" class="selectize-input-200" config="macsInputConfig" options="macsInputOptions" ng-model="macInputModel"
|
||||
placeholder="机器"></selectize>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!--.tools-header -->
|
||||
<div class="card-body" style="padding: 0px 0px;">
|
||||
<table class="table" style="border-left: none; border-right:none;margin-top: 10px;">
|
||||
<thead>
|
||||
<tr style="background: #F3F5F7;">
|
||||
<td>
|
||||
API 名称
|
||||
</td>
|
||||
<td>
|
||||
匹配模式
|
||||
</td>
|
||||
<td>
|
||||
匹配串
|
||||
</td>
|
||||
<td>
|
||||
操作
|
||||
</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr dir-paginate="api in apis | filter: searchKey | itemsPerPage: apisPageConfig.pageSize" current-page="apisPageConfig.currentPageIndex"
|
||||
pagination-id="entriesPagination">
|
||||
<td ng-if="api.firstFlag==0" rowspan="{{api.itemSize}}" style="vertical-align: middle;">{{api.apiName}}</td>
|
||||
<td>
|
||||
<span ng-if="api.matchStrategy == 0">精确</span>
|
||||
<span ng-if="api.matchStrategy == 1">前缀</span>
|
||||
<span ng-if="api.matchStrategy == 2">正则</span>
|
||||
</td>
|
||||
<td>{{api.pattern}}</td>
|
||||
<td ng-if="api.firstFlag==0" rowspan="{{api.itemSize}}" style="vertical-align: middle;">
|
||||
<button class="btn btn-xs btn-outline-primary" type="button" ng-click="editApi(api)" style="font-size: 12px; height:25px;">编辑</button>
|
||||
<button class="btn btn-xs btn-outline-danger" type="button" ng-click="deleteApi(api)" style="font-size: 12px; height:25px;">删除</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!-- .card-body -->
|
||||
<div class="pagination-footer">
|
||||
<dir-pagination-controls boundary-links="true" template-url="app/views/pagination.tpl.html" pagination-id="entriesPagination"
|
||||
on-page-change="">
|
||||
</dir-pagination-controls>
|
||||
<div class="tools" style="">
|
||||
<span>共 {{apisPageConfig.totalCount}} 条记录, </span>
|
||||
<span>
|
||||
每页
|
||||
<input class="form-control" ng-model="apisPageConfig.pageSize"> 条记录
|
||||
</span>
|
||||
<!--<span>第 {{apisPageConfig.currentPageIndex}} / {{apisPageConfig.totalPage}} 页</span>-->
|
||||
</div>
|
||||
<!-- .tools -->
|
||||
</div>
|
||||
<!-- pagination-footer -->
|
||||
</div>
|
||||
<!-- .card -->
|
||||
</div>
|
||||
<!-- .col-md-12 -->
|
||||
</div>
|
||||
<!-- -->
|
||||
</div>
|
||||
<!-- .container-fluid -->
|
||||
@@ -0,0 +1,94 @@
|
||||
<div class="row" style="margin-left: 1px; margin-top:10px; height: 50px;">
|
||||
<div class="col-md-6" style="margin-bottom: 10px;">
|
||||
<span style="font-size: 30px;font-weight: bold;">{{app}}</span>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<button class="btn btn-outline-primary" style="float: right; margin-right: 10px;" ng-disabled="!macInputModel" ng-click="addNewRule()">
|
||||
<i class="fa fa-plus"></i> 新增网关流控规则</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="separator"></div>
|
||||
|
||||
<div class="container-fluid">
|
||||
<div class="row" style="margin-top: 20px; margin-bottom: 20px;">
|
||||
<div class="col-md-12">
|
||||
<div class="card">
|
||||
<div class="inputs-header">
|
||||
<span class="brand" style="font-size: 13px;">网关流控规则</span>
|
||||
<button class="btn btn-primary" style="float: right; margin-right: 10px; height: 30px;font-size: 12px;" ng-click="getMachineRules()">刷新</button>
|
||||
<input class="form-control witdh-200" placeholder="关键字" ng-model="searchKey">
|
||||
<div class="control-group" style="float:right;margin-right: 10px;margin-bottom: -10px;">
|
||||
<selectize id="gsInput" class="selectize-input-200" config="macsInputConfig" options="macsInputOptions" ng-model="macInputModel"
|
||||
placeholder="机器"></selectize>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!--.tools-header -->
|
||||
<div class="card-body" style="padding: 0px 0px;">
|
||||
<table class="table" style="border-left: none; border-right:none;margin-top: 10px;">
|
||||
<thead>
|
||||
<tr style="background: #F3F5F7;">
|
||||
<td>
|
||||
API 名称
|
||||
</td>
|
||||
<td>
|
||||
API 类型
|
||||
</td>
|
||||
<td>
|
||||
阈值类型
|
||||
</td>
|
||||
<td>
|
||||
单机阈值
|
||||
</td>
|
||||
<td>
|
||||
操作
|
||||
</td>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
<tr dir-paginate="rule in rules | filter: searchKey | itemsPerPage: rulesPageConfig.pageSize " current-page="rulesPageConfig.currentPageIndex"
|
||||
pagination-id="entriesPagination">
|
||||
<td>{{rule.resource}}</td>
|
||||
<td>
|
||||
<span ng-if="rule.resourceMode == 0">Route ID</span>
|
||||
<span ng-if="rule.resourceMode == 1">API 分组</span>
|
||||
</td>
|
||||
<td>
|
||||
<span ng-if="rule.grade == 1">QPS</span>
|
||||
<span ng-if="rule.grade == 0">线程数</span>
|
||||
</td>
|
||||
<td>{{rule.count}}</td>
|
||||
<td>
|
||||
<button class="btn btn-xs btn-outline-primary" type="button" ng-click="editRule(rule)" style="font-size: 12px; height:25px;">编辑</button>
|
||||
<button class="btn btn-xs btn-outline-danger" type="button" ng-click="deleteRule(rule)" style="font-size: 12px; height:25px;">删除</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!-- .card-body -->
|
||||
<div class="pagination-footer">
|
||||
<dir-pagination-controls boundary-links="true" template-url="app/views/pagination.tpl.html" pagination-id="entriesPagination"
|
||||
on-page-change="">
|
||||
</dir-pagination-controls>
|
||||
<div class="tools" style="">
|
||||
<span>共 {{rulesPageConfig.totalCount}} 条记录, </span>
|
||||
<span>
|
||||
每页
|
||||
<input class="form-control" ng-model="rulesPageConfig.pageSize"> 条记录
|
||||
</span>
|
||||
<!--<span>第 {{apisPageConfig.currentPageIndex}} / {{apisPageConfig.totalPage}} 页</span>-->
|
||||
</div>
|
||||
<!-- .tools -->
|
||||
</div>
|
||||
<!-- pagination-footer -->
|
||||
</div>
|
||||
<!-- .card -->
|
||||
</div>
|
||||
<!-- .col-md-12 -->
|
||||
</div>
|
||||
<!-- -->
|
||||
</div>
|
||||
<!-- .container-fluid -->
|
||||
@@ -0,0 +1,98 @@
|
||||
<div class="row" style="margin-left: 1px; margin-top:10px; height: 50px;">
|
||||
<div class="col-md-6" style="margin-bottom: 10px;">
|
||||
<span style="font-size: 30px;font-weight: bold;">{{app}}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="separator"></div>
|
||||
|
||||
<div class="container-fluid">
|
||||
<div class="row" style="margin-top: 20px; margin-bottom: 20px;">
|
||||
<div class="col-md-12">
|
||||
<div class="card">
|
||||
<div class="inputs-header">
|
||||
<span class="brand" style="font-size: 13px;">请求链路</span>
|
||||
<button class="btn btn-primary" style="float: right; margin-right: 10px; height: 30px;font-size: 12px;" ng-click="queryIdentities()">刷新</button>
|
||||
<input class="form-control witdh-200" placeholder="关键字" ng-model="searchKey" ng-change="searchChange(searchKey)">
|
||||
<div class="control-group" style="float:right;margin-right: 10px;margin-bottom: -10px;">
|
||||
<selectize id="gsInput" class="selectize-input-200" config="macsInputConfig" options="macsInputOptions" ng-model="macInputModel"
|
||||
placeholder="机器"></selectize>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!--.tools-header -->
|
||||
<div class="card-body" style="padding: 0px 0px;">
|
||||
<table rz-table id="identities" class="table" style="border-left: none; border-right:none;margin-top: 10px;">
|
||||
<thead>
|
||||
<thead>
|
||||
<tr style="background: #F3F5F7;">
|
||||
<td style="width: 34%;">
|
||||
API 名称
|
||||
</td>
|
||||
<td style="width: 8%;">
|
||||
API 类型
|
||||
</td>
|
||||
<td style="width: 7%;">通过 QPS</td>
|
||||
<td style="width: 7%;">拒绝 QPS</td>
|
||||
<td style="width: 5%;">线程数</td>
|
||||
<td style="width: 6%;">平均 RT</td>
|
||||
<td style="width: 6%;">分钟通过</td>
|
||||
<td style="width: 6%;">分钟拒绝</td>
|
||||
<td style="width: 20%">操作</td>
|
||||
</tr>
|
||||
<tr></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr dir-paginate="resource in identities | itemsPerPage: pageSize" current-page="currentPage" pagination-id="entriesPagination"
|
||||
data-tt-id="{{resource.ttId}}" data-tt-parent-id="{{resource.parentTtId}}" data-tt-visible="{{resource.visible}}">
|
||||
<td style="white-space: normal; text-align: left;">
|
||||
<span style="word-wrap:break-word;word-break:break-all;">{{resource.resource}}</span>
|
||||
</td>
|
||||
<td>
|
||||
<span ng-if="apiNames.indexOf(resource.resource) == -1">Route ID</span>
|
||||
<span ng-if="apiNames.indexOf(resource.resource) != -1">自定义 API</span>
|
||||
</td>
|
||||
<td>{{resource.passQps}}</td>
|
||||
<td>{{resource.blockQps}}</td>
|
||||
<td>{{resource.threadNum}}</td>
|
||||
<td>{{resource.averageRt}}</td>
|
||||
<td>{{resource.oneMinutePass}}</td>
|
||||
<td ng-if="$index==pageSize-1 || (currentPage>=identities.length/pageSize && $index==identities.length%pageSize-1)" ng-init="initTreeTable()">
|
||||
{{resource.oneMinuteBlock}}</td>
|
||||
<td ng-if="!($index==pageSize-1 || (currentPage>=identities.length/pageSize && $index==identities.length%pageSize-1))"> {{resource.oneMinuteBlock}}</td>
|
||||
<td>
|
||||
<div class="control-group">
|
||||
<button class="btn btn-xs btn-default" type="button" ng-click="addNewGatewayFlowRule(resource.resource)" style="font-size: 12px; height:25px;">
|
||||
<i class="fa fa-plus"></i> 流控</button>
|
||||
<button class="btn btn-xs btn-default" type="button" ng-click="addNewDegradeRule(resource.resource)" style="font-size: 12px; height:25px;">
|
||||
<i class="fa fa-plus"></i> 降级</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!-- .card-body -->
|
||||
<div class="pagination-footer">
|
||||
<dir-pagination-controls boundary-links="true" template-url="app/views/pagination.tpl.html" pagination-id="entriesPagination"
|
||||
on-page-change="">
|
||||
</dir-pagination-controls>
|
||||
<div class="tools">
|
||||
<span>共 {{totalCount}} 条记录, </span>
|
||||
<span>
|
||||
每页
|
||||
<input class="form-control" ng-model="pageSize"> 条记录
|
||||
</span>
|
||||
<!--<span>第 {{currentPage}} / {{totalPage}} 页</span>-->
|
||||
</div>
|
||||
<!-- .tools -->
|
||||
</div>
|
||||
<!-- pagination-footer -->
|
||||
</div>
|
||||
<!-- .card -->
|
||||
</div>
|
||||
<!-- .col-md-12 -->
|
||||
</div>
|
||||
<!-- -->
|
||||
</div>
|
||||
<!-- .container-fluid -->
|
||||
@@ -0,0 +1,110 @@
|
||||
<div class="row" style="margin-left: 1px; margin-top:10px; height: 50px;">
|
||||
<div class="col-md-6" style="margin-bottom: 10px;">
|
||||
<span style="font-size: 30px;font-weight: bold;">{{app}}</span>
|
||||
</div>
|
||||
<div class="btn-group" style="float:right; margin-right: 30px;" ng-show="!isTreeView" ng-click="treeView()">
|
||||
<button class="btn btn-default-inverse" style="height: 30px;font-size: 12px;">树状视图</button>
|
||||
<button class="btn btn-main" style="height: 30px;font-size: 12px;">列表视图</button>
|
||||
</div>
|
||||
<div class="btn-group" style="float:right; margin-right: 30px;" ng-show="isTreeView" ng-click="listView()">
|
||||
<button class="btn btn-main" style="height: 30px;font-size: 12px;">树状视图</button>
|
||||
<button class="btn btn-default-inverse" style="height: 30px;font-size: 12px;">列表视图</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="separator"></div>
|
||||
|
||||
<div class="container-fluid">
|
||||
<div class="row" style="margin-top: 20px; margin-bottom: 20px;">
|
||||
<div class="col-md-12">
|
||||
<div class="card">
|
||||
<div class="inputs-header">
|
||||
<span class="brand" style="font-size: 13px;">簇点链路</span>
|
||||
<button class="btn btn-primary" style="float: right; margin-right: 10px; height: 30px;font-size: 12px;" ng-click="queryIdentities()">刷新</button>
|
||||
<button class="btn btn-danger" style="float: right;margin-right: 10px;height: 30px;font-size: 12px;" ng-click="expandAll()"
|
||||
ng-if="!isExpand && isTreeView">展开</button>
|
||||
<button class="btn btn-danger" style="float: right;margin-right: 10px;height: 30px;font-size: 12px;" ng-click="collapseAll()"
|
||||
ng-if="isExpand && isTreeView">折叠</button>
|
||||
<input class="form-control witdh-200" placeholder="关键字" ng-model="searchKey" ng-change="searchChange(searchKey)">
|
||||
<div class="control-group" style="float:right;margin-right: 10px;margin-bottom: -10px;">
|
||||
<selectize id="gsInput" class="selectize-input-200" config="macsInputConfig" options="macsInputOptions" ng-model="macInputModel"
|
||||
placeholder="机器"></selectize>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!--.tools-header -->
|
||||
<div class="card-body" style="padding: 0px 0px;">
|
||||
<table rz-table id="identities" class="table" style="border-left: none; border-right:none;margin-top: 10px;">
|
||||
<thead>
|
||||
<thead>
|
||||
<tr style="background: #F3F5F7;">
|
||||
<td style="width: 40%;">
|
||||
资源名
|
||||
</td>
|
||||
<td style="width: 7%;">通过QPS</td>
|
||||
<td style="width: 7%;">拒绝QPS</td>
|
||||
<td style="width: 5%;">并发数</td>
|
||||
<td style="width: 6%;">平均RT</td>
|
||||
<td style="width: 6%;">分钟通过</td>
|
||||
<td style="width: 6%;">分钟拒绝</td>
|
||||
<td style="width: 23%">操作</td>
|
||||
</tr>
|
||||
<tr></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr dir-paginate="resource in identities | itemsPerPage: pageSize" current-page="currentPage" pagination-id="entriesPagination"
|
||||
data-tt-id="{{resource.ttId}}" data-tt-parent-id="{{resource.parentTtId}}" data-tt-visible="{{resource.visible}}">
|
||||
<td style="white-space: normal; text-align: left;">
|
||||
<a ng-click="copyIdentity(resource.resource)"
|
||||
title="{{resource.resource}} 单击复制到剪切板">
|
||||
<span style="word-wrap:break-word;word-break:break-all;">{{resource.resource}}</span>
|
||||
</a>
|
||||
</td>
|
||||
<td>{{resource.passQps}}</td>
|
||||
<td>{{resource.blockQps}}</td>
|
||||
<td>{{resource.threadNum}}</td>
|
||||
<td>{{resource.averageRt}}</td>
|
||||
<td>{{resource.oneMinutePass}}</td>
|
||||
<td ng-if="$index==pageSize-1 || (currentPage>=identities.length/pageSize && $index==identities.length%pageSize-1)" ng-init="initTreeTable()">
|
||||
{{resource.oneMinuteBlock}}</td>
|
||||
<td ng-if="!($index==pageSize-1 || (currentPage>=identities.length/pageSize && $index==identities.length%pageSize-1))"> {{resource.oneMinuteBlock}}</td>
|
||||
<td>
|
||||
<div class="control-group">
|
||||
<button class="btn btn-xs btn-default" type="button" ng-click="addNewFlowRule(resource.resource)" style="font-size: 12px; height:25px;">
|
||||
<i class="fa fa-plus"></i> 流控</button>
|
||||
<button class="btn btn-xs btn-default" type="button" ng-click="addNewDegradeRule(resource.resource)" style="font-size: 12px; height:25px;">
|
||||
<i class="fa fa-plus"></i> 熔断</button>
|
||||
<button class="btn btn-xs btn-default" type="button" ng-click="addNewParamFlowRule(resource.resource)" style="font-size: 12px; height:25px;">
|
||||
<i class="fa fa-plus"></i> 热点</button>
|
||||
<button class="btn btn-xs btn-default" type="button" ng-click="addNewAuthorityRule(resource.resource)" style="font-size: 12px; height:25px;">
|
||||
<i class="fa fa-plus"></i> 授权</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!-- .card-body -->
|
||||
<div class="pagination-footer">
|
||||
<dir-pagination-controls boundary-links="true" template-url="app/views/pagination.tpl.html" pagination-id="entriesPagination"
|
||||
on-page-change="">
|
||||
</dir-pagination-controls>
|
||||
<div class="tools">
|
||||
<span>共 {{totalCount}} 条记录, </span>
|
||||
<span>
|
||||
每页
|
||||
<input class="form-control" ng-model="pageSize"> 条记录
|
||||
</span>
|
||||
<span>第 {{currentPage}} / {{totalPage}} 页</span>
|
||||
</div>
|
||||
<!-- .tools -->
|
||||
</div>
|
||||
<!-- pagination-footer -->
|
||||
</div>
|
||||
<!-- .card -->
|
||||
</div>
|
||||
<!-- .col-md-12 -->
|
||||
</div>
|
||||
<!-- -->
|
||||
</div>
|
||||
<!-- .container-fluid -->
|
||||
@@ -0,0 +1,34 @@
|
||||
<div class="container">
|
||||
<div class="row" style="margin: 200px auto 15px auto; text-align: center;">
|
||||
<img src="./assets/img/sentinel-logo.png" alt="Sentinel Logo"
|
||||
style="max-height: 25%; max-width: 25%;">
|
||||
<!-- <h1 id='login_title'>Sentinel 控制台</h1> -->
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-4" >
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<form class="form-horizontal">
|
||||
<div class="form-group">
|
||||
<label class="col-md-2 control-label">用户</label>
|
||||
<div class="col-md-9">
|
||||
<input class="form-control" type="text" ng-model="username" autofocus="autofocus" required/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-md-2 control-label">密码</label>
|
||||
<div class="col-md-9">
|
||||
<input class="form-control" type="password" ng-model="password" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row text-center" style="margin-top: 20px;">
|
||||
<button class="btn btn-outline-success" style="height: 30px;font-size: 12px;"
|
||||
ng-click="login()">登录</button>
|
||||
<button class="btn btn-outline-primary" style="height: 30px;font-size: 12px;margin-left: 10px;"
|
||||
type="reset">清空</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,76 @@
|
||||
<div class="row" style="margin-left: 1px; margin-top:10px; height: 50px;">
|
||||
<div class="col-md-6" style="margin-bottom: 10px;">
|
||||
<span style="font-size: 30px;font-weight: bold;">{{app}}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!--<div>-->
|
||||
<!--<span>实例总数 {{machines.length}}, 健康 {{healthyCount}}, 失联 {{machines.length-healthyCount}}</span>-->
|
||||
<!--</div>-->
|
||||
|
||||
<div class="separator"></div>
|
||||
<div class="container-fluid">
|
||||
<div class="row" style="margin-top: 20px; margin-bottom: 20px;">
|
||||
<div class="col-md-12">
|
||||
<div class="card">
|
||||
<div class="inputs-header">
|
||||
<span class="brand" style="font-size: 13px;">机器列表</span>
|
||||
<span>实例总数 {{machines.length}}, 健康 {{healthyCount}}, 失联 {{machines.length - healthyCount}}.</span>
|
||||
<input class="form-control width-300" placeholder="关键字" ng-model="searchKey">
|
||||
</div>
|
||||
|
||||
<!--.tools-header -->
|
||||
<div class="card-body" style="padding: 0px 0px;">
|
||||
<table class="table" style="border-left: none; border-right:none;margin-top: 10px;">
|
||||
<thead>
|
||||
<tr style="background: #F3F5F7;">
|
||||
<td>机器名</td>
|
||||
<td>IP 地址</td>
|
||||
<td>端口号</td>
|
||||
<td>Sentinel 客户端版本</td>
|
||||
<td>健康状态</td>
|
||||
<td>心跳时间</td>
|
||||
<td>操作</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr dir-paginate="entry in machines | filter : searchKey | itemsPerPage: machinesPageConfig.pageSize " current-page="machinesPageConfig.currentPageIndex"
|
||||
pagination-id="entriesPagination">
|
||||
<td style="word-wrap:break-word;word-break:break-all;">{{entry.hostname}}</td>
|
||||
<td style="word-wrap:break-word;word-break:break-all;">{{entry.ip}}</td>
|
||||
<td> {{entry.port}} </td>
|
||||
<td> {{entry.version}} </td>
|
||||
<td ng-if="entry.healthy">健康</td>
|
||||
<td ng-if="!entry.healthy" style="color: red">失联</td>
|
||||
<td>{{entry.lastHeartbeat | date: 'yyyy/MM/dd HH:mm:ss'}}</td>
|
||||
<td>
|
||||
<button ng-if="!entry.healthy" class="btn btn-xs btn-outline-danger" style="height: 25px; font-size: 12px;" ng-click="removeMachine(entry.ip, entry.port)">移除</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!-- .card-body -->
|
||||
<div class="pagination-footer">
|
||||
<dir-pagination-controls boundary-links="true" template-url="app/views/pagination.tpl.html" pagination-id="entriesPagination"
|
||||
on-page-change="">
|
||||
</dir-pagination-controls>
|
||||
<div class="tools" style="">
|
||||
<span>共 {{machinesPageConfig.totalCount}} 条记录, </span>
|
||||
<span>
|
||||
每页
|
||||
<input class="form-control" ng-model="machinesPageConfig.pageSize"> 条记录,
|
||||
</span>
|
||||
<span>第 {{machinesPageConfig.currentPageIndex}} / {{machinesPageConfig.totalPage}} 页</span>
|
||||
</div>
|
||||
<!-- .tools -->
|
||||
</div>
|
||||
<!-- pagination-footer -->
|
||||
</div>
|
||||
<!-- .card -->
|
||||
</div>
|
||||
<!-- .col-md-12 -->
|
||||
</div>
|
||||
<!-- -->
|
||||
</div>
|
||||
<!-- .container-fluid -->
|
||||
@@ -0,0 +1,117 @@
|
||||
<div class="row" style="margin-left: 1px; margin-top:10px; height: 50px;">
|
||||
<div class="col-md-6" style="margin-bottom: 10px;">
|
||||
<span style="font-size: 30px;font-weight: bold;">{{app}}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="separator"></div>
|
||||
<div class="container-fluid">
|
||||
<div class="row" style="margin-top: 20px; margin-bottom: 20px;">
|
||||
<div class="col-md-12">
|
||||
<div class="card">
|
||||
<div class="inputs-header">
|
||||
<span class="brand" style="font-size: 13px;">
|
||||
<i class="fa fa-sort-amount-asc" aria-hidden="true" ng-show="!isDescOrder"></i>
|
||||
<i class="fa fa-sort-amount-desc" aria-hidden="true" ng-show="isDescOrder"></i>
|
||||
{{metricTypeDesc}} 实时监控
|
||||
</span>
|
||||
<button class="btn btn-outline-primary" style="float: right;margin-right: 10px;height: 30px;font-size: 12px;" ng-click="setDescOrder()"
|
||||
ng-if="!isDescOrder">降序</button>
|
||||
<button class="btn btn-outline-primary" style="float: right;margin-right: 10px;height: 30px;font-size: 12px;" ng-click="setAscOrder()"
|
||||
ng-if="isDescOrder">升序</button>
|
||||
|
||||
<input class="form-control witdh-200" placeholder="关键字" ng-model="serviceQuery" ng-change="searchService()" style="margin-right: 20px;">
|
||||
</div>
|
||||
<!--.tools-header -->
|
||||
<!--<div id="g2container"></div>-->
|
||||
<div class="card-body">
|
||||
<div class="row" style="margin-left: 3px;margin-right: 3px;">
|
||||
<div class="card-body" style="padding: 0px 0px;">
|
||||
<div class="col-md-12" ng-repeat="metric in metrics" style="padding-left: 10px;padding-right: 10px;">
|
||||
|
||||
<span ng-if="$index == metrics.length-1" ng-init="initAllChart()"></span>
|
||||
<div class="card" style="margin-bottom:20px;" ng-if="$index < metrics.length-1">
|
||||
<div class="tools-header" style="height: 45px;">
|
||||
<span class="brand" style="font-weight:normal;;word-break:break-all;word-wrap:break-word;"> {{metric.resource}}</span>
|
||||
<a ng-href="">
|
||||
<i class="fa fa-align-justify"></i>
|
||||
</a>
|
||||
</div>
|
||||
<!-- .tools-header -->
|
||||
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<!--<div ng-if="$index != metrics.length-1" id="chart{{$index}}"></div>-->
|
||||
<!--<div ng-if="$index == metrics.length-1" id="chart{{$index}}" ng-init="initAllChart()"></div>-->
|
||||
<div id="chart{{$index}}"></div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<table class="table table-hover table-striped table-bordered">
|
||||
<thead>
|
||||
<tr style="font-size: 13px;text-align:center;font-weight: bold;">
|
||||
<td style="word-wrap:break-word;word-break:break-all;">时间</td>
|
||||
<td style="word-wrap:break-word;word-break:break-all;" title="通过 QPS (pass qps)">通过 QPS</td>
|
||||
<td style="word-wrap:break-word;word-break:break-all;" title="拒绝 QPS (blocked qps)">拒绝QPS</td>
|
||||
<td style="word-wrap:break-word;word-break:break-all;" title="RT: 平均响应时间(毫秒)">响应时间(ms)</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<!-- thead -->
|
||||
<tbody>
|
||||
<tr style="font-size: 12px;text-align:center;" ng-repeat="tableObj in metric.shortData">
|
||||
<td style="word-wrap:break-word;word-break:break-all; ">{{tableObj.timestamp | date: 'HH:mm:ss'}}</td>
|
||||
<td style="word-wrap:break-word;word-break:break-all; ">{{tableObj.passQps | number : 1}}</td>
|
||||
<td style="word-wrap:break-word;word-break:break-all; ">{{tableObj.blockQps | number : 1}}</td>
|
||||
<td style="word-wrap:break-word;word-break:break-all;">{{tableObj.rt | number : 1}}</td>
|
||||
</tr>
|
||||
<tr style="font-size: 12px;text-align:center;" ng-repeat="_tr in [] | range : (6 - metric.shortData.length)">
|
||||
<td ng-repeat="_td in [] | range : 4">-</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<!-- tbody -->
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- .card-body -->
|
||||
</div>
|
||||
<!-- .card -->
|
||||
</div>
|
||||
<!-- .col-md-6 -->
|
||||
|
||||
</div>
|
||||
<!-- .card-body -->
|
||||
</div>
|
||||
<!-- .row -->
|
||||
</div>
|
||||
|
||||
<div style="display:none;">
|
||||
<li dir-paginate="empty in emptyObjs| itemsPerPage : servicePageConfig.pageSize" current-page="servicePageConfig.currentPageIndex"
|
||||
pagination-id="servicesPagination"></li>
|
||||
</div>
|
||||
|
||||
<div class="pagination-footer" style="margin-top:5px;">
|
||||
<dir-pagination-controls boundary-links="true" template-url="app/views/pagination.tpl.html" pagination-id="servicesPagination"
|
||||
on-page-change="pageChanged(newPageNumber)">
|
||||
</dir-pagination-controls>
|
||||
<div class="tools" style="">
|
||||
<span>共 {{emptyObjs.length}} 条记录, </span>
|
||||
<span>
|
||||
每页 {{servicePageConfig.pageSize}} 条记录
|
||||
<!--每页-->
|
||||
<!--<input class="form-control" ng-model="servicePageConfig.pageSize" ng-change="pageSizeChanged()">-->
|
||||
<!--条记录,-->
|
||||
</span>
|
||||
<span>第 {{servicePageConfig.currentPageIndex}} / {{servicePageConfig.totalPage}} 页</span>
|
||||
</div>
|
||||
<!-- .tools -->
|
||||
</div>
|
||||
<!-- pagination-footer -->
|
||||
<div class="row" style="margin-bottom:20px"></div>
|
||||
</div>
|
||||
<!-- .card -->
|
||||
</div>
|
||||
<!-- .col-md-12 -->
|
||||
</div>
|
||||
<!-- -->
|
||||
</div>
|
||||
@@ -0,0 +1,18 @@
|
||||
<div class="pagination" ng-if="1 < pages.length || !autoHide">
|
||||
<a href="" class="btn btn-default btn-xs" ng-if="boundaryLinks" ng-class="{ disabled : pagination.current == 1 }" ng-click="setCurrent(1)">
|
||||
<i class="fa fa-angle-double-left"></i>
|
||||
</a>
|
||||
<a href="" class="btn btn-default btn-xs" ng-if="directionLinks" ng-class="{ disabled : pagination.current == 1 }" ng-click="setCurrent(pagination.current - 1)">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
<a href="" class="btn btn-default btn-xs" ng-repeat="pageNumber in pages track by tracker(pageNumber, $index)" ng-class="{ active : pagination.current == pageNumber, disabled : pageNumber == '...' }"
|
||||
ng-click="setCurrent(pageNumber)">{{ pageNumber }}</a>
|
||||
<a href="" class="btn btn-default btn-xs" ng-if="directionLinks" ng-class="{ disabled : pagination.current == pagination.last }"
|
||||
ng-click="setCurrent(pagination.current + 1)">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
<a href="" class="btn btn-default btn-xs" ng-if="boundaryLinks" ng-class="{ disabled : pagination.current == pagination.last }"
|
||||
ng-click="setCurrent(pagination.last)">
|
||||
<i class="fa fa-angle-double-right"></i>
|
||||
</a>
|
||||
</div>
|
||||
@@ -0,0 +1,118 @@
|
||||
<div class="row" style="margin-left: 1px; margin-top:10px; height: 50px;">
|
||||
<div class="col-md-6" style="margin-bottom: 10px;">
|
||||
<span style="font-size: 30px;font-weight: bold;">{{app}}</span>
|
||||
</div>
|
||||
<div class="col-md-6" ng-if="!loadError">
|
||||
<button class="btn btn-default-inverse" style="float: right; margin-right: 10px;" ng-disabled="!macInputModel" ng-click="addNewRule()">
|
||||
<i class="fa fa-plus"></i> 新增热点限流规则</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="separator"></div>
|
||||
|
||||
<div class="container-fluid">
|
||||
<div class="row" style="margin-top: 20px; margin-bottom: 20px;">
|
||||
<div class="col-md-12">
|
||||
<div class="card">
|
||||
<div class="inputs-header">
|
||||
<span class="brand" style="font-size: 13px;">热点参数限流规则</span>
|
||||
<button class="btn btn-primary" style="float: right; margin-right: 10px; height: 30px;font-size: 12px;" ng-click="getMachineRules()">刷新</button>
|
||||
<input class="form-control witdh-200" placeholder="关键字" ng-model="searchKey">
|
||||
<div class="control-group" style="float:right;margin-right: 10px;margin-bottom: -10px;">
|
||||
<selectize id="gsInput" class="selectize-input-200" config="macsInputConfig" options="macsInputOptions" ng-model="macInputModel"
|
||||
placeholder="机器"></selectize>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- error panel -->
|
||||
<div class="row clearfix" ng-if="loadError">
|
||||
<div class="col-md-6 col-md-offset-3">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-body">
|
||||
<center>
|
||||
<p>{{loadError.message}}</p>
|
||||
</center>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Table and pagination start -->
|
||||
<!--.tools-header -->
|
||||
<div class="card-body" style="padding: 0px 0px;" ng-if="!loadError">
|
||||
<table class="table" style="border-left: none; border-right:none;margin-top: 10px;">
|
||||
<thead>
|
||||
<tr style="background: #F3F5F7;">
|
||||
<td style="width: 40%">
|
||||
资源名
|
||||
</td>
|
||||
<td style="width: 10%;">
|
||||
参数索引
|
||||
</td>
|
||||
<td style="width: 10%;">
|
||||
流控模式
|
||||
</td>
|
||||
<td style="width: 10%;">
|
||||
阈值
|
||||
</td>
|
||||
<td style="width: 8%;">
|
||||
是否集群
|
||||
</td>
|
||||
<td style="width: 10%;">
|
||||
例外项数目
|
||||
</td>
|
||||
<td style="width: 12%;">
|
||||
操作
|
||||
</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr dir-paginate="ruleEntity in rules | filter: searchKey | itemsPerPage: rulesPageConfig.pageSize " current-page="rulesPageConfig.currentPageIndex"
|
||||
pagination-id="entriesPagination">
|
||||
<td style="word-wrap:break-word;word-break:break-all;">{{ruleEntity.rule.resource}}</td>
|
||||
<td style="word-wrap:break-word;word-break:break-all;">{{ruleEntity.rule.paramIdx}}</td>
|
||||
<td>
|
||||
{{ruleEntity.rule.grade == 1 ? 'QPS' : '未知'}}
|
||||
</td>
|
||||
<td style="word-wrap:break-word;word-break:break-all;">
|
||||
{{ruleEntity.rule.count}}
|
||||
</td>
|
||||
<td>
|
||||
<span ng-if="ruleEntity.rule.clusterMode">是</span>
|
||||
<span ng-if="!ruleEntity.rule.clusterMode">否</span>
|
||||
</td>
|
||||
<td>
|
||||
{{ruleEntity.rule.paramFlowItemList == undefined ? 0 : ruleEntity.rule.paramFlowItemList.length}}
|
||||
</td>
|
||||
<td>
|
||||
<button class="btn btn-xs btn-default" type="button" ng-click="editRule(ruleEntity)" style="font-size: 12px; height:25px;">编辑</button>
|
||||
<button class="btn btn-xs btn-default" type="button" ng-click="deleteRule(ruleEntity)" style="font-size: 12px; height:25px;">删除</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!-- .card-body -->
|
||||
<div class="pagination-footer" ng-if="!loadError">
|
||||
<dir-pagination-controls boundary-links="true" template-url="app/views/pagination.tpl.html" pagination-id="entriesPagination"
|
||||
on-page-change="">
|
||||
</dir-pagination-controls>
|
||||
<div class="tools" style="">
|
||||
<span>共 {{rulesPageConfig.totalCount}} 条记录, </span>
|
||||
<span>每页 <input class="form-control" ng-model="rulesPageConfig.pageSize"> 条记录</span>
|
||||
<!--<span>第 {{rulesPageConfig.currentPageIndex}} / {{rulesPageConfig.totalPage}} 页</span>-->
|
||||
</div>
|
||||
<!-- .tools -->
|
||||
</div>
|
||||
<!-- pagination-footer -->
|
||||
<!-- Table and pagination end -->
|
||||
|
||||
</div>
|
||||
<!-- .card -->
|
||||
</div>
|
||||
<!-- .col-md-12 -->
|
||||
</div>
|
||||
<!-- -->
|
||||
</div>
|
||||
<!-- .container-fluid -->
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
<div class="row" style="margin-left: 1px; margin-top:10px; height: 50px;">
|
||||
<div class="col-md-6" style="margin-bottom: 10px;">
|
||||
<span style="font-size: 30px;font-weight: bold;">{{app}}</span>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<button class="btn btn-default-inverse" style="float: right; margin-right: 10px;" ng-disabled="!macInputModel" ng-click="addNewRule()">
|
||||
<i class="fa fa-plus"></i> 新增系统规则</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="separator"></div>
|
||||
|
||||
<div class="container-fluid">
|
||||
<div class="row" style="margin-top: 20px; margin-bottom: 20px;">
|
||||
<div class="col-md-12">
|
||||
<div class="card">
|
||||
<div class="inputs-header">
|
||||
<span class="brand" style="font-size: 13px;">系统规则</span>
|
||||
<!--<button class="btn btn-danger" style="float: right;margin-right: 10px;height: 30px;font-size: 12px;" ng-click="disableAll()">全部禁用</button>-->
|
||||
<button class="btn btn-primary" style="float: right; margin-right: 10px; height: 30px;font-size: 12px;" ng-click="getMachineRules()">刷新</button>
|
||||
<input class="form-control witdh-200" placeholder="关键字" ng-model="searchKey">
|
||||
<div class="control-group" style="float:right;margin-right: 10px;margin-bottom: -10px;">
|
||||
<selectize id="gsInput" class="selectize-input-200" config="macsInputConfig" options="macsInputOptions" ng-model="macInputModel"
|
||||
placeholder="机器"></selectize>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!--.tools-header -->
|
||||
<div class="card-body" style="padding: 0px 0px;">
|
||||
<table class="table" style="border-left: none; border-right:none;margin-top: 10px;">
|
||||
<thead>
|
||||
<tr style="background: #F3F5F7;">
|
||||
<td style="width: 40%;">
|
||||
阈值类型
|
||||
</td>
|
||||
<td style="width: 40%;">
|
||||
单机阈值
|
||||
</td>
|
||||
<td style="width: 12%;">
|
||||
操作
|
||||
</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr dir-paginate="rule in rules | filter : searchKey | itemsPerPage: rulesPageConfig.pageSize " current-page="rulesPageConfig.currentPageIndex"
|
||||
pagination-id="entriesPagination">
|
||||
<td style="word-wrap:break-word;word-break:break-all;">
|
||||
<span ng-if="rule.highestSystemLoad >= 0">系统 load(自适应)</span>
|
||||
<span ng-if="rule.avgRt >= 0">平均 RT</span>
|
||||
<span ng-if="rule.maxThread >= 0">并发数</span>
|
||||
<span ng-if="rule.qps >= 0">入口 QPS</span>
|
||||
<span ng-if="rule.highestCpuUsage >= 0">CPU 使用率</span>
|
||||
</td>
|
||||
<td style="word-wrap:break-word;word-break:break-all;">
|
||||
<span ng-if="rule.highestSystemLoad >= 0">{{rule.highestSystemLoad}}</span>
|
||||
<span ng-if="rule.avgRt >= 0">{{rule.avgRt}}</span>
|
||||
<span ng-if="rule.maxThread >= 0">{{rule.maxThread}}</span>
|
||||
<span ng-if="rule.qps >= 0">{{rule.qps}}</span>
|
||||
<span ng-if="rule.highestCpuUsage >= 0">{{rule.highestCpuUsage}}</span>
|
||||
</td>
|
||||
<td>
|
||||
<button class="btn btn-xs btn-default" type="button" ng-click="editRule(rule)" style="font-size: 12px; height:25px;">编辑</button>
|
||||
<button class="btn btn-xs btn-default" type="button" ng-click="deleteRule(rule)" style="font-size: 12px; height:25px;">删除</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!-- .card-body -->
|
||||
<div class="pagination-footer">
|
||||
<dir-pagination-controls boundary-links="true" template-url="app/views/pagination.tpl.html" pagination-id="entriesPagination"
|
||||
on-page-change="">
|
||||
</dir-pagination-controls>
|
||||
<div class="tools" style="">
|
||||
<span>共 {{rulesPageConfig.totalCount}} 条记录, </span>
|
||||
<span>
|
||||
每页
|
||||
<input class="form-control" ng-model="rulesPageConfig.pageSize"> 条记录,
|
||||
</span>
|
||||
<span>第 {{rulesPageConfig.currentPageIndex}} / {{rulesPageConfig.totalPage}} 页</span>
|
||||
</div>
|
||||
<!-- .tools -->
|
||||
</div>
|
||||
<!-- pagination-footer -->
|
||||
</div>
|
||||
<!-- .card -->
|
||||
</div>
|
||||
<!-- .col-md-12 -->
|
||||
</div>
|
||||
<!-- -->
|
||||
</div>
|
||||
<!-- .container-fluid -->
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 28 KiB |
3202
visual/sentinel-dashboard-pro/src/main/webapp/resources/dist/css/app.css
vendored
Normal file
3202
visual/sentinel-dashboard-pro/src/main/webapp/resources/dist/css/app.css
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1295
visual/sentinel-dashboard-pro/src/main/webapp/resources/dist/js/app.js
vendored
Normal file
1295
visual/sentinel-dashboard-pro/src/main/webapp/resources/dist/js/app.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
21574
visual/sentinel-dashboard-pro/src/main/webapp/resources/dist/js/app.vendor.js
vendored
Normal file
21574
visual/sentinel-dashboard-pro/src/main/webapp/resources/dist/js/app.vendor.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@@ -0,0 +1,134 @@
|
||||
const gulp = require('gulp');
|
||||
const plugins = require('gulp-load-plugins')();
|
||||
const open = require('open');
|
||||
const app = {
|
||||
srcPath: 'app/', // 源代码
|
||||
devPath: 'tmp/', // 开发打包
|
||||
prdPath: 'dist/' // 生产打包
|
||||
};
|
||||
|
||||
const JS_LIBS = [
|
||||
'node_modules/angular-ui-router/release/angular-ui-router.js',
|
||||
'node_modules/oclazyload/dist/ocLazyLoad.min.js',
|
||||
'node_modules/angular-loading-bar/build/loading-bar.min.js',
|
||||
'node_modules/angular-bootstrap/ui-bootstrap-tpls.min.js',
|
||||
'node_modules/moment/moment.js',
|
||||
'node_modules/angular-date-time-input/src/dateTimeInput.js',
|
||||
'node_modules/angularjs-bootstrap-datetimepicker/src/js/datetimepicker.js',
|
||||
'node_modules/angular-table-resize/dist/angular-table-resize.min.js',
|
||||
'node_modules/angular-clipboard/angular-clipboard.js',
|
||||
'node_modules/selectize/dist/js/standalone/selectize.js',
|
||||
'node_modules/angular-selectize2/dist/selectize.js',
|
||||
'node_modules/bootstrap-switch/dist/js/bootstrap-switch.min.js',
|
||||
'node_modules/ng-dialog/js/ngDialog.js',
|
||||
'node_modules/angular-ui-notification/dist/angular-ui-notification.min.js',
|
||||
'node_modules/angular-utils-pagination/dirPagination.js',
|
||||
'app/scripts/libs/treeTable.js',
|
||||
];
|
||||
|
||||
const CSS_APP = [
|
||||
'node_modules/angular-loading-bar/build/loading-bar.min.css',
|
||||
'node_modules/bootstrap-switch/dist/css/bootstrap3/bootstrap-switch.css',
|
||||
'node_modules/ng-dialog/css/ngDialog.min.css',
|
||||
'node_modules/ng-dialog/css/ngDialog-theme-default.css',
|
||||
'node_modules/angularjs-bootstrap-datetimepicker/src/css/datetimepicker.css',
|
||||
'node_modules/angular-ui-notification/dist/angular-ui-notification.min.css',
|
||||
'node_modules/angular-table-resize/dist/angular-table-resize.css',
|
||||
'node_modules/selectize/dist/css/selectize.css',
|
||||
'app/styles/page.css',
|
||||
'app/styles/timeline.css',
|
||||
'app/styles/main.css'
|
||||
];
|
||||
|
||||
const JS_APP = [
|
||||
'app/scripts/app.js',
|
||||
'app/scripts/filters/filters.js',
|
||||
'app/scripts/services/version_service.js',
|
||||
'app/scripts/services/auth_service.js',
|
||||
'app/scripts/services/appservice.js',
|
||||
'app/scripts/services/flow_service_v1.js',
|
||||
'app/scripts/services/flow_service_v2.js',
|
||||
'app/scripts/services/degrade_service.js',
|
||||
'app/scripts/services/systemservice.js',
|
||||
'app/scripts/services/machineservice.js',
|
||||
'app/scripts/services/identityservice.js',
|
||||
'app/scripts/services/metricservice.js',
|
||||
'app/scripts/services/param_flow_service.js',
|
||||
'app/scripts/services/authority_service.js',
|
||||
'app/scripts/services/cluster_state_service.js',
|
||||
'app/scripts/services/gateway/api_service.js',
|
||||
'app/scripts/services/gateway/flow_service.js',
|
||||
];
|
||||
|
||||
gulp.task('lib', function () {
|
||||
gulp.src(JS_LIBS)
|
||||
.pipe(plugins.concat('app.vendor.js'))
|
||||
.pipe(gulp.dest(app.devPath + 'js'))
|
||||
.pipe(plugins.uglify())
|
||||
.pipe(gulp.dest(app.prdPath + 'js'))
|
||||
.pipe(plugins.connect.reload());
|
||||
});
|
||||
|
||||
/*
|
||||
* css任务
|
||||
* 在src下创建style文件夹,里面存放less文件。
|
||||
*/
|
||||
gulp.task('css', function () {
|
||||
gulp.src(CSS_APP)
|
||||
.pipe(plugins.concat('app.css'))
|
||||
.pipe(gulp.dest(app.devPath + 'css'))
|
||||
.pipe(plugins.cssmin())
|
||||
.pipe(gulp.dest(app.prdPath + 'css'))
|
||||
.pipe(plugins.connect.reload());
|
||||
});
|
||||
|
||||
/*
|
||||
* js任务
|
||||
* 在src目录下创建script文件夹,里面存放所有的js文件
|
||||
*/
|
||||
gulp.task('js', function () {
|
||||
gulp.src(JS_APP)
|
||||
.pipe(plugins.concat('app.js'))
|
||||
.pipe(gulp.dest(app.devPath + 'js'))
|
||||
.pipe(plugins.uglify())
|
||||
.pipe(gulp.dest(app.prdPath + 'js'))
|
||||
.pipe(plugins.connect.reload());
|
||||
});
|
||||
|
||||
/*
|
||||
* js任务
|
||||
* 在src目录下创建script文件夹,里面存放所有的js文件
|
||||
*/
|
||||
gulp.task('jshint', function () {
|
||||
gulp.src(JS_APP)
|
||||
.pipe(plugins.jshint())
|
||||
.pipe(plugins.jshint.reporter());
|
||||
});
|
||||
|
||||
// 每次发布的时候,可能需要把之前目录内的内容清除,避免旧的文件对新的容有所影响。 需要在每次发布前删除dist和build目录
|
||||
gulp.task('clean', function () {
|
||||
gulp.src([app.devPath, app.prdPath])
|
||||
.pipe(plugins.clean());
|
||||
});
|
||||
|
||||
// 总任务
|
||||
gulp.task('build', ['clean', 'jshint', 'lib', 'js', 'css']);
|
||||
|
||||
// 服务
|
||||
gulp.task('serve', ['build'], function () {
|
||||
plugins.connect.server({ //启动一个服务器
|
||||
root: [app.devPath], // 服务器从哪个路径开始读取,默认从开发路径读取
|
||||
livereload: true, // 自动刷新
|
||||
port: 1234
|
||||
});
|
||||
// 打开浏览器
|
||||
setTimeout(() => {
|
||||
open('http://localhost:8080/index_dev.htm')
|
||||
}, 200);
|
||||
// 监听
|
||||
gulp.watch(app.srcPath + '**/*.js', ['js']);
|
||||
gulp.watch(app.srcPath + '**/*.css', ['css']);
|
||||
});
|
||||
|
||||
// 定义default任务
|
||||
gulp.task('default', ['serve']);
|
||||
@@ -0,0 +1,30 @@
|
||||
<!doctype html>
|
||||
<html class="no-js">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Sentinel Dashboard</title>
|
||||
<meta name="description" content="Sentinel 控制台,Sentinel Dashboard,Admin">
|
||||
<meta name="viewport" content="width=device-width">
|
||||
|
||||
<link rel="stylesheet" href="./lib/css/bootstrap.min.css" />
|
||||
<link rel="stylesheet" href="./lib/css/font-awesome.min.css">
|
||||
<link rel="stylesheet" href="./dist/css/app.css">
|
||||
<link rel="icon" type="image/x-icon" href="./static/favicon.ico">
|
||||
<base href="./">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div ng-app="sentinelDashboardApp">
|
||||
<div ui-view></div>
|
||||
</div>
|
||||
<script src="./lib/js/jquery.min.js"></script>
|
||||
<script src="./lib/js/bootstrap.min.js"></script>
|
||||
<script src="./lib/js/angular.min.js"></script>
|
||||
<script src="./lib/js/g2.min.js"></script>
|
||||
|
||||
<script src="./dist/js/app.vendor.js"></script>
|
||||
<script src="./dist/js/app.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,30 @@
|
||||
<!doctype html>
|
||||
<html class="no-js">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Sentinel 控制台</title>
|
||||
<meta name="description" content="Sentinel 控制台,Dashboard,Admin">
|
||||
<meta name="viewport" content="width=device-width">
|
||||
|
||||
<link rel="stylesheet" href="//cdn.bootcss.com/bootstrap/3.0.3/css/bootstrap.min.css" />
|
||||
<link rel="stylesheet" href="//cdn.bootcss.com/font-awesome/4.7.0/css/font-awesome.min.css">
|
||||
<link rel="stylesheet" href="http://localhost:1234/css/app.css">
|
||||
<link rel="icon" type="image/x-icon" href="./static/favicon.ico">
|
||||
<base href="./">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div ng-app="sentinelDashboardApp">
|
||||
<div ui-view></div>
|
||||
</div>
|
||||
<script src="//cdn.bootcss.com/jquery/2.1.4/jquery.min.js"></script>
|
||||
<script src="//cdn.bootcss.com/bootstrap/3.0.3/js/bootstrap.min.js"></script>
|
||||
<script src="//cdn.bootcss.com/angular.js/1.4.8/angular.js"></script>
|
||||
<script src="//gw.alipayobjects.com/os/antv/pkg/_antv.g2-3.4.10/dist/g2.min.js"></script>
|
||||
|
||||
<script src="http://localhost:1234/js/app.vendor.js"></script>
|
||||
<script src="http://localhost:1234/js/app.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
7
visual/sentinel-dashboard-pro/src/main/webapp/resources/lib/css/bootstrap.min.css
vendored
Normal file
7
visual/sentinel-dashboard-pro/src/main/webapp/resources/lib/css/bootstrap.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
4
visual/sentinel-dashboard-pro/src/main/webapp/resources/lib/css/font-awesome.min.css
vendored
Normal file
4
visual/sentinel-dashboard-pro/src/main/webapp/resources/lib/css/font-awesome.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user