- Update dashboard API client to support authority rule commands - Add REST API and frontend page for authority rules Signed-off-by: Eric Zhao <sczyh16@gmail.com>master
@@ -331,6 +331,27 @@ public class SentinelApiClient { | |||||
return true; | return true; | ||||
} | } | ||||
public boolean setAuthorityRuleOfMachine(String app, String ip, int port, List<AuthorityRuleEntity> rules) { | |||||
if (rules == null) { | |||||
return true; | |||||
} | |||||
if (StringUtil.isBlank(ip) || port <= 0) { | |||||
throw new IllegalArgumentException("Invalid IP or port"); | |||||
} | |||||
String data = JSON.toJSONString( | |||||
rules.stream().map(AuthorityRuleEntity::getRule).collect(Collectors.toList())); | |||||
try { | |||||
data = URLEncoder.encode(data, DEFAULT_CHARSET.name()); | |||||
} catch (UnsupportedEncodingException e) { | |||||
logger.info("Encode rule error", e); | |||||
return false; | |||||
} | |||||
String url = "http://" + ip + ":" + port + "/" + SET_RULES_PATH + "?type=" + AUTHORITY_TYPE + "&data=" + data; | |||||
String result = httpGetContent(url); | |||||
logger.info("Push authority rules: " + result); | |||||
return true; | |||||
} | |||||
public CompletableFuture<Void> setParamFlowRuleOfMachine(String app, String ip, int port, List<ParamFlowRuleEntity> rules) { | public CompletableFuture<Void> setParamFlowRuleOfMachine(String app, String ip, int port, List<ParamFlowRuleEntity> rules) { | ||||
if (rules == null) { | if (rules == null) { | ||||
return CompletableFuture.completedFuture(null); | return CompletableFuture.completedFuture(null); | ||||
@@ -18,12 +18,16 @@ package com.taobao.csp.sentinel.dashboard.datasource.entity.rule; | |||||
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRule; | import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRule; | ||||
import com.alibaba.csp.sentinel.util.AssertUtil; | import com.alibaba.csp.sentinel.util.AssertUtil; | ||||
import com.fasterxml.jackson.annotation.JsonIgnore; | |||||
/** | /** | ||||
* @author Eric Zhao | * @author Eric Zhao | ||||
* @since 0.2.1 | * @since 0.2.1 | ||||
*/ | */ | ||||
public class AuthorityRuleEntity extends AbstractRuleEntity<AuthorityRule> { | public class AuthorityRuleEntity extends AbstractRuleEntity<AuthorityRule> { | ||||
public AuthorityRuleEntity() {} | |||||
public AuthorityRuleEntity(AuthorityRule authorityRule) { | public AuthorityRuleEntity(AuthorityRule authorityRule) { | ||||
AssertUtil.notNull(authorityRule, "Authority rule should not be null"); | AssertUtil.notNull(authorityRule, "Authority rule should not be null"); | ||||
this.rule = authorityRule; | this.rule = authorityRule; | ||||
@@ -37,14 +41,17 @@ public class AuthorityRuleEntity extends AbstractRuleEntity<AuthorityRule> { | |||||
return entity; | return entity; | ||||
} | } | ||||
@JsonIgnore | |||||
public String getLimitApp() { | public String getLimitApp() { | ||||
return rule.getLimitApp(); | return rule.getLimitApp(); | ||||
} | } | ||||
@JsonIgnore | |||||
public String getResource() { | public String getResource() { | ||||
return rule.getResource(); | return rule.getResource(); | ||||
} | } | ||||
@JsonIgnore | |||||
public int getStrategy() { | public int getStrategy() { | ||||
return rule.getStrategy(); | return rule.getStrategy(); | ||||
} | } | ||||
@@ -0,0 +1,38 @@ | |||||
/* | |||||
* Copyright 1999-2018 Alibaba Group Holding Ltd. | |||||
* | |||||
* Licensed under the Apache License, Version 2.0 (the "License"); | |||||
* you may not use this file except in compliance with the License. | |||||
* You may obtain a copy of the License at | |||||
* | |||||
* http://www.apache.org/licenses/LICENSE-2.0 | |||||
* | |||||
* Unless required by applicable law or agreed to in writing, software | |||||
* distributed under the License is distributed on an "AS IS" BASIS, | |||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
* See the License for the specific language governing permissions and | |||||
* limitations under the License. | |||||
*/ | |||||
package com.taobao.csp.sentinel.dashboard.repository.rule; | |||||
import java.util.concurrent.atomic.AtomicLong; | |||||
import com.taobao.csp.sentinel.dashboard.datasource.entity.rule.AuthorityRuleEntity; | |||||
import org.springframework.stereotype.Component; | |||||
/** | |||||
* In-memory storage for authority rules. | |||||
* | |||||
* @author Eric Zhao | |||||
* @since 0.2.1 | |||||
*/ | |||||
@Component | |||||
public class InMemAuthorityRuleStore extends InMemoryRuleRepositoryAdapter<AuthorityRuleEntity> { | |||||
private static AtomicLong ids = new AtomicLong(0); | |||||
@Override | |||||
protected long nextId() { | |||||
return ids.incrementAndGet(); | |||||
} | |||||
} |
@@ -15,11 +15,27 @@ | |||||
*/ | */ | ||||
package com.taobao.csp.sentinel.dashboard.view; | package com.taobao.csp.sentinel.dashboard.view; | ||||
import java.util.Date; | |||||
import java.util.List; | |||||
import com.alibaba.csp.sentinel.slots.block.RuleConstant; | |||||
import com.alibaba.csp.sentinel.util.StringUtil; | |||||
import com.taobao.csp.sentinel.dashboard.client.SentinelApiClient; | import com.taobao.csp.sentinel.dashboard.client.SentinelApiClient; | ||||
import com.taobao.csp.sentinel.dashboard.datasource.entity.rule.AuthorityRuleEntity; | |||||
import com.taobao.csp.sentinel.dashboard.discovery.MachineInfo; | |||||
import com.taobao.csp.sentinel.dashboard.repository.rule.RuleRepository; | |||||
import org.slf4j.Logger; | import org.slf4j.Logger; | ||||
import org.slf4j.LoggerFactory; | import org.slf4j.LoggerFactory; | ||||
import org.springframework.beans.factory.annotation.Autowired; | import org.springframework.beans.factory.annotation.Autowired; | ||||
import org.springframework.web.bind.annotation.DeleteMapping; | |||||
import org.springframework.web.bind.annotation.GetMapping; | |||||
import org.springframework.web.bind.annotation.PathVariable; | |||||
import org.springframework.web.bind.annotation.PostMapping; | |||||
import org.springframework.web.bind.annotation.PutMapping; | |||||
import org.springframework.web.bind.annotation.RequestBody; | |||||
import org.springframework.web.bind.annotation.RequestMapping; | import org.springframework.web.bind.annotation.RequestMapping; | ||||
import org.springframework.web.bind.annotation.RequestParam; | |||||
import org.springframework.web.bind.annotation.RestController; | import org.springframework.web.bind.annotation.RestController; | ||||
/** | /** | ||||
@@ -34,4 +50,134 @@ public class AuthorityRuleController { | |||||
@Autowired | @Autowired | ||||
private SentinelApiClient sentinelApiClient; | private SentinelApiClient sentinelApiClient; | ||||
@Autowired | |||||
private RuleRepository<AuthorityRuleEntity, Long> repository; | |||||
@GetMapping("/rules") | |||||
public Result<List<AuthorityRuleEntity>> apiQueryAllRulesForMachine(@RequestParam String app, | |||||
@RequestParam String ip, | |||||
@RequestParam Integer port) { | |||||
if (StringUtil.isEmpty(app)) { | |||||
return Result.ofFail(-1, "app cannot be null or empty"); | |||||
} | |||||
if (StringUtil.isEmpty(ip)) { | |||||
return Result.ofFail(-1, "ip cannot be null or empty"); | |||||
} | |||||
if (port == null || port <= 0) { | |||||
return Result.ofFail(-1, "Invalid parameter: port"); | |||||
} | |||||
try { | |||||
List<AuthorityRuleEntity> rules = sentinelApiClient.fetchAuthorityRulesOfMachine(app, ip, port); | |||||
rules = repository.saveAll(rules); | |||||
return Result.ofSuccess(rules); | |||||
} catch (Throwable throwable) { | |||||
logger.error("Error when querying authority rules", throwable); | |||||
return Result.ofFail(-1, throwable.getMessage()); | |||||
} | |||||
} | |||||
private <R> Result<R> checkEntityInternal(AuthorityRuleEntity entity) { | |||||
if (entity == null) { | |||||
return Result.ofFail(-1, "bad rule body"); | |||||
} | |||||
if (StringUtil.isBlank(entity.getApp())) { | |||||
return Result.ofFail(-1, "app can't be null or empty"); | |||||
} | |||||
if (StringUtil.isBlank(entity.getIp())) { | |||||
return Result.ofFail(-1, "ip can't be null or empty"); | |||||
} | |||||
if (entity.getPort() == null || entity.getPort() <= 0) { | |||||
return Result.ofFail(-1, "port can't be null"); | |||||
} | |||||
if (entity.getRule() == null) { | |||||
return Result.ofFail(-1, "rule can't be null"); | |||||
} | |||||
if (StringUtil.isBlank(entity.getResource())) { | |||||
return Result.ofFail(-1, "resource name cannot be null or empty"); | |||||
} | |||||
if (StringUtil.isBlank(entity.getLimitApp())) { | |||||
return Result.ofFail(-1, "limitApp should be valid"); | |||||
} | |||||
if (entity.getStrategy() != RuleConstant.AUTHORITY_WHITE | |||||
&& entity.getStrategy() != RuleConstant.AUTHORITY_BLACK) { | |||||
return Result.ofFail(-1, "Unknown strategy (must be blacklist or whitelist)"); | |||||
} | |||||
return null; | |||||
} | |||||
@PostMapping("/rule") | |||||
public Result<AuthorityRuleEntity> apiAddAuthorityRule(@RequestBody AuthorityRuleEntity entity) { | |||||
Result<AuthorityRuleEntity> checkResult = checkEntityInternal(entity); | |||||
if (checkResult != null) { | |||||
return checkResult; | |||||
} | |||||
entity.setId(null); | |||||
Date date = new Date(); | |||||
entity.setGmtCreate(date); | |||||
entity.setGmtModified(date); | |||||
try { | |||||
entity = repository.save(entity); | |||||
} catch (Throwable throwable) { | |||||
logger.error("Failed to add authority rule", throwable); | |||||
return Result.ofThrowable(-1, throwable); | |||||
} | |||||
if (!publishRules(entity.getApp(), entity.getIp(), entity.getPort())) { | |||||
logger.info("Publish authority rules failed after rule add"); | |||||
} | |||||
return Result.ofSuccess(entity); | |||||
} | |||||
@PutMapping("/rule/{id}") | |||||
public Result<AuthorityRuleEntity> apiUpdateParamFlowRule(@PathVariable("id") Long id, | |||||
@RequestBody AuthorityRuleEntity entity) { | |||||
if (id == null || id <= 0) { | |||||
return Result.ofFail(-1, "Invalid id"); | |||||
} | |||||
Result<AuthorityRuleEntity> checkResult = checkEntityInternal(entity); | |||||
if (checkResult != null) { | |||||
return checkResult; | |||||
} | |||||
entity.setId(id); | |||||
Date date = new Date(); | |||||
entity.setGmtCreate(null); | |||||
entity.setGmtModified(date); | |||||
try { | |||||
entity = repository.save(entity); | |||||
if (entity == null) { | |||||
return Result.ofFail(-1, "Failed to save authority rule"); | |||||
} | |||||
} catch (Throwable throwable) { | |||||
logger.error("Failed to save authority rule", throwable); | |||||
return Result.ofThrowable(-1, throwable); | |||||
} | |||||
if (!publishRules(entity.getApp(), entity.getIp(), entity.getPort())) { | |||||
logger.info("Publish authority rules failed after rule update"); | |||||
} | |||||
return Result.ofSuccess(entity); | |||||
} | |||||
@DeleteMapping("/rule/{id}") | |||||
public Result<Long> apiDeleteRule(@PathVariable("id") Long id) { | |||||
if (id == null) { | |||||
return Result.ofFail(-1, "id cannot be null"); | |||||
} | |||||
AuthorityRuleEntity oldEntity = repository.findById(id); | |||||
if (oldEntity == null) { | |||||
return Result.ofSuccess(null); | |||||
} | |||||
try { | |||||
repository.delete(id); | |||||
} catch (Exception e) { | |||||
return Result.ofFail(-1, e.getMessage()); | |||||
} | |||||
if (!publishRules(oldEntity.getApp(), oldEntity.getIp(), oldEntity.getPort())) { | |||||
logger.error("Publish authority rules failed after rule delete"); | |||||
} | |||||
return Result.ofSuccess(id); | |||||
} | |||||
private boolean publishRules(String app, String ip, Integer port) { | |||||
List<AuthorityRuleEntity> rules = repository.findAllByMachine(MachineInfo.of(app, ip, port)); | |||||
return sentinelApiClient.setAuthorityRuleOfMachine(app, ip, port, rules); | |||||
} | |||||
} | } |
@@ -98,6 +98,22 @@ angular | |||||
} | } | ||||
}) | }) | ||||
.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', { | .state('dashboard.degrade', { | ||||
templateUrl: 'app/views/degrade.html', | templateUrl: 'app/views/degrade.html', | ||||
url: '/degrade/:app', | url: '/degrade/:app', | ||||
@@ -0,0 +1,243 @@ | |||||
/** | |||||
* 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 = 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 | |||||
}); | |||||
}; | |||||
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; | |||||
} | |||||
$scope.saveRule = function () { | |||||
if (!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.health) { | |||||
$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(); | |||||
} | |||||
}); | |||||
}]); |
@@ -48,6 +48,10 @@ | |||||
<a ui-sref="dashboard.system({app: entry.app})"> | <a ui-sref="dashboard.system({app: entry.app})"> | ||||
<i class="glyphicon glyphicon-lock"></i> 系统规则</a> | <i class="glyphicon glyphicon-lock"></i> 系统规则</a> | ||||
</li> | </li> | ||||
<li ui-sref-active="active"> | |||||
<a ui-sref="dashboard.authority({app: entry.app})"> | |||||
<i class="glyphicon glyphicon-check"></i> 授权规则</a> | |||||
</li> | |||||
<li ui-sref-active="active"> | <li ui-sref-active="active"> | ||||
<a ui-sref="dashboard.machine({app: entry.app})"> | <a ui-sref="dashboard.machine({app: entry.app})"> | ||||
<i class="glyphicon glyphicon-th-list"></i> 机器列表</a> | <i class="glyphicon glyphicon-th-list"></i> 机器列表</a> | ||||
@@ -0,0 +1,40 @@ | |||||
/** | |||||
* 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' | |||||
}); | |||||
}; | |||||
}]); |
@@ -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,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-default-inverse" style="float:right; height: 30px;font-size: 12px;margin-left: 10px;" ng-click="closeThisDialog()">取消</button> | |||||
<button class="btn btn-danger-inverse" 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> |
@@ -51,6 +51,7 @@ const JS_APP = [ | |||||
'app/scripts/services/identityservice.js', | 'app/scripts/services/identityservice.js', | ||||
'app/scripts/services/metricservice.js', | 'app/scripts/services/metricservice.js', | ||||
'app/scripts/services/param_flow_service.js', | 'app/scripts/services/param_flow_service.js', | ||||
'app/scripts/services/authority_service.js', | |||||
]; | ]; | ||||
gulp.task('lib', function () { | gulp.task('lib', function () { | ||||