Merge remote-tracking branch 'origin/fxz_dev' into leng_dev

This commit is contained in:
lbw committed 2022-04-19 23:26:23 +08:00
commit 7032f8b8b0
119 files changed
+1004 -234

No files matched your search

+36
View File
@@ -42,6 +42,7 @@
<javaformat.plugin.version>0.0.23</javaformat.plugin.version>
<docker.plugin.version>0.33.0</docker.plugin.version>
<cloud.plugin.version>1.0.0</cloud.plugin.version>
<sentinel.version>1.8.4</sentinel.version>
</properties>
<dependencyManagement>
@@ -287,6 +288,41 @@
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-core</artifactId>
<version>${sentinel.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-web-servlet</artifactId>
<version>${sentinel.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-transport-simple-http</artifactId>
<version>${sentinel.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-parameter-flow-control</artifactId>
<version>${sentinel.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-api-gateway-adapter-common</artifactId>
<version>${sentinel.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
@@ -6,7 +6,7 @@ server:
db:
num: 1
user: ${MYSQL_USER:root}
password: ${MYSQL_PWD:root}
password: ${MYSQL_PWD:127255}
url:
0: jdbc:mysql://${MYSQL_HOST:pigx-mysql}:${MYSQL_PORT:3306}/${MYSQL_DB:pigxx_config}?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=GMT%2B8&nullCatalogMeansCurrent=true&allowPublicKeyRetrieval=true
@@ -1,4 +1,4 @@
<#assign CACHE_VERSION = "v=1.0.33">
<#assign CACHE_VERSION = "v=v=1.0.7">
<!DOCTYPE html>
<html>
<head>
@@ -1,11 +1,11 @@
<#assign CACHE_VERSION = "v=1.0.5">
<#assign CACHE_VERSION = "v=1.0.7">
<#assign config_id = "${id!''}">
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>报表设计器 - 积木报表, 免费可视化报表工具!</title>
<title>积木报表—免费可视化Web报表工具</title>
<script>
let base = "${base}";
@@ -1058,7 +1058,7 @@
}
const options = {
"domain": 'http://localhost:8080/jeecg-boot',
"viewLocalImage":"/jmreport/img",//预览本地图片方法
"viewLocalImage":JM_VIEW_IMG_URL,//预览本地图片方法
"uploadUrl":"/jmreport/upload", //统一上传地址
"uploadExcelUrl":"/jmreport/importExcel?token="+token,//上传excel方法
pageSize: viewPageSize, //分页条数
@@ -1299,6 +1299,8 @@
</script>
<script type="text/javascript" src="${base}${customPrePath}/jmreport/desreport_/js/util.js?${CACHE_VERSION}"></script>
<script type="text/javascript" src="${base}${customPrePath}/jmreport/desreport_/js/biz/design.js?${CACHE_VERSION}"></script>
<script type="text/javascript" src="${base}${customPrePath}/jmreport/desreport_/cdn/vue/md5.min.js?${CACHE_VERSION}"></script>
<script type="text/javascript" src="${base}${customPrePath}/jmreport/desreport_/js/biz/SignMd5Util.js?${CACHE_VERSION}"></script>
<script>
window.onbeforeunload = function(event){
return '您可能有数据没有保存';
@@ -1,4 +1,4 @@
<#assign CACHE_VERSION = "v=1.0.5">
<#assign CACHE_VERSION = "v=v=1.0.7">
<!DOCTYPE html>
<html>
<head>
@@ -116,10 +116,10 @@
<#include "./template/list.ftl">
<div id="app" style="padding-left: 30px">
<div class="layout" style="margin-left: -30px;margin-top: -10px;">
<#-- <div style="background-color: #1890FF;height: 47px">-->
<#-- <span class="aui-logo"></span>-->
<#-- <span class="jimu-header">欢迎进入积木报表工作台 </span>-->
<#-- </div>-->
<div style="background-color: #1890FF;height: 47px">
<span class="aui-logo"></span>
<span class="jimu-header">欢迎进入积木报表工作台 </span>
</div>
<Layout>
<Sider breakpoint="md" collapsible :collapsed-width="78" v-model="isCollapsed">
<i-menu theme="primary" width="auto" :class="menuitemClasses" active-name="datainfo" :open-names="['sub']" @on-select="onMenuSelect">
@@ -467,7 +467,7 @@
slot: 'action',
className: 'table-background',
fixed:'right'
}
}
],//列表的列
},
computed: {
@@ -592,7 +592,7 @@
/**
* 为路径拼接token
* @param url 需要拼接的路径
* @return 拼接后的token
* @return 拼接后的token
*/
splicingToken(url){
if(this.token && "null" != this.token){
@@ -669,7 +669,7 @@
//报表设计和模板案例点击事件
tabsClick(name){
this.name=""
this.loadData(name)
this.loadData(name)
},
//回车搜索事件
enterSearchClick(){
@@ -742,7 +742,7 @@
}else{
//update-begin---author:wangshuai ---date:20220315 for:[issues/I4WWKE]分享链接的预览密码能否忽略------------
//兼容老数据没有是否显示密码的情况下;
// 0没有密码 1有密码;原来取消分享后的默认应该是1
//previewLockStatus 密码锁状态(0不存在密码锁,1存在密码锁);原来取消分享后的默认应该是1
if(!result.previewLockStatus){
result.previewLockStatus = "1"
}
@@ -765,7 +765,7 @@
if(item.thumb.indexOf('http')==0){
return item.thumb
}else{
return "${base}"+"${customPrePath}"+"/jmreport/img/"+item.thumb
return "${base}"+"${customPrePath}"+JM_VIEW_IMG_URL+"/"+item.thumb
}
}
}
@@ -1170,10 +1170,15 @@
//update-end---author:wangshuai ---date:20220221 for:[issues/I4OXTC]SQL数据集里数据预览报错
//update-end--Author:wangshuai--Date:20211014--for:参数为空不传递
this.tab3Loading=true;
//update-begin-author:taoyan date:20220324 for: JMREP-2518【安全】动态执行sql的接口,加上签名check,参考jeecgboot
let header = SignMd5Util.getHeader(api.loadTableData, data)
$http.post({
url:api.loadTableData,
data:data,
timeout:10000,
contentType: 'json',
header: header,
//update-end-author:taoyan date:20220324 for: JMREP-2518【安全】动态执行sql的接口,加上签名check,参考jeecgboot
success:(result)=>{
this.tab3Loading=false;
this.spinShow=false;
@@ -1453,11 +1458,15 @@
reqData['paramArray'] = JSON.stringify(this.tab2.data);
}
//update-end--Author:liusq Date:20210812 for:mongodb数据源传递报表参数--------------------
//update-begin-author:taoyan date:20220324 for: JMREP-2518【安全】动态执行sql的接口,加上签名check,参考jeecgboot
let header = SignMd5Util.getHeader(api.queryFieldBySql, reqData)
let dataStr = JSON.stringify(reqData);
$http.post({
url: api.queryFieldBySql,
contentType: 'json',
data: dataStr,
header: header,
//update-end-author:taoyan date:20220324 for: JMREP-2518【安全】动态执行sql的接口,加上签名check,参考jeecgboot
success:(result)=>{
let message = result['message'];
if(message){
@@ -170,7 +170,7 @@
//update-begin---author:wangshuai ---date:20220315 for:[issues/I4WWKE]分享链接的预览密码能否忽略------------
textarea.value = "预览地址: "+previewUrl+" \n"
//兼容老数据,如果是否分享密码为空的话,也得需要加上预览密码
//0没有密码 1有密码
//previewLockStatus 密码锁状态(0不存在密码锁,1存在密码锁)
if("0" != this.jurisdictionData.previewLockStatus){
textarea.value = textarea.value + "预览密码: "+previewLock+"\n"
}
@@ -33,7 +33,9 @@
</div>
</Row>
<Row class="ivurow" v-if="mapOption.mapType=='1' || mapOption.mapType=='2' || mapOption.mapType=='3'">
<p>地图级别&nbsp;&nbsp;</p>
<p v-if="mapOption.mapType=='1'">选择省份&nbsp;&nbsp;</p>
<p v-if="mapOption.mapType=='2'">选择城市&nbsp;&nbsp;</p>
<p v-if="mapOption.mapType=='3'">选择区域&nbsp;&nbsp;</p>
<div class="iSelect">
<Cascader v-if="mapOption.mapType=='1' " clearable transfer="true" :data="provinceData" size="small" @on-change="(value, selectedData) => provinceChange(value, selectedData)" v-model="mapOption.mapCode"></Cascader>
<Cascader v-if="mapOption.mapType=='2' " clearable transfer="true" :data="cityData" size="small" @on-change="(value, selectedData) => provinceChange(value, selectedData)" v-model="mapOption.mapCode"></Cascader>
@@ -285,7 +287,7 @@
"label": "市级",
"value": "2"
}, {
"label": "县级",
"label": "区级",
"value": "3"
}],//地图级别集合
mapUrl:"/jmreport/desreport_/regionaljson/", //请求路径
@@ -495,7 +497,9 @@
//type 1 省的json 2省市的json 3省市区的json
loadMapData(url,type) {
$http.get({
url:"${base}"+this.mapUrl+url,
//update-begin---author:wangshuai ---date:20220412 for:[issues/890]微服务下customPrePath不起作用------------
url:"${base}"+"${customPrePath}"+this.mapUrl+url,
//update-end---author:wangshuai ---date:20220412 for:[issues/890]微服务下customPrePath不起作用--------------
fail:(res)=>{
if(type == 1){
this.provinceData = res.data
@@ -0,0 +1,491 @@
<#--多选组件,用于远程搜索查询
item:传递的参数,包含字典code,查询key
value:view组件传递的值 v-model
index:下标-->
<script type="text/x-template" id="select-scroll-multiple-template">
<div :id="'click'+index">
<div class="ivu-select ivu-select-multiple ivu-select-small" style="width: 100%">
<div class="ivu-select-selection" @click="handleClick">
<span class="select-dict-text" v-if="dictText!=''">{{dictText}}</span>
<span v-else class="ivu-select-input" style="color: rgb(197,201,203)">{{'请选择'+dataList.name}}</span>
</div>
<i class="ivu-icon ivu-icon-md-close icon-position" @click="handleIconClick"></i>
</div>
<div class="scroll-class" v-if="scrollShow">
<Scroll :class="noData == true?'no-loading':''" :height="dictHeight" :on-reach-bottom="handleReachBottom"
:distance-to-edge="1">
<input v-model="searchText" type="text" placeholder="请输入查询条件" class="ivu-select-input bottom-border" />
<ul v-for="(item,index) in dictList" :key="index" @mousedown="scrollListClick(item,index)"
class="scroll-list-class">
<li :class="item.active == true?'select-item-selected':''"><span>{{ item.text }}</span></li>
</ul>
<ul class="no-more-data" v-if="noData">
<li style="font-size: 12px;text-align: center;margin-top:5px">暂无更多数据</li>
</ul>
</Scroll>
</div>
</div>
</script>
<script>
Vue.component('j-select-scroll-multiple', {
template: '#select-scroll-multiple-template',
props: {
item: {
type: Object,
default: () => []
},
value: {
type: String,
default: () => ""
},
index: {
type: Number
}
},
data() {
return {
// 表格高度
dictHeight: 200,
// 数据总数
tableCount: 0,
//下拉框数据
dataList: {},
dictList: [],
//字典文本
dictText: "",
// 是否加载中
tableLoading: false,
//滚动组件是否显示
scrollShow: false,
userpage: 1,
noData: false,
//初始字典text
initText:"",
//是否初始值
initValue:true,
//复制一份字典集合
copyDictList:[],
//搜索的文本
searchText:"",
//字典值
dictValue:""
}
},
create() {
},
watch: {
item: {
deep: true,
immediate: true,
handler: function () {
this.dictData();
}
},
value: {
immediate: true,
handler: function () {
this.setDictText();
}
},
searchText(){
this.handleOnChange();
}
},
mounted() {
//页面初始化监听click事件
document.addEventListener("click", e => {
this.closeScroll(e);
});
},
beforeDestroy() {
//页面销毁移除监听事件
document.removeEventListener("click", this.closeScroll);
},
methods: {
/**
* 关闭滚动弹窗
*/
closeScroll(e) {
let self = this;
let click = document.getElementById("click"+this.index);
if (click && !click.contains(e.target)) {
if (self.scrollShow == true){
self.scrollShow = false;
}
}
},
/**
* 获取字典数据
*/
dictData() {
if (this.item) {
this.dataList = JSON.parse(this.item)
if (this.dataList) {
this.dictList = this.dataList.dictList
this.copyDictList = this.dataList.dictList
//目前只有不勾选复选框并且不是select的时候才会分页,api和普通的字典不用去后台查数据
if(this.dataList.dictCode && this.dataList.dictCode.indexOf("select") ==-1 || this.dataList.dictList.length<7) {
this.noData = true
}
}
this.dictHeight = this.dictList.length>6?200:this.dictList.length*32+82
}
},
/**
*为字典值赋值
*/
setDictText() {
if (this.value.length == 0) {
this.dictText = "";
this.searchText = ""
//重置选中事件
this.restActive();
}else{
this.loopData(this.dictList);
}
},
/**
*赋值字典集合及是否选中
* @param dictList 字典集合
*/
loopData(dictList){
//初始化的时候才赋默认值
let dictText = this.dictText
let values = this.value
for (let i = 0; i <dictList.length ; i++) {
let value = dictList[i].value;
if(values.indexOf(value)!=-1){
//字典文本不为空,得判断时候包含此文本
if(dictText && dictText.indexOf(dictText) != 0){
dictText = dictText + dictList[i].text +","
}
//字典文本是空,直接相等即可
if(!dictText){
dictText = dictList[i].text;
}
dictList[i].active = true
}
}
if(dictText && dictText.charAt(dictText.length-1) == ","){
dictText = dictText.substring(0,dictText.length-1)
}
this.dictText = dictText
this.dictList = dictList
},
/**
* 重置选中状态
*/
restActive(){
let dictList = this.dictList
for (let i = 0; i <dictList.length ; i++) {
if(dictList[i].active){
dictList[i].active = false
}
}
},
/**
* 列表点击事件
* @param val 字典对象(value,text)
* @param index 当前循环的到第几个
*/
scrollListClick(val,index) {
if(this.dictText){
let dictTextArray = this.dictText.split(",");
let dictValueArray = this.value
//是否是已删除的
let remove = false
for (let i = 0; i < dictTextArray.length; i++) {
if(dictTextArray[i]==val.text){
dictTextArray.splice(i,1)
dictValueArray.splice(i,1)
remove =true
}
}
if(!remove){
dictTextArray.push(val.text)
dictValueArray.push(val.value)
}
if(dictTextArray.length>0){
this.dictText = dictTextArray.toString()
this.value = dictValueArray
}else{
this.dictText = ""
this.value = []
}
}else{
this.dictText = val.text
let valueArray = [];
valueArray.push(val.value)
this.value =valueArray
}
this.scollShow = true;
let active = this.dictList[index].active;
this.dictList[index].active = !active;
//返回值
this.returnDictValue(val.value);
},
/**
*输入框点击事件
*/
handleClick() {
this.scrollShow = !this.scrollShow;
},
/**
*滚动条无限加载
*/
handleReachBottom() {
if (!this.noData) {
return new Promise(resolve => {
if (this.noData) {
resolve();
}
this.userpage = this.userpage + 1;
setTimeout(() => {
if(!this.dataList.dictCode || this.dataList.dictCode.indexOf("select") !=-1) {
this.getDictData(0);
}else{
//前台进行搜索
if(this.dictText){
this.searchDictByText();
}else{
this.dictList = this.copyDictList
}
}
resolve();
}, 500);
});
}
},
/**
*值发生改变请求后台
*/
handleOnChange() {
this.userpage = 1;
this.scrollShow = true
//字典为空或者包含select语句才会后台搜索,否则直接在前台搜索
if(!this.dataList.dictCode || this.dataList.dictCode.indexOf("select") !=-1) {
//后台进行搜索
this.getDictData(1);
}else{
//前台进行搜索
if(this.dictText){
this.searchDictByText();
}else{
this.dictList = this.copyDictList
}
this.dictHeight = this.dictList.length>6?200:this.dictList.length*32+82
}
},
/**
* 查询字典数据
* @param type 字典值需要拼接还是直接替换 0拼接 1 替换
*/
getDictData(type) {
let paramSearch = this.dataList.paramSearch
let dbKey = this.dataList.dbCode + "__" + this.dataList.name
let params = getRequestUrlParam();
let dataStr = {
"reportId": configId,
"text": this.searchText,
"key": dbKey,
"paramSearch": paramSearch,
"params": params,
"pageNo": this.userpage
}
$http.get({
url: api.dictCodeSearch,
data: dataStr,
success: (res) => {
if (res && res.length > 0) {
let dictList = this.dictList;
//滚动条无限加载中直接push
if (type == 0) {
for (let i = 0; i < res.length; i++) {
dictList.push(res[i])
}
} else {
//查询直接赋值给字典数组
dictList = res
}
//循环数据赋是否已点击
this.loopData(dictList);
//判断长度,是否有数据
if(res.length<7){
this.noData = true
}else{
this.noData = false
}
} else {
if (type == 1) {
this.dictList = []
}
this.noData = true
}
//为下拉框设置高度
this.dictHeight = this.dictList.length>6?200:this.dictList.length*32+82
}
})
},
/**
*前台搜索
*/
searchDictByText(){
let dictList = this.dictList
let searchText = this.searchText
let newDictList = [];
if(!this.searchText){
this.dictList = this.copyDictList;
}else{
for (let i = 0; i <dictList.length; i++) {
let value = dictList[i].value;
if(value.indexOf(searchText) >= 0 || value == searchText){
newDictList.push(dictList[i])
}
}
if(newDictList.length>0){
this.dictList = newDictList;
}
}
this.dictHeight = this.dictList.length>6?150:this.dictList.length*32+82
},
/**
*返回父组件的值
*/
returnDictValue() {
let val = {};
val.value = this.value
val.key = this.dataList.key;
this.$emit('dictmultipleok', val)
},
/**
* 图标点击事件
*/
handleIconClick(){
this.dictText = ""
this.value = []
let val = {};
val.value = []
val.key = this.dataList.key;
this.$emit('dictmultipleok', val)
this.scollShow = false
}
}
})
</script>
<style scoped>
::-webkit-scrollbar {
width: 6px;
height: 16px;
background-color: #F5F5F5;
}
/*定义滚动条轨道 内阴影+圆角*/
::-webkit-scrollbar-track {
border-radius: 3px;
background-color: #f5f7f9;
}
/*定义滑块 内阴影+圆角*/
::-webkit-scrollbar-thumb {
border-radius: 3px;
background-color: #ccc;
}
.scroll-class {
position: absolute;
background: white;
z-index: 9999;
overflow: hidden;
border: 1px solid #dddee1;
border-radius: 4px;
top: 35px;
-webkit-box-shadow: 0 1px 6px rgba(0, 0, 0, 0.2);
box-shadow: 0 1px 6px rgba(0, 0, 0, 0.2);
}
.scroll-list-class {
cursor: pointer;
z-index: 9999;
list-style-type: none;
}
.scroll-list-class li span {
margin-left: 15px;
font-size: 14px;
}
.scroll-list-class :hover {
background: #f3f3f3;
}
.no-more-data {
border-top: 1px solid #f0f0f0;
}
.no-loading .ivu-spin-text {
display: none;
}
.no-loading .ivu-scroll-content-loading {
opacity: 1;
}
.ivu-scroll-loader {
margin-top: -5px;
}
.select-item-selected{
color:rgba(45,140,240,.9);
position:relative;
}
.select-item-selected :after{
display: inline-block;
font-family: Ionicons, serif;
speak: none;
font-style: normal;
font-weight: 400;
font-variant: normal;
text-transform: none;
text-rendering: optimizeLegibility;
line-height: 1;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
vertical-align: -0.125em;
text-align: center;
font-size: 24px;
content: "\F171";
color: rgba(45,140,240,.9);
position: absolute;
top: 2px;
right: 8px;
}
.select-dict-text{
display:inline;
float: left;
overflow: hidden;
text-overflow: ellipsis;
-o-text-overflow: ellipsis;
white-space:nowrap;
margin-left: 5px;
max-width:80%;
position:relative;
}
.bottom-border{
border-bottom:1px solid #f0f0f0;
}
.ivu-select-input{
display: inline;
height: 32px;
line-height: 32px;
width: 190px;
font-size: 14px;
outline: 0;
-webkit-box-sizing: border-box;
box-sizing: border-box;
color: #515a6e;
position: relative;
cursor: pointer;
padding-left: 16px;
}
.ivu-select-multiple .ivu-select-selection{
padding: 0 0 0 4px;
float: left;
}
.icon-position{
position: relative;
right: 25px;
top: 6px;
cursor: pointer;
}
</style>
@@ -5,7 +5,7 @@ value:view组件传递的值 v-model-->
<div>
<input ref="clickInput" class="ivu-input ivu-input-default" v-model="dictText"
:placeholder="'请选择'+dataList.name" @blur="clickBlur"
@click="handleClick"/>
@click="handleClick" style="cursor: pointer"/>
<div class="scoll-class" v-if="scollShow">
<Scroll :class="noData == true?'no-loading':''" :height="dictHeight" :on-reach-bottom="handleReachBottom"
:distance-to-edge="1">
@@ -75,8 +75,11 @@ value:view组件传递的值 v-model-->
this.setDictText();
}
},
dictText(val) {
this.handleOnChange()
dictText:{
immediate: true,
handler: function () {
this.handleOnChange()
}
}
},
methods: {
@@ -111,6 +114,9 @@ value:view组件传递的值 v-model-->
this.dictText = dict[0].text
this.initText = dict[0].text
}
//update-begin---author:wangshuai ---date:20220315 for:下拉单选样式出现混乱--------
this.dictHeight = this.dictList.length > 6 ? 200 : this.dictList.length * 32 + 50
//update-end---author:wangshuai ---date:20220315 for:下拉单选样式出现混乱--------
}
},
//无限加载中的事件
@@ -146,7 +152,13 @@ value:view组件传递的值 v-model-->
handleOnChange() {
let val = {}
val.key = this.dataList.key
val.value = this.dictText
//update-begin---author:wangshuai ---date:20220315 for:[JMREP-2552]下拉单选重置就出不来了--------
if(!this.dictText){
val.value = ""
}else{
val.value = this.value
}
//update-end---author:wangshuai ---date:20220315 for:[JMREP-2552]下拉单选重置就出不来了--------
this.$emit('dictok', val)
this.searchLocalOrApi();
},
@@ -162,7 +174,9 @@ value:view组件传递的值 v-model-->
this.searchDictByText();
} else {
this.dictList = this.copyDictList
this.dictHeight = this.dictList.length > 6 ? 200 : this.dictList.length * 30 + 20
//update-begin---author:wangshuai ---date:20220315 for:下拉单选样式出现混乱--------
this.dictHeight = this.dictList.length > 6 ? 200 : this.dictList.length * 32 + 50
//update-end---author:wangshuai ---date:20220315 for:下拉单选样式出现混乱--------
}
}
},
@@ -221,7 +235,9 @@ value:view组件传递的值 v-model-->
}
}
this.dictList = newDictList;
this.dictHeight = this.dictList.length > 6 ? 200 : this.dictList.length * 30 + 20
//update-begin---author:wangshuai ---date:20220315 for:下拉单选样式出现混乱--------
this.dictHeight = this.dictList.length > 6 ? 200 : this.dictList.length * 32 + 50
//update-end---author:wangshuai ---date:20220315 for:下拉单选样式出现混乱--------
}
}
})
@@ -39,6 +39,11 @@
</i-option>
</i-select>
</Row>
<Row class="ivurow rightFontSize">
<p style="margin-bottom: 10px;">是否显示数值&nbsp;&nbsp;</p>
<i-switch size="small" style="margin-left: 6px;" v-model="izShowNumber" @on-change="onSeriesFormatterChange"/>
</i-select>
</Row>
</div>
</Submenu>
</script>
@@ -59,9 +64,11 @@
textStyle_color:'',
textStyle_fontSize:'',
textStyle_fontWeight:'',
position:''
position:'',
formatter:''
},
labelPositions:[]
labelPositions:[],
izShowNumber:false
}
},
watch: {
@@ -77,11 +84,30 @@
initData: function (){
if (this.settings){
this.seriesOption = Object.assign(this.seriesOption, this.settings)
//update-begin---author:wangshuai ---date:20220407 for:[issues/I50IKB]饼图 如何直接在图上显示各分类对应的数值------------
if(this.seriesOption.formatter && "{b}"!=this.seriesOption.formatter){
this.izShowNumber = true;
}else{
this.izShowNumber = false;
}
//update-end---author:wangshuai ---date:20220407 for:[issues/I50IKB]饼图 如何直接在图上显示各分类对应的数值------------
}
},
onSeriesLabelChange(){
let {labelPositionArray,...otherOptions}=this.seriesOption;
this.$emit('change', otherOptions,'label')
},
/**
* 图表设置格式化
*/
onSeriesFormatterChange(){
if(this.izShowNumber){
this.seriesOption.formatter="{b}\n{c}"
}else{
this.seriesOption.formatter="{b}"
}
let {labelPositionArray,...otherOptions}=this.seriesOption;
this.$emit('change', otherOptions,'label')
}
}
})
@@ -1 +1,2 @@
<#include "components/select_scroll_radio.ftl">
<#include "components/select_scroll_radio.ftl">
<#include "components/select_scroll_multiple.ftl">
@@ -1,4 +1,4 @@
<#assign CACHE_VERSION = "v=1.0.5">
<#assign CACHE_VERSION = "v=v=1.0.7">
<#assign config_id = "${id!''}">
<#assign shareView = "${shareView}">
<!DOCTYPE html>
@@ -209,9 +209,7 @@ function getLocalRequestUrl() {
<template v-else>
<template v-if="item.dictList && item.dictList.length>0 && (item.mode==4 ||item.mode==3)">
<!-- 多选 -->
<i-select size="small" :ref="item.key" v-if="item.mode==3" :remote-method="(query)=>{dictCodeSearch(query,item)}" class="jm-select-box" filterable :transfer="true" v-model="queryInfo['onlyshow_'+item.key]" multiple :max-tag-count="1" @on-change="(arr)=>handleQueryMultiSelectChange(arr, item.key)" :placeholder="'请选择'+item.title">
<i-option v-for="(dict, dIndex) in item.dictList" :key="dIndex" :index="index" :value="dict.value">{{ dict.text }}</i-option>
</i-select>
<j-select-scroll-multiple v-if="item.mode==3" v-model="queryInfo['onlyshow_'+item.key]" :item="JSON.stringify(item)" @dictmultipleok="handleDictMultipleOk" :index="index"></j-select-scroll-multiple>
<!-- 单选 -->
<j-select-scroll-radio v-if="item.mode==4" v-model="queryInfo[item.key]" :item="JSON.stringify(item)" @dictok="handleDictOk"></j-select-scroll-radio>
@@ -301,5 +299,7 @@ function getLocalRequestUrl() {
<script type="text/javascript" src="${base}${customPrePath}/jmreport/desreport_/cdn/vue/xss-0.3.3.min.js?${CACHE_VERSION}"></script>
<script type="text/javascript" src="${base}${customPrePath}/jmreport/desreport_/js/biz/view.js?${CACHE_VERSION}"></script>
<#include "./common/tj.ftl">
<script type="text/javascript" src="${base}${customPrePath}/jmreport/desreport_/cdn/vue/md5.min.js?${CACHE_VERSION}"></script>
<script type="text/javascript" src="${base}${customPrePath}/jmreport/desreport_/js/biz/SignMd5Util.js?${CACHE_VERSION}"></script>
</body>
</html>
@@ -16,7 +16,7 @@
package com.alibaba.csp.sentinel.dashboard.auth;
/**
* Interface for authentication and authorization.
* Interface for authentication and authorization. 不需要改
*
* @author Carpenter Lee
* @since 1.5.0
@@ -15,59 +15,15 @@
*/
package com.alibaba.csp.sentinel.dashboard.auth;
import com.alibaba.csp.sentinel.dashboard.domain.Result;
import com.alibaba.fastjson.JSON;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;
/**
* The web interceptor for privilege-based authorization.
* The web interceptor for privilege-based authorization. 不需要改
*
* @author lkxiaolou
* @author wxq
* @since 1.7.1
*/
@Component
public class AuthorizationInterceptor implements HandlerInterceptor {
@Autowired
private AuthService<HttpServletRequest> authService;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
if (handler.getClass().isAssignableFrom(HandlerMethod.class)) {
Method method = ((HandlerMethod) handler).getMethod();
AuthAction authAction = method.getAnnotation(AuthAction.class);
if (authAction != null) {
AuthService.AuthUser authUser = authService.getAuthUser(request);
if (authUser == null) {
responseNoPrivilegeMsg(response, authAction.message());
return false;
}
String target = request.getParameter(authAction.targetName());
if (!authUser.authTarget(target, authAction.value())) {
responseNoPrivilegeMsg(response, authAction.message());
return false;
}
}
}
return true;
}
private void responseNoPrivilegeMsg(HttpServletResponse response, String message) throws IOException {
Result result = Result.ofFail(-1, message);
response.addHeader("Content-Type", "application/json;charset=UTF-8");
response.getOutputStream().write(JSON.toJSONBytes(result));
}
public interface AuthorizationInterceptor extends HandlerInterceptor {
}
@@ -0,0 +1,75 @@
/*
* 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.alibaba.csp.sentinel.dashboard.auth;
import com.alibaba.csp.sentinel.dashboard.domain.Result;
import com.alibaba.fastjson.JSON;
import org.springframework.web.method.HandlerMethod;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;
/**
* The web interceptor for privilege-based authorization. 不需要
* <p>
* move from old {@link AuthorizationInterceptor}.
*
* @author lkxiaolou
* @author wxq
* @since 1.7.1
*/
public class DefaultAuthorizationInterceptor implements AuthorizationInterceptor {
private final AuthService<HttpServletRequest> authService;
public DefaultAuthorizationInterceptor(AuthService<HttpServletRequest> authService) {
this.authService = authService;
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
if (handler.getClass().isAssignableFrom(HandlerMethod.class)) {
Method method = ((HandlerMethod) handler).getMethod();
AuthAction authAction = method.getAnnotation(AuthAction.class);
if (authAction != null) {
AuthService.AuthUser authUser = authService.getAuthUser(request);
if (authUser == null) {
responseNoPrivilegeMsg(response, authAction.message());
return false;
}
String target = request.getParameter(authAction.targetName());
if (!authUser.authTarget(target, authAction.value())) {
responseNoPrivilegeMsg(response, authAction.message());
return false;
}
}
}
return true;
}
private void responseNoPrivilegeMsg(HttpServletResponse response, String message) throws IOException {
Result result = Result.ofFail(-1, message);
response.addHeader("Content-Type", "application/json;charset=UTF-8");
response.getOutputStream().write(JSON.toJSONBytes(result));
}
}
@@ -0,0 +1,131 @@
/*
* 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.alibaba.csp.sentinel.dashboard.auth;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.util.AntPathMatcher;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
/**
* <p>
* The Servlet filter for authentication.
* </p>
*
* <p>
* Note: some urls are excluded as they needn't auth, such as:
* </p>
* <ul>
* <li>index url: {@code /}</li>
* <li>authentication request url: {@code /login}, {@code /logout}</li>
* <li>machine registry: {@code /registry/machine}</li>
* <li>static resources</li>
* </ul>
* <p>
* The excluded urls and urlSuffixes could be configured in {@code application.properties}
* file.
*
* @author cdfive 不需要
* @since 1.6.0
*/
public class DefaultLoginAuthenticationFilter implements LoginAuthenticationFilter {
private static final AntPathMatcher PATH_MATCHER = new AntPathMatcher();
private static final String URL_SUFFIX_DOT = ".";
/**
* 忽略鉴权的url
*/
@Value("#{'${auth.filter.exclude-urls}'.split(',')}")
private List<String> authFilterExcludeUrls;
/**
* 根据后缀不需要鉴权的url
*/
@Value("#{'${auth.filter.exclude-url-suffixes}'.split(',')}")
private List<String> authFilterExcludeUrlSuffixes;
/**
* Authentication using AuthService interface.
*/
private final AuthService<HttpServletRequest> authService;
public DefaultLoginAuthenticationFilter(AuthService<HttpServletRequest> authService) {
this.authService = authService;
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
String servletPath = httpRequest.getServletPath();
// Exclude the urls which needn't auth
boolean authFilterExcludeMatch = authFilterExcludeUrls.stream()
.anyMatch(authFilterExcludeUrl -> PATH_MATCHER.match(authFilterExcludeUrl, servletPath));
if (authFilterExcludeMatch) {
chain.doFilter(request, response);
return;
}
// Exclude the urls with suffixes which needn't auth
for (String authFilterExcludeUrlSuffix : authFilterExcludeUrlSuffixes) {
if (StringUtils.isBlank(authFilterExcludeUrlSuffix)) {
continue;
}
// Add . for url suffix so that we needn't add . in property file
if (!authFilterExcludeUrlSuffix.startsWith(URL_SUFFIX_DOT)) {
authFilterExcludeUrlSuffix = URL_SUFFIX_DOT + authFilterExcludeUrlSuffix;
}
if (servletPath.endsWith(authFilterExcludeUrlSuffix)) {
chain.doFilter(request, response);
return;
}
}
AuthService.AuthUser authUser = authService.getAuthUser(httpRequest);
HttpServletResponse httpResponse = (HttpServletResponse) response;
if (authUser == null) {
// If auth fail, set response status code to 401
httpResponse.setStatus(HttpStatus.UNAUTHORIZED.value());
}
else {
chain.doFilter(request, response);
}
}
@Override
public void destroy() {
}
}
@@ -15,7 +15,8 @@
*/
package com.alibaba.csp.sentinel.dashboard.auth;
import org.springframework.stereotype.Component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.http.HttpServletRequest;
@@ -25,9 +26,14 @@ import javax.servlet.http.HttpServletRequest;
* @author Carpenter Lee
* @since 1.5.0
*/
@Component
public class FakeAuthServiceImpl implements AuthService<HttpServletRequest> {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
public FakeAuthServiceImpl() {
this.logger.warn("there is no auth, use {} by implementation {}", AuthService.class, this.getClass());
}
@Override
public AuthUser getAuthUser(HttpServletRequest request) {
return new AuthUserImpl();
@@ -15,18 +15,7 @@
*/
package com.alibaba.csp.sentinel.dashboard.auth;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
import javax.servlet.Filter;
/**
* <p>
@@ -42,92 +31,14 @@ import java.util.List;
* <li>machine registry: {@code /registry/machine}</li>
* <li>static resources</li>
* </ul>
*
* <p>
* The excluded urls and urlSuffixes could be configured in {@code application.properties}
* file.
*
* @author cdfive
* @author cdfive 不需要
* @author wxq
* @since 1.6.0
*/
@Component
public class LoginAuthenticationFilter implements Filter {
private static final String URL_SUFFIX_DOT = ".";
private static final AntPathMatcher PATH_MATCHER = new AntPathMatcher();
/**
* Some urls which needn't auth, such as /auth/login, /registry/machine and so on.
*/
@Value("#{'${auth.filter.exclude-urls}'.split(',')}")
private List<String> authFilterExcludeUrls;
/**
* Some urls with suffixes which needn't auth, such as htm, html, js and so on.
*/
@Value("#{'${auth.filter.exclude-url-suffixes}'.split(',')}")
private List<String> authFilterExcludeUrlSuffixes;
/**
* Authentication using AuthService interface.
*/
@Autowired
private AuthService<HttpServletRequest> authService;
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
String servletPath = httpRequest.getServletPath();
// Exclude the urls which needn't auth
if (authFilterExcludeUrls.stream().anyMatch(s -> PATH_MATCHER.match(s, servletPath))) {
chain.doFilter(request, response);
return;
}
if (authFilterExcludeUrls.contains(servletPath)) {
chain.doFilter(request, response);
return;
}
// Exclude the urls with suffixes which needn't auth
for (String authFilterExcludeUrlSuffix : authFilterExcludeUrlSuffixes) {
if (StringUtils.isBlank(authFilterExcludeUrlSuffix)) {
continue;
}
// Add . for url suffix so that we needn't add . in property file
if (!authFilterExcludeUrlSuffix.startsWith(URL_SUFFIX_DOT)) {
authFilterExcludeUrlSuffix = URL_SUFFIX_DOT + authFilterExcludeUrlSuffix;
}
if (servletPath.endsWith(authFilterExcludeUrlSuffix)) {
chain.doFilter(request, response);
return;
}
}
AuthService.AuthUser authUser = authService.getAuthUser(httpRequest);
HttpServletResponse httpResponse = (HttpServletResponse) response;
if (authUser == null) {
// If auth fail, set response status code to 401
httpResponse.setStatus(HttpStatus.UNAUTHORIZED.value());
}
else {
chain.doFilter(request, response);
}
}
@Override
public void destroy() {
}
public interface LoginAuthenticationFilter extends Filter {
}
@@ -15,20 +15,13 @@
*/
package com.alibaba.csp.sentinel.dashboard.auth;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
/**
* @author cdfive
* @author cdfive 不需要
* @since 1.6.0
*/
@Component
@Primary
@ConditionalOnProperty(name = "auth.enabled", matchIfMissing = true)
public class SimpleWebAuthServiceImpl implements AuthService<HttpServletRequest> {
public static final String WEB_SESSION_KEY = "session_sentinel_admin";
@@ -171,8 +171,8 @@ public class SentinelApiClient {
}
/**
* Check wheter target instance (identified by tuple of app-ip:port) supports the form
* of "xxxxx; xx=xx" in "Content-Type" header.
* Check whether target instance (identified by tuple of app-ip:port) supports the
* form of "xxxxx; xx=xx" in "Content-Type" header.
* @param app target app name
* @param ip target node's address
* @param port target node's port
@@ -520,7 +520,7 @@ public class SentinelApiClient {
AssertUtil.notEmpty(ip, "Bad machine IP");
AssertUtil.isTrue(port > 0, "Bad machine port");
return fetchItemsAsync(ip, port, GET_PARAM_RULE_PATH, null, ParamFlowRule.class)
.thenApply(rules -> rules.stream().map(e -> ParamFlowRuleEntity.fromAuthorityRule(app, ip, port, e))
.thenApply(rules -> rules.stream().map(e -> ParamFlowRuleEntity.fromParamFlowRule(app, ip, port, e))
.collect(Collectors.toList()));
}
catch (Exception e) {
@@ -0,0 +1,59 @@
/*
* 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.alibaba.csp.sentinel.dashboard.config;
import com.alibaba.csp.sentinel.dashboard.auth.*;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.servlet.http.HttpServletRequest;
@Configuration
@EnableConfigurationProperties(AuthProperties.class)
public class AuthConfiguration {
private final AuthProperties authProperties;
public AuthConfiguration(AuthProperties authProperties) {
this.authProperties = authProperties;
}
@Bean
@ConditionalOnMissingBean
public AuthService<HttpServletRequest> httpServletRequestAuthService() {
if (this.authProperties.isEnabled()) {
return new SimpleWebAuthServiceImpl();
}
return new FakeAuthServiceImpl();
}
@Bean
@ConditionalOnMissingBean
public LoginAuthenticationFilter loginAuthenticationFilter(
AuthService<HttpServletRequest> httpServletRequestAuthService) {
return new DefaultLoginAuthenticationFilter(httpServletRequestAuthService);
}
@Bean
@ConditionalOnMissingBean
public AuthorizationInterceptor authorizationInterceptor(
AuthService<HttpServletRequest> httpServletRequestAuthService) {
return new DefaultAuthorizationInterceptor(httpServletRequestAuthService);
}
}
@@ -0,0 +1,33 @@
/*
* 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.alibaba.csp.sentinel.dashboard.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "auth")
public class AuthProperties {
private boolean enabled = true;
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
}
@@ -16,7 +16,6 @@
package com.alibaba.csp.sentinel.dashboard.controller;
import com.alibaba.csp.sentinel.dashboard.discovery.AppManagement;
import com.alibaba.csp.sentinel.dashboard.discovery.MachineDiscovery;
import com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo;
import com.alibaba.csp.sentinel.dashboard.domain.Result;
import com.alibaba.csp.sentinel.util.StringUtil;
@@ -28,6 +27,7 @@ import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import sun.net.util.IPAddressUtil;
@Controller
@RequestMapping(value = "/registry", produces = MediaType.APPLICATION_JSON_VALUE)
@@ -43,20 +43,27 @@ public class MachineRegistryController {
public Result<?> receiveHeartBeat(String app,
@RequestParam(value = "app_type", required = false, defaultValue = "0") Integer appType, Long version,
String v, String hostname, String ip, Integer port) {
if (app == null) {
app = MachineDiscovery.UNKNOWN_APP_NAME;
if (StringUtil.isBlank(app) || app.length() > 256) {
return Result.ofFail(-1, "invalid appName");
}
if (ip == null) {
return Result.ofFail(-1, "ip can't be null");
if (StringUtil.isBlank(ip) || ip.length() > 128) {
return Result.ofFail(-1, "invalid ip: " + ip);
}
if (port == null) {
return Result.ofFail(-1, "port can't be null");
if (!IPAddressUtil.isIPv4LiteralAddress(ip) && !IPAddressUtil.isIPv6LiteralAddress(ip)) {
return Result.ofFail(-1, "invalid ip: " + ip);
}
if (port == null || port < -1) {
return Result.ofFail(-1, "invalid port");
}
if (hostname != null && hostname.length() > 256) {
return Result.ofFail(-1, "hostname too long");
}
if (port == -1) {
logger.info("Receive heartbeat from " + ip + " but port not set yet");
logger.warn("Receive heartbeat from " + ip + " but port not set yet");
return Result.ofFail(-1, "your port not set yet");
}
String sentinelVersion = StringUtil.isEmpty(v) ? "unknown" : v;
String sentinelVersion = StringUtil.isBlank(v) ? "unknown" : v;
version = version == null ? System.currentTimeMillis() : version;
try {
MachineInfo machineInfo = new MachineInfo();
@@ -16,7 +16,6 @@
package com.alibaba.csp.sentinel.dashboard.controller;
import com.alibaba.csp.sentinel.dashboard.auth.AuthAction;
import com.alibaba.csp.sentinel.dashboard.auth.AuthService;
import com.alibaba.csp.sentinel.dashboard.auth.AuthService.PrivilegeType;
import com.alibaba.csp.sentinel.dashboard.client.CommandNotFoundException;
import com.alibaba.csp.sentinel.dashboard.client.SentinelApiClient;
@@ -111,7 +110,7 @@ public class ParamFlowRuleController {
}
@PostMapping("/rule")
@AuthAction(AuthService.PrivilegeType.WRITE_RULE)
@AuthAction(PrivilegeType.WRITE_RULE)
public Result<ParamFlowRuleEntity> apiAddParamFlowRule(@RequestBody ParamFlowRuleEntity entity) {
Result<ParamFlowRuleEntity> checkResult = checkEntityInternal(entity);
if (checkResult != null) {
@@ -183,7 +182,7 @@ public class ParamFlowRuleController {
}
@PutMapping("/rule/{id}")
@AuthAction(AuthService.PrivilegeType.WRITE_RULE)
@AuthAction(PrivilegeType.WRITE_RULE)
public Result<ParamFlowRuleEntity> apiUpdateParamFlowRule(@PathVariable("id") Long id,
@RequestBody ParamFlowRuleEntity entity) {
if (id == null || id <= 0) {
@@ -16,7 +16,6 @@
package com.alibaba.csp.sentinel.dashboard.controller.v2;
import com.alibaba.csp.sentinel.dashboard.auth.AuthAction;
import com.alibaba.csp.sentinel.dashboard.auth.AuthService;
import com.alibaba.csp.sentinel.dashboard.auth.AuthService.PrivilegeType;
import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;
import com.alibaba.csp.sentinel.dashboard.domain.Result;
@@ -127,7 +126,7 @@ public class FlowControllerV2 {
}
@PostMapping("/rule")
@AuthAction(value = AuthService.PrivilegeType.WRITE_RULE)
@AuthAction(value = PrivilegeType.WRITE_RULE)
public Result<FlowRuleEntity> apiAddFlowRule(@RequestBody FlowRuleEntity entity) {
Result<FlowRuleEntity> checkResult = checkEntityInternal(entity);
@@ -152,7 +151,7 @@ public class FlowControllerV2 {
}
@PutMapping("/rule/{id}")
@AuthAction(AuthService.PrivilegeType.WRITE_RULE)
@AuthAction(PrivilegeType.WRITE_RULE)
public Result<FlowRuleEntity> apiUpdateFlowRule(@PathVariable("id") Long id, @RequestBody FlowRuleEntity entity) {
if (id == null || id <= 0) {
@@ -38,7 +38,7 @@ public class ParamFlowRuleEntity extends AbstractRuleEntity<ParamFlowRule> {
this.rule = rule;
}
public static ParamFlowRuleEntity fromAuthorityRule(String app, String ip, Integer port, ParamFlowRule rule) {
public static ParamFlowRuleEntity fromParamFlowRule(String app, String ip, Integer port, ParamFlowRule rule) {
ParamFlowRuleEntity entity = new ParamFlowRuleEntity(rule);
entity.setApp(app);
entity.setIp(ip);
@@ -23,8 +23,21 @@ package com.alibaba.csp.sentinel.dashboard.domain.vo.gateway.api;
*/
public class ApiPredicateItemVo {
/**
* The pattern for matching url.
*/
private String pattern;
/**
* The matching Strategy in url. Constants are defined in class
* SentinelGatewayConstants.\
*
* <ul>
* <li>0(URL_MATCH_STRATEGY_EXACT): exact match mode</li>
* <li>1(URL_MATCH_STRATEGY_PREFIX): prefix match mode</li>
* <li>2(URL_MATCH_STRATEGY_REGEX): regex match mode</li>
* </ul>
*/
private Integer matchStrategy;
public String getPattern() {
@@ -84,7 +84,7 @@ public class MetricFetcher {
@SuppressWarnings("PMD.ThreadPoolCreationRule")
private ScheduledExecutorService fetchScheduleService = Executors.newScheduledThreadPool(1,
new NamedThreadFactory("sentinel-dashboard-metrics-fetch-task"));
new NamedThreadFactory("sentinel-dashboard-metrics-fetch-task", true));
private ExecutorService fetchService;
@@ -96,11 +96,11 @@ public class MetricFetcher {
int queueSize = 2048;
RejectedExecutionHandler handler = new DiscardPolicy();
fetchService = new ThreadPoolExecutor(cores, cores, keepAliveTime, TimeUnit.MILLISECONDS,
new ArrayBlockingQueue<>(queueSize), new NamedThreadFactory("sentinel-dashboard-metrics-fetchService"),
handler);
new ArrayBlockingQueue<>(queueSize),
new NamedThreadFactory("sentinel-dashboard-metrics-fetchService", true), handler);
fetchWorker = new ThreadPoolExecutor(cores, cores, keepAliveTime, TimeUnit.MILLISECONDS,
new ArrayBlockingQueue<>(queueSize), new NamedThreadFactory("sentinel-dashboard-metrics-fetchWorker"),
handler);
new ArrayBlockingQueue<>(queueSize),
new NamedThreadFactory("sentinel-dashboard-metrics-fetchWorker", true), handler);
IOReactorConfig ioConfig = IOReactorConfig.custom().setConnectTimeout(3000).setSoTimeout(3000)
.setIoThreadCount(Runtime.getRuntime().availableProcessors() * 2).build();
@@ -249,7 +249,7 @@ public class MetricFetcher {
catch (Exception e) {
logger.info(msg + " metric, wait http client error:", e);
}
long cost = System.currentTimeMillis() - start;
// long cost = System.currentTimeMillis() - start;
// logger.info("finished " + msg + " metric for " + app + ", time intervalMs [" +
// startTime + ", " + endTime
// + "], total machines=" + machines.size() + ", dead=" + dead + ", fetch
@@ -268,7 +268,7 @@ public class MetricFetcher {
lastFetchMs = lastFetchMs / 1000 * 1000;
long endTime = lastFetchMs + FETCH_INTERVAL_SECOND * 1000;
if (endTime > now - 1000 * 2) {
// to near
// too near
return;
}
// update last_fetch in advance.
@@ -336,26 +336,24 @@ public class MetricFetcher {
* aggregation metrics by app_resource_timeSecond, ignore ip and port.
*/
String key = buildMetricKey(machine.getApp(), node.getResource(), node.getTimestamp());
MetricEntity entity = map.get(key);
if (entity != null) {
entity.addPassQps(node.getPassQps());
entity.addBlockQps(node.getBlockQps());
entity.addRtAndSuccessQps(node.getRt(), node.getSuccessQps());
entity.addExceptionQps(node.getExceptionQps());
entity.addCount(1);
}
else {
entity = new MetricEntity();
entity.setApp(machine.getApp());
entity.setTimestamp(new Date(node.getTimestamp()));
entity.setPassQps(node.getPassQps());
entity.setBlockQps(node.getBlockQps());
entity.setRtAndSuccessQps(node.getRt(), node.getSuccessQps());
entity.setExceptionQps(node.getExceptionQps());
entity.setCount(1);
entity.setResource(node.getResource());
map.put(key, entity);
}
MetricEntity metricEntity = map.computeIfAbsent(key, s -> {
MetricEntity initMetricEntity = new MetricEntity();
initMetricEntity.setApp(machine.getApp());
initMetricEntity.setTimestamp(new Date(node.getTimestamp()));
initMetricEntity.setPassQps(0L);
initMetricEntity.setBlockQps(0L);
initMetricEntity.setRtAndSuccessQps(0, 0L);
initMetricEntity.setExceptionQps(0L);
initMetricEntity.setCount(0);
initMetricEntity.setResource(node.getResource());
return initMetricEntity;
});
metricEntity.addPassQps(node.getPassQps());
metricEntity.addBlockQps(node.getBlockQps());
metricEntity.addRtAndSuccessQps(node.getRt(), node.getSuccessQps());
metricEntity.addExceptionQps(node.getExceptionQps());
metricEntity.addCount(1);
}
catch (Exception e) {
logger.warn("handleBody line exception, machine: {}, line: {}", machine.toLogString(), line);
@@ -107,7 +107,8 @@ public final class ClusterEntityUtils {
if (mode == ClusterStateManager.CLUSTER_SERVER) {
String serverAddress = getIp(ip);
int port = stateVO.getState().getServer().getPort();
map.computeIfAbsent(serverAddress, v -> new ClusterGroupEntity().setBelongToApp(true)
String targetAddress = serverAddress + ":" + port;
map.computeIfAbsent(targetAddress, v -> new ClusterGroupEntity().setBelongToApp(true)
.setMachineId(ip + '@' + stateVO.getCommandPort()).setIp(ip).setPort(port));
}
}
@@ -120,8 +121,8 @@ public final class ClusterEntityUtils {
if (StringUtil.isBlank(targetServer) || targetPort == null || targetPort <= 0) {
continue;
}
ClusterGroupEntity group = map.computeIfAbsent(targetServer, v -> new ClusterGroupEntity()
String targetAddress = targetServer + ":" + targetPort;
ClusterGroupEntity group = map.computeIfAbsent(targetAddress, v -> new ClusterGroupEntity()
.setBelongToApp(true).setMachineId(targetServer).setIp(targetServer).setPort(targetPort));
group.getClientSet().add(ip + '@' + stateVO.getCommandPort());
}
File mode changed.
File mode changed.
File mode changed.
File mode changed.
File mode changed.
File mode changed.
File mode changed.
File mode changed.
File mode changed.
File mode changed.
File mode changed.
File mode changed.
File mode changed.
File mode changed.
File mode changed.
File mode changed.
File mode changed.
File mode changed.
File mode changed.
File mode changed.
File mode changed.
File mode changed.
File mode changed.
File mode changed.
Loaded 100 of 119 files, more files were not shown because too many files have changed in this diff. Show more