commit
fb4108e980
@ -0,0 +1,9 @@ |
||||
CREATE TABLE `config_ip_blacklist` ( |
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, |
||||
`ip` varchar(64) NOT NULL DEFAULT '' COMMENT 'ip', |
||||
`remark` varchar(128) DEFAULT NULL COMMENT '备注', |
||||
`gmt_create` datetime DEFAULT CURRENT_TIMESTAMP, |
||||
`gmt_modified` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, |
||||
PRIMARY KEY (`id`), |
||||
UNIQUE KEY `uk_ip` (`ip`) USING BTREE |
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='IP黑名单'; |
@ -0,0 +1,98 @@ |
||||
package com.gitee.sop.adminserver.api.service; |
||||
|
||||
import com.alibaba.fastjson.JSON; |
||||
import com.gitee.easyopen.annotation.Api; |
||||
import com.gitee.easyopen.annotation.ApiService; |
||||
import com.gitee.easyopen.doc.annotation.ApiDoc; |
||||
import com.gitee.easyopen.doc.annotation.ApiDocMethod; |
||||
import com.gitee.easyopen.util.CopyUtil; |
||||
import com.gitee.fastmybatis.core.query.Query; |
||||
import com.gitee.fastmybatis.core.query.Sort; |
||||
import com.gitee.fastmybatis.core.support.PageEasyui; |
||||
import com.gitee.fastmybatis.core.util.MapperUtil; |
||||
import com.gitee.sop.adminserver.api.service.param.ConfigIpBlackForm; |
||||
import com.gitee.sop.adminserver.api.service.param.ConfigIpBlacklistPageParam; |
||||
import com.gitee.sop.adminserver.api.service.result.ConfigIpBlacklistVO; |
||||
import com.gitee.sop.adminserver.bean.ChannelMsg; |
||||
import com.gitee.sop.adminserver.bean.ConfigLimitDto; |
||||
import com.gitee.sop.adminserver.bean.ZookeeperContext; |
||||
import com.gitee.sop.adminserver.common.BizException; |
||||
import com.gitee.sop.adminserver.entity.ConfigIpBlacklist; |
||||
import com.gitee.sop.adminserver.mapper.ConfigIpBlacklistMapper; |
||||
import lombok.extern.slf4j.Slf4j; |
||||
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
||||
/** |
||||
* @author tanghc |
||||
*/ |
||||
@ApiService |
||||
@ApiDoc("IP黑名单管理") |
||||
@Slf4j |
||||
public class IPBlacklistApi { |
||||
|
||||
@Autowired |
||||
ConfigIpBlacklistMapper configIpBlacklistMapper; |
||||
|
||||
@ApiDocMethod(description = "获取IP黑名单,分页") |
||||
@Api(name = "ip.blacklist.page") |
||||
PageEasyui<ConfigIpBlacklistVO> page(ConfigIpBlacklistPageParam form) { |
||||
Query query = Query.build(form); |
||||
query.orderby("id", Sort.DESC); |
||||
return MapperUtil.queryForEasyuiDatagrid(configIpBlacklistMapper, query, ConfigIpBlacklistVO.class); |
||||
} |
||||
|
||||
@ApiDocMethod(description = "IP黑名单--新增") |
||||
@Api(name = "ip.blacklist.add") |
||||
void add(ConfigIpBlackForm form) { |
||||
ConfigIpBlacklist rec = configIpBlacklistMapper.getByColumn("ip", form.getIp()); |
||||
if (rec != null) { |
||||
throw new BizException("IP已添加"); |
||||
} |
||||
rec = new ConfigIpBlacklist(); |
||||
CopyUtil.copyPropertiesIgnoreNull(form, rec); |
||||
configIpBlacklistMapper.saveIgnoreNull(rec); |
||||
try { |
||||
this.sendIpBlacklistMsg(rec, BlacklistMsgType.ADD); |
||||
} catch (Exception e) { |
||||
log.error("推送IP黑名单失败, rec:{}",rec, e); |
||||
throw new BizException("推送IP黑名单失败"); |
||||
} |
||||
} |
||||
|
||||
@ApiDocMethod(description = "IP黑名单--修改") |
||||
@Api(name = "ip.blacklist.update") |
||||
void update(ConfigIpBlackForm form) { |
||||
ConfigIpBlacklist rec = configIpBlacklistMapper.getById(form.getId()); |
||||
CopyUtil.copyPropertiesIgnoreNull(form, rec); |
||||
configIpBlacklistMapper.updateIgnoreNull(rec); |
||||
} |
||||
|
||||
@ApiDocMethod(description = "IP黑名单--删除") |
||||
@Api(name = "ip.blacklist.del") |
||||
void del(long id) { |
||||
ConfigIpBlacklist rec = configIpBlacklistMapper.getById(id); |
||||
if (rec == null) { |
||||
return; |
||||
} |
||||
configIpBlacklistMapper.deleteById(id); |
||||
try { |
||||
this.sendIpBlacklistMsg(rec, BlacklistMsgType.DELETE); |
||||
} catch (Exception e) { |
||||
log.error("推送IP黑名单失败, rec:{}",rec, e); |
||||
throw new BizException("推送IP黑名单失败"); |
||||
} |
||||
} |
||||
|
||||
public void sendIpBlacklistMsg(ConfigIpBlacklist configIpBlacklist, BlacklistMsgType blacklistMsgType) throws Exception { |
||||
String configData = JSON.toJSONString(configIpBlacklist); |
||||
ChannelMsg channelMsg = new ChannelMsg(blacklistMsgType.name().toLowerCase(), configData); |
||||
String jsonData = JSON.toJSONString(channelMsg); |
||||
String path = ZookeeperContext.getIpBlacklistChannelPath(); |
||||
log.info("消息推送--IP黑名单设置({}), path:{}, data:{}",blacklistMsgType.name(), path, jsonData); |
||||
ZookeeperContext.createOrUpdateData(path, jsonData); |
||||
} |
||||
|
||||
enum BlacklistMsgType { |
||||
ADD, DELETE |
||||
} |
||||
} |
@ -0,0 +1,28 @@ |
||||
package com.gitee.sop.adminserver.api.service.param; |
||||
|
||||
import lombok.Data; |
||||
import org.hibernate.validator.constraints.Length; |
||||
|
||||
import javax.validation.constraints.NotBlank; |
||||
|
||||
|
||||
/** |
||||
* 表名:config_ip_black |
||||
* 备注:IP黑名单 |
||||
* |
||||
* @author tanghc |
||||
*/ |
||||
@Data |
||||
public class ConfigIpBlackForm { |
||||
/** 数据库字段:id */ |
||||
private Long id; |
||||
|
||||
/** ip, 数据库字段:ip */ |
||||
@NotBlank(message = "不能为空") |
||||
@Length(max = 30, message = "ip长度太长") |
||||
private String ip; |
||||
|
||||
/** 备注, 数据库字段:remark */ |
||||
@Length(max = 100, message = "备注长度太长") |
||||
private String remark; |
||||
} |
@ -0,0 +1,19 @@ |
||||
package com.gitee.sop.adminserver.api.service.param; |
||||
|
||||
import com.gitee.easyopen.doc.annotation.ApiDocField; |
||||
import com.gitee.fastmybatis.core.query.Operator; |
||||
import com.gitee.fastmybatis.core.query.annotation.Condition; |
||||
import com.gitee.fastmybatis.core.query.param.PageParam; |
||||
import lombok.Getter; |
||||
import lombok.Setter; |
||||
|
||||
/** |
||||
* @author tanghc |
||||
*/ |
||||
@Getter |
||||
@Setter |
||||
public class ConfigIpBlacklistPageParam extends PageParam { |
||||
@ApiDocField(description = "IP") |
||||
@Condition(operator = Operator.like) |
||||
private String ip; |
||||
} |
@ -0,0 +1,30 @@ |
||||
package com.gitee.sop.adminserver.api.service.result; |
||||
|
||||
import lombok.Data; |
||||
|
||||
import java.util.Date; |
||||
|
||||
|
||||
/** |
||||
* 表名:config_ip_black |
||||
* 备注:IP黑名单 |
||||
* |
||||
* @author tanghc |
||||
*/ |
||||
@Data |
||||
public class ConfigIpBlacklistVO { |
||||
/** 数据库字段:id */ |
||||
private Long id; |
||||
|
||||
/** ip, 数据库字段:ip */ |
||||
private String ip; |
||||
|
||||
/** 备注, 数据库字段:remark */ |
||||
private String remark; |
||||
|
||||
/** 数据库字段:gmt_create */ |
||||
private Date gmtCreate; |
||||
|
||||
/** 数据库字段:gmt_modified */ |
||||
private Date gmtModified; |
||||
} |
@ -0,0 +1,39 @@ |
||||
package com.gitee.sop.adminserver.entity; |
||||
|
||||
import lombok.Data; |
||||
|
||||
import javax.persistence.Column; |
||||
import javax.persistence.GeneratedValue; |
||||
import javax.persistence.GenerationType; |
||||
import javax.persistence.Id; |
||||
import javax.persistence.Table; |
||||
import java.util.Date; |
||||
|
||||
|
||||
/** |
||||
* 表名:config_ip_blacklist |
||||
* 备注:IP黑名单 |
||||
* |
||||
* @author tanghc |
||||
*/ |
||||
@Table(name = "config_ip_blacklist") |
||||
@Data |
||||
public class ConfigIpBlacklist { |
||||
@Id |
||||
@Column(name = "id") |
||||
@GeneratedValue(strategy = GenerationType.IDENTITY) |
||||
/** 数据库字段:id */ |
||||
private Long id; |
||||
|
||||
/** ip, 数据库字段:ip */ |
||||
private String ip; |
||||
|
||||
/** 备注, 数据库字段:remark */ |
||||
private String remark; |
||||
|
||||
/** 数据库字段:gmt_create */ |
||||
private Date gmtCreate; |
||||
|
||||
/** 数据库字段:gmt_modified */ |
||||
private Date gmtModified; |
||||
} |
@ -0,0 +1,12 @@ |
||||
package com.gitee.sop.adminserver.mapper; |
||||
|
||||
import com.gitee.fastmybatis.core.mapper.CrudMapper; |
||||
|
||||
import com.gitee.sop.adminserver.entity.ConfigIpBlacklist; |
||||
|
||||
|
||||
/** |
||||
* @author tanghc |
||||
*/ |
||||
public interface ConfigIpBlacklistMapper extends CrudMapper<ConfigIpBlacklist, Long> { |
||||
} |
@ -1 +1 @@ |
||||
<!DOCTYPE html><html><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge,chrome=1"><meta name=viewport content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no"><link rel=icon href=favicon.ico><title>SOP Admin</title><link href=static/css/chunk-elementUI.81cf475c.css rel=stylesheet><link href=static/css/chunk-libs.3dfb7769.css rel=stylesheet><link href=static/css/app.4f0872ef.css rel=stylesheet></head><body><noscript><strong>We're sorry but SOP Admin doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id=app></div><script>(function(e){function t(t){for(var r,c,o=t[0],i=t[1],f=t[2],l=0,d=[];l<o.length;l++)c=o[l],u[c]&&d.push(u[c][0]),u[c]=0;for(r in i)Object.prototype.hasOwnProperty.call(i,r)&&(e[r]=i[r]);h&&h(t);while(d.length)d.shift()();return a.push.apply(a,f||[]),n()}function n(){for(var e,t=0;t<a.length;t++){for(var n=a[t],r=!0,c=1;c<n.length;c++){var o=n[c];0!==u[o]&&(r=!1)}r&&(a.splice(t--,1),e=i(i.s=n[0]))}return e}var r={},c={runtime:0},u={runtime:0},a=[];function o(e){return i.p+"static/js/"+({}[e]||e)+"."+{"chunk-009073d4":"28312808","chunk-238a81e9":"5955f13d","chunk-25908fca":"ca176fa6","chunk-29e7142c":"994a3ac0","chunk-2d2085ef":"a63a74dc","chunk-2d22c2e3":"4a098244","chunk-37401378":"4e39ec9b","chunk-4a59cbe4":"a6360c68","chunk-6a68a33e":"b6685cb9","chunk-73b2dcec":"14f248eb"}[e]+".js"}function i(t){if(r[t])return r[t].exports;var n=r[t]={i:t,l:!1,exports:{}};return e[t].call(n.exports,n,n.exports,i),n.l=!0,n.exports}i.e=function(e){var t=[],n={"chunk-009073d4":1,"chunk-238a81e9":1,"chunk-25908fca":1,"chunk-29e7142c":1,"chunk-37401378":1,"chunk-6a68a33e":1,"chunk-73b2dcec":1};c[e]?t.push(c[e]):0!==c[e]&&n[e]&&t.push(c[e]=new Promise(function(t,n){for(var r="static/css/"+({}[e]||e)+"."+{"chunk-009073d4":"0af16c7e","chunk-238a81e9":"e8e2beee","chunk-25908fca":"89ab33e8","chunk-29e7142c":"d10599db","chunk-2d2085ef":"31d6cfe0","chunk-2d22c2e3":"31d6cfe0","chunk-37401378":"a43114f3","chunk-4a59cbe4":"31d6cfe0","chunk-6a68a33e":"3b12267b","chunk-73b2dcec":"99cf6327"}[e]+".css",u=i.p+r,a=document.getElementsByTagName("link"),o=0;o<a.length;o++){var f=a[o],l=f.getAttribute("data-href")||f.getAttribute("href");if("stylesheet"===f.rel&&(l===r||l===u))return t()}var d=document.getElementsByTagName("style");for(o=0;o<d.length;o++){f=d[o],l=f.getAttribute("data-href");if(l===r||l===u)return t()}var h=document.createElement("link");h.rel="stylesheet",h.type="text/css",h.onload=t,h.onerror=function(t){var r=t&&t.target&&t.target.src||u,a=new Error("Loading CSS chunk "+e+" failed.\n("+r+")");a.code="CSS_CHUNK_LOAD_FAILED",a.request=r,delete c[e],h.parentNode.removeChild(h),n(a)},h.href=u;var s=document.getElementsByTagName("head")[0];s.appendChild(h)}).then(function(){c[e]=0}));var r=u[e];if(0!==r)if(r)t.push(r[2]);else{var a=new Promise(function(t,n){r=u[e]=[t,n]});t.push(r[2]=a);var f,l=document.createElement("script");l.charset="utf-8",l.timeout=120,i.nc&&l.setAttribute("nonce",i.nc),l.src=o(e),f=function(t){l.onerror=l.onload=null,clearTimeout(d);var n=u[e];if(0!==n){if(n){var r=t&&("load"===t.type?"missing":t.type),c=t&&t.target&&t.target.src,a=new Error("Loading chunk "+e+" failed.\n("+r+": "+c+")");a.type=r,a.request=c,n[1](a)}u[e]=void 0}};var d=setTimeout(function(){f({type:"timeout",target:l})},12e4);l.onerror=l.onload=f,document.head.appendChild(l)}return Promise.all(t)},i.m=e,i.c=r,i.d=function(e,t,n){i.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},i.r=function(e){"undefined"!==typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},i.t=function(e,t){if(1&t&&(e=i(e)),8&t)return e;if(4&t&&"object"===typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(i.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var r in e)i.d(n,r,function(t){return e[t]}.bind(null,r));return n},i.n=function(e){var t=e&&e.__esModule?function(){return e["default"]}:function(){return e};return i.d(t,"a",t),t},i.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},i.p="",i.oe=function(e){throw console.error(e),e};var f=window["webpackJsonp"]=window["webpackJsonp"]||[],l=f.push.bind(f);f.push=t,f=f.slice();for(var d=0;d<f.length;d++)t(f[d]);var h=l;n()})([]);</script><script src=static/js/chunk-elementUI.8ebdfbab.js></script><script src=static/js/chunk-libs.9cf9cc40.js></script><script src=static/js/app.095e8b9c.js></script></body></html> |
||||
<!DOCTYPE html><html><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge,chrome=1"><meta name=viewport content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no"><link rel=icon href=favicon.ico><title>SOP Admin</title><link href=static/css/chunk-elementUI.81cf475c.css rel=stylesheet><link href=static/css/chunk-libs.3dfb7769.css rel=stylesheet><link href=static/css/app.4f0872ef.css rel=stylesheet></head><body><noscript><strong>We're sorry but SOP Admin doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id=app></div><script>(function(e){function n(n){for(var r,c,a=n[0],f=n[1],i=n[2],l=0,d=[];l<a.length;l++)c=a[l],u[c]&&d.push(u[c][0]),u[c]=0;for(r in f)Object.prototype.hasOwnProperty.call(f,r)&&(e[r]=f[r]);h&&h(n);while(d.length)d.shift()();return o.push.apply(o,i||[]),t()}function t(){for(var e,n=0;n<o.length;n++){for(var t=o[n],r=!0,c=1;c<t.length;c++){var a=t[c];0!==u[a]&&(r=!1)}r&&(o.splice(n--,1),e=f(f.s=t[0]))}return e}var r={},c={runtime:0},u={runtime:0},o=[];function a(e){return f.p+"static/js/"+({}[e]||e)+"."+{"chunk-238a81e9":"5955f13d","chunk-25908fca":"ca176fa6","chunk-29e7142c":"994a3ac0","chunk-2d2085ef":"7c741493","chunk-2d208c3a":"93f165b2","chunk-2d221c34":"8f017357","chunk-34c76be7":"98e1e7e5","chunk-37401378":"4e39ec9b","chunk-6f78c9fe":"f1ed64fa","chunk-73b2dcec":"14f248eb","chunk-9b31c83a":"2758df30"}[e]+".js"}function f(n){if(r[n])return r[n].exports;var t=r[n]={i:n,l:!1,exports:{}};return e[n].call(t.exports,t,t.exports,f),t.l=!0,t.exports}f.e=function(e){var n=[],t={"chunk-238a81e9":1,"chunk-25908fca":1,"chunk-29e7142c":1,"chunk-34c76be7":1,"chunk-37401378":1,"chunk-73b2dcec":1,"chunk-9b31c83a":1};c[e]?n.push(c[e]):0!==c[e]&&t[e]&&n.push(c[e]=new Promise(function(n,t){for(var r="static/css/"+({}[e]||e)+"."+{"chunk-238a81e9":"e8e2beee","chunk-25908fca":"89ab33e8","chunk-29e7142c":"d10599db","chunk-2d2085ef":"31d6cfe0","chunk-2d208c3a":"31d6cfe0","chunk-2d221c34":"31d6cfe0","chunk-34c76be7":"f531fb07","chunk-37401378":"a43114f3","chunk-6f78c9fe":"31d6cfe0","chunk-73b2dcec":"99cf6327","chunk-9b31c83a":"3b12267b"}[e]+".css",u=f.p+r,o=document.getElementsByTagName("link"),a=0;a<o.length;a++){var i=o[a],l=i.getAttribute("data-href")||i.getAttribute("href");if("stylesheet"===i.rel&&(l===r||l===u))return n()}var d=document.getElementsByTagName("style");for(a=0;a<d.length;a++){i=d[a],l=i.getAttribute("data-href");if(l===r||l===u)return n()}var h=document.createElement("link");h.rel="stylesheet",h.type="text/css",h.onload=n,h.onerror=function(n){var r=n&&n.target&&n.target.src||u,o=new Error("Loading CSS chunk "+e+" failed.\n("+r+")");o.code="CSS_CHUNK_LOAD_FAILED",o.request=r,delete c[e],h.parentNode.removeChild(h),t(o)},h.href=u;var s=document.getElementsByTagName("head")[0];s.appendChild(h)}).then(function(){c[e]=0}));var r=u[e];if(0!==r)if(r)n.push(r[2]);else{var o=new Promise(function(n,t){r=u[e]=[n,t]});n.push(r[2]=o);var i,l=document.createElement("script");l.charset="utf-8",l.timeout=120,f.nc&&l.setAttribute("nonce",f.nc),l.src=a(e),i=function(n){l.onerror=l.onload=null,clearTimeout(d);var t=u[e];if(0!==t){if(t){var r=n&&("load"===n.type?"missing":n.type),c=n&&n.target&&n.target.src,o=new Error("Loading chunk "+e+" failed.\n("+r+": "+c+")");o.type=r,o.request=c,t[1](o)}u[e]=void 0}};var d=setTimeout(function(){i({type:"timeout",target:l})},12e4);l.onerror=l.onload=i,document.head.appendChild(l)}return Promise.all(n)},f.m=e,f.c=r,f.d=function(e,n,t){f.o(e,n)||Object.defineProperty(e,n,{enumerable:!0,get:t})},f.r=function(e){"undefined"!==typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},f.t=function(e,n){if(1&n&&(e=f(e)),8&n)return e;if(4&n&&"object"===typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(f.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&n&&"string"!=typeof e)for(var r in e)f.d(t,r,function(n){return e[n]}.bind(null,r));return t},f.n=function(e){var n=e&&e.__esModule?function(){return e["default"]}:function(){return e};return f.d(n,"a",n),n},f.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},f.p="",f.oe=function(e){throw console.error(e),e};var i=window["webpackJsonp"]=window["webpackJsonp"]||[],l=i.push.bind(i);i.push=n,i=i.slice();for(var d=0;d<i.length;d++)n(i[d]);var h=l;t()})([]);</script><script src=static/js/chunk-elementUI.8ebdfbab.js></script><script src=static/js/chunk-libs.9cf9cc40.js></script><script src=static/js/app.4a507d5e.js></script></body></html> |
@ -1 +1 @@ |
||||
.custom-tree-node{-webkit-box-flex:1;-ms-flex:1;flex:1;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;font-size:14px;padding-right:8px}.el-input.is-disabled .el-input__inner,.el-radio__input.is-disabled+span.el-radio__label{color:#909399}.limit-tip[data-v-b81bfbde]{cursor:pointer;margin-left:10px} |
||||
.custom-tree-node{-webkit-box-flex:1;-ms-flex:1;flex:1;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;font-size:14px;padding-right:8px}.el-input.is-disabled .el-input__inner,.el-radio__input.is-disabled+span.el-radio__label{color:#909399}.limit-tip[data-v-51d6f4a2]{cursor:pointer;margin-left:10px} |
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1 +1 @@ |
||||
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-2d22c2e3"],{f1ac:function(t,e,n){"use strict";n.r(e);var a=function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("div",{staticClass:"app-container"},[n("el-form",{staticClass:"demo-form-inline",attrs:{inline:!0,model:t.searchFormData,size:"mini"}},[n("el-form-item",{attrs:{label:"serviceId"}},[n("el-input",{staticStyle:{width:"250px"},attrs:{clearable:!0,placeholder:"serviceId"},model:{value:t.searchFormData.serviceId,callback:function(e){t.$set(t.searchFormData,"serviceId",e)},expression:"searchFormData.serviceId"}})],1),t._v(" "),n("el-form-item",[n("el-button",{attrs:{type:"primary",icon:"el-icon-search"},on:{click:t.onSearchTable}},[t._v("查询")])],1)],1),t._v(" "),n("el-table",{staticStyle:{width:"100%","margin-bottom":"20px"},attrs:{data:t.tableData,border:"","row-key":"id"}},[n("el-table-column",{attrs:{prop:"serviceId",label:"服务名称",width:"200"},scopedSlots:t._u([{key:"default",fn:function(e){return[n("span",{domProps:{innerHTML:t._s(t.renderServiceName(e.row))}})]}}])}),t._v(" "),n("el-table-column",{attrs:{prop:"ipPort",label:"IP端口",width:"250"}}),t._v(" "),n("el-table-column",{attrs:{prop:"status",label:"服务状态",width:"100"},scopedSlots:t._u([{key:"default",fn:function(e){return[e.row.parentId>0&&"UP"===e.row.status?n("el-tag",{attrs:{type:"success"}},[t._v("已上线")]):t._e(),t._v(" "),e.row.parentId>0&&"STARTING"===e.row.status?n("el-tag",{attrs:{type:"info"}},[t._v("正在启动")]):t._e(),t._v(" "),e.row.parentId>0&&"UNKNOWN"===e.row.status?n("el-tag",[t._v("未知")]):t._e(),t._v(" "),e.row.parentId>0&&("OUT_OF_SERVICE"===e.row.status||"DOWN"===e.row.status)?n("el-tag",{attrs:{type:"danger"}},[t._v("已下线")]):t._e()]}}])}),t._v(" "),n("el-table-column",{attrs:{prop:"updateTime",label:"最后更新时间",width:"160"}}),t._v(" "),n("el-table-column",{attrs:{label:"操作",width:"100"},scopedSlots:t._u([{key:"default",fn:function(e){return[e.row.parentId>0&&"UP"===e.row.status?n("el-button",{attrs:{type:"text",size:"mini"},on:{click:function(n){return t.onOffline(e.row)}}},[t._v("下线")]):t._e(),t._v(" "),e.row.parentId>0&&"OUT_OF_SERVICE"===e.row.status?n("el-button",{attrs:{type:"text",size:"mini"},on:{click:function(n){return t.onOnline(e.row)}}},[t._v("上线")]):t._e()]}}])})],1)],1)},r=[],i=(n("ac6a"),{data:function(){return{searchFormData:{serviceId:""},tableData:[]}},created:function(){this.loadTable()},methods:{loadTable:function(){this.post("service.instance.list",this.searchFormData,function(t){this.tableData=this.buildTreeData(t.data)})},buildTreeData:function(t){return t.forEach(function(e){var n=e.parentId;0===n||t.forEach(function(t){if(t.id===n){var a=t.children;a||(a=[]),a.push(e),t.children=a}})}),t=t.filter(function(t){return 0===t.parentId}),t},onSearchTable:function(){this.loadTable()},onOffline:function(t){this.confirm("确定要下线【"+t.serviceId+"】吗?",function(e){this.post("service.instance.offline",t,function(){this.tip("下线成功"),e()})})},onOnline:function(t){this.confirm("确定要上线【"+t.serviceId+"】吗?",function(e){this.post("service.instance.online",t,function(){this.tip("上线成功"),e()})})},renderServiceName:function(t){var e="";if(t.children&&t.children.length>0){var n=t.children.filter(function(t){return"UP"===t.status}).length;e=" (".concat(n,"/").concat(t.children.length,")")}return t.serviceId+e}}}),o=i,s=n("2877"),l=Object(s["a"])(o,a,r,!1,null,null,null);e["default"]=l.exports}}]); |
||||
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-2d208c3a"],{a5d4:function(t,e,n){"use strict";n.r(e);var a=function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("div",{staticClass:"app-container"},[n("el-form",{staticClass:"demo-form-inline",attrs:{inline:!0,model:t.searchFormData,size:"mini"}},[n("el-form-item",{attrs:{label:"serviceId"}},[n("el-input",{staticStyle:{width:"250px"},attrs:{clearable:!0,placeholder:"serviceId"},model:{value:t.searchFormData.serviceId,callback:function(e){t.$set(t.searchFormData,"serviceId",e)},expression:"searchFormData.serviceId"}})],1),t._v(" "),n("el-form-item",[n("el-button",{attrs:{type:"primary",icon:"el-icon-search"},on:{click:t.onSearchTable}},[t._v("查询")])],1)],1),t._v(" "),n("el-table",{staticStyle:{width:"100%","margin-bottom":"20px"},attrs:{data:t.tableData,border:"","row-key":"id"}},[n("el-table-column",{attrs:{prop:"serviceId",label:"服务名称",width:"200"},scopedSlots:t._u([{key:"default",fn:function(e){return[n("span",{domProps:{innerHTML:t._s(t.renderServiceName(e.row))}})]}}])}),t._v(" "),n("el-table-column",{attrs:{prop:"ipPort",label:"IP端口",width:"250"}}),t._v(" "),n("el-table-column",{attrs:{prop:"status",label:"服务状态",width:"100"},scopedSlots:t._u([{key:"default",fn:function(e){return[e.row.parentId>0&&"UP"===e.row.status?n("el-tag",{attrs:{type:"success"}},[t._v("已上线")]):t._e(),t._v(" "),e.row.parentId>0&&"STARTING"===e.row.status?n("el-tag",{attrs:{type:"info"}},[t._v("正在启动")]):t._e(),t._v(" "),e.row.parentId>0&&"UNKNOWN"===e.row.status?n("el-tag",[t._v("未知")]):t._e(),t._v(" "),e.row.parentId>0&&("OUT_OF_SERVICE"===e.row.status||"DOWN"===e.row.status)?n("el-tag",{attrs:{type:"danger"}},[t._v("已下线")]):t._e()]}}])}),t._v(" "),n("el-table-column",{attrs:{prop:"updateTime",label:"最后更新时间",width:"160"}}),t._v(" "),n("el-table-column",{attrs:{label:"操作",width:"100"},scopedSlots:t._u([{key:"default",fn:function(e){return[e.row.parentId>0&&"UP"===e.row.status?n("el-button",{attrs:{type:"text",size:"mini"},on:{click:function(n){return t.onOffline(e.row)}}},[t._v("下线")]):t._e(),t._v(" "),e.row.parentId>0&&"OUT_OF_SERVICE"===e.row.status?n("el-button",{attrs:{type:"text",size:"mini"},on:{click:function(n){return t.onOnline(e.row)}}},[t._v("上线")]):t._e()]}}])})],1)],1)},r=[],i=(n("ac6a"),{data:function(){return{searchFormData:{serviceId:""},tableData:[]}},created:function(){this.loadTable()},methods:{loadTable:function(){this.post("service.instance.list",this.searchFormData,function(t){this.tableData=this.buildTreeData(t.data)})},buildTreeData:function(t){return t.forEach(function(e){var n=e.parentId;0===n||t.forEach(function(t){if(t.id===n){var a=t.children;a||(a=[]),a.push(e),t.children=a}})}),t=t.filter(function(t){return 0===t.parentId}),t},onSearchTable:function(){this.loadTable()},onOffline:function(t){this.confirm("确定要下线【"+t.serviceId+"】吗?",function(e){this.post("service.instance.offline",t,function(){this.tip("下线成功"),e()})})},onOnline:function(t){this.confirm("确定要上线【"+t.serviceId+"】吗?",function(e){this.post("service.instance.online",t,function(){this.tip("上线成功"),e()})})},renderServiceName:function(t){var e="";if(t.children&&t.children.length>0){var n=t.children.filter(function(t){return"UP"===t.status}).length;e=" (".concat(n,"/").concat(t.children.length,")")}return t.serviceId+e}}}),o=i,s=n("2877"),l=Object(s["a"])(o,a,r,!1,null,null,null);e["default"]=l.exports}}]); |
@ -0,0 +1 @@ |
||||
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-2d221c34"],{cc75:function(a,t,e){"use strict";e.r(t);var i=function(){var a=this,t=a.$createElement,e=a._self._c||t;return e("div",{staticClass:"app-container"},[e("el-form",{staticClass:"demo-form-inline",attrs:{inline:!0,model:a.searchFormData,size:"mini"}},[e("el-form-item",{attrs:{label:"IP"}},[e("el-input",{staticStyle:{width:"250px"},attrs:{clearable:!0,placeholder:"输入IP"},model:{value:a.searchFormData.ip,callback:function(t){a.$set(a.searchFormData,"ip",t)},expression:"searchFormData.ip"}})],1),a._v(" "),e("el-form-item",[e("el-button",{attrs:{type:"primary",icon:"el-icon-search"},on:{click:a.loadTable}},[a._v("查询")])],1)],1),a._v(" "),e("el-button",{staticStyle:{"margin-bottom":"10px"},attrs:{type:"primary",size:"mini",icon:"el-icon-plus"},on:{click:a.onAdd}},[a._v("新增IP")]),a._v(" "),e("el-table",{attrs:{data:a.pageInfo.rows,border:"","highlight-current-row":""}},[e("el-table-column",{attrs:{prop:"ip",label:"IP",width:"200"}}),a._v(" "),e("el-table-column",{attrs:{prop:"remark",label:"备注",width:"300"}}),a._v(" "),e("el-table-column",{attrs:{prop:"gmtCreate",label:"添加时间",width:"160"}}),a._v(" "),e("el-table-column",{attrs:{prop:"gmtModified",label:"修改时间",width:"160"}}),a._v(" "),e("el-table-column",{attrs:{label:"操作",width:"150"},scopedSlots:a._u([{key:"default",fn:function(t){return[e("el-button",{attrs:{type:"text",size:"mini"},on:{click:function(e){return a.onTableUpdate(t.row)}}},[a._v("修改")]),a._v(" "),e("el-button",{attrs:{type:"text",size:"mini"},on:{click:function(e){return a.onTableDelete(t.row)}}},[a._v("删除")])]}}])})],1),a._v(" "),e("el-pagination",{staticStyle:{"margin-top":"5px"},attrs:{background:"","current-page":a.searchFormData.pageIndex,"page-size":a.searchFormData.pageSize,"page-sizes":[5,10,20,40],total:a.pageInfo.total,layout:"total, sizes, prev, pager, next"},on:{"size-change":a.onSizeChange,"current-change":a.onPageIndexChange}}),a._v(" "),e("el-dialog",{attrs:{title:a.dialogTitle,visible:a.dialogVisible,"close-on-click-modal":!1},on:{"update:visible":function(t){a.dialogVisible=t},close:function(t){return a.resetForm("dialogForm")}}},[e("el-form",{ref:"dialogForm",attrs:{rules:a.dialogFormRules,model:a.dialogFormData,"label-width":"120px",size:"mini"}},[e("el-form-item",{attrs:{prop:"ip",label:"IP"}},[e("el-input",{directives:[{name:"show",rawName:"v-show",value:0===a.dialogFormData.id,expression:"dialogFormData.id === 0"}],model:{value:a.dialogFormData.ip,callback:function(t){a.$set(a.dialogFormData,"ip",t)},expression:"dialogFormData.ip"}}),a._v(" "),e("span",{directives:[{name:"show",rawName:"v-show",value:a.dialogFormData.id>0,expression:"dialogFormData.id > 0"}]},[a._v(a._s(a.dialogFormData.ip))])],1),a._v(" "),e("el-form-item",{attrs:{prop:"remark",label:"备注"}},[e("el-input",{model:{value:a.dialogFormData.remark,callback:function(t){a.$set(a.dialogFormData,"remark",t)},expression:"dialogFormData.remark"}})],1)],1),a._v(" "),e("div",{staticClass:"dialog-footer",attrs:{slot:"footer"},slot:"footer"},[e("el-button",{on:{click:function(t){a.dialogVisible=!1}}},[a._v("取 消")]),a._v(" "),e("el-button",{attrs:{type:"primary"},on:{click:a.onDialogSave}},[a._v("保 存")])],1)],1)],1)},o=[],l={data:function(){var a=function(a,t,e){if(""===t)e(new Error("请输入IP"));else{var i=/^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])$/;i.test(t)||e(new Error("IP格式不正确")),e()}};return{searchFormData:{ip:"",pageIndex:1,pageSize:10},pageInfo:{rows:[],total:0},dialogVisible:!1,dialogTitle:"",dialogFormData:{id:0,ip:"",remark:""},dialogFormRules:{ip:[{validator:a,trigger:"blur"},{min:1,max:64,message:"长度在 1 到 64 个字符",trigger:"blur"}],remark:[{max:100,message:"不能超过 100 个字符",trigger:"blur"}]}}},created:function(){this.loadTable()},methods:{loadTable:function(){this.post("ip.blacklist.page",this.searchFormData,function(a){this.pageInfo=a.data})},onTableUpdate:function(a){var t=this;this.dialogTitle="修改IP",this.dialogVisible=!0,this.$nextTick(function(){Object.assign(t.dialogFormData,a)})},onTableDelete:function(a){this.confirm("确认要移除IP【".concat(a.ip,"】吗?"),function(t){var e={id:a.id};this.post("ip.blacklist.del",e,function(){t(),this.tip("删除成功"),this.loadTable()})})},onDialogSave:function(){var a=this;this.$refs.dialogForm.validate(function(t){if(t){var e=a.dialogFormData.id?"ip.blacklist.update":"ip.blacklist.add";a.post(e,a.dialogFormData,function(){this.dialogVisible=!1,this.loadTable()})}})},onSizeChange:function(a){this.searchFormData.pageSize=a,this.loadTable()},onAdd:function(){this.dialogTitle="新增IP",this.dialogVisible=!0,this.dialogFormData.id=0},onPageIndexChange:function(a){this.searchFormData.pageIndex=a,this.loadTable()}}},r=l,n=e("2877"),s=Object(n["a"])(r,i,o,!1,null,null,null);t["default"]=s.exports}}]); |
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -0,0 +1,184 @@ |
||||
<template> |
||||
<div class="app-container"> |
||||
<el-form :inline="true" :model="searchFormData" class="demo-form-inline" size="mini"> |
||||
<el-form-item label="IP"> |
||||
<el-input v-model="searchFormData.ip" :clearable="true" placeholder="输入IP" style="width: 250px;" /> |
||||
</el-form-item> |
||||
<el-form-item> |
||||
<el-button type="primary" icon="el-icon-search" @click="loadTable">查询</el-button> |
||||
</el-form-item> |
||||
</el-form> |
||||
<el-button type="primary" size="mini" icon="el-icon-plus" style="margin-bottom: 10px;" @click="onAdd">新增IP</el-button> |
||||
<el-table |
||||
:data="pageInfo.rows" |
||||
border |
||||
highlight-current-row |
||||
> |
||||
<el-table-column |
||||
prop="ip" |
||||
label="IP" |
||||
width="200" |
||||
/> |
||||
<el-table-column |
||||
prop="remark" |
||||
label="备注" |
||||
width="300" |
||||
/> |
||||
<el-table-column |
||||
prop="gmtCreate" |
||||
label="添加时间" |
||||
width="160" |
||||
/> |
||||
<el-table-column |
||||
prop="gmtModified" |
||||
label="修改时间" |
||||
width="160" |
||||
/> |
||||
<el-table-column |
||||
label="操作" |
||||
width="150" |
||||
> |
||||
<template slot-scope="scope"> |
||||
<el-button type="text" size="mini" @click="onTableUpdate(scope.row)">修改</el-button> |
||||
<el-button type="text" size="mini" @click="onTableDelete(scope.row)">删除</el-button> |
||||
</template> |
||||
</el-table-column> |
||||
</el-table> |
||||
<el-pagination |
||||
background |
||||
style="margin-top: 5px" |
||||
:current-page="searchFormData.pageIndex" |
||||
:page-size="searchFormData.pageSize" |
||||
:page-sizes="[5, 10, 20, 40]" |
||||
:total="pageInfo.total" |
||||
layout="total, sizes, prev, pager, next" |
||||
@size-change="onSizeChange" |
||||
@current-change="onPageIndexChange" |
||||
/> |
||||
<!--dialog--> |
||||
<el-dialog |
||||
:title="dialogTitle" |
||||
:visible.sync="dialogVisible" |
||||
:close-on-click-modal="false" |
||||
@close="resetForm('dialogForm')" |
||||
> |
||||
<el-form |
||||
ref="dialogForm" |
||||
:rules="dialogFormRules" |
||||
:model="dialogFormData" |
||||
label-width="120px" |
||||
size="mini" |
||||
> |
||||
<el-form-item prop="ip" label="IP"> |
||||
<el-input v-show="dialogFormData.id === 0" v-model="dialogFormData.ip" /> |
||||
<span v-show="dialogFormData.id > 0">{{ dialogFormData.ip }}</span> |
||||
</el-form-item> |
||||
<el-form-item prop="remark" label="备注"> |
||||
<el-input v-model="dialogFormData.remark" /> |
||||
</el-form-item> |
||||
</el-form> |
||||
<div slot="footer" class="dialog-footer"> |
||||
<el-button @click="dialogVisible = false">取 消</el-button> |
||||
<el-button type="primary" @click="onDialogSave">保 存</el-button> |
||||
</div> |
||||
</el-dialog> |
||||
</div> |
||||
</template> |
||||
|
||||
<script> |
||||
export default { |
||||
data() { |
||||
const ipValidator = (rule, value, callback) => { |
||||
if (value === '') { |
||||
callback(new Error('请输入IP')) |
||||
} else { |
||||
const regexIP = /^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])$/ |
||||
if (!regexIP.test(value)) { |
||||
callback(new Error('IP格式不正确')) |
||||
} |
||||
callback() |
||||
} |
||||
} |
||||
return { |
||||
searchFormData: { |
||||
ip: '', |
||||
pageIndex: 1, |
||||
pageSize: 10 |
||||
}, |
||||
pageInfo: { |
||||
rows: [], |
||||
total: 0 |
||||
}, |
||||
dialogVisible: false, |
||||
dialogTitle: '', |
||||
dialogFormData: { |
||||
id: 0, |
||||
ip: '', |
||||
remark: '' |
||||
}, |
||||
dialogFormRules: { |
||||
ip: [ |
||||
{ validator: ipValidator, trigger: 'blur' }, |
||||
{ min: 1, max: 64, message: '长度在 1 到 64 个字符', trigger: 'blur' } |
||||
], |
||||
remark: [ |
||||
{ max: 100, message: '不能超过 100 个字符', trigger: 'blur' } |
||||
] |
||||
} |
||||
} |
||||
}, |
||||
created() { |
||||
this.loadTable() |
||||
}, |
||||
methods: { |
||||
loadTable: function() { |
||||
this.post('ip.blacklist.page', this.searchFormData, function(resp) { |
||||
this.pageInfo = resp.data |
||||
}) |
||||
}, |
||||
onTableUpdate: function(row) { |
||||
this.dialogTitle = '修改IP' |
||||
this.dialogVisible = true |
||||
this.$nextTick(() => { |
||||
Object.assign(this.dialogFormData, row) |
||||
}) |
||||
}, |
||||
onTableDelete: function(row) { |
||||
this.confirm(`确认要移除IP【${row.ip}】吗?`, function(done) { |
||||
const data = { |
||||
id: row.id |
||||
} |
||||
this.post('ip.blacklist.del', data, function() { |
||||
done() |
||||
this.tip('删除成功') |
||||
this.loadTable() |
||||
}) |
||||
}) |
||||
}, |
||||
onDialogSave: function() { |
||||
this.$refs.dialogForm.validate((valid) => { |
||||
if (valid) { |
||||
const uri = this.dialogFormData.id ? 'ip.blacklist.update' : 'ip.blacklist.add' |
||||
this.post(uri, this.dialogFormData, function() { |
||||
this.dialogVisible = false |
||||
this.loadTable() |
||||
}) |
||||
} |
||||
}) |
||||
}, |
||||
onSizeChange: function(size) { |
||||
this.searchFormData.pageSize = size |
||||
this.loadTable() |
||||
}, |
||||
onAdd: function() { |
||||
this.dialogTitle = '新增IP' |
||||
this.dialogVisible = true |
||||
this.dialogFormData.id = 0 |
||||
}, |
||||
onPageIndexChange: function(pageIndex) { |
||||
this.searchFormData.pageIndex = pageIndex |
||||
this.loadTable() |
||||
} |
||||
} |
||||
} |
||||
</script> |
@ -1,347 +0,0 @@ |
||||
<template> |
||||
<div class="app-container"> |
||||
<el-container> |
||||
<el-aside style="min-height: 300px;width: 200px;"> |
||||
<el-input v-model="filterText" prefix-icon="el-icon-search" placeholder="搜索服务..." style="margin-bottom:20px;" size="mini" clearable /> |
||||
<el-tree |
||||
ref="tree2" |
||||
:data="treeData" |
||||
:props="defaultProps" |
||||
:filter-node-method="filterNode" |
||||
:highlight-current="true" |
||||
:expand-on-click-node="false" |
||||
empty-text="无数据" |
||||
node-key="id" |
||||
class="filter-tree" |
||||
default-expand-all |
||||
@node-click="onNodeClick" |
||||
> |
||||
<span slot-scope="{ node, data }" class="custom-tree-node"> |
||||
<span v-if="data.label.length < 15">{{ data.label }}</span> |
||||
<span v-else> |
||||
<el-tooltip :content="data.label" class="item" effect="light" placement="right"> |
||||
<span>{{ data.label.substring(0, 15) + '...' }}</span> |
||||
</el-tooltip> |
||||
</span> |
||||
</span> |
||||
</el-tree> |
||||
</el-aside> |
||||
<el-main style="padding-top:0"> |
||||
<el-form :inline="true" :model="searchFormData" class="demo-form-inline"> |
||||
<el-form-item label="路由名称"> |
||||
<el-input v-model="searchFormData.id" placeholder="输入接口名或版本号" size="mini" /> |
||||
</el-form-item> |
||||
<el-form-item> |
||||
<el-button type="primary" icon="el-icon-search" size="mini" @click="onSearchTable">查询</el-button> |
||||
</el-form-item> |
||||
</el-form> |
||||
<el-table |
||||
:data="tableData" |
||||
border |
||||
max-height="500" |
||||
> |
||||
<el-table-column |
||||
prop="name" |
||||
label="接口名 (版本号)" |
||||
width="200" |
||||
> |
||||
<template slot-scope="scope"> |
||||
{{ scope.row.name + (scope.row.version ? ' (' + scope.row.version + ')' : '') }} |
||||
</template> |
||||
</el-table-column> |
||||
<el-table-column |
||||
prop="limitType" |
||||
label="限流策略" |
||||
width="120" |
||||
> |
||||
<template slot-scope slot="header"> |
||||
限流策略 <i class="el-icon-question" style="cursor: pointer" @click="onLimitTypeTipClick"></i> |
||||
</template> |
||||
<template slot-scope="scope"> |
||||
<span v-if="scope.row.limitType === 1">漏桶策略</span> |
||||
<span v-if="scope.row.limitType === 2">令牌桶策略</span> |
||||
</template> |
||||
</el-table-column> |
||||
<el-table-column |
||||
prop="info" |
||||
label="限流信息" |
||||
width="500" |
||||
> |
||||
<template slot-scope="scope"> |
||||
<span v-html="infoRender(scope.row)"></span> |
||||
</template> |
||||
</el-table-column> |
||||
<el-table-column |
||||
prop="limitStatus" |
||||
label="状态" |
||||
width="80" |
||||
> |
||||
<template slot-scope="scope"> |
||||
<span v-if="scope.row.limitStatus === 1" style="color:#67C23A">已开启</span> |
||||
<span v-if="scope.row.limitStatus === 0" style="color:#909399">已关闭</span> |
||||
</template> |
||||
</el-table-column> |
||||
<el-table-column |
||||
label="操作" |
||||
width="80" |
||||
> |
||||
<template slot-scope="scope"> |
||||
<el-button type="text" size="mini" @click="onTableUpdate(scope.row)">修改</el-button> |
||||
</template> |
||||
</el-table-column> |
||||
</el-table> |
||||
<!-- dialog --> |
||||
<el-dialog |
||||
title="设置限流" |
||||
:visible.sync="limitDialogVisible" |
||||
:close-on-click-modal="false" |
||||
@close="onLimitDialogClose" |
||||
> |
||||
<el-form ref="limitDialogFormMain" :model="limitDialogFormData"> |
||||
<el-form-item label="id" :label-width="formLabelWidth"> |
||||
<el-input v-model="limitDialogFormData.routeId" readonly="readonly"/> |
||||
</el-form-item> |
||||
<el-form-item label="限流策略" :label-width="formLabelWidth"> |
||||
<el-radio-group v-model="limitDialogFormData.limitType"> |
||||
<el-radio :label="1">漏桶策略</el-radio> |
||||
<el-radio :label="2">令牌桶策略</el-radio> |
||||
</el-radio-group> |
||||
</el-form-item> |
||||
<el-form-item label="开启状态" :label-width="formLabelWidth"> |
||||
<el-switch |
||||
v-model="limitDialogFormData.limitStatus" |
||||
active-color="#13ce66" |
||||
inactive-color="#ff4949" |
||||
:active-value="1" |
||||
:inactive-value="0" |
||||
> |
||||
</el-switch> |
||||
</el-form-item> |
||||
</el-form> |
||||
<el-form |
||||
v-show="limitDialogFormData.limitType === 1 && limitDialogFormData.limitStatus" |
||||
ref="limitDialogFormLeaky" |
||||
:rules="rulesLeaky" |
||||
:model="limitDialogFormData" |
||||
> |
||||
<el-form-item label="每秒可处理请求数" prop="execCountPerSecond" :label-width="formLabelWidth"> |
||||
<el-input-number v-model="limitDialogFormData.execCountPerSecond" controls-position="right" :min="1" /> |
||||
</el-form-item> |
||||
<el-form-item label="错误码" prop="limitCode" :label-width="formLabelWidth"> |
||||
<el-input v-model="limitDialogFormData.limitCode" /> |
||||
</el-form-item> |
||||
<el-form-item label="错误信息" prop="limitMsg" :label-width="formLabelWidth"> |
||||
<el-input v-model="limitDialogFormData.limitMsg" /> |
||||
</el-form-item> |
||||
</el-form> |
||||
<el-form |
||||
v-show="limitDialogFormData.limitType === 2 && limitDialogFormData.limitStatus" |
||||
ref="limitDialogFormToken" |
||||
:rules="rulesToken" |
||||
:model="limitDialogFormData" |
||||
> |
||||
<el-form-item label="令牌桶容量" prop="tokenBucketCount" :label-width="formLabelWidth"> |
||||
<el-input-number v-model="limitDialogFormData.tokenBucketCount" controls-position="right" :min="1" /> |
||||
</el-form-item> |
||||
</el-form> |
||||
<div slot="footer" class="dialog-footer"> |
||||
<el-button @click="limitDialogVisible = false">取 消</el-button> |
||||
<el-button type="primary" @click="onLimitDialogSave">保 存</el-button> |
||||
</div> |
||||
</el-dialog> |
||||
</el-main> |
||||
</el-container> |
||||
</div> |
||||
</template> |
||||
|
||||
<script> |
||||
export default { |
||||
data() { |
||||
return { |
||||
filterText: '', |
||||
treeData: [], |
||||
tableData: [], |
||||
serviceId: '', |
||||
searchFormData: {}, |
||||
defaultProps: { |
||||
children: 'children', |
||||
label: 'label' |
||||
}, |
||||
// dialog |
||||
limitDialogFormData: { |
||||
routeId: '', |
||||
execCountPerSecond: 5, |
||||
limitCode: '', |
||||
limitMsg: '', |
||||
tokenBucketCount: 5, |
||||
limitStatus: 0, // 0: 停用,1:启用 |
||||
limitType: 1 |
||||
}, |
||||
rulesLeaky: { |
||||
execCountPerSecond: [ |
||||
{ required: true, message: '不能为空', trigger: 'blur' } |
||||
], |
||||
limitCode: [ |
||||
{ required: true, message: '不能为空', trigger: 'blur' }, |
||||
{ min: 1, max: 64, message: '长度在 1 到 64 个字符', trigger: 'blur' } |
||||
], |
||||
limitMsg: [ |
||||
{ required: true, message: '不能为空', trigger: 'blur' }, |
||||
{ min: 1, max: 100, message: '长度在 1 到 100 个字符', trigger: 'blur' } |
||||
] |
||||
}, |
||||
rulesToken: { |
||||
tokenBucketCount: [ |
||||
{ required: true, message: '不能为空', trigger: 'blur' } |
||||
] |
||||
}, |
||||
formLabelWidth: '150px', |
||||
limitDialogVisible: false |
||||
|
||||
} |
||||
}, |
||||
watch: { |
||||
filterText(val) { |
||||
this.$refs.tree2.filter(val) |
||||
} |
||||
}, |
||||
created() { |
||||
this.loadTree() |
||||
}, |
||||
methods: { |
||||
// 加载树 |
||||
loadTree: function() { |
||||
this.post('zookeeper.service.list', {}, function(resp) { |
||||
const respData = resp.data |
||||
this.treeData = this.convertToTreeData(respData, 0) |
||||
}) |
||||
}, |
||||
// 树搜索 |
||||
filterNode(value, data) { |
||||
if (!value) return true |
||||
return data.label.indexOf(value) !== -1 |
||||
}, |
||||
// 树点击事件 |
||||
onNodeClick(data, node, tree) { |
||||
if (data.parentId) { |
||||
this.serviceId = data.label |
||||
this.searchFormData.serviceId = this.serviceId |
||||
this.loadTable() |
||||
} |
||||
}, |
||||
/** |
||||
* 数组转成树状结构 |
||||
* @param data 数据结构 [{ |
||||
"_parentId": 14, |
||||
"gmtCreate": "2019-01-15 09:44:38", |
||||
"gmtUpdate": "2019-01-15 09:44:38", |
||||
"id": 15, |
||||
"isShow": 1, |
||||
"name": "用户注册", |
||||
"orderIndex": 10000, |
||||
"parentId": 14 |
||||
},...] |
||||
* @param pid 初始父节点id,一般是0 |
||||
* @return 返回结果 [{ |
||||
label: '一级 1', |
||||
children: [{ |
||||
label: '二级 1-1', |
||||
children: [{ |
||||
label: '三级 1-1-1' |
||||
}] |
||||
}] |
||||
} |
||||
*/ |
||||
convertToTreeData(data, pid) { |
||||
const result = [] |
||||
const root = { |
||||
label: '服务列表', |
||||
parentId: pid |
||||
} |
||||
const children = [] |
||||
for (let i = 0; i < data.length; i++) { |
||||
const item = { label: data[i].serviceId, parentId: 1 } |
||||
children.push(item) |
||||
} |
||||
root.children = children |
||||
result.push(root) |
||||
return result |
||||
}, |
||||
// table |
||||
loadTable: function() { |
||||
this.post('route.limit.list', this.searchFormData, function(resp) { |
||||
this.tableData = resp.data |
||||
}) |
||||
}, |
||||
onSearchTable: function() { |
||||
this.loadTable() |
||||
}, |
||||
onTableUpdate: function(row) { |
||||
this.limitDialogVisible = true |
||||
this.$nextTick(() => { |
||||
Object.assign(this.limitDialogFormData, row) |
||||
}) |
||||
}, |
||||
resetForm(formName) { |
||||
const frm = this.$refs[formName] |
||||
frm && frm.resetFields() |
||||
}, |
||||
onLimitDialogClose: function() { |
||||
this.resetForm('limitDialogFormLeaky') |
||||
this.resetForm('limitDialogFormToken') |
||||
this.limitDialogVisible = false |
||||
}, |
||||
infoRender: function(row) { |
||||
if (!row.hasRecord) { |
||||
return '--' |
||||
} |
||||
const html = [] |
||||
if (row.limitType === 1) { |
||||
html.push('每秒可处理请求数:' + row.execCountPerSecond) |
||||
html.push('subCode:' + row.limitCode) |
||||
html.push('subMsg:' + row.limitMsg) |
||||
} else if (row.limitType === 2) { |
||||
html.push('令牌桶容量:' + row.tokenBucketCount) |
||||
} |
||||
return html.join(',') |
||||
}, |
||||
onLimitDialogSave: function() { |
||||
this.doValidate(function() { |
||||
this.limitDialogFormData.serviceId = this.serviceId |
||||
this.post('route.limit.update', this.limitDialogFormData, function(resp) { |
||||
this.limitDialogVisible = false |
||||
this.loadTable() |
||||
}) |
||||
}) |
||||
}, |
||||
doValidate: function(callback) { |
||||
const that = this |
||||
if (this.limitDialogFormData.limitStatus === 0) { |
||||
callback.call(this) |
||||
return |
||||
} |
||||
if (this.limitDialogFormData.limitType === 1) { |
||||
this.$refs['limitDialogFormLeaky'].validate((valid) => { |
||||
if (valid) { |
||||
callback.call(that) |
||||
} |
||||
}) |
||||
} else { |
||||
this.$refs['limitDialogFormToken'].validate((valid) => { |
||||
if (valid) { |
||||
callback.call(that) |
||||
} |
||||
}) |
||||
} |
||||
}, |
||||
onLimitTypeTipClick: function() { |
||||
const leakyRemark = '漏桶策略:每秒处理固定数量的请求,超出请求返回错误信息。' |
||||
const tokenRemark = '令牌桶策略:每秒放置固定数量的令牌数,每个请求进来后先去拿令牌,拿到了令牌才能继续,拿不到则等候令牌重新生成了再拿。' |
||||
const content = leakyRemark + '<br>' + tokenRemark |
||||
this.$alert(content, '限流策略', { |
||||
dangerouslyUseHTMLString: true |
||||
}) |
||||
} |
||||
} |
||||
} |
||||
</script> |
@ -0,0 +1,38 @@ |
||||
package com.gitee.sop.gatewaycommon.manager; |
||||
|
||||
import com.google.common.collect.Sets; |
||||
import org.apache.commons.lang.StringUtils; |
||||
|
||||
import java.util.Set; |
||||
|
||||
/** |
||||
* ip黑名单管理 |
||||
* @author tanghc |
||||
*/ |
||||
public class DefaultIPBlacklistManager implements IPBlacklistManager { |
||||
|
||||
private static Set<String> ipList = Sets.newConcurrentHashSet(); |
||||
|
||||
@Override |
||||
public void add(String ip) { |
||||
ipList.add(ip); |
||||
} |
||||
|
||||
@Override |
||||
public void remove(String ip) { |
||||
ipList.remove(ip); |
||||
} |
||||
|
||||
@Override |
||||
public boolean contains(String ip) { |
||||
if (StringUtils.isBlank(ip)) { |
||||
return false; |
||||
} |
||||
return ipList.contains(ip); |
||||
} |
||||
|
||||
@Override |
||||
public void load() { |
||||
|
||||
} |
||||
} |
@ -0,0 +1,16 @@ |
||||
package com.gitee.sop.gatewaycommon.manager; |
||||
|
||||
import com.gitee.sop.gatewaycommon.bean.BeanInitializer; |
||||
|
||||
/** |
||||
* @author tanghc |
||||
*/ |
||||
public interface IPBlacklistManager extends BeanInitializer { |
||||
|
||||
void add(String ip); |
||||
|
||||
void remove(String ip); |
||||
|
||||
boolean contains(String ip); |
||||
|
||||
} |
@ -0,0 +1,64 @@ |
||||
package com.gitee.sop.gateway.manager; |
||||
|
||||
import com.alibaba.fastjson.JSON; |
||||
import com.gitee.sop.gateway.mapper.IPBlacklistMapper; |
||||
import com.gitee.sop.gatewaycommon.bean.ChannelMsg; |
||||
import com.gitee.sop.gatewaycommon.manager.DefaultIPBlacklistManager; |
||||
import com.gitee.sop.gatewaycommon.manager.ZookeeperContext; |
||||
import lombok.Data; |
||||
import lombok.extern.slf4j.Slf4j; |
||||
import org.springframework.beans.factory.annotation.Autowired; |
||||
import org.springframework.core.env.Environment; |
||||
|
||||
import javax.annotation.PostConstruct; |
||||
import java.util.List; |
||||
|
||||
/** |
||||
* 限流配置管理 |
||||
* @author tanghc |
||||
*/ |
||||
@Slf4j |
||||
public class DbIPBlacklistManager extends DefaultIPBlacklistManager { |
||||
|
||||
@Autowired |
||||
IPBlacklistMapper ipBlacklistMapper; |
||||
|
||||
@Autowired |
||||
Environment environment; |
||||
|
||||
@Override |
||||
public void load() { |
||||
List<String> ipList = ipBlacklistMapper.listAllIP(); |
||||
log.info("加载IP黑名单, size:{}", ipList.size()); |
||||
ipList.stream().forEach(this::add); |
||||
|
||||
} |
||||
|
||||
@PostConstruct |
||||
protected void after() throws Exception { |
||||
ZookeeperContext.setEnvironment(environment); |
||||
String path = ZookeeperContext.getIpBlacklistChannelPath(); |
||||
ZookeeperContext.listenPath(path, nodeCache -> { |
||||
String nodeData = new String(nodeCache.getCurrentData().getData()); |
||||
ChannelMsg channelMsg = JSON.parseObject(nodeData, ChannelMsg.class); |
||||
final IPDto ipDto = JSON.parseObject(channelMsg.getData(), IPDto.class); |
||||
String ip = ipDto.getIp(); |
||||
switch (channelMsg.getOperation()) { |
||||
case "add": |
||||
log.info("添加IP黑名单,ip:{}", ip); |
||||
add(ip); |
||||
break; |
||||
case "delete": |
||||
log.info("移除IP黑名单,ip:{}", ip); |
||||
remove(ip); |
||||
break; |
||||
} |
||||
}); |
||||
} |
||||
|
||||
@Data |
||||
private static class IPDto { |
||||
private String ip; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,22 @@ |
||||
package com.gitee.sop.gateway.mapper; |
||||
|
||||
import org.apache.ibatis.annotations.Mapper; |
||||
import org.apache.ibatis.annotations.Select; |
||||
|
||||
import java.util.List; |
||||
|
||||
/** |
||||
* IP黑名单 |
||||
* @author tanghc |
||||
*/ |
||||
@Mapper |
||||
public interface IPBlacklistMapper { |
||||
|
||||
/** |
||||
* 获取所有IP |
||||
* @return |
||||
*/ |
||||
@Select("SELECT ip FROM config_ip_blacklist") |
||||
List<String> listAllIP(); |
||||
|
||||
} |
Loading…
Reference in new issue