- 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; | |||
} | |||
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) { | |||
if (rules == 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.util.AssertUtil; | |||
import com.fasterxml.jackson.annotation.JsonIgnore; | |||
/** | |||
* @author Eric Zhao | |||
* @since 0.2.1 | |||
*/ | |||
public class AuthorityRuleEntity extends AbstractRuleEntity<AuthorityRule> { | |||
public AuthorityRuleEntity() {} | |||
public AuthorityRuleEntity(AuthorityRule authorityRule) { | |||
AssertUtil.notNull(authorityRule, "Authority rule should not be null"); | |||
this.rule = authorityRule; | |||
@@ -37,14 +41,17 @@ public class AuthorityRuleEntity extends AbstractRuleEntity<AuthorityRule> { | |||
return entity; | |||
} | |||
@JsonIgnore | |||
public String getLimitApp() { | |||
return rule.getLimitApp(); | |||
} | |||
@JsonIgnore | |||
public String getResource() { | |||
return rule.getResource(); | |||
} | |||
@JsonIgnore | |||
public int 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; | |||
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.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.LoggerFactory; | |||
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.RequestParam; | |||
import org.springframework.web.bind.annotation.RestController; | |||
/** | |||
@@ -34,4 +50,134 @@ public class AuthorityRuleController { | |||
@Autowired | |||
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', { | |||
templateUrl: 'app/views/degrade.html', | |||
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})"> | |||
<i class="glyphicon glyphicon-lock"></i> 系统规则</a> | |||
</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"> | |||
<a ui-sref="dashboard.machine({app: entry.app})"> | |||
<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/metricservice.js', | |||
'app/scripts/services/param_flow_service.js', | |||
'app/scripts/services/authority_service.js', | |||
]; | |||
gulp.task('lib', function () { | |||