Repository: dyc87112/SpringCloud-Learning Branch: master Commit: fb2079c338db Files: 710 Total size: 2.6 MB Directory structure: gitextract__j33z0th/ ├── .gitignore ├── 1-Brixton版教程示例/ │ ├── Chapter1-1-1/ │ │ ├── compute-service/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ ├── main/ │ │ │ │ ├── java/ │ │ │ │ │ └── com/ │ │ │ │ │ └── didispace/ │ │ │ │ │ ├── ComputeServiceApplication.java │ │ │ │ │ └── web/ │ │ │ │ │ └── ComputeController.java │ │ │ │ └── resources/ │ │ │ │ └── application.properties │ │ │ └── test/ │ │ │ └── java/ │ │ │ └── com/ │ │ │ └── didispace/ │ │ │ └── ApplicationTests.java │ │ └── eureka-server/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── didispace/ │ │ │ └── Application.java │ │ └── resources/ │ │ └── application.properties │ ├── Chapter1-1-2/ │ │ ├── eureka-feign/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── com/ │ │ │ │ └── didispace/ │ │ │ │ ├── FeignApplication.java │ │ │ │ ├── service/ │ │ │ │ │ └── ComputeClient.java │ │ │ │ └── web/ │ │ │ │ └── ConsumerController.java │ │ │ └── resources/ │ │ │ └── application.properties │ │ └── eureka-ribbon/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── didispace/ │ │ │ ├── RibbonApplication.java │ │ │ └── web/ │ │ │ └── ConsumerController.java │ │ └── resources/ │ │ └── application.properties │ ├── Chapter1-1-3/ │ │ ├── compute-service/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ ├── main/ │ │ │ │ ├── java/ │ │ │ │ │ └── com/ │ │ │ │ │ └── didispace/ │ │ │ │ │ ├── ComputeServiceApplication.java │ │ │ │ │ └── web/ │ │ │ │ │ └── ComputeController.java │ │ │ │ └── resources/ │ │ │ │ └── application.properties │ │ │ └── test/ │ │ │ └── java/ │ │ │ └── com/ │ │ │ └── didispace/ │ │ │ └── ApplicationTests.java │ │ ├── eureka-feign/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ └── java/ │ │ │ └── com/ │ │ │ └── didispace/ │ │ │ ├── FeignApplication.java │ │ │ ├── service/ │ │ │ │ ├── ComputeClient.java │ │ │ │ └── ComputeClientHystrix.java │ │ │ └── web/ │ │ │ └── ConsumerController.java │ │ ├── eureka-ribbon/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ └── java/ │ │ │ └── com/ │ │ │ └── didispace/ │ │ │ ├── RibbonApplication.java │ │ │ ├── service/ │ │ │ │ └── ComputeService.java │ │ │ └── web/ │ │ │ └── ConsumerController.java │ │ └── eureka-server/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── didispace/ │ │ │ └── Application.java │ │ └── resources/ │ │ └── application.properties │ ├── Chapter1-1-4/ │ │ ├── config-client/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── com/ │ │ │ │ └── didispace/ │ │ │ │ ├── Application.java │ │ │ │ └── web/ │ │ │ │ └── TestController.java │ │ │ └── resources/ │ │ │ └── bootstrap.properties │ │ ├── config-repo/ │ │ │ ├── didispace-dev.properties │ │ │ ├── didispace-prod.properties │ │ │ ├── didispace-test.properties │ │ │ └── didispace.properties │ │ └── config-server/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── didispace/ │ │ │ └── Application.java │ │ └── resources/ │ │ ├── application.properties │ │ ├── didispace-dev.properties │ │ ├── didispace-prod.properties │ │ ├── didispace-test.properties │ │ └── didispace.properties │ ├── Chapter1-1-5/ │ │ ├── api-gateway/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── com/ │ │ │ │ └── didispace/ │ │ │ │ ├── Application.java │ │ │ │ └── filter/ │ │ │ │ └── AccessFilter.java │ │ │ └── resources/ │ │ │ └── application.properties │ │ ├── eureka-server/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── com/ │ │ │ │ └── didispace/ │ │ │ │ └── Application.java │ │ │ └── resources/ │ │ │ └── application.properties │ │ ├── service-A/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ ├── main/ │ │ │ │ ├── java/ │ │ │ │ │ └── com/ │ │ │ │ │ └── didispace/ │ │ │ │ │ ├── ComputeServiceApplication.java │ │ │ │ │ └── web/ │ │ │ │ │ └── ComputeController.java │ │ │ │ └── resources/ │ │ │ │ └── application.properties │ │ │ └── test/ │ │ │ └── java/ │ │ │ └── com/ │ │ │ └── didispace/ │ │ │ └── ApplicationTests.java │ │ └── service-B/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── com/ │ │ │ │ └── didispace/ │ │ │ │ ├── ComputeServiceApplication.java │ │ │ │ └── web/ │ │ │ │ └── ComputeController.java │ │ │ └── resources/ │ │ │ └── application.properties │ │ └── test/ │ │ └── java/ │ │ └── com/ │ │ └── didispace/ │ │ └── ApplicationTests.java │ ├── Chapter1-1-6/ │ │ ├── compute-service/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ ├── main/ │ │ │ │ ├── java/ │ │ │ │ │ └── com/ │ │ │ │ │ └── didispace/ │ │ │ │ │ ├── ComputeServiceApplication.java │ │ │ │ │ └── web/ │ │ │ │ │ └── ComputeController.java │ │ │ │ └── resources/ │ │ │ │ └── application.properties │ │ │ └── test/ │ │ │ └── java/ │ │ │ └── com/ │ │ │ └── didispace/ │ │ │ └── ApplicationTests.java │ │ └── eureka-server/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── didispace/ │ │ │ └── Application.java │ │ └── resources/ │ │ ├── application-peer1.properties │ │ └── application-peer2.properties │ ├── Chapter1-1-7/ │ │ ├── config-client-eureka/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── com/ │ │ │ │ └── didispace/ │ │ │ │ ├── Application.java │ │ │ │ └── web/ │ │ │ │ └── TestController.java │ │ │ └── resources/ │ │ │ └── bootstrap.properties │ │ ├── config-client-eureka-kafka/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── com/ │ │ │ │ └── didispace/ │ │ │ │ ├── Application.java │ │ │ │ └── web/ │ │ │ │ └── TestController.java │ │ │ └── resources/ │ │ │ └── bootstrap.properties │ │ ├── config-repo/ │ │ │ ├── didispace-dev.properties │ │ │ ├── didispace-prod.properties │ │ │ ├── didispace-test.properties │ │ │ └── didispace.properties │ │ ├── config-server-eureka/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── com/ │ │ │ │ └── didispace/ │ │ │ │ └── Application.java │ │ │ └── resources/ │ │ │ ├── application.properties │ │ │ ├── didispace-dev.properties │ │ │ ├── didispace-prod.properties │ │ │ ├── didispace-test.properties │ │ │ └── didispace.properties │ │ └── config-server-eureka-kafka/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── didispace/ │ │ │ └── Application.java │ │ └── resources/ │ │ ├── application.properties │ │ ├── didispace-dev.properties │ │ ├── didispace-prod.properties │ │ ├── didispace-test.properties │ │ └── didispace.properties │ ├── Chapter1-1-8/ │ │ ├── config-client-eureka/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── com/ │ │ │ │ └── didispace/ │ │ │ │ ├── Application.java │ │ │ │ └── web/ │ │ │ │ └── TestController.java │ │ │ └── resources/ │ │ │ └── bootstrap.properties │ │ ├── config-repo/ │ │ │ ├── didispace-dev.properties │ │ │ ├── didispace-prod.properties │ │ │ ├── didispace-test.properties │ │ │ └── didispace.properties │ │ └── config-server-eureka/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── didispace/ │ │ │ └── Application.java │ │ └── resources/ │ │ ├── application.properties │ │ ├── didispace-dev.properties │ │ ├── didispace-prod.properties │ │ ├── didispace-test.properties │ │ └── didispace.properties │ └── README.md ├── 2-Dalston版教程示例/ │ ├── README.md │ ├── api-gateway/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── didispace/ │ │ │ └── Application.java │ │ └── resources/ │ │ └── application.yaml │ ├── api-gateway-with-eureka/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── didispace/ │ │ │ ├── AccessFilter.java │ │ │ └── Application.java │ │ └── resources/ │ │ └── application.yaml │ ├── config-client/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── didispace/ │ │ │ └── Application.java │ │ └── resources/ │ │ └── bootstrap.yml │ ├── config-server-git/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── didispace/ │ │ │ └── Application.java │ │ └── resources/ │ │ └── application.yml │ ├── consul-client/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── didispace/ │ │ │ ├── Application.java │ │ │ └── DcController.java │ │ └── resources/ │ │ └── application.properties │ ├── consul-consumer/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── didispace/ │ │ │ ├── Application.java │ │ │ └── DcController.java │ │ └── resources/ │ │ └── application.properties │ ├── eureka-client/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── didispace/ │ │ │ ├── Application.java │ │ │ └── DcController.java │ │ └── resources/ │ │ └── application.properties │ ├── eureka-consumer/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── didispace/ │ │ │ ├── Application.java │ │ │ └── DcController.java │ │ └── resources/ │ │ └── application.properties │ ├── eureka-consumer-feign/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── didispace/ │ │ │ ├── Application.java │ │ │ ├── DcClient.java │ │ │ └── DcController.java │ │ └── resources/ │ │ └── application.properties │ ├── eureka-consumer-feign-hystrix/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── didispace/ │ │ │ ├── Application.java │ │ │ ├── DcClient.java │ │ │ ├── DcClientFallback.java │ │ │ └── DcController.java │ │ └── resources/ │ │ └── application.properties │ ├── eureka-consumer-ribbon/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── didispace/ │ │ │ ├── Application.java │ │ │ └── DcController.java │ │ └── resources/ │ │ └── application.properties │ ├── eureka-consumer-ribbon-hystrix/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── didispace/ │ │ │ ├── Application.java │ │ │ └── DcController.java │ │ └── resources/ │ │ └── application.properties │ ├── eureka-feign-api/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── didispace/ │ │ └── api/ │ │ └── HelloService.java │ ├── eureka-feign-client/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── didispace/ │ │ │ └── api/ │ │ │ └── impl/ │ │ │ └── Application.java │ │ └── resources/ │ │ └── application.properties │ ├── eureka-feign-consumer/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── didispace/ │ │ │ └── api/ │ │ │ └── consumer/ │ │ │ └── Application.java │ │ └── resources/ │ │ └── application.properties │ ├── eureka-feign-upload-client/ │ │ ├── pom.xml │ │ ├── src/ │ │ │ ├── main/ │ │ │ │ ├── java/ │ │ │ │ │ └── com/ │ │ │ │ │ └── didispace/ │ │ │ │ │ └── api/ │ │ │ │ │ └── consumer/ │ │ │ │ │ ├── Application.java │ │ │ │ │ └── UploadService.java │ │ │ │ └── resources/ │ │ │ │ └── application.properties │ │ │ └── test/ │ │ │ └── java/ │ │ │ └── com/ │ │ │ └── didispace/ │ │ │ └── api/ │ │ │ └── consumer/ │ │ │ └── UploadTester.java │ │ └── upload.txt │ ├── eureka-feign-upload-server/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── didispace/ │ │ │ └── api/ │ │ │ └── consumer/ │ │ │ └── Application.java │ │ └── resources/ │ │ └── application.properties │ ├── eureka-server/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── didispace/ │ │ │ └── Application.java │ │ └── resources/ │ │ ├── application-peer1.properties │ │ ├── application-peer2.properties │ │ └── application.properties │ ├── hystrix-collapser-consumer/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── com/ │ │ │ │ └── didispace/ │ │ │ │ ├── Application.java │ │ │ │ └── UserService.java │ │ │ └── resources/ │ │ │ └── application.properties │ │ └── test/ │ │ └── java/ │ │ └── CollapserTest.java │ ├── hystrix-collapser-provider/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── didispace/ │ │ │ ├── Application.java │ │ │ └── UserController.java │ │ └── resources/ │ │ └── application.properties │ ├── hystrix-dashboard/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── didispace/ │ │ │ └── HystrixDashboardApplication.java │ │ └── resources/ │ │ └── application.properties │ ├── pom.xml │ ├── stream-consumer-group/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── com/ │ │ │ │ └── didispace/ │ │ │ │ └── stream/ │ │ │ │ ├── ExampleApplication.java │ │ │ │ ├── ExampleBinder.java │ │ │ │ └── ExampleReceiver.java │ │ │ └── resources/ │ │ │ └── application.properties │ │ └── test/ │ │ └── java/ │ │ └── com/ │ │ └── didispace/ │ │ └── ExampleApplicationTests.java │ ├── stream-consumer-self/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── didispace/ │ │ │ └── stream/ │ │ │ ├── TestApplication.java │ │ │ ├── TestController.java │ │ │ ├── TestListener.java │ │ │ └── TestTopic.java │ │ └── resources/ │ │ └── application.properties │ ├── stream-hello/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── com/ │ │ │ │ └── didispace/ │ │ │ │ └── stream/ │ │ │ │ ├── SinkApplication.java │ │ │ │ └── SinkReceiver.java │ │ │ └── resources/ │ │ │ └── application.properties │ │ └── test/ │ │ └── java/ │ │ └── com/ │ │ └── didispace/ │ │ └── SinkApplicationTests.java │ ├── swagger-api-gateway/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── didispace/ │ │ │ └── Application.java │ │ └── resources/ │ │ └── application.yaml │ ├── swagger-service-a/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── didispace/ │ │ │ └── Application.java │ │ └── resources/ │ │ └── application.properties │ ├── swagger-service-b/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── didispace/ │ │ │ └── Application.java │ │ └── resources/ │ │ └── application.properties │ ├── trace-1/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── didispace/ │ │ │ └── TraceApplication.java │ │ └── resources/ │ │ └── application.properties │ ├── trace-2/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── didispace/ │ │ │ └── TraceApplication.java │ │ └── resources/ │ │ ├── application.properties │ │ └── logback-spring.xml │ ├── turbine/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── didispace/ │ │ │ └── TurbineApplication.java │ │ └── resources/ │ │ └── application.properties │ ├── turbine-amqp/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── didispace/ │ │ │ └── TurbineApplication.java │ │ └── resources/ │ │ └── application.properties │ ├── zipkin-server/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── didispace/ │ │ │ └── ZipkinApplication.java │ │ └── resources/ │ │ └── application.properties │ └── zipkin-server-stream/ │ ├── pom.xml │ └── src/ │ └── main/ │ ├── java/ │ │ └── com/ │ │ └── didispace/ │ │ └── ZipkinApplication.java │ └── resources/ │ └── application.properties ├── 3-Edgware/ │ ├── README.md │ ├── config-client/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── didispace/ │ │ │ └── config/ │ │ │ └── client/ │ │ │ └── ConfigClientApplication.java │ │ └── resources/ │ │ └── bootstrap.properties │ ├── config-server-db/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── didispace/ │ │ │ └── config/ │ │ │ └── server/ │ │ │ └── db/ │ │ │ └── ConfigServerBootstrap.java │ │ └── resources/ │ │ ├── application.properties │ │ └── schema/ │ │ └── V1__Base_version.sql │ └── pom.xml ├── 4-Finchley/ │ ├── README.md │ ├── alibaba-dubbo-api/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── didispace/ │ │ └── alibaba/ │ │ └── dubbo/ │ │ └── api/ │ │ └── HelloService.java │ ├── alibaba-dubbo-client/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── didispace/ │ │ │ └── alibaba/ │ │ │ └── dubbo/ │ │ │ └── client/ │ │ │ └── DubboClientApplication.java │ │ └── resources/ │ │ └── application.properties │ ├── alibaba-dubbo-server/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── didispace/ │ │ │ └── alibaba/ │ │ │ └── dubbo/ │ │ │ └── server/ │ │ │ ├── DubboServerApplication.java │ │ │ └── HelloServiceImpl.java │ │ └── resources/ │ │ └── application.properties │ ├── alibaba-nacos-config-client/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── didispace/ │ │ │ └── alibaba/ │ │ │ └── nacos/ │ │ │ └── config/ │ │ │ └── client/ │ │ │ └── TestApplication.java │ │ └── resources/ │ │ └── bootstrap.properties │ ├── alibaba-nacos-discovery-client-common/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── didispace/ │ │ │ └── alibaba/ │ │ │ └── nacos/ │ │ │ └── discovery/ │ │ │ └── client/ │ │ │ └── TestApplication.java │ │ └── resources/ │ │ └── application.properties │ ├── alibaba-nacos-discovery-client-feign/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── didispace/ │ │ │ └── alibaba/ │ │ │ └── nacos/ │ │ │ └── discovery/ │ │ │ └── client/ │ │ │ └── TestApplication.java │ │ └── resources/ │ │ └── application.properties │ ├── alibaba-nacos-discovery-client-resttemplate/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── didispace/ │ │ │ └── alibaba/ │ │ │ └── nacos/ │ │ │ └── discovery/ │ │ │ └── client/ │ │ │ └── TestApplication.java │ │ └── resources/ │ │ └── application.properties │ ├── alibaba-nacos-discovery-client-webclient/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── didispace/ │ │ │ └── alibaba/ │ │ │ └── nacos/ │ │ │ └── discovery/ │ │ │ └── client/ │ │ │ └── TestApplication.java │ │ └── resources/ │ │ └── application.properties │ ├── alibaba-nacos-discovery-server/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── didispace/ │ │ │ └── alibaba/ │ │ │ └── nacos/ │ │ │ └── discovery/ │ │ │ └── server/ │ │ │ └── TestApplication.java │ │ └── resources/ │ │ └── application.properties │ ├── alibaba-sentinel-annotation/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── didispace/ │ │ │ └── alibaba/ │ │ │ └── sentinel/ │ │ │ ├── TestApplication.java │ │ │ ├── service/ │ │ │ │ └── TestService.java │ │ │ └── web/ │ │ │ └── TestController.java │ │ └── resources/ │ │ └── application.properties │ ├── alibaba-sentinel-dashboard-apollo/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── com/ │ │ │ │ └── alibaba/ │ │ │ │ └── csp/ │ │ │ │ └── sentinel/ │ │ │ │ └── dashboard/ │ │ │ │ ├── DashboardApplication.java │ │ │ │ ├── auth/ │ │ │ │ │ ├── AuthService.java │ │ │ │ │ ├── FakeAuthServiceImpl.java │ │ │ │ │ └── SimpleWebAuthServiceImpl.java │ │ │ │ ├── client/ │ │ │ │ │ ├── CommandFailedException.java │ │ │ │ │ ├── CommandNotFoundException.java │ │ │ │ │ └── SentinelApiClient.java │ │ │ │ ├── config/ │ │ │ │ │ ├── DashboardConfig.java │ │ │ │ │ └── WebConfig.java │ │ │ │ ├── controller/ │ │ │ │ │ ├── AppController.java │ │ │ │ │ ├── AuthController.java │ │ │ │ │ ├── AuthorityRuleController.java │ │ │ │ │ ├── DegradeController.java │ │ │ │ │ ├── DemoController.java │ │ │ │ │ ├── FlowControllerV1.java │ │ │ │ │ ├── MachineRegistryController.java │ │ │ │ │ ├── MetricController.java │ │ │ │ │ ├── ParamFlowRuleController.java │ │ │ │ │ ├── ResourceController.java │ │ │ │ │ ├── SystemController.java │ │ │ │ │ ├── cluster/ │ │ │ │ │ │ ├── ClusterAssignController.java │ │ │ │ │ │ └── ClusterConfigController.java │ │ │ │ │ └── v2/ │ │ │ │ │ └── FlowControllerV2.java │ │ │ │ ├── datasource/ │ │ │ │ │ └── entity/ │ │ │ │ │ ├── ApplicationEntity.java │ │ │ │ │ ├── MachineEntity.java │ │ │ │ │ ├── MetricEntity.java │ │ │ │ │ ├── MetricPositionEntity.java │ │ │ │ │ ├── SentinelVersion.java │ │ │ │ │ └── rule/ │ │ │ │ │ ├── AbstractRuleEntity.java │ │ │ │ │ ├── AuthorityRuleEntity.java │ │ │ │ │ ├── DegradeRuleEntity.java │ │ │ │ │ ├── FlowRuleEntity.java │ │ │ │ │ ├── ParamFlowRuleEntity.java │ │ │ │ │ ├── RuleEntity.java │ │ │ │ │ └── SystemRuleEntity.java │ │ │ │ ├── discovery/ │ │ │ │ │ ├── AppInfo.java │ │ │ │ │ ├── AppManagement.java │ │ │ │ │ ├── MachineDiscovery.java │ │ │ │ │ ├── MachineInfo.java │ │ │ │ │ └── SimpleMachineDiscovery.java │ │ │ │ ├── domain/ │ │ │ │ │ ├── ResourceTreeNode.java │ │ │ │ │ ├── Result.java │ │ │ │ │ ├── cluster/ │ │ │ │ │ │ ├── ClusterAppAssignResultVO.java │ │ │ │ │ │ ├── ClusterAppFullAssignRequest.java │ │ │ │ │ │ ├── ClusterAppSingleServerAssignRequest.java │ │ │ │ │ │ ├── ClusterClientInfoVO.java │ │ │ │ │ │ ├── ClusterGroupEntity.java │ │ │ │ │ │ ├── ClusterStateSingleVO.java │ │ │ │ │ │ ├── ConnectionDescriptorVO.java │ │ │ │ │ │ ├── ConnectionGroupVO.java │ │ │ │ │ │ ├── config/ │ │ │ │ │ │ │ ├── ClusterClientConfig.java │ │ │ │ │ │ │ ├── ServerFlowConfig.java │ │ │ │ │ │ │ └── ServerTransportConfig.java │ │ │ │ │ │ ├── request/ │ │ │ │ │ │ │ ├── ClusterAppAssignMap.java │ │ │ │ │ │ │ ├── ClusterClientModifyRequest.java │ │ │ │ │ │ │ ├── ClusterModifyRequest.java │ │ │ │ │ │ │ └── ClusterServerModifyRequest.java │ │ │ │ │ │ └── state/ │ │ │ │ │ │ ├── AppClusterClientStateWrapVO.java │ │ │ │ │ │ ├── AppClusterServerStateWrapVO.java │ │ │ │ │ │ ├── ClusterClientStateVO.java │ │ │ │ │ │ ├── ClusterRequestLimitVO.java │ │ │ │ │ │ ├── ClusterServerStateVO.java │ │ │ │ │ │ ├── ClusterStateSimpleEntity.java │ │ │ │ │ │ ├── ClusterUniversalStatePairVO.java │ │ │ │ │ │ └── ClusterUniversalStateVO.java │ │ │ │ │ └── vo/ │ │ │ │ │ ├── MachineInfoVo.java │ │ │ │ │ ├── MetricVo.java │ │ │ │ │ └── ResourceVo.java │ │ │ │ ├── filter/ │ │ │ │ │ └── AuthFilter.java │ │ │ │ ├── metric/ │ │ │ │ │ └── MetricFetcher.java │ │ │ │ ├── repository/ │ │ │ │ │ ├── metric/ │ │ │ │ │ │ ├── InMemoryMetricsRepository.java │ │ │ │ │ │ └── MetricsRepository.java │ │ │ │ │ └── rule/ │ │ │ │ │ ├── InMemAuthorityRuleStore.java │ │ │ │ │ ├── InMemDegradeRuleStore.java │ │ │ │ │ ├── InMemFlowRuleStore.java │ │ │ │ │ ├── InMemParamFlowRuleStore.java │ │ │ │ │ ├── InMemSystemRuleStore.java │ │ │ │ │ ├── InMemoryRuleRepositoryAdapter.java │ │ │ │ │ └── RuleRepository.java │ │ │ │ ├── rule/ │ │ │ │ │ ├── DynamicRuleProvider.java │ │ │ │ │ ├── DynamicRulePublisher.java │ │ │ │ │ ├── FlowRuleApiProvider.java │ │ │ │ │ ├── FlowRuleApiPublisher.java │ │ │ │ │ └── apollo/ │ │ │ │ │ ├── ApolloConfig.java │ │ │ │ │ ├── FlowRuleApolloProvider.java │ │ │ │ │ └── FlowRuleApolloPublisher.java │ │ │ │ ├── service/ │ │ │ │ │ ├── ClusterAssignService.java │ │ │ │ │ ├── ClusterAssignServiceImpl.java │ │ │ │ │ └── ClusterConfigService.java │ │ │ │ └── util/ │ │ │ │ ├── AsyncUtils.java │ │ │ │ ├── ClusterEntityUtils.java │ │ │ │ ├── MachineUtils.java │ │ │ │ └── VersionUtils.java │ │ │ ├── resources/ │ │ │ │ └── application.properties │ │ │ └── webapp/ │ │ │ └── resources/ │ │ │ ├── .gitignore │ │ │ ├── .jshintrc │ │ │ ├── README.md │ │ │ ├── README_zh.md │ │ │ ├── app/ │ │ │ │ ├── scripts/ │ │ │ │ │ ├── app.js │ │ │ │ │ ├── controllers/ │ │ │ │ │ │ ├── authority.js │ │ │ │ │ │ ├── cluster_app_assign_manage.js │ │ │ │ │ │ ├── cluster_app_server_list.js │ │ │ │ │ │ ├── cluster_app_server_manage.js │ │ │ │ │ │ ├── cluster_app_server_monitor.js │ │ │ │ │ │ ├── cluster_app_token_client_list.js │ │ │ │ │ │ ├── cluster_single.js │ │ │ │ │ │ ├── degrade.js │ │ │ │ │ │ ├── flow_v1.js │ │ │ │ │ │ ├── flow_v2.js │ │ │ │ │ │ ├── home.js │ │ │ │ │ │ ├── identity.js │ │ │ │ │ │ ├── login.js │ │ │ │ │ │ ├── machine.js │ │ │ │ │ │ ├── main.js │ │ │ │ │ │ ├── metric.js │ │ │ │ │ │ ├── param_flow.js │ │ │ │ │ │ └── system.js │ │ │ │ │ ├── directives/ │ │ │ │ │ │ ├── header/ │ │ │ │ │ │ │ ├── header.html │ │ │ │ │ │ │ └── header.js │ │ │ │ │ │ └── sidebar/ │ │ │ │ │ │ ├── sidebar-search/ │ │ │ │ │ │ │ ├── sidebar-search.html │ │ │ │ │ │ │ └── sidebar-search.js │ │ │ │ │ │ ├── sidebar.html │ │ │ │ │ │ └── sidebar.js │ │ │ │ │ ├── filters/ │ │ │ │ │ │ └── filters.js │ │ │ │ │ ├── libs/ │ │ │ │ │ │ └── treeTable.js │ │ │ │ │ └── services/ │ │ │ │ │ ├── appservice.js │ │ │ │ │ ├── auth_service.js │ │ │ │ │ ├── authority_service.js │ │ │ │ │ ├── cluster_state_service.js │ │ │ │ │ ├── degradeservice.js │ │ │ │ │ ├── flow_service_v1.js │ │ │ │ │ ├── flow_service_v2.js │ │ │ │ │ ├── identityservice.js │ │ │ │ │ ├── machineservice.js │ │ │ │ │ ├── metricservice.js │ │ │ │ │ ├── param_flow_service.js │ │ │ │ │ └── systemservice.js │ │ │ │ ├── styles/ │ │ │ │ │ ├── main.css │ │ │ │ │ ├── page.css │ │ │ │ │ └── timeline.css │ │ │ │ └── views/ │ │ │ │ ├── authority.html │ │ │ │ ├── cluster/ │ │ │ │ │ ├── client.html │ │ │ │ │ └── server.html │ │ │ │ ├── cluster_app_assign_manage.html │ │ │ │ ├── cluster_app_client_list.html │ │ │ │ ├── cluster_app_server_list.html │ │ │ │ ├── cluster_app_server_overview.html │ │ │ │ ├── cluster_single_config.html │ │ │ │ ├── dashboard/ │ │ │ │ │ ├── home.html │ │ │ │ │ └── main.html │ │ │ │ ├── degrade.html │ │ │ │ ├── dialog/ │ │ │ │ │ ├── authority-rule-dialog.html │ │ │ │ │ ├── cluster/ │ │ │ │ │ │ ├── cluster-client-config-dialog.html │ │ │ │ │ │ ├── cluster-server-assign-dialog.html │ │ │ │ │ │ └── cluster-server-connection-detail-dialog.html │ │ │ │ │ ├── confirm-dialog.html │ │ │ │ │ ├── degrade-rule-dialog.html │ │ │ │ │ ├── flow-rule-dialog.html │ │ │ │ │ ├── param-flow-rule-dialog.html │ │ │ │ │ └── system-rule-dialog.html │ │ │ │ ├── flow_v1.html │ │ │ │ ├── flow_v2.html │ │ │ │ ├── identity.html │ │ │ │ ├── login.html │ │ │ │ ├── machine.html │ │ │ │ ├── metric.html │ │ │ │ ├── pagination.tpl.html │ │ │ │ ├── param_flow.html │ │ │ │ └── system.html │ │ │ ├── dist/ │ │ │ │ ├── css/ │ │ │ │ │ └── app.css │ │ │ │ └── js/ │ │ │ │ ├── app.js │ │ │ │ └── app.vendor.js │ │ │ ├── gulpfile.js │ │ │ ├── index.htm │ │ │ ├── index_dev.htm │ │ │ ├── license-stat.csv │ │ │ └── package.json │ │ └── test/ │ │ └── java/ │ │ └── com/ │ │ └── alibaba/ │ │ └── csp/ │ │ └── sentinel/ │ │ └── dashboard/ │ │ ├── config/ │ │ │ └── DashboardConfigTest.java │ │ ├── datasource/ │ │ │ └── entity/ │ │ │ └── SentinelVersionTest.java │ │ ├── discovery/ │ │ │ ├── AppInfoTest.java │ │ │ └── MachineInfoTest.java │ │ ├── repository/ │ │ │ └── metric/ │ │ │ └── InMemoryMetricsRepositoryTest.java │ │ ├── rule/ │ │ │ ├── apollo/ │ │ │ │ ├── ApolloConfig.java │ │ │ │ ├── ApolloConfigUtil.java │ │ │ │ ├── FlowRuleApolloProvider.java │ │ │ │ └── FlowRuleApolloPublisher.java │ │ │ └── nacos/ │ │ │ ├── FlowRuleNacosProvider.java │ │ │ ├── FlowRuleNacosPublisher.java │ │ │ ├── NacosConfig.java │ │ │ └── NacosConfigUtil.java │ │ └── util/ │ │ └── VersionUtilsTest.java │ ├── alibaba-sentinel-dashboard-nacos/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── com/ │ │ │ │ └── alibaba/ │ │ │ │ └── csp/ │ │ │ │ └── sentinel/ │ │ │ │ └── dashboard/ │ │ │ │ ├── DashboardApplication.java │ │ │ │ ├── auth/ │ │ │ │ │ ├── AuthService.java │ │ │ │ │ ├── FakeAuthServiceImpl.java │ │ │ │ │ └── SimpleWebAuthServiceImpl.java │ │ │ │ ├── client/ │ │ │ │ │ ├── CommandFailedException.java │ │ │ │ │ ├── CommandNotFoundException.java │ │ │ │ │ └── SentinelApiClient.java │ │ │ │ ├── config/ │ │ │ │ │ ├── DashboardConfig.java │ │ │ │ │ └── WebConfig.java │ │ │ │ ├── controller/ │ │ │ │ │ ├── AppController.java │ │ │ │ │ ├── AuthController.java │ │ │ │ │ ├── AuthorityRuleController.java │ │ │ │ │ ├── DegradeController.java │ │ │ │ │ ├── DemoController.java │ │ │ │ │ ├── FlowControllerV1.java │ │ │ │ │ ├── MachineRegistryController.java │ │ │ │ │ ├── MetricController.java │ │ │ │ │ ├── ParamFlowRuleController.java │ │ │ │ │ ├── ResourceController.java │ │ │ │ │ ├── SystemController.java │ │ │ │ │ ├── cluster/ │ │ │ │ │ │ ├── ClusterAssignController.java │ │ │ │ │ │ └── ClusterConfigController.java │ │ │ │ │ └── v2/ │ │ │ │ │ └── FlowControllerV2.java │ │ │ │ ├── datasource/ │ │ │ │ │ └── entity/ │ │ │ │ │ ├── ApplicationEntity.java │ │ │ │ │ ├── MachineEntity.java │ │ │ │ │ ├── MetricEntity.java │ │ │ │ │ ├── MetricPositionEntity.java │ │ │ │ │ ├── SentinelVersion.java │ │ │ │ │ └── rule/ │ │ │ │ │ ├── AbstractRuleEntity.java │ │ │ │ │ ├── AuthorityRuleEntity.java │ │ │ │ │ ├── DegradeRuleEntity.java │ │ │ │ │ ├── FlowRuleEntity.java │ │ │ │ │ ├── ParamFlowRuleEntity.java │ │ │ │ │ ├── RuleEntity.java │ │ │ │ │ └── SystemRuleEntity.java │ │ │ │ ├── discovery/ │ │ │ │ │ ├── AppInfo.java │ │ │ │ │ ├── AppManagement.java │ │ │ │ │ ├── MachineDiscovery.java │ │ │ │ │ ├── MachineInfo.java │ │ │ │ │ └── SimpleMachineDiscovery.java │ │ │ │ ├── domain/ │ │ │ │ │ ├── ResourceTreeNode.java │ │ │ │ │ ├── Result.java │ │ │ │ │ ├── cluster/ │ │ │ │ │ │ ├── ClusterAppAssignResultVO.java │ │ │ │ │ │ ├── ClusterAppFullAssignRequest.java │ │ │ │ │ │ ├── ClusterAppSingleServerAssignRequest.java │ │ │ │ │ │ ├── ClusterClientInfoVO.java │ │ │ │ │ │ ├── ClusterGroupEntity.java │ │ │ │ │ │ ├── ClusterStateSingleVO.java │ │ │ │ │ │ ├── ConnectionDescriptorVO.java │ │ │ │ │ │ ├── ConnectionGroupVO.java │ │ │ │ │ │ ├── config/ │ │ │ │ │ │ │ ├── ClusterClientConfig.java │ │ │ │ │ │ │ ├── ServerFlowConfig.java │ │ │ │ │ │ │ └── ServerTransportConfig.java │ │ │ │ │ │ ├── request/ │ │ │ │ │ │ │ ├── ClusterAppAssignMap.java │ │ │ │ │ │ │ ├── ClusterClientModifyRequest.java │ │ │ │ │ │ │ ├── ClusterModifyRequest.java │ │ │ │ │ │ │ └── ClusterServerModifyRequest.java │ │ │ │ │ │ └── state/ │ │ │ │ │ │ ├── AppClusterClientStateWrapVO.java │ │ │ │ │ │ ├── AppClusterServerStateWrapVO.java │ │ │ │ │ │ ├── ClusterClientStateVO.java │ │ │ │ │ │ ├── ClusterRequestLimitVO.java │ │ │ │ │ │ ├── ClusterServerStateVO.java │ │ │ │ │ │ ├── ClusterStateSimpleEntity.java │ │ │ │ │ │ ├── ClusterUniversalStatePairVO.java │ │ │ │ │ │ └── ClusterUniversalStateVO.java │ │ │ │ │ └── vo/ │ │ │ │ │ ├── MachineInfoVo.java │ │ │ │ │ ├── MetricVo.java │ │ │ │ │ └── ResourceVo.java │ │ │ │ ├── filter/ │ │ │ │ │ └── AuthFilter.java │ │ │ │ ├── metric/ │ │ │ │ │ └── MetricFetcher.java │ │ │ │ ├── repository/ │ │ │ │ │ ├── metric/ │ │ │ │ │ │ ├── InMemoryMetricsRepository.java │ │ │ │ │ │ └── MetricsRepository.java │ │ │ │ │ └── rule/ │ │ │ │ │ ├── InMemAuthorityRuleStore.java │ │ │ │ │ ├── InMemDegradeRuleStore.java │ │ │ │ │ ├── InMemFlowRuleStore.java │ │ │ │ │ ├── InMemParamFlowRuleStore.java │ │ │ │ │ ├── InMemSystemRuleStore.java │ │ │ │ │ ├── InMemoryRuleRepositoryAdapter.java │ │ │ │ │ └── RuleRepository.java │ │ │ │ ├── rule/ │ │ │ │ │ ├── DynamicRuleProvider.java │ │ │ │ │ ├── DynamicRulePublisher.java │ │ │ │ │ ├── FlowRuleApiProvider.java │ │ │ │ │ ├── FlowRuleApiPublisher.java │ │ │ │ │ └── nacos/ │ │ │ │ │ ├── FlowRuleNacosProvider.java │ │ │ │ │ ├── FlowRuleNacosPublisher.java │ │ │ │ │ └── NacosConfig.java │ │ │ │ ├── service/ │ │ │ │ │ ├── ClusterAssignService.java │ │ │ │ │ ├── ClusterAssignServiceImpl.java │ │ │ │ │ └── ClusterConfigService.java │ │ │ │ └── util/ │ │ │ │ ├── AsyncUtils.java │ │ │ │ ├── ClusterEntityUtils.java │ │ │ │ ├── MachineUtils.java │ │ │ │ └── VersionUtils.java │ │ │ ├── resources/ │ │ │ │ └── application.properties │ │ │ └── webapp/ │ │ │ └── resources/ │ │ │ ├── .gitignore │ │ │ ├── .jshintrc │ │ │ ├── README.md │ │ │ ├── README_zh.md │ │ │ ├── app/ │ │ │ │ ├── scripts/ │ │ │ │ │ ├── app.js │ │ │ │ │ ├── controllers/ │ │ │ │ │ │ ├── authority.js │ │ │ │ │ │ ├── cluster_app_assign_manage.js │ │ │ │ │ │ ├── cluster_app_server_list.js │ │ │ │ │ │ ├── cluster_app_server_manage.js │ │ │ │ │ │ ├── cluster_app_server_monitor.js │ │ │ │ │ │ ├── cluster_app_token_client_list.js │ │ │ │ │ │ ├── cluster_single.js │ │ │ │ │ │ ├── degrade.js │ │ │ │ │ │ ├── flow_v1.js │ │ │ │ │ │ ├── flow_v2.js │ │ │ │ │ │ ├── home.js │ │ │ │ │ │ ├── identity.js │ │ │ │ │ │ ├── login.js │ │ │ │ │ │ ├── machine.js │ │ │ │ │ │ ├── main.js │ │ │ │ │ │ ├── metric.js │ │ │ │ │ │ ├── param_flow.js │ │ │ │ │ │ └── system.js │ │ │ │ │ ├── directives/ │ │ │ │ │ │ ├── header/ │ │ │ │ │ │ │ ├── header.html │ │ │ │ │ │ │ └── header.js │ │ │ │ │ │ └── sidebar/ │ │ │ │ │ │ ├── sidebar-search/ │ │ │ │ │ │ │ ├── sidebar-search.html │ │ │ │ │ │ │ └── sidebar-search.js │ │ │ │ │ │ ├── sidebar.html │ │ │ │ │ │ └── sidebar.js │ │ │ │ │ ├── filters/ │ │ │ │ │ │ └── filters.js │ │ │ │ │ ├── libs/ │ │ │ │ │ │ └── treeTable.js │ │ │ │ │ └── services/ │ │ │ │ │ ├── appservice.js │ │ │ │ │ ├── auth_service.js │ │ │ │ │ ├── authority_service.js │ │ │ │ │ ├── cluster_state_service.js │ │ │ │ │ ├── degradeservice.js │ │ │ │ │ ├── flow_service_v1.js │ │ │ │ │ ├── flow_service_v2.js │ │ │ │ │ ├── identityservice.js │ │ │ │ │ ├── machineservice.js │ │ │ │ │ ├── metricservice.js │ │ │ │ │ ├── param_flow_service.js │ │ │ │ │ └── systemservice.js │ │ │ │ ├── styles/ │ │ │ │ │ ├── main.css │ │ │ │ │ ├── page.css │ │ │ │ │ └── timeline.css │ │ │ │ └── views/ │ │ │ │ ├── authority.html │ │ │ │ ├── cluster/ │ │ │ │ │ ├── client.html │ │ │ │ │ └── server.html │ │ │ │ ├── cluster_app_assign_manage.html │ │ │ │ ├── cluster_app_client_list.html │ │ │ │ ├── cluster_app_server_list.html │ │ │ │ ├── cluster_app_server_overview.html │ │ │ │ ├── cluster_single_config.html │ │ │ │ ├── dashboard/ │ │ │ │ │ ├── home.html │ │ │ │ │ └── main.html │ │ │ │ ├── degrade.html │ │ │ │ ├── dialog/ │ │ │ │ │ ├── authority-rule-dialog.html │ │ │ │ │ ├── cluster/ │ │ │ │ │ │ ├── cluster-client-config-dialog.html │ │ │ │ │ │ ├── cluster-server-assign-dialog.html │ │ │ │ │ │ └── cluster-server-connection-detail-dialog.html │ │ │ │ │ ├── confirm-dialog.html │ │ │ │ │ ├── degrade-rule-dialog.html │ │ │ │ │ ├── flow-rule-dialog.html │ │ │ │ │ ├── param-flow-rule-dialog.html │ │ │ │ │ └── system-rule-dialog.html │ │ │ │ ├── flow_v1.html │ │ │ │ ├── flow_v2.html │ │ │ │ ├── identity.html │ │ │ │ ├── login.html │ │ │ │ ├── machine.html │ │ │ │ ├── metric.html │ │ │ │ ├── pagination.tpl.html │ │ │ │ ├── param_flow.html │ │ │ │ └── system.html │ │ │ ├── dist/ │ │ │ │ ├── css/ │ │ │ │ │ └── app.css │ │ │ │ └── js/ │ │ │ │ ├── app.js │ │ │ │ └── app.vendor.js │ │ │ ├── gulpfile.js │ │ │ ├── index.htm │ │ │ ├── index_dev.htm │ │ │ ├── license-stat.csv │ │ │ └── package.json │ │ └── test/ │ │ └── java/ │ │ └── com/ │ │ └── alibaba/ │ │ └── csp/ │ │ └── sentinel/ │ │ └── dashboard/ │ │ ├── config/ │ │ │ └── DashboardConfigTest.java │ │ ├── datasource/ │ │ │ └── entity/ │ │ │ └── SentinelVersionTest.java │ │ ├── discovery/ │ │ │ ├── AppInfoTest.java │ │ │ └── MachineInfoTest.java │ │ ├── repository/ │ │ │ └── metric/ │ │ │ └── InMemoryMetricsRepositoryTest.java │ │ ├── rule/ │ │ │ ├── apollo/ │ │ │ │ ├── ApolloConfig.java │ │ │ │ ├── ApolloConfigUtil.java │ │ │ │ ├── FlowRuleApolloProvider.java │ │ │ │ └── FlowRuleApolloPublisher.java │ │ │ └── nacos/ │ │ │ ├── FlowRuleNacosProvider.java │ │ │ ├── FlowRuleNacosPublisher.java │ │ │ ├── NacosConfig.java │ │ │ └── NacosConfigUtil.java │ │ └── util/ │ │ └── VersionUtilsTest.java │ ├── alibaba-sentinel-datasource-apollo/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── didispace/ │ │ │ └── alibaba/ │ │ │ └── sentinel/ │ │ │ └── TestApplication.java │ │ └── resources/ │ │ ├── apollo-env.properties │ │ └── application.properties │ ├── alibaba-sentinel-datasource-nacos/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── didispace/ │ │ │ └── alibaba/ │ │ │ └── sentinel/ │ │ │ └── TestApplication.java │ │ └── resources/ │ │ └── application.properties │ ├── alibaba-sentinel-rate-limiting/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── didispace/ │ │ │ └── alibaba/ │ │ │ └── sentinel/ │ │ │ └── TestApplication.java │ │ └── resources/ │ │ └── application.properties │ ├── pom.xml │ ├── stream-consumer-self/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── didispace/ │ │ │ └── stream/ │ │ │ ├── TestApplication.java │ │ │ ├── TestController.java │ │ │ ├── TestListener.java │ │ │ └── TestTopic.java │ │ └── resources/ │ │ └── application.properties │ ├── stream-content-route/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── didispace/ │ │ │ └── stream/ │ │ │ └── TestApplication.java │ │ └── resources/ │ │ └── application.properties │ ├── stream-delayed-message/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── didispace/ │ │ │ └── stream/ │ │ │ └── TestApplication.java │ │ └── resources/ │ │ └── application.properties │ ├── stream-exception-handler-1/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── didispace/ │ │ │ └── stream/ │ │ │ └── TestApplication.java │ │ └── resources/ │ │ └── application.properties │ ├── stream-exception-handler-2/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── didispace/ │ │ │ └── stream/ │ │ │ └── TestApplication.java │ │ └── resources/ │ │ └── application.properties │ ├── stream-exception-handler-3/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── didispace/ │ │ │ └── stream/ │ │ │ └── TestApplication.java │ │ └── resources/ │ │ └── application.properties │ └── stream-exception-handler-4/ │ ├── pom.xml │ └── src/ │ └── main/ │ ├── java/ │ │ └── com/ │ │ └── didispace/ │ │ └── stream/ │ │ └── TestApplication.java │ └── resources/ │ └── application.properties ├── README.md └── README_zh.md ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ *.class # Package Files # *.jar *.war *.ear # idea Files .idea *.iml target # log Files *.log 2-Dalston版教程示例/trace-2/build/bootstrap.json 2-Dalston版教程示例/trace-2/build/springAppName_IS_UNDEFINED.json 2-Dalston版教程示例/trace-2/build/trace-2.json ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-1/compute-service/pom.xml ================================================ 4.0.0 com.didispace compute-service 1.0.0 jar compute-service Spring Cloud project org.springframework.boot spring-boot-starter-parent 1.3.5.RELEASE UTF-8 1.8 org.springframework.cloud spring-cloud-starter-eureka org.springframework.boot spring-boot-starter-test test org.springframework.cloud spring-cloud-dependencies Brixton.RELEASE pom import org.springframework.boot spring-boot-maven-plugin ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-1/compute-service/src/main/java/com/didispace/ComputeServiceApplication.java ================================================ package com.didispace; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; @EnableDiscoveryClient @SpringBootApplication public class ComputeServiceApplication { public static void main(String[] args) { new SpringApplicationBuilder(ComputeServiceApplication.class).web(true).run(args); } } ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-1/compute-service/src/main/java/com/didispace/web/ComputeController.java ================================================ package com.didispace.web; import org.apache.log4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.discovery.DiscoveryClient; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController public class ComputeController { private final Logger logger = Logger.getLogger(getClass()); @Autowired private DiscoveryClient client; @RequestMapping(value = "/add" ,method = RequestMethod.GET) public Integer add(@RequestParam Integer a, @RequestParam Integer b) { ServiceInstance instance = client.getLocalServiceInstance(); Integer r = a + b; logger.info("/add, host:" + instance.getHost() + ", service_id:" + instance.getServiceId() + ", result:" + r); return r; } } ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-1/compute-service/src/main/resources/application.properties ================================================ spring.application.name=compute-service server.port=2222 eureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka/ ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-1/compute-service/src/test/java/com/didispace/ApplicationTests.java ================================================ package com.didispace; import com.didispace.web.ComputeController; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.boot.test.SpringApplicationConfiguration; import org.springframework.http.MediaType; import org.springframework.mock.web.MockServletContext; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import static org.hamcrest.Matchers.equalTo; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @RunWith(SpringJUnit4ClassRunner.class) @SpringApplicationConfiguration(classes = MockServletContext.class) @WebAppConfiguration public class ApplicationTests { private MockMvc mvc; @Before public void setUp() throws Exception { mvc = MockMvcBuilders.standaloneSetup(new ComputeController()).build(); } @Test public void getHello() throws Exception { mvc.perform(MockMvcRequestBuilders.get("/hello").accept(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andExpect(content().string(equalTo("Hello World"))); } } ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-1/eureka-server/pom.xml ================================================ 4.0.0 com.didispace eureka-server 1.0.0 jar eureka-server Spring Cloud project org.springframework.boot spring-boot-starter-parent 1.3.5.RELEASE UTF-8 1.8 org.springframework.boot spring-boot-starter-test test org.springframework.cloud spring-cloud-starter-eureka-server org.springframework.cloud spring-cloud-dependencies Brixton.RELEASE pom import org.springframework.boot spring-boot-maven-plugin ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-1/eureka-server/src/main/java/com/didispace/Application.java ================================================ package com.didispace; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer; @EnableEurekaServer @SpringBootApplication public class Application { public static void main(String[] args) { new SpringApplicationBuilder(Application.class).web(true).run(args); } } ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-1/eureka-server/src/main/resources/application.properties ================================================ server.port=1111 #eureka.instance.hostname=localhost eureka.client.register-with-eureka=false eureka.client.fetch-registry=false eureka.client.serviceUrl.defaultZone=http://localhost:${server.port}/eureka/ ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-2/eureka-feign/pom.xml ================================================ 4.0.0 com.didispace eureka-feign 1.0.0 jar eureka-feign Spring Cloud project org.springframework.boot spring-boot-starter-parent 1.3.5.RELEASE UTF-8 1.8 org.springframework.cloud spring-cloud-starter-feign org.springframework.cloud spring-cloud-starter-eureka org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-test test org.springframework.cloud spring-cloud-dependencies Brixton.RELEASE pom import org.springframework.boot spring-boot-maven-plugin ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-2/eureka-feign/src/main/java/com/didispace/FeignApplication.java ================================================ package com.didispace; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.netflix.feign.EnableFeignClients; @SpringBootApplication @EnableDiscoveryClient @EnableFeignClients public class FeignApplication { public static void main(String[] args) { SpringApplication.run(FeignApplication.class, args); } } ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-2/eureka-feign/src/main/java/com/didispace/service/ComputeClient.java ================================================ package com.didispace.service; import org.springframework.cloud.netflix.feign.FeignClient; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; @FeignClient("compute-service") public interface ComputeClient { @RequestMapping(method = RequestMethod.GET, value = "/add") Integer add(@RequestParam(value = "a") Integer a, @RequestParam(value = "b") Integer b); } ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-2/eureka-feign/src/main/java/com/didispace/web/ConsumerController.java ================================================ package com.didispace.web; import com.didispace.service.ComputeClient; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; @RestController public class ConsumerController { @Autowired ComputeClient computeClient; @RequestMapping(value = "/add", method = RequestMethod.GET) public Integer add() { return computeClient.add(10, 20); } } ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-2/eureka-feign/src/main/resources/application.properties ================================================ spring.application.name=feign-consumer server.port=3333 eureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka/ ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-2/eureka-ribbon/pom.xml ================================================ 4.0.0 com.didispace eureka-ribbon 1.0.0 jar eureka-ribbon Spring Cloud project org.springframework.boot spring-boot-starter-parent 1.3.5.RELEASE UTF-8 1.8 org.springframework.cloud spring-cloud-starter-ribbon org.springframework.cloud spring-cloud-starter-eureka org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-test test org.springframework.cloud spring-cloud-dependencies Brixton.RELEASE pom import org.springframework.boot spring-boot-maven-plugin ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-2/eureka-ribbon/src/main/java/com/didispace/RibbonApplication.java ================================================ package com.didispace; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.context.annotation.Bean; import org.springframework.web.client.RestTemplate; @SpringBootApplication @EnableDiscoveryClient public class RibbonApplication { @Bean @LoadBalanced RestTemplate restTemplate() { return new RestTemplate(); } public static void main(String[] args) { SpringApplication.run(RibbonApplication.class, args); } } ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-2/eureka-ribbon/src/main/java/com/didispace/web/ConsumerController.java ================================================ package com.didispace.web; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; @RestController public class ConsumerController { @Autowired RestTemplate restTemplate; @RequestMapping(value = "/add", method = RequestMethod.GET) public String add() { return restTemplate.getForEntity("http://COMPUTE-SERVICE/add?a=10&b=20", String.class).getBody(); } } ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-2/eureka-ribbon/src/main/resources/application.properties ================================================ spring.application.name=ribbon-consumer server.port=3333 eureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka/ ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-3/compute-service/pom.xml ================================================ 4.0.0 com.didispace compute-service 1.0.0 jar compute-service Spring Cloud project org.springframework.boot spring-boot-starter-parent 1.3.5.RELEASE UTF-8 1.8 org.springframework.cloud spring-cloud-starter-eureka org.springframework.boot spring-boot-starter-test test org.springframework.cloud spring-cloud-dependencies Brixton.RELEASE pom import org.springframework.boot spring-boot-maven-plugin ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-3/compute-service/src/main/java/com/didispace/ComputeServiceApplication.java ================================================ package com.didispace; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; @EnableDiscoveryClient @SpringBootApplication public class ComputeServiceApplication { public static void main(String[] args) { new SpringApplicationBuilder(ComputeServiceApplication.class).web(true).run(args); } } ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-3/compute-service/src/main/java/com/didispace/web/ComputeController.java ================================================ package com.didispace.web; import org.apache.log4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.discovery.DiscoveryClient; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController public class ComputeController { private final Logger logger = Logger.getLogger(getClass()); @Autowired private DiscoveryClient client; @RequestMapping(value = "/add" ,method = RequestMethod.GET) public Integer add(@RequestParam Integer a, @RequestParam Integer b) { ServiceInstance instance = client.getLocalServiceInstance(); Integer r = a + b; logger.info("/add, host:" + instance.getHost() + ", service_id:" + instance.getServiceId() + ", result:" + r); return r; } } ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-3/compute-service/src/main/resources/application.properties ================================================ spring.application.name=compute-service server.port=2222 eureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka/ ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-3/compute-service/src/test/java/com/didispace/ApplicationTests.java ================================================ package com.didispace; import com.didispace.web.ComputeController; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.boot.test.SpringApplicationConfiguration; import org.springframework.http.MediaType; import org.springframework.mock.web.MockServletContext; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import static org.hamcrest.Matchers.equalTo; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @RunWith(SpringJUnit4ClassRunner.class) @SpringApplicationConfiguration(classes = MockServletContext.class) @WebAppConfiguration public class ApplicationTests { private MockMvc mvc; @Before public void setUp() throws Exception { mvc = MockMvcBuilders.standaloneSetup(new ComputeController()).build(); } @Test public void getHello() throws Exception { mvc.perform(MockMvcRequestBuilders.get("/hello").accept(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andExpect(content().string(equalTo("Hello World"))); } } ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-3/eureka-feign/pom.xml ================================================ 4.0.0 com.didispace eureka-feign 1.0.0 jar eureka-feign Spring Cloud project org.springframework.boot spring-boot-starter-parent 1.3.5.RELEASE UTF-8 1.8 org.springframework.cloud spring-cloud-starter-feign org.springframework.cloud spring-cloud-starter-eureka org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-test test org.springframework.cloud spring-cloud-dependencies Brixton.RELEASE pom import org.springframework.boot spring-boot-maven-plugin ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-3/eureka-feign/src/main/java/com/didispace/FeignApplication.java ================================================ package com.didispace; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.netflix.feign.EnableFeignClients; @SpringBootApplication @EnableDiscoveryClient @EnableFeignClients public class FeignApplication { public static void main(String[] args) { SpringApplication.run(FeignApplication.class, args); } } ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-3/eureka-feign/src/main/java/com/didispace/service/ComputeClient.java ================================================ package com.didispace.service; import org.springframework.cloud.netflix.feign.FeignClient; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; @FeignClient(value = "compute-service", fallback = ComputeClientHystrix.class) public interface ComputeClient { @RequestMapping(method = RequestMethod.GET, value = "/add") Integer add(@RequestParam(value = "a") Integer a, @RequestParam(value = "b") Integer b); } ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-3/eureka-feign/src/main/java/com/didispace/service/ComputeClientHystrix.java ================================================ package com.didispace.service; import org.springframework.stereotype.Component; import org.springframework.web.bind.annotation.RequestParam; @Component public class ComputeClientHystrix implements ComputeClient { @Override public Integer add(@RequestParam(value = "a") Integer a, @RequestParam(value = "b") Integer b) { return -9999; } } ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-3/eureka-feign/src/main/java/com/didispace/web/ConsumerController.java ================================================ package com.didispace.web; import com.didispace.service.ComputeClient; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; @RestController public class ConsumerController { @Autowired ComputeClient computeClient; @RequestMapping(value = "/add", method = RequestMethod.GET) public Integer add() { return computeClient.add(10, 20); } } ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-3/eureka-ribbon/pom.xml ================================================ 4.0.0 com.didispace eureka-ribbon 1.0.0 jar eureka-ribbon Spring Cloud project org.springframework.boot spring-boot-starter-parent 1.3.5.RELEASE UTF-8 1.8 org.springframework.cloud spring-cloud-starter-hystrix org.springframework.cloud spring-cloud-starter-ribbon org.springframework.cloud spring-cloud-starter-eureka org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-test test org.springframework.cloud spring-cloud-dependencies Brixton.RELEASE pom import org.springframework.boot spring-boot-maven-plugin ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-3/eureka-ribbon/src/main/java/com/didispace/RibbonApplication.java ================================================ package com.didispace; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.context.annotation.Bean; import org.springframework.web.client.RestTemplate; @SpringBootApplication @EnableDiscoveryClient @EnableCircuitBreaker public class RibbonApplication { @Bean @LoadBalanced RestTemplate restTemplate() { return new RestTemplate(); } public static void main(String[] args) { SpringApplication.run(RibbonApplication.class, args); } } ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-3/eureka-ribbon/src/main/java/com/didispace/service/ComputeService.java ================================================ package com.didispace.service; import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate; @Service public class ComputeService { @Autowired RestTemplate restTemplate; @HystrixCommand(fallbackMethod = "addServiceFallback") public String addService() { return restTemplate.getForEntity("http://COMPUTE-SERVICE/add?a=10&b=20", String.class).getBody(); } public String addServiceFallback() { return "error"; } } ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-3/eureka-ribbon/src/main/java/com/didispace/web/ConsumerController.java ================================================ package com.didispace.web; import com.didispace.service.ComputeService; import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; import java.util.Map; @RestController public class ConsumerController { @Autowired private ComputeService computeService; @RequestMapping(value = "/add", method = RequestMethod.GET) public String add() { return computeService.addService(); } } ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-3/eureka-server/pom.xml ================================================ 4.0.0 com.didispace eureka-server 1.0.0 jar eureka-server Spring Cloud project org.springframework.boot spring-boot-starter-parent 1.3.5.RELEASE UTF-8 1.8 org.springframework.boot spring-boot-starter-test test org.springframework.cloud spring-cloud-starter-eureka-server org.springframework.cloud spring-cloud-dependencies Brixton.RELEASE pom import org.springframework.boot spring-boot-maven-plugin ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-3/eureka-server/src/main/java/com/didispace/Application.java ================================================ package com.didispace; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer; @EnableEurekaServer @SpringBootApplication public class Application { public static void main(String[] args) { new SpringApplicationBuilder(Application.class).web(true).run(args); } } ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-3/eureka-server/src/main/resources/application.properties ================================================ server.port=1111 #eureka.instance.hostname=localhost eureka.client.register-with-eureka=false eureka.client.fetch-registry=false eureka.client.serviceUrl.defaultZone=http://localhost:${server.port}/eureka/ ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-4/config-client/pom.xml ================================================ 4.0.0 com.didispace config-client 1.0.0 jar config-client Spring Cloud project org.springframework.boot spring-boot-starter-parent 1.3.5.RELEASE UTF-8 1.8 org.springframework.boot spring-boot-starter-test test org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-config org.springframework.cloud spring-cloud-dependencies Brixton.RELEASE pom import org.springframework.boot spring-boot-maven-plugin ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-4/config-client/src/main/java/com/didispace/Application.java ================================================ package com.didispace; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; @SpringBootApplication public class Application { public static void main(String[] args) { new SpringApplicationBuilder(Application.class).web(true).run(args); } } ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-4/config-client/src/main/java/com/didispace/web/TestController.java ================================================ package com.didispace.web; import org.springframework.beans.factory.annotation.Value; import org.springframework.cloud.context.config.annotation.RefreshScope; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RefreshScope @RestController class TestController { @Value("${from}") private String from; @RequestMapping("/from") public String from() { return this.from; } public void setFrom(String from) { this.from = from; } public String getFrom() { return from; } } ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-4/config-client/src/main/resources/bootstrap.properties ================================================ spring.application.name=didispace spring.cloud.config.profile=dev spring.cloud.config.uri=http://localhost:7001/ server.port=7002 ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-4/config-repo/didispace-dev.properties ================================================ from=git-dev-1.0 ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-4/config-repo/didispace-prod.properties ================================================ from=git-prod-1.0 ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-4/config-repo/didispace-test.properties ================================================ from=git-test-1.0 ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-4/config-repo/didispace.properties ================================================ from=git-default-1.0 ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-4/config-server/pom.xml ================================================ 4.0.0 com.didispace config-server 1.0.0 jar config-server Spring Cloud project org.springframework.boot spring-boot-starter-parent 1.3.5.RELEASE UTF-8 1.8 org.springframework.boot spring-boot-starter-test test org.springframework.cloud spring-cloud-config-server org.springframework.cloud spring-cloud-dependencies Brixton.RELEASE pom import org.springframework.boot spring-boot-maven-plugin ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-4/config-server/src/main/java/com/didispace/Application.java ================================================ package com.didispace; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.cloud.config.server.EnableConfigServer; @EnableConfigServer @SpringBootApplication public class Application { public static void main(String[] args) { new SpringApplicationBuilder(Application.class).web(true).run(args); } } ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-4/config-server/src/main/resources/application.properties ================================================ spring.application.name=config-server server.port=7001 # git spring.cloud.config.server.git.uri=http://git.oschina.net/didispace/SpringBoot-Learning/ spring.cloud.config.server.git.searchPaths=Chapter9-1-4/config-repo spring.cloud.config.server.git.username=username spring.cloud.config.server.git.password=password # spring.profiles.active=native ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-4/config-server/src/main/resources/didispace-dev.properties ================================================ from=local-dev ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-4/config-server/src/main/resources/didispace-prod.properties ================================================ from=local-prod ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-4/config-server/src/main/resources/didispace-test.properties ================================================ from=local-test ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-4/config-server/src/main/resources/didispace.properties ================================================ from=local ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-5/api-gateway/pom.xml ================================================ 4.0.0 com.didispace api-gateway 1.0.0 jar api-gateway Spring Cloud project org.springframework.boot spring-boot-starter-parent 1.3.5.RELEASE UTF-8 1.8 org.springframework.cloud spring-cloud-starter-zuul org.springframework.cloud spring-cloud-starter-eureka org.springframework.cloud spring-cloud-dependencies Brixton.RELEASE pom import org.springframework.boot spring-boot-maven-plugin ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-5/api-gateway/src/main/java/com/didispace/Application.java ================================================ package com.didispace; import com.didispace.filter.AccessFilter; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.cloud.client.SpringCloudApplication; import org.springframework.cloud.netflix.zuul.EnableZuulProxy; import org.springframework.context.annotation.Bean; @EnableZuulProxy @SpringCloudApplication public class Application { public static void main(String[] args) { new SpringApplicationBuilder(Application.class).web(true).run(args); } @Bean public AccessFilter accessFilter() { return new AccessFilter(); } } ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-5/api-gateway/src/main/java/com/didispace/filter/AccessFilter.java ================================================ package com.didispace.filter; import com.netflix.zuul.ZuulFilter; import com.netflix.zuul.context.RequestContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.servlet.http.HttpServletRequest; public class AccessFilter extends ZuulFilter { private static Logger log = LoggerFactory.getLogger(AccessFilter.class); @Override public String filterType() { return "pre"; } @Override public int filterOrder() { return 0; } @Override public boolean shouldFilter() { return true; } @Override public Object run() { RequestContext ctx = RequestContext.getCurrentContext(); HttpServletRequest request = ctx.getRequest(); log.info(String.format("%s request to %s", request.getMethod(), request.getRequestURL().toString())); Object accessToken = request.getParameter("accessToken"); if(accessToken == null) { log.warn("access token is empty"); ctx.setSendZuulResponse(false); ctx.setResponseStatusCode(401); return null; } log.info("access token ok"); return null; } } ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-5/api-gateway/src/main/resources/application.properties ================================================ spring.application.name=api-gateway server.port=5555 # routes to serviceId zuul.routes.api-a.path=/api-a/** zuul.routes.api-a.serviceId=service-A zuul.routes.api-b.path=/api-b/** zuul.routes.api-b.serviceId=service-B # routes to url zuul.routes.api-a-url.path=/api-a-url/** zuul.routes.api-a-url.url=http://localhost:2222/ eureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka/ ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-5/eureka-server/pom.xml ================================================ 4.0.0 com.didispace eureka-server 1.0.0 jar eureka-server Spring Cloud project org.springframework.boot spring-boot-starter-parent 1.3.5.RELEASE UTF-8 1.8 org.springframework.boot spring-boot-starter-test test org.springframework.cloud spring-cloud-starter-eureka-server org.springframework.cloud spring-cloud-dependencies Brixton.RELEASE pom import org.springframework.boot spring-boot-maven-plugin ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-5/eureka-server/src/main/java/com/didispace/Application.java ================================================ package com.didispace; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer; @EnableEurekaServer @SpringBootApplication public class Application { public static void main(String[] args) { new SpringApplicationBuilder(Application.class).web(true).run(args); } } ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-5/eureka-server/src/main/resources/application.properties ================================================ server.port=1111 #eureka.instance.hostname=localhost eureka.client.register-with-eureka=false eureka.client.fetch-registry=false eureka.client.serviceUrl.defaultZone=http://localhost:${server.port}/eureka/ ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-5/service-A/pom.xml ================================================ 4.0.0 com.didispace service-A 1.0.0 jar service-A Spring Cloud project org.springframework.boot spring-boot-starter-parent 1.3.5.RELEASE UTF-8 1.8 org.springframework.cloud spring-cloud-starter-eureka org.springframework.boot spring-boot-starter-test test org.springframework.cloud spring-cloud-dependencies Brixton.RELEASE pom import org.springframework.boot spring-boot-maven-plugin ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-5/service-A/src/main/java/com/didispace/ComputeServiceApplication.java ================================================ package com.didispace; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; @EnableDiscoveryClient @SpringBootApplication public class ComputeServiceApplication { public static void main(String[] args) { new SpringApplicationBuilder(ComputeServiceApplication.class).web(true).run(args); } } ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-5/service-A/src/main/java/com/didispace/web/ComputeController.java ================================================ package com.didispace.web; import org.apache.log4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.discovery.DiscoveryClient; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController public class ComputeController { private final Logger logger = Logger.getLogger(getClass()); @Autowired private DiscoveryClient client; @RequestMapping(value = "/add" ,method = RequestMethod.GET) public String add(@RequestParam Integer a, @RequestParam Integer b) { ServiceInstance instance = client.getLocalServiceInstance(); Integer r = a + b; logger.info("/add, host:" + instance.getHost() + ", service_id:" + instance.getServiceId() + ", result:" + r); return "From Service-A, Result is " + r; } } ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-5/service-A/src/main/resources/application.properties ================================================ spring.application.name=service-A server.port=2222 eureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka/ ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-5/service-A/src/test/java/com/didispace/ApplicationTests.java ================================================ package com.didispace; import com.didispace.web.ComputeController; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.boot.test.SpringApplicationConfiguration; import org.springframework.http.MediaType; import org.springframework.mock.web.MockServletContext; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import static org.hamcrest.Matchers.equalTo; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @RunWith(SpringJUnit4ClassRunner.class) @SpringApplicationConfiguration(classes = MockServletContext.class) @WebAppConfiguration public class ApplicationTests { private MockMvc mvc; @Before public void setUp() throws Exception { mvc = MockMvcBuilders.standaloneSetup(new ComputeController()).build(); } @Test public void getHello() throws Exception { } } ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-5/service-B/pom.xml ================================================ 4.0.0 com.didispace service-B 1.0.0 jar service-B Spring Cloud project org.springframework.boot spring-boot-starter-parent 1.3.5.RELEASE UTF-8 1.8 org.springframework.cloud spring-cloud-starter-eureka org.springframework.boot spring-boot-starter-test test org.springframework.cloud spring-cloud-dependencies Brixton.RELEASE pom import org.springframework.boot spring-boot-maven-plugin ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-5/service-B/src/main/java/com/didispace/ComputeServiceApplication.java ================================================ package com.didispace; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; @EnableDiscoveryClient @SpringBootApplication public class ComputeServiceApplication { public static void main(String[] args) { new SpringApplicationBuilder(ComputeServiceApplication.class).web(true).run(args); } } ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-5/service-B/src/main/java/com/didispace/web/ComputeController.java ================================================ package com.didispace.web; import org.apache.log4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.discovery.DiscoveryClient; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController public class ComputeController { private final Logger logger = Logger.getLogger(getClass()); @Autowired private DiscoveryClient client; @RequestMapping(value = "/add" ,method = RequestMethod.GET) public String add(@RequestParam Integer a, @RequestParam Integer b) { ServiceInstance instance = client.getLocalServiceInstance(); Integer r = a + b; logger.info("/add, host:" + instance.getHost() + ", service_id:" + instance.getServiceId() + ", result:" + r); return "From Service-B, Result is " + r; } } ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-5/service-B/src/main/resources/application.properties ================================================ spring.application.name=service-B server.port=3333 eureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka/ ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-5/service-B/src/test/java/com/didispace/ApplicationTests.java ================================================ package com.didispace; import com.didispace.web.ComputeController; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.boot.test.SpringApplicationConfiguration; import org.springframework.mock.web.MockServletContext; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.setup.MockMvcBuilders; @RunWith(SpringJUnit4ClassRunner.class) @SpringApplicationConfiguration(classes = MockServletContext.class) @WebAppConfiguration public class ApplicationTests { private MockMvc mvc; @Before public void setUp() throws Exception { mvc = MockMvcBuilders.standaloneSetup(new ComputeController()).build(); } @Test public void getHello() throws Exception { } } ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-6/compute-service/pom.xml ================================================ 4.0.0 com.didispace compute-service 1.0.0 jar compute-service Spring Cloud project org.springframework.boot spring-boot-starter-parent 1.3.5.RELEASE UTF-8 1.8 org.springframework.cloud spring-cloud-starter-eureka org.springframework.boot spring-boot-starter-test test org.springframework.cloud spring-cloud-dependencies Brixton.RELEASE pom import org.springframework.boot spring-boot-maven-plugin ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-6/compute-service/src/main/java/com/didispace/ComputeServiceApplication.java ================================================ package com.didispace; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; @EnableDiscoveryClient @SpringBootApplication public class ComputeServiceApplication { public static void main(String[] args) { new SpringApplicationBuilder(ComputeServiceApplication.class).web(true).run(args); } } ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-6/compute-service/src/main/java/com/didispace/web/ComputeController.java ================================================ package com.didispace.web; import org.apache.log4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.discovery.DiscoveryClient; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController public class ComputeController { private final Logger logger = Logger.getLogger(getClass()); @Autowired private DiscoveryClient client; @RequestMapping(value = "/add" ,method = RequestMethod.GET) public Integer add(@RequestParam Integer a, @RequestParam Integer b) { ServiceInstance instance = client.getLocalServiceInstance(); Integer r = a + b; logger.info("/add, host:" + instance.getHost() + ", service_id:" + instance.getServiceId() + ", result:" + r); return r; } } ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-6/compute-service/src/main/resources/application.properties ================================================ spring.application.name=compute-service server.port=2222 eureka.client.serviceUrl.defaultZone=http://peer1:1111/eureka/,http://peer2:1112/eureka/ ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-6/compute-service/src/test/java/com/didispace/ApplicationTests.java ================================================ package com.didispace; import com.didispace.web.ComputeController; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.boot.test.SpringApplicationConfiguration; import org.springframework.http.MediaType; import org.springframework.mock.web.MockServletContext; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import static org.hamcrest.Matchers.equalTo; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @RunWith(SpringJUnit4ClassRunner.class) @SpringApplicationConfiguration(classes = MockServletContext.class) @WebAppConfiguration public class ApplicationTests { private MockMvc mvc; @Before public void setUp() throws Exception { mvc = MockMvcBuilders.standaloneSetup(new ComputeController()).build(); } @Test public void getHello() throws Exception { mvc.perform(MockMvcRequestBuilders.get("/hello").accept(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andExpect(content().string(equalTo("Hello World"))); } } ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-6/eureka-server/pom.xml ================================================ 4.0.0 com.didispace eureka-server 1.0.0 jar eureka-server Spring Cloud project org.springframework.boot spring-boot-starter-parent 1.3.5.RELEASE UTF-8 1.8 org.springframework.cloud spring-cloud-starter-eureka-server org.springframework.cloud spring-cloud-dependencies Brixton.SR5 pom import org.springframework.boot spring-boot-maven-plugin ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-6/eureka-server/src/main/java/com/didispace/Application.java ================================================ package com.didispace; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer; @EnableEurekaServer @SpringBootApplication public class Application { public static void main(String[] args) { new SpringApplicationBuilder(Application.class).web(true).run(args); } } ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-6/eureka-server/src/main/resources/application-peer1.properties ================================================ spring.application.name=eureka-server server.port=1111 eureka.instance.hostname=peer1 eureka.client.serviceUrl.defaultZone=http://peer2:1112/eureka/ ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-6/eureka-server/src/main/resources/application-peer2.properties ================================================ spring.application.name=eureka-server server.port=1112 eureka.instance.hostname=peer2 eureka.client.serviceUrl.defaultZone=http://peer1:1111/eureka/ ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-7/config-client-eureka/pom.xml ================================================ 4.0.0 com.didispace config-client-eureka 1.0.0 jar config-client-eureka Spring Cloud project org.springframework.boot spring-boot-starter-parent 1.3.7.RELEASE UTF-8 1.8 org.springframework.retry spring-retry org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-aop org.springframework.boot spring-boot-starter-actuator org.springframework.cloud spring-cloud-starter-config org.springframework.cloud spring-cloud-starter-eureka org.springframework.cloud spring-cloud-starter-bus-amqp org.springframework.cloud spring-cloud-dependencies Brixton.SR5 pom import org.springframework.boot spring-boot-maven-plugin ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-7/config-client-eureka/src/main/java/com/didispace/Application.java ================================================ package com.didispace; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; @EnableDiscoveryClient @SpringBootApplication public class Application { public static void main(String[] args) { new SpringApplicationBuilder(Application.class).web(true).run(args); } } ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-7/config-client-eureka/src/main/java/com/didispace/web/TestController.java ================================================ package com.didispace.web; import org.springframework.beans.factory.annotation.Value; import org.springframework.cloud.context.config.annotation.RefreshScope; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RefreshScope @RestController public class TestController { @Value("${from}") private String from; @RequestMapping("/from") public String from() { return this.from; } public void setFrom(String from) { this.from = from; } public String getFrom() { return from; } } ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-7/config-client-eureka/src/main/resources/bootstrap.properties ================================================ spring.application.name=didispace server.port=7002 eureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka/ spring.cloud.config.discovery.enabled=true spring.cloud.config.discovery.serviceId=config-server spring.cloud.config.profile=dev spring.cloud.config.failFast=true spring.rabbitmq.host=localhost spring.rabbitmq.port=5672 spring.rabbitmq.username=springcloud spring.rabbitmq.password=123456 ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-7/config-client-eureka-kafka/pom.xml ================================================ 4.0.0 com.didispace config-client-eureka-kafka 1.0.0 jar config-client-eureka-kafka Spring Cloud project org.springframework.boot spring-boot-starter-parent 1.3.7.RELEASE UTF-8 1.8 org.springframework.retry spring-retry org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-aop org.springframework.boot spring-boot-starter-actuator org.springframework.cloud spring-cloud-starter-config org.springframework.cloud spring-cloud-starter-eureka org.springframework.cloud spring-cloud-starter-bus-kafka org.springframework.cloud spring-cloud-dependencies Brixton.SR5 pom import org.springframework.boot spring-boot-maven-plugin ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-7/config-client-eureka-kafka/src/main/java/com/didispace/Application.java ================================================ package com.didispace; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; @EnableDiscoveryClient @SpringBootApplication public class Application { public static void main(String[] args) { new SpringApplicationBuilder(Application.class).web(true).run(args); } } ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-7/config-client-eureka-kafka/src/main/java/com/didispace/web/TestController.java ================================================ package com.didispace.web; import org.springframework.beans.factory.annotation.Value; import org.springframework.cloud.context.config.annotation.RefreshScope; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RefreshScope @RestController public class TestController { @Value("${from}") private String from; @RequestMapping("/from") public String from() { return this.from; } public void setFrom(String from) { this.from = from; } public String getFrom() { return from; } } ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-7/config-client-eureka-kafka/src/main/resources/bootstrap.properties ================================================ spring.application.name=didispace server.port=7002 eureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka/ spring.cloud.config.discovery.enabled=true spring.cloud.config.discovery.serviceId=config-server spring.cloud.config.profile=dev spring.cloud.config.failFast=true #kafka spring.cloud.stream.kafka.binder.zk-nodes=localhost:2181 spring.cloud.stream.kafka.binder.brokers=localhost:9092 spring.cloud.bus.trace.enabled=true ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-7/config-repo/didispace-dev.properties ================================================ from=git-dev-3.0 ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-7/config-repo/didispace-prod.properties ================================================ from=git-prod-1.0 ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-7/config-repo/didispace-test.properties ================================================ from=git-test-1.0 ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-7/config-repo/didispace.properties ================================================ from=git-default-1.0 ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-7/config-server-eureka/pom.xml ================================================ 4.0.0 com.didispace config-server-eureka 1.0.0 jar config-server-eureka Spring Cloud project org.springframework.boot spring-boot-starter-parent 1.3.7.RELEASE UTF-8 1.8 org.springframework.cloud spring-cloud-config-server org.springframework.cloud spring-cloud-starter-eureka org.springframework.cloud spring-cloud-starter-bus-amqp org.springframework.cloud spring-cloud-dependencies Brixton.SR5 pom import org.springframework.boot spring-boot-maven-plugin ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-7/config-server-eureka/src/main/java/com/didispace/Application.java ================================================ package com.didispace; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.config.server.EnableConfigServer; @EnableDiscoveryClient @EnableConfigServer @SpringBootApplication public class Application { public static void main(String[] args) { new SpringApplicationBuilder(Application.class).web(true).run(args); } } ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-7/config-server-eureka/src/main/resources/application.properties ================================================ spring.application.name=config-server server.port=7001 eureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka/ # git spring.cloud.config.server.git.uri=http://git.oschina.net/didispace/SpringCloud-Learning/ spring.cloud.config.server.git.searchPaths=Chapter1-1-8/config-repo spring.cloud.config.server.git.username=username spring.cloud.config.server.git.password=password ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-7/config-server-eureka/src/main/resources/didispace-dev.properties ================================================ from=local-dev ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-7/config-server-eureka/src/main/resources/didispace-prod.properties ================================================ from=local-prod ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-7/config-server-eureka/src/main/resources/didispace-test.properties ================================================ from=local-test ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-7/config-server-eureka/src/main/resources/didispace.properties ================================================ from=local ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-7/config-server-eureka-kafka/pom.xml ================================================ 4.0.0 com.didispace config-server-eureka-kafka 1.0.0 jar config-server-eureka-kafka Spring Cloud project org.springframework.boot spring-boot-starter-parent 1.3.7.RELEASE UTF-8 1.8 org.springframework.cloud spring-cloud-config-server org.springframework.cloud spring-cloud-starter-eureka org.springframework.cloud spring-cloud-starter-bus-kafka org.springframework.cloud spring-cloud-dependencies Brixton.SR5 pom import org.springframework.boot spring-boot-maven-plugin ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-7/config-server-eureka-kafka/src/main/java/com/didispace/Application.java ================================================ package com.didispace; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.config.server.EnableConfigServer; @EnableDiscoveryClient @EnableConfigServer @SpringBootApplication public class Application { public static void main(String[] args) { new SpringApplicationBuilder(Application.class).web(true).run(args); } } ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-7/config-server-eureka-kafka/src/main/resources/application.properties ================================================ spring.application.name=config-server server.port=7001 eureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka/ # git spring.cloud.config.server.git.uri=http://git.oschina.net/didispace/SpringCloud-Learning/ spring.cloud.config.server.git.searchPaths=Chapter1-1-7/config-repo spring.cloud.config.server.git.username=username spring.cloud.config.server.git.password=password #kafka spring.cloud.stream.kafka.binder.zk-nodes=localhost:2181 spring.cloud.stream.kafka.binder.brokers=localhost:9092 ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-7/config-server-eureka-kafka/src/main/resources/didispace-dev.properties ================================================ from=local-dev ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-7/config-server-eureka-kafka/src/main/resources/didispace-prod.properties ================================================ from=local-prod ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-7/config-server-eureka-kafka/src/main/resources/didispace-test.properties ================================================ from=local-test ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-7/config-server-eureka-kafka/src/main/resources/didispace.properties ================================================ from=local ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-8/config-client-eureka/pom.xml ================================================ 4.0.0 com.didispace config-client-eureka 1.0.0 jar config-client-eureka Spring Cloud project org.springframework.boot spring-boot-starter-parent 1.3.7.RELEASE UTF-8 1.8 org.springframework.retry spring-retry org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-aop org.springframework.cloud spring-cloud-starter-config org.springframework.cloud spring-cloud-starter-eureka org.springframework.cloud spring-cloud-dependencies Brixton.SR5 pom import org.springframework.boot spring-boot-maven-plugin ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-8/config-client-eureka/src/main/java/com/didispace/Application.java ================================================ package com.didispace; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; @EnableDiscoveryClient @SpringBootApplication public class Application { public static void main(String[] args) { new SpringApplicationBuilder(Application.class).web(true).run(args); } } ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-8/config-client-eureka/src/main/java/com/didispace/web/TestController.java ================================================ package com.didispace.web; import org.springframework.beans.factory.annotation.Value; import org.springframework.cloud.context.config.annotation.RefreshScope; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RefreshScope @RestController public class TestController { @Value("${from}") private String from; @RequestMapping("/from") public String from() { return this.from; } public void setFrom(String from) { this.from = from; } public String getFrom() { return from; } } ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-8/config-client-eureka/src/main/resources/bootstrap.properties ================================================ spring.application.name=didispace server.port=7002 eureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka/ spring.cloud.config.discovery.enabled=true spring.cloud.config.discovery.serviceId=config-server spring.cloud.config.profile=dev spring.cloud.config.failFast=true ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-8/config-repo/didispace-dev.properties ================================================ from=git-dev-1.0 ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-8/config-repo/didispace-prod.properties ================================================ from=git-prod-1.0 ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-8/config-repo/didispace-test.properties ================================================ from=git-test-1.0 ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-8/config-repo/didispace.properties ================================================ from=git-default-1.0 ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-8/config-server-eureka/pom.xml ================================================ 4.0.0 com.didispace config-server-eureka 1.0.0 jar config-server-eureka Spring Cloud project org.springframework.boot spring-boot-starter-parent 1.3.7.RELEASE UTF-8 1.8 org.springframework.cloud spring-cloud-config-server org.springframework.cloud spring-cloud-starter-eureka org.springframework.cloud spring-cloud-dependencies Brixton.SR5 pom import org.springframework.boot spring-boot-maven-plugin ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-8/config-server-eureka/src/main/java/com/didispace/Application.java ================================================ package com.didispace; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.config.server.EnableConfigServer; @EnableDiscoveryClient @EnableConfigServer @SpringBootApplication public class Application { public static void main(String[] args) { new SpringApplicationBuilder(Application.class).web(true).run(args); } } ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-8/config-server-eureka/src/main/resources/application.properties ================================================ spring.application.name=config-server server.port=7001 eureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka/ # git¹ÜÀíÅäÖà spring.cloud.config.server.git.uri=http://git.oschina.net/didispace/SpringCloud-Learning/ spring.cloud.config.server.git.searchPaths=Chapter1-1-8/config-repo spring.cloud.config.server.git.username=username spring.cloud.config.server.git.password=password ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-8/config-server-eureka/src/main/resources/didispace-dev.properties ================================================ from=local-dev ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-8/config-server-eureka/src/main/resources/didispace-prod.properties ================================================ from=local-prod ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-8/config-server-eureka/src/main/resources/didispace-test.properties ================================================ from=local-test ================================================ FILE: 1-Brixton版教程示例/Chapter1-1-8/config-server-eureka/src/main/resources/didispace.properties ================================================ from=local ================================================ FILE: 1-Brixton版教程示例/README.md ================================================ # Spring Cloud教程 本项目内容为Spring Cloud教程的程序样例。如您觉得该项目对您有用,欢迎点击右上方的**Star**按钮,给予支持!! - 我的博客:http://blog.didispace.com - **我的小密圈(深度交流与问答):https://t.xiaomiquan.com/zfEiY3v** - GitHub:https://github.com/dyc87112/SpringCloud-Learning - Gitee:https://gitee.com/didispace/SpringCloud-Learning - Spring For All社区:http://www.spring4all.com/ - Spring Boot基础教程:http://blog.didispace.com/Spring-Boot%E5%9F%BA%E7%A1%80%E6%95%99%E7%A8%8B/ - Spring Cloud基础教程:http://blog.didispace.com/Spring-Cloud%E5%9F%BA%E7%A1%80%E6%95%99%E7%A8%8B/ - **公益调试Eureka:http://eureka.didispace.com** **优惠云服务推荐** - [阿里云:ECS云服务器2折起](https://www.aliyun.com/minisite/goods?userCode=wxfqkr0o&share_source=copy_link) - [腾讯云:3年时长最低265元/年](https://cloud.tencent.com/redirect.php?redirect=1005&cps_key=f6a8af1297bfac40b9d10ffa1270029a) 本项目教程因做了版本更新,所以对目录结构做了调整,根目录下的各个章节为了博文链接兼容暂时保留。 ## 教程列表 #### 《Spring Cloud构建微服务架构》系列博文Brixton版示例 - 1-Brixton版教程示例/chapter1-1-1:[Spring Cloud构建微服务架构(一)服务注册与发现](http://blog.didispace.com/springcloud1/) - 1-Brixton版教程示例/chapter1-1-2:[Spring Cloud构建微服务架构(二)服务消费者](http://blog.didispace.com/springcloud2/) - 1-Brixton版教程示例/chapter1-1-3:[Spring Cloud构建微服务架构(三)断路器](http://blog.didispace.com/springcloud3/) - 1-Brixton版教程示例/chapter1-1-4:[Spring Cloud构建微服务架构(四)分布式配置中心](http://blog.didispace.com/springcloud4/) - 1-Brixton版教程示例/chapter1-1-8:[Spring Cloud构建微服务架构(四)分布式配置中心(续)](http://blog.didispace.com/springcloud4-2/) - 1-Brixton版教程示例/chapter1-1-5:[Spring Cloud构建微服务架构(五)服务网关](http://blog.didispace.com/springcloud5/) - 1-Brixton版教程示例/chapter1-1-6:[Spring Cloud构建微服务架构(六)高可用服务注册中心](http://blog.didispace.com/springcloud6/) - 1-Brixton版教程示例/chapter1-1-7:[Spring Cloud构建微服务架构(七)消息总线(Rabbit)](http://blog.didispace.com/springcloud7/) - 1-Brixton版教程示例/chapter1-1-7:[Spring Cloud构建微服务架构(七)消息总线(Kafka)](http://blog.didispace.com/springcloud7-2/) ## 我的公众号 ![输入图片说明](http://git.oschina.net/uploads/images/2017/0105/082137_85109d07_437188.jpeg "在这里输入图片标题") ## 推荐我的书 ![输入图片说明](https://git.oschina.net/uploads/images/2017/0416/233656_dd3bce94_437188.png "在这里输入图片标题") ## 其他推荐 - [Spring Boot基础教程](http://blog.didispace.com/Spring-Boot%E5%9F%BA%E7%A1%80%E6%95%99%E7%A8%8B/) - [Spring Boot 2.0新特性](http://blog.didispace.com/Spring-Boot-2-0-feature/) - [Spring Cloud基础教程](http://blog.didispace.com/Spring-Cloud%E5%9F%BA%E7%A1%80%E6%95%99%E7%A8%8B/) - [微服务架构专题](http://blog.didispace.com/micro-serivces-arch/) ================================================ FILE: 2-Dalston版教程示例/README.md ================================================ # Spring Cloud教程 本项目内容为Spring Cloud教程的程序样例。如您觉得该项目对您有用,欢迎点击右上方的**Star**按钮,给予支持!! - 我的博客:http://blog.didispace.com - **我的小密圈(深度交流与问答):https://t.xiaomiquan.com/zfEiY3v** - GitHub:https://github.com/dyc87112/SpringCloud-Learning - Gitee:https://gitee.com/didispace/SpringCloud-Learning - Spring For All社区:http://www.spring4all.com/ - Spring Boot基础教程:http://blog.didispace.com/Spring-Boot%E5%9F%BA%E7%A1%80%E6%95%99%E7%A8%8B/ - Spring Cloud基础教程:http://blog.didispace.com/Spring-Cloud%E5%9F%BA%E7%A1%80%E6%95%99%E7%A8%8B/ - **公益调试Eureka:http://eureka.didispace.com** **优惠云服务推荐** - [阿里云:ECS云服务器2折起](https://www.aliyun.com/sale-season/2020/procurement-new-members?userCode=wxfqkr0o) - [腾讯云:3年时长最低265元/年](https://url.cn/5iF8JP2) ## 教程列表 #### 《Spring Cloud构建微服务架构》系列博文Dalston版 - [服务注册与发现(Eureka、Consul)](http://blog.didispace.com/spring-cloud-starter-dalston-1/) - [服务消费者(基础)](http://blog.didispace.com/spring-cloud-starter-dalston-2-1/) - [服务消费者(Ribbon)](http://blog.didispace.com/spring-cloud-starter-dalston-2-2/) - [服务消费者(Feign)](http://blog.didispace.com/spring-cloud-starter-dalston-2-3/) - [服务消费者(Feign)传文件](http://blog.didispace.com/spring-cloud-starter-dalston-2-4/) - [分布式配置中心](http://blog.didispace.com/spring-cloud-starter-dalston-3) - [服务容错保护(Hystrix服务降级)](http://blog.didispace.com/spring-cloud-starter-dalston-4-1) - [服务容错保护(Hystrix依赖隔离)](http://blog.didispace.com/spring-cloud-starter-dalston-4-2) - [服务容错保护(Hystrix断路器)](http://blog.didispace.com/spring-cloud-starter-dalston-4-3) - [Hystrix监控面板](http://blog.didispace.com/spring-cloud-starter-dalston-5-1/) - [Hystrix监控数据聚合](http://blog.didispace.com/spring-cloud-starter-dalston-5-2/) - [服务网关(基础)](http://blog.didispace.com/spring-cloud-starter-dalston-6-1/) - [服务网关(路由配置)](http://blog.didispace.com/spring-cloud-starter-dalston-6-2/) - [服务网关(过滤器)](http://blog.didispace.com/spring-cloud-starter-dalston-6-3/) - [服务网关(API文档汇总)](http://blog.didispace.com/Spring-Cloud-Zuul-use-Swagger-API-doc/) - [消息驱动的微服务(入门)](http://blog.didispace.com/spring-cloud-starter-dalston-7-1/) - [消息驱动的微服务(核心概念)](http://blog.didispace.com/spring-cloud-starter-dalston-7-2/) - [消息驱动的微服务(消费组)](http://blog.didispace.com/spring-cloud-starter-dalston-7-3/) - [消息驱动的微服务(消费组案例:解决消息重复消费)](http://blog.didispace.com/spring-cloud-starter-dalston-7-5/) - [消息驱动的微服务(消息分区)](http://blog.didispace.com/spring-cloud-starter-dalston-7-4/) - [分布式服务跟踪(入门)](http://blog.didispace.com/spring-cloud-starter-dalston-8-1/) - [分布式服务跟踪(跟踪原理)](http://blog.didispace.com/spring-cloud-starter-dalston-8-2/) - [分布式服务跟踪(整合logstash)](http://blog.didispace.com/spring-cloud-starter-dalston-8-3/) - [分布式服务跟踪(整合zipkin)](http://blog.didispace.com/spring-cloud-starter-dalston-8-4/) - [分布式服务跟踪(收集原理)](http://blog.didispace.com/spring-cloud-starter-dalston-8-5/) - [分布式服务跟踪(抽样收集)](http://blog.didispace.com/spring-cloud-starter-dalston-8-6/) ## 我的公众号 ![输入图片说明](http://git.oschina.net/uploads/images/2017/0105/082137_85109d07_437188.jpeg "在这里输入图片标题") ## 推荐我的书 ![输入图片说明](https://git.oschina.net/uploads/images/2017/0416/233656_dd3bce94_437188.png "在这里输入图片标题") ## 其他推荐 - [Spring Boot基础教程](http://blog.didispace.com/Spring-Boot%E5%9F%BA%E7%A1%80%E6%95%99%E7%A8%8B/) - [Spring Boot 2.0新特性](http://blog.didispace.com/Spring-Boot-2-0-feature/) - [Spring Cloud基础教程](http://blog.didispace.com/Spring-Cloud%E5%9F%BA%E7%A1%80%E6%95%99%E7%A8%8B/) - [微服务架构专题](http://blog.didispace.com/micro-serivces-arch/) ================================================ FILE: 2-Dalston版教程示例/api-gateway/pom.xml ================================================ 4.0.0 com.didispace api-gateway 1.0.0 jar api-gateway Spring Cloud In Action org.springframework.boot spring-boot-starter-parent 1.5.4.RELEASE UTF-8 1.8 org.springframework.cloud spring-cloud-starter-zuul org.springframework.cloud spring-cloud-dependencies Dalston.SR1 pom import org.springframework.boot spring-boot-maven-plugin ================================================ FILE: 2-Dalston版教程示例/api-gateway/src/main/java/com/didispace/Application.java ================================================ package com.didispace; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.cloud.client.SpringCloudApplication; import org.springframework.cloud.netflix.zuul.EnableZuulProxy; @EnableZuulProxy @SpringCloudApplication public class Application { public static void main(String[] args) { new SpringApplicationBuilder(Application.class).web(true).run(args); } } ================================================ FILE: 2-Dalston版教程示例/api-gateway/src/main/resources/application.yaml ================================================ spring: application: name: api-gateway server: port: 1101 zuul: routes: # single-instance api-a: path: /api-a/** url: http://localhost:2001/ # multi-instance api-b: path: /api-b/** serviceId: api-b api-b: ribbon: listOfServers: http://localhost:2001/, http://localhost:2002/ ================================================ FILE: 2-Dalston版教程示例/api-gateway-with-eureka/pom.xml ================================================ 4.0.0 com.didispace api-gateway-with-eureka 1.0.0 jar api-gateway-with-eureka Spring Cloud In Action org.springframework.boot spring-boot-starter-parent 1.5.4.RELEASE UTF-8 1.8 org.springframework.cloud spring-cloud-starter-zuul org.springframework.cloud spring-cloud-starter-eureka org.springframework.cloud spring-cloud-dependencies Dalston.SR1 pom import org.springframework.boot spring-boot-maven-plugin ================================================ FILE: 2-Dalston版教程示例/api-gateway-with-eureka/src/main/java/com/didispace/AccessFilter.java ================================================ package com.didispace; import com.netflix.zuul.ZuulFilter; import com.netflix.zuul.context.RequestContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.servlet.http.HttpServletRequest; public class AccessFilter extends ZuulFilter { private static Logger log = LoggerFactory.getLogger(AccessFilter.class); @Override public String filterType() { return "pre"; } @Override public int filterOrder() { return 0; } @Override public boolean shouldFilter() { return true; } @Override public Object run() { RequestContext ctx = RequestContext.getCurrentContext(); HttpServletRequest request = ctx.getRequest(); log.info("send {} request to {}", request.getMethod(), request.getRequestURL().toString()); Object accessToken = request.getParameter("accessToken"); if(accessToken == null) { log.warn("access token is empty"); ctx.setSendZuulResponse(false); ctx.setResponseStatusCode(401); return null; } log.info("access token ok"); return null; } } ================================================ FILE: 2-Dalston版教程示例/api-gateway-with-eureka/src/main/java/com/didispace/Application.java ================================================ package com.didispace; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.cloud.client.SpringCloudApplication; import org.springframework.cloud.netflix.zuul.EnableZuulProxy; @EnableZuulProxy @SpringCloudApplication public class Application { public static void main(String[] args) { new SpringApplicationBuilder(Application.class).web(true).run(args); } } ================================================ FILE: 2-Dalston版教程示例/api-gateway-with-eureka/src/main/resources/application.yaml ================================================ spring: application: name: api-gateway server: port: 1101 zuul: routes: api-a: path: /api-a/** serviceId: eureka-client eureka: client: serviceUrl: defaultZone: http://localhost:1001/eureka/ ================================================ FILE: 2-Dalston版教程示例/config-client/pom.xml ================================================ 4.0.0 com.didispace config-client 1.0.0 jar config-client Spring Cloud In Action org.springframework.boot spring-boot-starter-parent 1.5.4.RELEASE UTF-8 1.8 org.springframework.cloud spring-cloud-starter-config org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-actuator org.springframework.cloud spring-cloud-dependencies Camden.SR5 pom import org.springframework.boot spring-boot-maven-plugin ================================================ FILE: 2-Dalston版教程示例/config-client/src/main/java/com/didispace/Application.java ================================================ package com.didispace; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; //@EnableDiscoveryClient @SpringBootApplication public class Application { public static void main(String[] args) { new SpringApplicationBuilder(Application.class).web(true).run(args); } } ================================================ FILE: 2-Dalston版教程示例/config-client/src/main/resources/bootstrap.yml ================================================ spring: application: name: config-client cloud: config: uri: http://localhost:1201/ profile: default label: master server: port: 2001 ================================================ FILE: 2-Dalston版教程示例/config-server-git/pom.xml ================================================ 4.0.0 com.didispace config-server-git 1.0.0 jar config-server-git Spring Cloud In Action org.springframework.boot spring-boot-starter-parent 1.5.4.RELEASE UTF-8 1.8 org.springframework.cloud spring-cloud-config-server org.springframework.cloud spring-cloud-dependencies Dalston.SR1 pom import org.springframework.boot spring-boot-maven-plugin ================================================ FILE: 2-Dalston版教程示例/config-server-git/src/main/java/com/didispace/Application.java ================================================ package com.didispace; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.cloud.config.server.EnableConfigServer; @EnableConfigServer @SpringBootApplication public class Application { public static void main(String[] args) { new SpringApplicationBuilder(Application.class).web(true).run(args); } } ================================================ FILE: 2-Dalston版教程示例/config-server-git/src/main/resources/application.yml ================================================ spring: application: name: config-server cloud: config: server: git: uri: http://git.oschina.net/didispace/config-repo-demo/ # search-paths: repo # username: # password: server: port: 1201 ================================================ FILE: 2-Dalston版教程示例/consul-client/pom.xml ================================================ 4.0.0 com.didispace consul-client 1.0.0 jar consul-client Spring Cloud In Action org.springframework.boot spring-boot-starter-parent 1.5.4.RELEASE UTF-8 1.8 org.springframework.cloud spring-cloud-starter-consul-discovery org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-actuator org.springframework.cloud spring-cloud-dependencies Dalston.SR1 pom import org.springframework.boot spring-boot-maven-plugin ================================================ FILE: 2-Dalston版教程示例/consul-client/src/main/java/com/didispace/Application.java ================================================ package com.didispace; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; @EnableDiscoveryClient @SpringBootApplication public class Application { public static void main(String[] args) { new SpringApplicationBuilder(Application.class).web(true).run(args); } } ================================================ FILE: 2-Dalston版教程示例/consul-client/src/main/java/com/didispace/DcController.java ================================================ package com.didispace; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.client.discovery.DiscoveryClient; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; /** * @author 翟永超 * @create 2017/4/15. * @blog http://blog.didispace.com */ @RestController public class DcController { @Autowired DiscoveryClient discoveryClient; @GetMapping("/dc") public String dc() { String services = "Services: " + discoveryClient.getServices(); System.out.println(services); return services; } } ================================================ FILE: 2-Dalston版教程示例/consul-client/src/main/resources/application.properties ================================================ spring.application.name=consul-client server.port=2001 spring.cloud.consul.host=localhost spring.cloud.consul.port=8500 ================================================ FILE: 2-Dalston版教程示例/consul-consumer/pom.xml ================================================ 4.0.0 com.didispace consul-consumer 1.0.0 jar consul-consumer Spring Cloud In Action org.springframework.boot spring-boot-starter-parent 1.5.4.RELEASE UTF-8 1.8 org.springframework.cloud spring-cloud-starter-consul-discovery org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-actuator org.springframework.cloud spring-cloud-dependencies Dalston.SR1 pom import org.springframework.boot spring-boot-maven-plugin ================================================ FILE: 2-Dalston版教程示例/consul-consumer/src/main/java/com/didispace/Application.java ================================================ package com.didispace; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.context.annotation.Bean; import org.springframework.web.client.RestTemplate; @EnableDiscoveryClient @SpringBootApplication public class Application { @Bean public RestTemplate restTemplate() { return new RestTemplate(); } public static void main(String[] args) { new SpringApplicationBuilder(Application.class).web(true).run(args); } } ================================================ FILE: 2-Dalston版教程示例/consul-consumer/src/main/java/com/didispace/DcController.java ================================================ package com.didispace; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.loadbalancer.LoadBalancerClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; /** * @author 翟永超 * @create 2017/4/15. * @blog http://blog.didispace.com */ @RestController public class DcController { @Autowired LoadBalancerClient loadBalancerClient; @Autowired RestTemplate restTemplate; @GetMapping("/consumer") public String dc() { ServiceInstance serviceInstance = loadBalancerClient.choose("consul-client"); String url = "http://" + serviceInstance.getHost() + ":" + serviceInstance.getPort() + "/dc"; System.out.println(url); return restTemplate.getForObject(url, String.class); } } ================================================ FILE: 2-Dalston版教程示例/consul-consumer/src/main/resources/application.properties ================================================ spring.application.name=consul-consumer server.port=2101 spring.cloud.consul.host=localhost spring.cloud.consul.port=8500 logging.file=${spring.application.name}.log ================================================ FILE: 2-Dalston版教程示例/eureka-client/pom.xml ================================================ 4.0.0 com.didispace eureka-client 1.0.0 jar eureka-client Spring Cloud In Action org.springframework.boot spring-boot-starter-parent 1.5.4.RELEASE UTF-8 1.8 org.springframework.cloud spring-cloud-starter-eureka org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-dependencies Dalston.SR1 pom import org.springframework.boot spring-boot-maven-plugin ================================================ FILE: 2-Dalston版教程示例/eureka-client/src/main/java/com/didispace/Application.java ================================================ package com.didispace; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; @EnableDiscoveryClient @SpringBootApplication public class Application { public static void main(String[] args) { new SpringApplicationBuilder(Application.class).web(true).run(args); } } ================================================ FILE: 2-Dalston版教程示例/eureka-client/src/main/java/com/didispace/DcController.java ================================================ package com.didispace; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.client.discovery.DiscoveryClient; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; /** * @author 翟永超 * @create 2017/4/15. * @blog http://blog.didispace.com */ @RestController public class DcController { @Autowired DiscoveryClient discoveryClient; @GetMapping("/dc") public String dc() { String services = "Services: " + discoveryClient.getServices(); System.out.println(services); return services; } } ================================================ FILE: 2-Dalston版教程示例/eureka-client/src/main/resources/application.properties ================================================ spring.application.name=eureka-client server.port=2001 eureka.client.serviceUrl.defaultZone=http://localhost:1001/eureka/ #eureka.client.serviceUrl.defaultZone=http://peer1:1001/eureka/,http://peer2:1002/eureka/ logging.file=${spring.application.name}.log #eureka.instance.prefer-ip-address=true # #eureka.client.healthcheck.enabled=true # ˿ #eureka.instance.instance-id=${spring.application.name}:${random.int} #server.port=0 #server.port=${random.int[10000,19999]} ================================================ FILE: 2-Dalston版教程示例/eureka-consumer/pom.xml ================================================ 4.0.0 com.didispace eureka-consumer 1.0.0 jar eureka-consumer Spring Cloud In Action org.springframework.boot spring-boot-starter-parent 1.5.4.RELEASE UTF-8 1.8 org.springframework.cloud spring-cloud-starter-eureka org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-actuator org.springframework.cloud spring-cloud-dependencies Dalston.SR1 pom import org.springframework.boot spring-boot-maven-plugin ================================================ FILE: 2-Dalston版教程示例/eureka-consumer/src/main/java/com/didispace/Application.java ================================================ package com.didispace; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.context.annotation.Bean; import org.springframework.web.client.RestTemplate; @EnableDiscoveryClient @SpringBootApplication public class Application { @Bean public RestTemplate restTemplate() { return new RestTemplate(); } public static void main(String[] args) { new SpringApplicationBuilder(Application.class).web(true).run(args); } } ================================================ FILE: 2-Dalston版教程示例/eureka-consumer/src/main/java/com/didispace/DcController.java ================================================ package com.didispace; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.loadbalancer.LoadBalancerClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; /** * @author 翟永超 * @create 2017/4/15. * @blog http://blog.didispace.com */ @RestController public class DcController { @Autowired LoadBalancerClient loadBalancerClient; @Autowired RestTemplate restTemplate; @GetMapping("/consumer") public String dc() { ServiceInstance serviceInstance = loadBalancerClient.choose("eureka-client"); String url = "http://" + serviceInstance.getHost() + ":" + serviceInstance.getPort() + "/dc"; System.out.println(url); return restTemplate.getForObject(url, String.class); } } ================================================ FILE: 2-Dalston版教程示例/eureka-consumer/src/main/resources/application.properties ================================================ spring.application.name=eureka-consumer server.port=2101 eureka.client.serviceUrl.defaultZone=http://localhost:1001/eureka/ logging.file=${spring.application.name}.log ================================================ FILE: 2-Dalston版教程示例/eureka-consumer-feign/pom.xml ================================================ 4.0.0 com.didispace eureka-consumer-feign 1.0.0 jar eureka-consumer-feign Spring Cloud In Action org.springframework.boot spring-boot-starter-parent 1.5.4.RELEASE UTF-8 1.8 org.springframework.cloud spring-cloud-starter-eureka org.springframework.cloud spring-cloud-starter-feign org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-actuator org.springframework.cloud spring-cloud-dependencies Dalston.SR1 pom import org.springframework.boot spring-boot-maven-plugin ================================================ FILE: 2-Dalston版教程示例/eureka-consumer-feign/src/main/java/com/didispace/Application.java ================================================ package com.didispace; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.netflix.feign.EnableFeignClients; @EnableFeignClients @EnableDiscoveryClient @SpringBootApplication public class Application { public static void main(String[] args) { new SpringApplicationBuilder(Application.class).web(true).run(args); } } ================================================ FILE: 2-Dalston版教程示例/eureka-consumer-feign/src/main/java/com/didispace/DcClient.java ================================================ package com.didispace; import org.springframework.cloud.netflix.feign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; /** * @author 翟永超 * @create 2017/6/24. * @blog http://blog.didispace.com */ @FeignClient("eureka-client") public interface DcClient { @GetMapping("/dc") String consumer(); } ================================================ FILE: 2-Dalston版教程示例/eureka-consumer-feign/src/main/java/com/didispace/DcController.java ================================================ package com.didispace; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; /** * @author 翟永超 * @create 2017/4/15. * @blog http://blog.didispace.com */ @RestController public class DcController { @Autowired DcClient dcClient; @GetMapping("/consumer") public String dc() { return dcClient.consumer(); } } ================================================ FILE: 2-Dalston版教程示例/eureka-consumer-feign/src/main/resources/application.properties ================================================ spring.application.name=eureka-consumer server.port=2101 eureka.client.serviceUrl.defaultZone=http://localhost:1001/eureka/ logging.file=${spring.application.name}.log ================================================ FILE: 2-Dalston版教程示例/eureka-consumer-feign-hystrix/pom.xml ================================================ 4.0.0 com.didispace eureka-consumer-feign-hystrix 1.0.0 jar eureka-consumer-feign-hystrix Spring Cloud In Action org.springframework.boot spring-boot-starter-parent 1.5.4.RELEASE UTF-8 1.8 org.springframework.cloud spring-cloud-starter-eureka org.springframework.cloud spring-cloud-starter-feign org.springframework.cloud spring-cloud-starter-hystrix org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-actuator org.springframework.cloud spring-cloud-dependencies Dalston.SR1 pom import org.springframework.boot spring-boot-maven-plugin ================================================ FILE: 2-Dalston版教程示例/eureka-consumer-feign-hystrix/src/main/java/com/didispace/Application.java ================================================ package com.didispace; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.netflix.feign.EnableFeignClients; @EnableFeignClients @EnableDiscoveryClient @SpringBootApplication public class Application { public static void main(String[] args) { new SpringApplicationBuilder(Application.class).web(true).run(args); } } ================================================ FILE: 2-Dalston版教程示例/eureka-consumer-feign-hystrix/src/main/java/com/didispace/DcClient.java ================================================ package com.didispace; import org.springframework.cloud.netflix.feign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; /** * @author 翟永超 * @create 2017/6/24. * @blog http://blog.didispace.com */ @FeignClient(name = "eureka-client", fallback = DcClientFallback.class) public interface DcClient { @GetMapping("/dc") String consumer(); } ================================================ FILE: 2-Dalston版教程示例/eureka-consumer-feign-hystrix/src/main/java/com/didispace/DcClientFallback.java ================================================ package com.didispace; import org.springframework.stereotype.Component; /** * @author 翟永超 * @create 2017/6/24. * @blog http://blog.didispace.com */ @Component public class DcClientFallback implements DcClient { @Override public String consumer() { return "fallback"; } } ================================================ FILE: 2-Dalston版教程示例/eureka-consumer-feign-hystrix/src/main/java/com/didispace/DcController.java ================================================ package com.didispace; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; /** * @author 翟永超 * @create 2017/4/15. * @blog http://blog.didispace.com */ @RestController public class DcController { @Autowired DcClient dcClient; @GetMapping("/consumer") public String dc() { return dcClient.consumer(); } } ================================================ FILE: 2-Dalston版教程示例/eureka-consumer-feign-hystrix/src/main/resources/application.properties ================================================ spring.application.name=eureka-consumer server.port=2101 eureka.client.serviceUrl.defaultZone=http://localhost:1001/eureka/ feign.hystrix.enabled=true logging.file=${spring.application.name}.log ================================================ FILE: 2-Dalston版教程示例/eureka-consumer-ribbon/pom.xml ================================================ 4.0.0 com.didispace eureka-consumer-ribbon 1.0.0 jar eureka-consumer-ribbon Spring Cloud In Action org.springframework.boot spring-boot-starter-parent 1.5.4.RELEASE UTF-8 1.8 org.springframework.cloud spring-cloud-starter-eureka org.springframework.cloud spring-cloud-starter-ribbon org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-actuator org.springframework.cloud spring-cloud-dependencies Dalston.SR1 pom import org.springframework.boot spring-boot-maven-plugin ================================================ FILE: 2-Dalston版教程示例/eureka-consumer-ribbon/src/main/java/com/didispace/Application.java ================================================ package com.didispace; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.context.annotation.Bean; import org.springframework.web.client.RestTemplate; @EnableDiscoveryClient @SpringBootApplication public class Application { @Bean @LoadBalanced public RestTemplate restTemplate() { return new RestTemplate(); } public static void main(String[] args) { new SpringApplicationBuilder(Application.class).web(true).run(args); } } ================================================ FILE: 2-Dalston版教程示例/eureka-consumer-ribbon/src/main/java/com/didispace/DcController.java ================================================ package com.didispace; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; /** * @author 翟永超 * @create 2017/4/15. * @blog http://blog.didispace.com */ @RestController public class DcController { @Autowired RestTemplate restTemplate; @GetMapping("/consumer") public String dc() { return restTemplate.getForObject("http://eureka-client/dc", String.class); } } ================================================ FILE: 2-Dalston版教程示例/eureka-consumer-ribbon/src/main/resources/application.properties ================================================ spring.application.name=eureka-consumer server.port=2101 eureka.client.serviceUrl.defaultZone=http://localhost:1001/eureka/ logging.file=${spring.application.name}.log ================================================ FILE: 2-Dalston版教程示例/eureka-consumer-ribbon-hystrix/pom.xml ================================================ 4.0.0 com.didispace eureka-consumer-ribbon-hystrix 1.0.0 jar eureka-consumer-ribbon-hystrix Spring Cloud In Action org.springframework.boot spring-boot-starter-parent 1.5.4.RELEASE UTF-8 1.8 org.springframework.cloud spring-cloud-starter-eureka org.springframework.cloud spring-cloud-starter-ribbon org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-actuator org.springframework.cloud spring-cloud-starter-hystrix org.springframework.cloud spring-cloud-dependencies Dalston.SR1 pom import org.springframework.boot spring-boot-maven-plugin ================================================ FILE: 2-Dalston版教程示例/eureka-consumer-ribbon-hystrix/src/main/java/com/didispace/Application.java ================================================ package com.didispace; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.cloud.netflix.hystrix.EnableHystrix; import org.springframework.context.annotation.Bean; import org.springframework.web.client.RestTemplate; @EnableCircuitBreaker @EnableDiscoveryClient @SpringBootApplication public class Application { @Bean @LoadBalanced public RestTemplate restTemplate() { return new RestTemplate(); } public static void main(String[] args) { new SpringApplicationBuilder(Application.class).web(true).run(args); } } ================================================ FILE: 2-Dalston版教程示例/eureka-consumer-ribbon-hystrix/src/main/java/com/didispace/DcController.java ================================================ package com.didispace; import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; import com.netflix.ribbon.proxy.annotation.Hystrix; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.stereotype.Service; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; /** * @author 翟永超 * @create 2017/4/15. * @blog http://blog.didispace.com */ @RestController public class DcController { @Autowired ConsumerService consumerService; @GetMapping("/consumer") public String dc() { return consumerService.consumer(); } @Service class ConsumerService { @Autowired RestTemplate restTemplate; @HystrixCommand(fallbackMethod = "fallback") public String consumer() { return restTemplate.getForObject("http://eureka-client/dc", String.class); } public String fallback() { return "fallbck"; } } } ================================================ FILE: 2-Dalston版教程示例/eureka-consumer-ribbon-hystrix/src/main/resources/application.properties ================================================ spring.application.name=eureka-consumer-ribbon-hystrix server.port=2101 eureka.client.serviceUrl.defaultZone=http://localhost:1001/eureka/ logging.file=${spring.application.name}.log ================================================ FILE: 2-Dalston版教程示例/eureka-feign-api/pom.xml ================================================ 4.0.0 com.didispace eureka-feign-api 1.0.0 jar eureka-feign-api Spring Cloud In Action org.springframework.boot spring-boot-starter-parent 1.5.6.RELEASE UTF-8 1.8 org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-dependencies Dalston.SR2 pom import ================================================ FILE: 2-Dalston版教程示例/eureka-feign-api/src/main/java/com/didispace/api/HelloService.java ================================================ package com.didispace.api; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; /** * @author 翟永超 * @create 2017/8/8. * @blog http://blog.didispace.com */ public interface HelloService { @GetMapping("/hello") String hello(@RequestParam(value = "name") String name); } ================================================ FILE: 2-Dalston版教程示例/eureka-feign-client/pom.xml ================================================ 4.0.0 com.didispace eureka-feign-client 1.0.0 jar eureka-feign-client Spring Cloud In Action org.springframework.boot spring-boot-starter-parent 1.5.6.RELEASE UTF-8 1.8 org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-eureka com.didispace eureka-feign-api 1.0.0 org.springframework.cloud spring-cloud-dependencies Dalston.SR2 pom import org.springframework.boot spring-boot-maven-plugin ================================================ FILE: 2-Dalston版教程示例/eureka-feign-client/src/main/java/com/didispace/api/impl/Application.java ================================================ package com.didispace.api.impl; import com.didispace.api.HelloService; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.web.bind.annotation.RestController; @EnableDiscoveryClient @SpringBootApplication public class Application { @RestController class HelloController implements HelloService { @Override public String hello(String name) { return "hello " + name; } } public static void main(String[] args) { new SpringApplicationBuilder(Application.class).web(true).run(args); } } ================================================ FILE: 2-Dalston版教程示例/eureka-feign-client/src/main/resources/application.properties ================================================ spring.application.name=eureka-feign-client server.port=2101 eureka.client.serviceUrl.defaultZone=http://eureka.didispace.com/eureka/ ================================================ FILE: 2-Dalston版教程示例/eureka-feign-consumer/pom.xml ================================================ 4.0.0 com.didispace eureka-feign-consumer 1.0.0 jar eureka-feign-consumer Spring Cloud In Action org.springframework.boot spring-boot-starter-parent 1.5.6.RELEASE UTF-8 1.8 org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-eureka org.springframework.cloud spring-cloud-starter-feign com.didispace eureka-feign-api 1.0.0 org.springframework.cloud spring-cloud-dependencies Dalston.SR2 pom import org.springframework.boot spring-boot-maven-plugin ================================================ FILE: 2-Dalston版教程示例/eureka-feign-consumer/src/main/java/com/didispace/api/consumer/Application.java ================================================ package com.didispace.api.consumer; import com.didispace.api.HelloService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.netflix.feign.EnableFeignClients; import org.springframework.cloud.netflix.feign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @EnableFeignClients @EnableDiscoveryClient @SpringBootApplication public class Application { @FeignClient("eureka-feign-client") interface HelloServiceClient extends HelloService { } @RestController class TestController { @Autowired private HelloServiceClient helloServiceClient; @GetMapping("/test") public String test(String name) { return helloServiceClient.hello(name); } } public static void main(String[] args) { new SpringApplicationBuilder(Application.class).web(true).run(args); } } ================================================ FILE: 2-Dalston版教程示例/eureka-feign-consumer/src/main/resources/application.properties ================================================ spring.application.name=eureka-feign-consumer server.port=2102 eureka.client.serviceUrl.defaultZone=http://eureka.didispace.com/eureka/ ================================================ FILE: 2-Dalston版教程示例/eureka-feign-upload-client/pom.xml ================================================ 4.0.0 com.didispace eureka-feign-upload-client 1.0.0 jar eureka-feign-upload-client Spring Cloud In Action org.springframework.boot spring-boot-starter-parent 1.5.6.RELEASE UTF-8 1.8 org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-test test org.springframework.cloud spring-cloud-starter-eureka org.springframework.cloud spring-cloud-starter-feign io.github.openfeign.form feign-form 3.0.3 io.github.openfeign.form feign-form-spring 3.0.3 commons-fileupload commons-fileupload 1.3.3 org.projectlombok lombok 1.16.20 provided org.springframework.cloud spring-cloud-dependencies Dalston.SR2 pom import org.springframework.boot spring-boot-maven-plugin ================================================ FILE: 2-Dalston版教程示例/eureka-feign-upload-client/src/main/java/com/didispace/api/consumer/Application.java ================================================ package com.didispace.api.consumer; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.netflix.feign.EnableFeignClients; @EnableFeignClients @EnableDiscoveryClient @SpringBootApplication public class Application { public static void main(String[] args) { new SpringApplicationBuilder(Application.class).web(true).run(args); } } ================================================ FILE: 2-Dalston版教程示例/eureka-feign-upload-client/src/main/java/com/didispace/api/consumer/UploadService.java ================================================ package com.didispace.api.consumer; import feign.codec.Encoder; import feign.form.spring.SpringFormEncoder; import org.springframework.cloud.netflix.feign.FeignClient; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.multipart.MultipartFile; @FeignClient(value = "eureka-feign-upload-server", configuration = UploadService.MultipartSupportConfig.class) public interface UploadService { @PostMapping(value = "/uploadFile", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) String handleFileUpload(@RequestPart(value = "file") MultipartFile file); @Configuration class MultipartSupportConfig { @Bean public Encoder feignFormEncoder() { return new SpringFormEncoder(); } } } ================================================ FILE: 2-Dalston版教程示例/eureka-feign-upload-client/src/main/resources/application.properties ================================================ spring.application.name=eureka-feign-upload-client server.port=10002 eureka.client.serviceUrl.defaultZone=http://eureka.didispace.com/eureka/ ================================================ FILE: 2-Dalston版教程示例/eureka-feign-upload-client/src/test/java/com/didispace/api/consumer/UploadTester.java ================================================ package com.didispace.api.consumer; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.apache.commons.fileupload.disk.DiskFileItem; import org.apache.commons.fileupload.disk.DiskFileItemFactory; import org.apache.commons.io.IOUtils; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.http.MediaType; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.commons.CommonsMultipartFile; import java.io.File; import java.io.FileInputStream; import java.io.InputStream; import java.io.OutputStream; @Slf4j @RunWith(SpringJUnit4ClassRunner.class) @SpringBootTest public class UploadTester { @Autowired private UploadService uploadService; @Test @SneakyThrows public void testHandleFileUpload() { File file = new File("upload.txt"); DiskFileItem fileItem = (DiskFileItem) new DiskFileItemFactory().createItem("file", MediaType.TEXT_PLAIN_VALUE, true, file.getName()); try (InputStream input = new FileInputStream(file); OutputStream os = fileItem.getOutputStream()) { IOUtils.copy(input, os); } catch (Exception e) { throw new IllegalArgumentException("Invalid file: " + e, e); } MultipartFile multi = new CommonsMultipartFile(fileItem); log.info(uploadService.handleFileUpload(multi)); } } ================================================ FILE: 2-Dalston版教程示例/eureka-feign-upload-client/upload.txt ================================================ aaa bbb ================================================ FILE: 2-Dalston版教程示例/eureka-feign-upload-server/pom.xml ================================================ 4.0.0 com.didispace eureka-feign-upload-server 1.0.0 jar eureka-feign-upload-server Spring Cloud In Action org.springframework.boot spring-boot-starter-parent 1.5.6.RELEASE UTF-8 1.8 org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-eureka org.springframework.cloud spring-cloud-starter-feign org.springframework.cloud spring-cloud-dependencies Dalston.SR2 pom import org.springframework.boot spring-boot-maven-plugin ================================================ FILE: 2-Dalston版教程示例/eureka-feign-upload-server/src/main/java/com/didispace/api/consumer/Application.java ================================================ package com.didispace.api.consumer; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.netflix.feign.EnableFeignClients; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; @EnableFeignClients @EnableDiscoveryClient @SpringBootApplication public class Application { @RestController public class UploadController { @PostMapping(value = "/uploadFile", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) public String handleFileUpload(@RequestPart(value = "file") MultipartFile file) { return file.getName(); } } public static void main(String[] args) { new SpringApplicationBuilder(Application.class).web(true).run(args); } } ================================================ FILE: 2-Dalston版教程示例/eureka-feign-upload-server/src/main/resources/application.properties ================================================ spring.application.name=eureka-feign-upload-server server.port=10001 eureka.client.serviceUrl.defaultZone=http://eureka.didispace.com/eureka/ ================================================ FILE: 2-Dalston版教程示例/eureka-server/pom.xml ================================================ 4.0.0 com.didispace eureka-server 1.0.0 jar eureka-server Spring Cloud In Action org.springframework.boot spring-boot-starter-parent 1.5.4.RELEASE UTF-8 1.8 org.springframework.cloud spring-cloud-starter-eureka-server org.springframework.cloud spring-cloud-dependencies Dalston.SR1 pom import org.springframework.boot spring-boot-maven-plugin ================================================ FILE: 2-Dalston版教程示例/eureka-server/src/main/java/com/didispace/Application.java ================================================ package com.didispace; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer; @EnableEurekaServer @SpringBootApplication public class Application { public static void main(String[] args) { new SpringApplicationBuilder(Application.class).web(true).run(args); } } ================================================ FILE: 2-Dalston版教程示例/eureka-server/src/main/resources/application-peer1.properties ================================================ spring.application.name=eureka-server server.port=1001 eureka.instance.hostname=peer1 eureka.client.serviceUrl.defaultZone=http://peer2:1002/eureka/ ================================================ FILE: 2-Dalston版教程示例/eureka-server/src/main/resources/application-peer2.properties ================================================ spring.application.name=eureka-server server.port=1002 eureka.instance.hostname=peer2 eureka.client.serviceUrl.defaultZone=http://peer1:1001/eureka/ ================================================ FILE: 2-Dalston版教程示例/eureka-server/src/main/resources/application.properties ================================================ spring.application.name=eureka-server server.port=1001 eureka.instance.hostname=localhost eureka.client.register-with-eureka=false eureka.client.fetch-registry=false logging.file=${spring.application.name}.log # رձ #eureka.server.enable-self-preservation=false ================================================ FILE: 2-Dalston版教程示例/hystrix-collapser-consumer/pom.xml ================================================ 4.0.0 com.didispace hystrix-collapser-consumer 1.0.0 jar hystrix-collapser-consumer Spring Cloud In Action org.springframework.boot spring-boot-starter-parent 1.5.4.RELEASE UTF-8 1.8 org.springframework.cloud spring-cloud-starter-eureka org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-hystrix org.springframework.boot spring-boot-starter-test test org.projectlombok lombok org.springframework.cloud spring-cloud-dependencies Dalston.SR1 pom import org.springframework.boot spring-boot-maven-plugin ================================================ FILE: 2-Dalston版教程示例/hystrix-collapser-consumer/src/main/java/com/didispace/Application.java ================================================ package com.didispace; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.cloud.netflix.hystrix.EnableHystrix; import org.springframework.context.annotation.Bean; import org.springframework.web.client.RestTemplate; @EnableHystrix @EnableDiscoveryClient @SpringBootApplication public class Application { @Bean @LoadBalanced public RestTemplate restTemplate() { return new RestTemplate(); } public static void main(String[] args) { new SpringApplicationBuilder(Application.class).web(true).run(args); } } ================================================ FILE: 2-Dalston版教程示例/hystrix-collapser-consumer/src/main/java/com/didispace/UserService.java ================================================ package com.didispace; import com.netflix.hystrix.contrib.javanica.annotation.HystrixCollapser; import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Future; /** * @author 翟永超 * @create 2017/7/25. * @blog http://blog.didispace.com */ @Slf4j @Service public class UserService { @Autowired RestTemplate restTemplate; @HystrixCollapser( scope = com.netflix.hystrix.HystrixCollapser.Scope.GLOBAL, batchMethod = "findByIds", collapserProperties = { @HystrixProperty(name="timerDelayInMilliseconds", value = "100") } ) public Future findById(Long id) { log.info("findById : " + id); return null; } @HystrixCommand(commandProperties = @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "5000")) public List findByIds(List ids) { log.info("findByIds : " + ids); List result = restTemplate.getForObject("http://hystrix-collapser-provider/users?ids={1}", List.class, StringUtils.join(ids, ",")); log.info(result.toString()); return result; } } ================================================ FILE: 2-Dalston版教程示例/hystrix-collapser-consumer/src/main/resources/application.properties ================================================ spring.application.name=hystrix-collapser-consumer server.port=2002 eureka.client.serviceUrl.defaultZone=http://localhost:1001/eureka/ ================================================ FILE: 2-Dalston版教程示例/hystrix-collapser-consumer/src/test/java/CollapserTest.java ================================================ import com.didispace.Application; import com.didispace.UserService; import lombok.extern.slf4j.Slf4j; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.util.concurrent.Future; /** * @author 翟永超 * @create 2017/7/25. * @blog http://blog.didispace.com */ @Slf4j @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class CollapserTest { @Autowired UserService userService; @Test public void test() throws Exception { Future u1 = userService.findById(1L); Future u2 = userService.findById(2L); Future u3 = userService.findById(3L); Future u4 = userService.findById(4L); log.info(u1.get().toString()); log.info(u2.get().toString()); log.info(u3.get().toString()); log.info(u4.get().toString()); Assert.assertEquals("aaa",u1.get()); Assert.assertEquals("bbb",u2.get()); Assert.assertEquals("ccc",u3.get()); Assert.assertEquals("ddd",u4.get()); } } ================================================ FILE: 2-Dalston版教程示例/hystrix-collapser-provider/pom.xml ================================================ 4.0.0 com.didispace hystrix-collapser-provider 1.0.0 jar hystrix-collapser-provider Spring Cloud In Action org.springframework.boot spring-boot-starter-parent 1.5.4.RELEASE UTF-8 1.8 org.springframework.cloud spring-cloud-starter-eureka org.springframework.boot spring-boot-starter-web org.projectlombok lombok org.springframework.cloud spring-cloud-dependencies Dalston.SR1 pom import org.springframework.boot spring-boot-maven-plugin ================================================ FILE: 2-Dalston版教程示例/hystrix-collapser-provider/src/main/java/com/didispace/Application.java ================================================ package com.didispace; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; @EnableDiscoveryClient @SpringBootApplication public class Application { public static void main(String[] args) { new SpringApplicationBuilder(Application.class).web(true).run(args); } } ================================================ FILE: 2-Dalston版教程示例/hystrix-collapser-provider/src/main/java/com/didispace/UserController.java ================================================ package com.didispace; import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.*; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * @author 翟永超 * @create 2017/4/15. * @blog http://blog.didispace.com */ @Slf4j @RestController public class UserController { private static Map users = new HashMap<>(); static { users.put(1L, "aaa"); users.put(2L, "bbb"); users.put(3L, "ccc"); users.put(4L, "ddd"); users.put(5L, "eee"); } @RequestMapping(value = "/users/{id}", method = RequestMethod.GET) public String findById(@PathVariable Long id) { log.info("findById : " + id); return users.get(id); } @RequestMapping(value = "/users", method = RequestMethod.GET) public List findByIds(@RequestParam String ids) { log.info("findByIds : " + ids); List result = new ArrayList<>(); for(String id : ids.split(",")) { if(users.get(Long.valueOf(id)) != null) result.add(users.get(Long.valueOf(id))); } log.info("findByIds : " + result); return result; } } ================================================ FILE: 2-Dalston版教程示例/hystrix-collapser-provider/src/main/resources/application.properties ================================================ spring.application.name=hystrix-collapser-provider server.port=2001 eureka.client.serviceUrl.defaultZone=http://localhost:1001/eureka/ ================================================ FILE: 2-Dalston版教程示例/hystrix-dashboard/pom.xml ================================================ 4.0.0 com.didispace hystrix-dashboard 0.0.1-SNAPSHOT jar hystrix-dashboard org.springframework.cloud spring-cloud-starter-parent Dalston.SR1 UTF-8 UTF-8 1.8 org.springframework.cloud spring-cloud-starter-hystrix org.springframework.cloud spring-cloud-starter-hystrix-dashboard org.springframework.boot spring-boot-starter-actuator org.springframework.boot spring-boot-maven-plugin ================================================ FILE: 2-Dalston版教程示例/hystrix-dashboard/src/main/java/com/didispace/HystrixDashboardApplication.java ================================================ package com.didispace; import org.springframework.boot.SpringApplication; import org.springframework.cloud.client.SpringCloudApplication; import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard; @EnableHystrixDashboard @SpringCloudApplication public class HystrixDashboardApplication { public static void main(String[] args) { SpringApplication.run(HystrixDashboardApplication.class, args); } } ================================================ FILE: 2-Dalston版教程示例/hystrix-dashboard/src/main/resources/application.properties ================================================ spring.application.name=hystrix-dashboard server.port=1301 ================================================ FILE: 2-Dalston版教程示例/pom.xml ================================================ 4.0.0 com.didispace spring-cloud-dalston pom 1.0 eureka-server eureka-client consul-client eureka-consumer consul-consumer eureka-consumer-ribbon eureka-consumer-feign config-server-git config-client eureka-consumer-ribbon-hystrix eureka-consumer-feign-hystrix hystrix-dashboard turbine turbine-amqp eureka-feign-api eureka-feign-client eureka-feign-consumer eureka-feign-upload-client eureka-feign-upload-server hystrix-collapser-provider hystrix-collapser-consumer api-gateway api-gateway-with-eureka stream-hello stream-consumer-group stream-consumer-self trace-1 trace-2 zipkin-server zipkin-server-stream swagger-service-a swagger-service-b swagger-api-gateway ================================================ FILE: 2-Dalston版教程示例/stream-consumer-group/pom.xml ================================================ 4.0.0 com.didispace stream-consumer-group 0.0.1-SNAPSHOT jar org.springframework.boot spring-boot-starter-parent 1.5.9.RELEASE UTF-8 UTF-8 1.8 org.springframework.boot spring-boot-starter-test test org.springframework.cloud spring-cloud-starter-stream-rabbit org.springframework.cloud spring-cloud-dependencies Dalston.SR4 pom import org.springframework.boot spring-boot-maven-plugin ================================================ FILE: 2-Dalston版教程示例/stream-consumer-group/src/main/java/com/didispace/stream/ExampleApplication.java ================================================ package com.didispace.stream; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class ExampleApplication { public static void main(String[] args) { SpringApplication.run(ExampleApplication.class, args); } } ================================================ FILE: 2-Dalston版教程示例/stream-consumer-group/src/main/java/com/didispace/stream/ExampleBinder.java ================================================ package com.didispace.stream; import org.springframework.cloud.stream.annotation.Input; import org.springframework.messaging.SubscribableChannel; interface ExampleBinder { String NAME = "example-topic"; @Input(NAME) SubscribableChannel input(); } ================================================ FILE: 2-Dalston版教程示例/stream-consumer-group/src/main/java/com/didispace/stream/ExampleReceiver.java ================================================ package com.didispace.stream; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cloud.stream.annotation.EnableBinding; import org.springframework.cloud.stream.annotation.StreamListener; @EnableBinding(ExampleBinder.class) public class ExampleReceiver { private static Logger logger = LoggerFactory.getLogger(ExampleReceiver.class); @StreamListener(ExampleBinder.NAME) public void receive(String payload) { logger.info("Received: " + payload); } } ================================================ FILE: 2-Dalston版教程示例/stream-consumer-group/src/main/resources/application.properties ================================================ spring.application.name=stream-consumer-group server.port=0 spring.cloud.stream.bindings.example-topic.group=aaa ================================================ FILE: 2-Dalston版教程示例/stream-consumer-group/src/test/java/com/didispace/ExampleApplicationTests.java ================================================ package com.didispace; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.stream.annotation.EnableBinding; import org.springframework.cloud.stream.annotation.Output; import org.springframework.integration.support.MessageBuilder; import org.springframework.messaging.MessageChannel; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @EnableBinding(value = {ExampleApplicationTests.ExampleBinder.class}) public class ExampleApplicationTests { @Autowired private ExampleBinder exampleBinder; @Test public void exampleBinderTester() { exampleBinder.output().send(MessageBuilder.withPayload("Produce a message from : http://blog.didispace.com").build()); } public interface ExampleBinder { String NAME = "example-topic"; @Output(NAME) MessageChannel output(); } } ================================================ FILE: 2-Dalston版教程示例/stream-consumer-self/pom.xml ================================================ 4.0.0 com.didispace stream-consumer-self 0.0.1-SNAPSHOT jar org.springframework.boot spring-boot-starter-parent 1.5.9.RELEASE UTF-8 UTF-8 1.8 org.projectlombok lombok 1.18.2 org.springframework.boot spring-boot-starter-test test org.springframework.cloud spring-cloud-starter-stream-rabbit org.springframework.cloud spring-cloud-dependencies Dalston.SR4 pom import org.springframework.boot spring-boot-maven-plugin ================================================ FILE: 2-Dalston版教程示例/stream-consumer-self/src/main/java/com/didispace/stream/TestApplication.java ================================================ package com.didispace.stream; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.stream.annotation.EnableBinding; @EnableBinding(TestTopic.class) @SpringBootApplication public class TestApplication { public static void main(String[] args) { SpringApplication.run(TestApplication.class, args); } } ================================================ FILE: 2-Dalston版教程示例/stream-consumer-self/src/main/java/com/didispace/stream/TestController.java ================================================ package com.didispace.stream; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.integration.support.MessageBuilder; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @Slf4j @RestController public class TestController { @Autowired private TestTopic testTopic; @GetMapping("/sendMessage") public String messageWithMQ(@RequestParam String message) { testTopic.output().send(MessageBuilder.withPayload(message).build()); return "ok"; } } ================================================ FILE: 2-Dalston版教程示例/stream-consumer-self/src/main/java/com/didispace/stream/TestListener.java ================================================ package com.didispace.stream; import lombok.extern.slf4j.Slf4j; import org.springframework.cloud.stream.annotation.StreamListener; import org.springframework.stereotype.Component; @Slf4j @Component public class TestListener { @StreamListener(TestTopic.INPUT) public void receive(String payload) { log.info("Received: " + payload); } } ================================================ FILE: 2-Dalston版教程示例/stream-consumer-self/src/main/java/com/didispace/stream/TestTopic.java ================================================ package com.didispace.stream; import org.springframework.cloud.stream.annotation.Input; import org.springframework.cloud.stream.annotation.Output; import org.springframework.messaging.MessageChannel; import org.springframework.messaging.SubscribableChannel; public interface TestTopic { String OUTPUT = "example-topic"; String INPUT = "example-topic"; @Output(OUTPUT) MessageChannel output(); @Input(INPUT) SubscribableChannel input(); } ================================================ FILE: 2-Dalston版教程示例/stream-consumer-self/src/main/resources/application.properties ================================================ spring.application.name=stream-consumer-self server.port=8080 ================================================ FILE: 2-Dalston版教程示例/stream-hello/pom.xml ================================================ 4.0.0 com.didispace stream-hello 0.0.1-SNAPSHOT jar stream-hello Demo project for Spring Boot org.springframework.boot spring-boot-starter-parent 1.5.9.RELEASE UTF-8 UTF-8 1.8 org.springframework.boot spring-boot-starter-test test org.springframework.cloud spring-cloud-starter-stream-rabbit org.springframework.cloud spring-cloud-dependencies Dalston.SR4 pom import org.springframework.boot spring-boot-maven-plugin ================================================ FILE: 2-Dalston版教程示例/stream-hello/src/main/java/com/didispace/stream/SinkApplication.java ================================================ package com.didispace.stream; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class SinkApplication { public static void main(String[] args) { SpringApplication.run(SinkApplication.class, args); } } ================================================ FILE: 2-Dalston版教程示例/stream-hello/src/main/java/com/didispace/stream/SinkReceiver.java ================================================ package com.didispace.stream; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cloud.stream.annotation.EnableBinding; import org.springframework.cloud.stream.annotation.StreamListener; import org.springframework.cloud.stream.messaging.Sink; @EnableBinding(Sink.class) public class SinkReceiver { private static Logger logger = LoggerFactory.getLogger(SinkReceiver.class); @StreamListener(Sink.INPUT) public void receive(Object payload) { logger.info("Received: " + payload); } } ================================================ FILE: 2-Dalston版教程示例/stream-hello/src/main/resources/application.properties ================================================ spring.application.name=stream-hello server.port=0 ================================================ FILE: 2-Dalston版教程示例/stream-hello/src/test/java/com/didispace/SinkApplicationTests.java ================================================ package com.didispace; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.stream.annotation.EnableBinding; import org.springframework.cloud.stream.annotation.Output; import org.springframework.integration.support.MessageBuilder; import org.springframework.messaging.MessageChannel; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @EnableBinding(value = {SinkApplicationTests.SinkSender.class}) public class SinkApplicationTests { @Autowired private SinkSender sinkSender; @Test public void sinkSenderTester() { sinkSender.output().send(MessageBuilder.withPayload("produce a message :http://blog.didispace.com").build()); } public interface SinkSender { String OUTPUT = "input"; @Output(SinkSender.OUTPUT) MessageChannel output(); } } ================================================ FILE: 2-Dalston版教程示例/swagger-api-gateway/pom.xml ================================================ 4.0.0 com.didispace swagger-api-gateway 1.0.0 jar swagger-api-gateway Spring Cloud In Action org.springframework.boot spring-boot-starter-parent 1.5.4.RELEASE UTF-8 1.8 org.springframework.cloud spring-cloud-starter-zuul org.springframework.cloud spring-cloud-starter-eureka com.spring4all swagger-spring-boot-starter 1.7.0.RELEASE org.springframework.cloud spring-cloud-dependencies Dalston.SR1 pom import org.springframework.boot spring-boot-maven-plugin ================================================ FILE: 2-Dalston版教程示例/swagger-api-gateway/src/main/java/com/didispace/Application.java ================================================ package com.didispace; import com.spring4all.swagger.EnableSwagger2Doc; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.cloud.client.SpringCloudApplication; import org.springframework.cloud.netflix.zuul.EnableZuulProxy; import org.springframework.context.annotation.Primary; import org.springframework.stereotype.Component; import springfox.documentation.swagger.web.SwaggerResource; import springfox.documentation.swagger.web.SwaggerResourcesProvider; import java.util.ArrayList; import java.util.List; @EnableSwagger2Doc @EnableZuulProxy @SpringCloudApplication public class Application { public static void main(String[] args) { new SpringApplicationBuilder(Application.class).web(true).run(args); } @Component @Primary class DocumentationConfig implements SwaggerResourcesProvider { @Override public List get() { List resources = new ArrayList<>(); resources.add(swaggerResource("service-a", "/swagger-service-a/v2/api-docs", "2.0")); resources.add(swaggerResource("service-b", "/swagger-service-b/v2/api-docs", "2.0")); return resources; } private SwaggerResource swaggerResource(String name, String location, String version) { SwaggerResource swaggerResource = new SwaggerResource(); swaggerResource.setName(name); swaggerResource.setLocation(location); swaggerResource.setSwaggerVersion(version); return swaggerResource; } } } ================================================ FILE: 2-Dalston版教程示例/swagger-api-gateway/src/main/resources/application.yaml ================================================ spring: application: name: swagger-api-gateway server: port: 11000 eureka: client: serviceUrl: defaultZone: http://eureka.didispace.com/eureka/ ================================================ FILE: 2-Dalston版教程示例/swagger-service-a/pom.xml ================================================ 4.0.0 com.didispace swagger-service-a 1.0.0 jar swagger-service-a Spring Cloud In Action org.springframework.boot spring-boot-starter-parent 1.5.10.RELEASE UTF-8 1.8 org.springframework.cloud spring-cloud-starter-eureka org.springframework.boot spring-boot-starter-web com.spring4all swagger-spring-boot-starter 1.7.0.RELEASE org.springframework.cloud spring-cloud-dependencies Dalston.SR1 pom import org.springframework.boot spring-boot-maven-plugin ================================================ FILE: 2-Dalston版教程示例/swagger-service-a/src/main/java/com/didispace/Application.java ================================================ package com.didispace; import com.spring4all.swagger.EnableSwagger2Doc; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.cloud.client.discovery.DiscoveryClient; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @EnableSwagger2Doc @EnableDiscoveryClient @SpringBootApplication public class Application { public static void main(String[] args) { new SpringApplicationBuilder(Application.class).web(true).run(args); } @RestController class AaaController { @Autowired DiscoveryClient discoveryClient; @GetMapping("/service-a") public String dc() { String services = "Services: " + discoveryClient.getServices(); System.out.println(services); return services; } } } ================================================ FILE: 2-Dalston版教程示例/swagger-service-a/src/main/resources/application.properties ================================================ spring.application.name=swagger-service-a server.port=10010 eureka.client.serviceUrl.defaultZone=http://eureka.didispace.com/eureka/ swagger.base-package=com.didispace ================================================ FILE: 2-Dalston版教程示例/swagger-service-b/pom.xml ================================================ 4.0.0 com.didispace swagger-service-b 1.0.0 jar swagger-service-b Spring Cloud In Action org.springframework.boot spring-boot-starter-parent 1.5.10.RELEASE UTF-8 1.8 org.springframework.cloud spring-cloud-starter-eureka org.springframework.boot spring-boot-starter-web com.spring4all swagger-spring-boot-starter 1.7.0.RELEASE org.springframework.cloud spring-cloud-dependencies Dalston.SR1 pom import org.springframework.boot spring-boot-maven-plugin ================================================ FILE: 2-Dalston版教程示例/swagger-service-b/src/main/java/com/didispace/Application.java ================================================ package com.didispace; import com.spring4all.swagger.EnableSwagger2Doc; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.cloud.client.discovery.DiscoveryClient; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @EnableSwagger2Doc @EnableDiscoveryClient @SpringBootApplication public class Application { public static void main(String[] args) { new SpringApplicationBuilder(Application.class).web(true).run(args); } @RestController class BbbController { @Autowired DiscoveryClient discoveryClient; @GetMapping("/service-b") public String dc() { String services = "Services: " + discoveryClient.getServices(); System.out.println(services); return services; } } } ================================================ FILE: 2-Dalston版教程示例/swagger-service-b/src/main/resources/application.properties ================================================ spring.application.name=swagger-service-b server.port=10020 eureka.client.serviceUrl.defaultZone=http://eureka.didispace.com/eureka/ swagger.base-package=com.didispace ================================================ FILE: 2-Dalston版教程示例/trace-1/pom.xml ================================================ 4.0.0 com.didispace trace-1 0.0.1-SNAPSHOT jar trace-1 Demo project for Spring Boot org.springframework.boot spring-boot-starter-parent 1.5.10.RELEASE UTF-8 UTF-8 1.8 org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-eureka org.springframework.cloud spring-cloud-starter-sleuth org.springframework.cloud spring-cloud-starter-ribbon org.springframework.cloud spring-cloud-sleuth-zipkin net.logstash.logback logstash-logback-encoder 4.6 org.springframework.cloud spring-cloud-dependencies Dalston.SR5 pom import org.springframework.boot spring-boot-maven-plugin ================================================ FILE: 2-Dalston版教程示例/trace-1/src/main/java/com/didispace/TraceApplication.java ================================================ package com.didispace; import org.apache.log4j.Logger; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.context.annotation.Bean; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; @RestController @EnableDiscoveryClient @SpringBootApplication public class TraceApplication { private final Logger logger = Logger.getLogger(getClass()); // @Bean // public AlwaysSampler defaultSampler() { // return new AlwaysSampler(); // } @Bean @LoadBalanced RestTemplate restTemplate() { return new RestTemplate(); } @RequestMapping(value = "/trace-1", method = RequestMethod.GET) public String trace() { logger.info("======"); return restTemplate().getForEntity("http://trace-2/trace-2", String.class).getBody(); } public static void main(String[] args) { SpringApplication.run(TraceApplication.class, args); } } ================================================ FILE: 2-Dalston版教程示例/trace-1/src/main/resources/application.properties ================================================ spring.application.name=trace-1 server.port=9101 eureka.client.serviceUrl.defaultZone=http://eureka.didispace.com/eureka/ #spring.zipkin.base-url=http://localhost:9411 #spring.sleuth.sampler.percentage=0.1 # log trace detail #logging.level.org.springframework.web.servlet.DispatcherServlet=DEBUG ================================================ FILE: 2-Dalston版教程示例/trace-2/pom.xml ================================================ 4.0.0 com.didispace trace-2 0.0.1-SNAPSHOT jar trace-2 Demo project for Spring Boot org.springframework.boot spring-boot-starter-parent 1.5.10.RELEASE UTF-8 UTF-8 1.8 org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-eureka org.springframework.cloud spring-cloud-starter-ribbon org.springframework.cloud spring-cloud-starter-sleuth net.logstash.logback logstash-logback-encoder 4.6 org.springframework.cloud spring-cloud-sleuth-zipkin org.springframework.cloud spring-cloud-dependencies Dalston.SR5 pom import org.springframework.boot spring-boot-maven-plugin ================================================ FILE: 2-Dalston版教程示例/trace-2/src/main/java/com/didispace/TraceApplication.java ================================================ package com.didispace; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; import javax.servlet.http.HttpServletRequest; @RestController @EnableDiscoveryClient @SpringBootApplication public class TraceApplication { private final Logger logger = LoggerFactory.getLogger(getClass()); @RequestMapping(value = "/trace-2", method = RequestMethod.GET) public String trace(HttpServletRequest request) { logger.info("======", request.getHeader("X-B3-TraceId"), request.getHeader("X-B3-SpanId")); return "Trace"; } public static void main(String[] args) { SpringApplication.run(TraceApplication.class, args); } } ================================================ FILE: 2-Dalston版教程示例/trace-2/src/main/resources/application.properties ================================================ spring.application.name=trace-2 server.port=9102 eureka.client.serviceUrl.defaultZone=http://eureka.didispace.com/eureka/ #spring.zipkin.base-url=http://localhost:9411 # #spring.sleuth.sampler.percentage=0.5 # ## log trace detail #logging.level.org.springframework.web.servlet.DispatcherServlet=DEBUG ================================================ FILE: 2-Dalston版教程示例/trace-2/src/main/resources/logback-spring.xml ================================================ INFO ${CONSOLE_LOG_PATTERN} utf8 ${LOG_FILE}.json ${LOG_FILE}.json.%d{yyyy-MM-dd}.gz 7 UTC { "severity": "%level", "service": "${springAppName:-}", "trace": "%X{X-B3-TraceId:-}", "span": "%X{X-B3-SpanId:-}", "exportable": "%X{X-Span-Export:-}", "pid": "${PID:-}", "thread": "%thread", "class": "%logger{40}", "rest": "%message" } ================================================ FILE: 2-Dalston版教程示例/turbine/pom.xml ================================================ 4.0.0 com.didispace turbine 0.0.1-SNAPSHOT jar turbine org.springframework.cloud spring-cloud-starter-parent Dalston.SR1 UTF-8 UTF-8 1.8 org.springframework.cloud spring-cloud-starter-turbine org.springframework.boot spring-boot-starter-actuator org.springframework.boot spring-boot-maven-plugin ================================================ FILE: 2-Dalston版教程示例/turbine/src/main/java/com/didispace/TurbineApplication.java ================================================ package com.didispace; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.netflix.turbine.EnableTurbine; import org.springframework.context.annotation.Configuration; @Configuration @EnableAutoConfiguration @EnableTurbine @EnableDiscoveryClient public class TurbineApplication { public static void main(String[] args) { SpringApplication.run(TurbineApplication.class, args); } } ================================================ FILE: 2-Dalston版教程示例/turbine/src/main/resources/application.properties ================================================ spring.application.name=turbine server.port=8989 management.port=8990 eureka.client.serviceUrl.defaultZone=http://localhost:1001/eureka/ turbine.app-config=eureka-consumer-ribbon-hystrix turbine.cluster-name-expression="default" turbine.combine-host-port=true ================================================ FILE: 2-Dalston版教程示例/turbine-amqp/pom.xml ================================================ 4.0.0 com.didispace turbine-amqp 0.0.1-SNAPSHOT jar turbine-amqp org.springframework.cloud spring-cloud-starter-parent Dalston.SR1 UTF-8 UTF-8 1.8 org.springframework.cloud spring-cloud-starter-turbine-amqp org.springframework.boot spring-boot-starter-actuator org.springframework.boot spring-boot-maven-plugin ================================================ FILE: 2-Dalston版教程示例/turbine-amqp/src/main/java/com/didispace/TurbineApplication.java ================================================ package com.didispace; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.netflix.turbine.stream.EnableTurbineStream; import org.springframework.context.annotation.Configuration; @Configuration @EnableAutoConfiguration @EnableTurbineStream @EnableDiscoveryClient public class TurbineApplication { public static void main(String[] args) { SpringApplication.run(TurbineApplication.class, args); } } ================================================ FILE: 2-Dalston版教程示例/turbine-amqp/src/main/resources/application.properties ================================================ spring.application.name=turbine-amqp server.port=8989 management.port=8990 eureka.client.serviceUrl.defaultZone=http://localhost:1001/eureka/ ================================================ FILE: 2-Dalston版教程示例/zipkin-server/pom.xml ================================================ 4.0.0 com.didispace zipkin-server 0.0.1-SNAPSHOT jar zipkin-server Demo project for Spring Boot org.springframework.boot spring-boot-starter-parent 1.5.10.RELEASE UTF-8 UTF-8 1.8 io.zipkin.java zipkin-server io.zipkin.java zipkin-autoconfigure-ui org.springframework.cloud spring-cloud-dependencies Dalston.SR5 pom import org.springframework.boot spring-boot-maven-plugin ================================================ FILE: 2-Dalston版教程示例/zipkin-server/src/main/java/com/didispace/ZipkinApplication.java ================================================ package com.didispace; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import zipkin.server.EnableZipkinServer; @EnableZipkinServer @SpringBootApplication public class ZipkinApplication { public static void main(String[] args) { SpringApplication.run(ZipkinApplication.class, args); } } ================================================ FILE: 2-Dalston版教程示例/zipkin-server/src/main/resources/application.properties ================================================ spring.application.name=zipkin-server server.port=9411 #eureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka/ ================================================ FILE: 2-Dalston版教程示例/zipkin-server-stream/pom.xml ================================================ 4.0.0 com.didispace zipkin-server-stream 0.0.1-SNAPSHOT jar zipkin-server-stream Demo project for Spring Boot org.springframework.boot spring-boot-starter-parent 1.5.10.RELEASE UTF-8 UTF-8 1.8 org.springframework.cloud spring-cloud-sleuth-zipkin-stream org.springframework.cloud spring-cloud-starter-stream-rabbit io.zipkin.java zipkin-autoconfigure-ui org.springframework.cloud spring-cloud-dependencies Dalston.SR5 pom import org.springframework.boot spring-boot-maven-plugin ================================================ FILE: 2-Dalston版教程示例/zipkin-server-stream/src/main/java/com/didispace/ZipkinApplication.java ================================================ package com.didispace; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.sleuth.zipkin.stream.EnableZipkinStreamServer; import org.springframework.context.annotation.Bean; import zipkin.server.EnableZipkinServer; import zipkin.storage.SpanStore; @EnableZipkinStreamServer @SpringBootApplication public class ZipkinApplication { public static void main(String[] args) { SpringApplication.run(ZipkinApplication.class, args); } } ================================================ FILE: 2-Dalston版教程示例/zipkin-server-stream/src/main/resources/application.properties ================================================ spring.application.name=zipkin-server-stream server.port=9411 spring.rabbitmq.host=localhost spring.rabbitmq.port=5672 spring.rabbitmq.username=springcloud spring.rabbitmq.password=123456 #eureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka/ logging.file=${spring.application.name}.log ================================================ FILE: 3-Edgware/README.md ================================================ # Spring Cloud教程 本项目内容为Spring Cloud教程的程序样例。如您觉得该项目对您有用,欢迎点击右上方的**Star**按钮,给予支持!! - 我的博客:http://blog.didispace.com - **我的小密圈(深度交流与问答):https://t.xiaomiquan.com/zfEiY3v** - GitHub:https://github.com/dyc87112/SpringCloud-Learning - Gitee:https://gitee.com/didispace/SpringCloud-Learning - Spring For All社区:http://www.spring4all.com/ - Spring Boot基础教程:http://blog.didispace.com/Spring-Boot%E5%9F%BA%E7%A1%80%E6%95%99%E7%A8%8B/ - Spring Cloud基础教程:http://blog.didispace.com/Spring-Cloud%E5%9F%BA%E7%A1%80%E6%95%99%E7%A8%8B/ - **公益调试Eureka:http://eureka.didispace.com** **优惠云服务推荐** - [阿里云:ECS云服务器2折起](https://www.aliyun.com/sale-season/2020/procurement-new-members?userCode=wxfqkr0o) - [腾讯云:3年时长最低265元/年](https://url.cn/5iF8JP2) ## 教程列表 ### 《Spring Cloud构建微服务架构》系列博文 #### Finchley版 **本系列主要补充之前版本新增或是变动的主要内容,基础使用依然可以参考Dalston版教程** - [Spring Cloud Finchley版中Consul多实例注册的问题处理](http://blog.didispace.com/Spring-Cloud-Finchley-Consul-InstanceId/) ##### Spring Cloud Aliabab专题 - [Spring Cloud Alibaba与Spring Boot、Spring Cloud之间不得不说的版本关系](http://blog.didispace.com/spring-cloud-alibaba-version/) - [说说我为什么看好Spring Cloud Alibaba](http://blog.didispace.com/spring-cloud-alibaba-significance/) - [Spring Cloud Alibaba到底坑不坑?](http://blog.didispace.com/bo-kengdie-spring-cloud-alibaba/) *注册中心与配置中心:Nacos* - [Spring Cloud Alibaba基础教程:使用Nacos实现服务注册与发现](http://blog.didispace.com/spring-cloud-alibaba-1/) - [Spring Cloud Alibaba基础教程:Nacos 生产级版本 0.8.0](http://blog.didispace.com/spring-cloud-alibaba-nacos-1/) - [Spring Cloud Alibaba基础教程:支持的几种服务消费方式(RestTemplate、WebClient、Feign)](http://blog.didispace.com/spring-cloud-alibaba-2/) - [Spring Cloud Alibaba基础教程:使用Nacos作为配置中心](http://blog.didispace.com/spring-cloud-alibaba-3/) - [Spring Cloud Alibaba基础教程:Nacos配置的加载规则详解](http://blog.didispace.com/spring-cloud-alibaba-nacos-config-1/) - [Spring Cloud Alibaba基础教程:Nacos配置的多环境管理](http://blog.didispace.com/spring-cloud-alibaba-nacos-config-2/) - [Spring Cloud Alibaba基础教程:Nacos配置的多文件加载与共享配置](http://blog.didispace.com/spring-cloud-alibaba-nacos-config-3/) - [Spring Cloud Alibaba基础教程:Nacos的数据持久化](http://blog.didispace.com/spring-cloud-alibaba-4/) - [Spring Cloud Alibaba基础教程:Nacos的集群部署](http://blog.didispace.com/spring-cloud-alibaba-5/) *分布式流量防卫兵:Sentinel* - [Spring Cloud Alibaba基础教程:使用Sentinel实现接口限流](http://blog.didispace.com/spring-cloud-alibaba-sentinel-1/) - [Spring Cloud Alibaba基础教程:Sentinel使用Nacos存储规则](http://blog.didispace.com/spring-cloud-alibaba-sentinel-2-1/) - [Spring Cloud Alibaba基础教程:Sentinel使用Apollo存储规则](http://blog.didispace.com/spring-cloud-alibaba-sentinel-2-2/) - [Spring Cloud Alibaba基础教程:Sentinel Dashboard中修改规则同步到Apollo](http://blog.didispace.com/spring-cloud-alibaba-sentinel-2-3/) - [Spring Cloud Alibaba基础教程:Sentinel Dashboard中修改规则同步到Nacos](http://blog.didispace.com/spring-cloud-alibaba-sentinel-2-4/) - [Spring Cloud Alibaba基础教程:@SentinelResource注解使用详解](http://blog.didispace.com/spring-cloud-alibaba-sentinel-2-5/) **Spring Cloud Stream专题补充** - [Spring Cloud Stream如何消费自己生产的消息](http://blog.didispace.com/spring-cloud-starter-finchley-7-1) - [Spring Cloud Stream同一通道根据消息内容分发不同的消费逻辑](http://blog.didispace.com/spring-cloud-starter-finchley-7-6) - [Spring Cloud Stream使用延迟消息实现定时任务(RabbitMQ)](http://blog.didispace.com/spring-cloud-starter-finchley-7-7) - [Spring Cloud Stream消费失败后的处理策略(一):自动重试](http://blog.didispace.com/spring-cloud-starter-finchley-7-2) - [Spring Cloud Stream消费失败后的处理策略(二):自定义错误处理逻辑](http://blog.didispace.com/spring-cloud-starter-finchley-7-3) - [Spring Cloud Stream消费失败后的处理策略(三):使用DLQ队列(RabbitMQ)](http://blog.didispace.com/spring-cloud-starter-finchley-7-4) - [Spring Cloud Stream消费失败后的处理策略(四):重新入队(RabbitMQ)](http://blog.didispace.com/spring-cloud-starter-finchley-7-5) #### Edgware版 > 本系列主要是对Dalston版的补充,包含Edgware版的主要新增或变动的内容,对于Spring Cloud的基础使用依然建议参考Dalston版教程 - [分布式配置中心(数据库存储)](http://blog.didispace.com/spring-cloud-starter-edgware-3-1) #### Dalston版 - [服务注册与发现(Eureka、Consul)](http://blog.didispace.com/spring-cloud-starter-dalston-1/) - [服务消费者(基础)](http://blog.didispace.com/spring-cloud-starter-dalston-2-1/) - [服务消费者(Ribbon)](http://blog.didispace.com/spring-cloud-starter-dalston-2-2/) - [服务消费者(Feign)](http://blog.didispace.com/spring-cloud-starter-dalston-2-3/) - [服务消费者(Feign)传文件](http://blog.didispace.com/spring-cloud-starter-dalston-2-4/) - [分布式配置中心](http://blog.didispace.com/spring-cloud-starter-dalston-3) - [服务容错保护(Hystrix服务降级)](http://blog.didispace.com/spring-cloud-starter-dalston-4-1) - [服务容错保护(Hystrix依赖隔离)](http://blog.didispace.com/spring-cloud-starter-dalston-4-2) - [服务容错保护(Hystrix断路器)](http://blog.didispace.com/spring-cloud-starter-dalston-4-3) - [Hystrix监控面板](http://blog.didispace.com/spring-cloud-starter-dalston-5-1/) - [Hystrix监控数据聚合](http://blog.didispace.com/spring-cloud-starter-dalston-5-2/) - [服务网关(基础)](http://blog.didispace.com/spring-cloud-starter-dalston-6-1/) - [服务网关(路由配置)](http://blog.didispace.com/spring-cloud-starter-dalston-6-2/) - [服务网关(过滤器)](http://blog.didispace.com/spring-cloud-starter-dalston-6-3/) - [服务网关(API文档汇总)](http://blog.didispace.com/Spring-Cloud-Zuul-use-Swagger-API-doc/) - [消息驱动的微服务(入门)](http://blog.didispace.com/spring-cloud-starter-dalston-7-1/) - [消息驱动的微服务(核心概念)](http://blog.didispace.com/spring-cloud-starter-dalston-7-2/) - [消息驱动的微服务(消费组)](http://blog.didispace.com/spring-cloud-starter-dalston-7-3/) - [消息驱动的微服务(消费组案例:解决消息重复消费)](http://blog.didispace.com/spring-cloud-starter-dalston-7-5/) - [消息驱动的微服务(消息分区)](http://blog.didispace.com/spring-cloud-starter-dalston-7-4/) - [分布式服务跟踪(入门)](http://blog.didispace.com/spring-cloud-starter-dalston-8-1/) - [分布式服务跟踪(跟踪原理)](http://blog.didispace.com/spring-cloud-starter-dalston-8-2/) - [分布式服务跟踪(整合logstash)](http://blog.didispace.com/spring-cloud-starter-dalston-8-3/) - [分布式服务跟踪(整合zipkin)](http://blog.didispace.com/spring-cloud-starter-dalston-8-4/) - [分布式服务跟踪(收集原理)](http://blog.didispace.com/spring-cloud-starter-dalston-8-5/) - [分布式服务跟踪(抽样收集)](http://blog.didispace.com/spring-cloud-starter-dalston-8-6/) #### Brixton版 - 1-Brixton版教程示例/chapter1-1-1:[Spring Cloud构建微服务架构(一)服务注册与发现](http://blog.didispace.com/springcloud1/) - 1-Brixton版教程示例/chapter1-1-2:[Spring Cloud构建微服务架构(二)服务消费者](http://blog.didispace.com/springcloud2/) - 1-Brixton版教程示例/chapter1-1-3:[Spring Cloud构建微服务架构(三)断路器](http://blog.didispace.com/springcloud3/) - 1-Brixton版教程示例/chapter1-1-4:[Spring Cloud构建微服务架构(四)分布式配置中心](http://blog.didispace.com/springcloud4/) - 1-Brixton版教程示例/chapter1-1-8:[Spring Cloud构建微服务架构(四)分布式配置中心(续)](http://blog.didispace.com/springcloud4-2/) - 1-Brixton版教程示例/chapter1-1-5:[Spring Cloud构建微服务架构(五)服务网关](http://blog.didispace.com/springcloud5/) - 1-Brixton版教程示例/chapter1-1-6:[Spring Cloud构建微服务架构(六)高可用服务注册中心](http://blog.didispace.com/springcloud6/) - 1-Brixton版教程示例/chapter1-1-7:[Spring Cloud构建微服务架构(七)消息总线(Rabbit)](http://blog.didispace.com/springcloud7/) - 1-Brixton版教程示例/chapter1-1-7:[Spring Cloud构建微服务架构(七)消息总线(Kafka)](http://blog.didispace.com/springcloud7-2/) ### 《Spring Cloud源码分析》系列博文 - [Spring Cloud源码分析(一)Eureka](http://blog.didispace.com/springcloud-sourcecode-eureka/) - [Spring Cloud源码分析(二)Ribbon](http://blog.didispace.com/springcloud-sourcecode-ribbon/) - [Spring Cloud源码分析(二)Ribbon](http://blog.didispace.com/springcloud-sourcecode-ribbon/) - [Spring Cloud源码分析(四)Zuul:核心过滤器](http://blog.didispace.com/spring-cloud-source-zuul/) - 未完待续 ### 《Spring Cloud实战小贴士》系列博文 - [Spring Cloud实战小贴士:版本依赖关系](http://blog.didispace.com/spring-cloud-tips-1/) - [Spring Cloud实战小贴士:随机端口](http://blog.didispace.com/spring-cloud-tips-2/) - [Spring Cloud实战小贴士:健康检查](http://blog.didispace.com/spring-cloud-tips-3/) - [Spring Cloud实战小贴士:Zuul处理Cookie和重定向](http://blog.didispace.com/spring-cloud-zuul-cookie-redirect/) - [Spring Cloud实战小贴士:Zuul统一异常处理(一)](http://blog.didispace.com/spring-cloud-zuul-exception/) - [Spring Cloud实战小贴士:Zuul统一异常处理(二)](http://blog.didispace.com/spring-cloud-zuul-exception-2/) - [Spring Cloud实战小贴士:Zuul统一异常处理(三)【Dalston版】](http://blog.didispace.com/spring-cloud-zuul-exception-3/) - [Spring Cloud实战小贴士:Turbine如何聚合设置了context-path的Hystrix数据](http://blog.didispace.com/spring-cloud-tips-4/) - [Spring Cloud实战小贴士:Feign的继承特性(伪RPC模式)](http://blog.didispace.com/spring-cloud-tips-feign-rpc/) - [Spring Cloud实战小贴士:Ribbon的饥饿加载(eager-load)模式](http://blog.didispace.com/spring-cloud-tips-ribbon-eager/) - [Spring Cloud实战小贴士:Zuul的饥饿加载(eager-load)使用](http://blog.didispace.com/spring-cloud-tips-zuul-eager/) #### 其他文章 - [使用Intellij中的Spring Initializr来快速构建Spring Boot/Cloud工程](http://blog.didispace.com/spring-initializr-in-intellij/) - [为Spring Cloud Ribbon配置请求重试(Camden.SR2+)](http://blog.didispace.com/spring-cloud-ribbon-failed-retry/) - [Consul注销实例时候的问题](http://blog.didispace.com/consul-deregister/) - [使用Spring Boot Actuator、Jolokia和Grafana实现准实时监控](http://blog.didispace.com/spring-boot-jolokia-grafana-monitor/) - [Netflix Zuul与Nginx的性能对比](http://blog.didispace.com/zuul-vs-nginx-performance/) - [基于Consul的分布式锁实现](http://blog.didispace.com/spring-cloud-consul-lock-and-semphore/) - [基于Consul的分布式信号量实现](http://blog.didispace.com/spring-cloud-consul-lock-and-semphore-2/) ## 我的公众号 ![](http://git.oschina.net/uploads/images/2017/0105/082137_85109d07_437188.jpeg "在这里输入图片标题") ## 推荐我的书 ![](https://git.oschina.net/uploads/images/2017/0416/233656_dd3bce94_437188.png "在这里输入图片标题") ## 其他推荐 - [Spring Boot基础教程](http://blog.didispace.com/Spring-Boot%E5%9F%BA%E7%A1%80%E6%95%99%E7%A8%8B/) - [Spring Boot 2.0新特性](http://blog.didispace.com/Spring-Boot-2-0-feature/) - [Spring Cloud基础教程](http://blog.didispace.com/Spring-Cloud%E5%9F%BA%E7%A1%80%E6%95%99%E7%A8%8B/) - [微服务架构专题](http://blog.didispace.com/micro-serivces-arch/) ================================================ FILE: 3-Edgware/config-client/pom.xml ================================================ 4.0.0 com.didispace config-client 1.0.0 jar config-client Spring Cloud In Action org.springframework.boot spring-boot-starter-parent 1.5.11.RELEASE UTF-8 1.8 org.springframework.cloud spring-cloud-starter-config org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-actuator org.springframework.cloud spring-cloud-dependencies Edgware.SR3 pom import org.springframework.boot spring-boot-maven-plugin ================================================ FILE: 3-Edgware/config-client/src/main/java/com/didispace/config/client/ConfigClientApplication.java ================================================ package com.didispace.config.client; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.context.config.annotation.RefreshScope; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @SpringBootApplication public class ConfigClientApplication { public static void main(String[] args) { SpringApplication.run(ConfigClientApplication.class); } @RefreshScope @RestController class TestController { @Value("${com.didispace.message}") private String message; @GetMapping("/test") public String test() { return message; } } } ================================================ FILE: 3-Edgware/config-client/src/main/resources/bootstrap.properties ================================================ spring.application.name=config-client server.port=10025 spring.cloud.config.uri=http://localhost:10020/ spring.cloud.config.profile=stage spring.cloud.config.label=master management.security.enabled=false ================================================ FILE: 3-Edgware/config-server-db/pom.xml ================================================ 4.0.0 com.didispace config-server-db 1.0.0 jar config-server-db Spring Cloud In Action org.springframework.boot spring-boot-starter-parent 1.5.11.RELEASE UTF-8 1.8 org.springframework.cloud spring-cloud-config-server org.springframework.boot spring-boot-starter-jdbc org.flywaydb flyway-core 5.0.3 mysql mysql-connector-java 5.1.21 org.springframework.boot spring-boot-starter-test test org.springframework.cloud spring-cloud-dependencies Edgware.SR3 pom import org.springframework.boot spring-boot-maven-plugin ================================================ FILE: 3-Edgware/config-server-db/src/main/java/com/didispace/config/server/db/ConfigServerBootstrap.java ================================================ package com.didispace.config.server.db; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.config.server.EnableConfigServer; import org.springframework.context.ApplicationContext; import org.springframework.jdbc.core.JdbcTemplate; @EnableConfigServer @SpringBootApplication public class ConfigServerBootstrap { public static void main(String[] args) { ApplicationContext context = SpringApplication.run(ConfigServerBootstrap.class); JdbcTemplate jdbcTemplate = context.getBean(JdbcTemplate.class); jdbcTemplate.execute("delete from properties"); jdbcTemplate.execute("INSERT INTO properties VALUES(1, 'com.didispace.message', 'test-stage-master', 'config-client', 'stage', 'master')"); jdbcTemplate.execute("INSERT INTO properties VALUES(2, 'com.didispace.message', 'test-online-master', 'config-client', 'online', 'master')"); jdbcTemplate.execute("INSERT INTO properties VALUES(3, 'com.didispace.message', 'test-online-develop', 'config-client', 'online', 'develop')"); jdbcTemplate.execute("INSERT INTO properties VALUES(4, 'com.didispace.message', 'hello-online-master', 'hello-service', 'online', 'master')"); jdbcTemplate.execute("INSERT INTO properties VALUES(5, 'com.didispace.message', 'hello-online-develop', 'hello-service', 'online', 'develop')"); } // 1. 直接访问config-server-db获取获取信息 // curl http://localhost:10020/config-client/stage/ // { // "name": "config-client", // "profiles": [ // "stage" // ], // "label": null, // "version": null, // "state": null, // "propertySources": [ // { // "name": "config-client-stage", // "source": { // "com.didispace.message": "test-stage-master" // } // } // ] //} // curl http://localhost:10020/hello-service/stage/develop // { // "name": "hello-service", // "profiles": [ // "online" // ], // "label": "develop", // "version": null, // "state": null, // "propertySources": [ // { // "name": "hello-service-online", // "source": { // "com.didispace.message": "hello-online-develop" // } // } // ] //} // 2. 访问客户端获取数据 // curl http://localhost:10025/test // test-stage-master // 3. 数据库中修改配置,刷新配置 // curl -X POST http://localhost:10025/refresh // ["com.didispace.message"] // 4. 验证配置 // curl http://localhost:10025/test // test-stage-master-xxx } ================================================ FILE: 3-Edgware/config-server-db/src/main/resources/application.properties ================================================ spring.application.name=config-server-db server.port=10020 spring.profiles.active=jdbc spring.cloud.config.server.jdbc.sql=SELECT `KEY`, `VALUE` from PROPERTIES where APPLICATION=? and PROFILE=? and LABEL=? spring.datasource.url=jdbc:mysql://localhost:3306/config-server-db spring.datasource.username=root spring.datasource.password= spring.datasource.driver-class-name=com.mysql.jdbc.Driver flyway.locations=/schema ================================================ FILE: 3-Edgware/config-server-db/src/main/resources/schema/V1__Base_version.sql ================================================ CREATE TABLE `properties` ( `id` int(11) NOT NULL, `key` varchar(50) NOT NULL, `value` varchar(500) NOT NULL, `application` varchar(50) NOT NULL, `profile` varchar(50) NOT NULL, `label` varchar(50) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; ================================================ FILE: 3-Edgware/pom.xml ================================================ 4.0.0 com.didispace spring-cloud-edgware pom 1.0 config-server-db config-client ================================================ FILE: 4-Finchley/README.md ================================================ # Spring Cloud教程 本项目内容为Spring Cloud教程的程序样例。如您觉得该项目对您有用,欢迎点击右上方的**Star**按钮,给予支持!! - 我的博客:http://blog.didispace.com - **我的小密圈(深度交流与问答):https://t.xiaomiquan.com/zfEiY3v** - GitHub:https://github.com/dyc87112/SpringCloud-Learning - Gitee:https://gitee.com/didispace/SpringCloud-Learning - Spring For All社区:http://www.spring4all.com/ - Spring Boot基础教程:http://blog.didispace.com/Spring-Boot%E5%9F%BA%E7%A1%80%E6%95%99%E7%A8%8B/ - Spring Cloud基础教程:http://blog.didispace.com/Spring-Cloud%E5%9F%BA%E7%A1%80%E6%95%99%E7%A8%8B/ - **公益调试Eureka:http://eureka.didispace.com** **优惠云服务推荐** - [阿里云:ECS云服务器2折起](https://www.aliyun.com/sale-season/2020/procurement-new-members?userCode=wxfqkr0o) - [腾讯云:3年时长最低265元/年](https://url.cn/5iF8JP2) ## 教程列表 ### 《Spring Cloud构建微服务架构》系列博文 #### Finchley版 本系列主要补充之前版本新增或是变动的主要内容,基础使用依然可以参考Dalston版教程 - [Spring Cloud Finchley版中Consul多实例注册的问题处理](http://blog.didispace.com/Spring-Cloud-Finchley-Consul-InstanceId/) ##### Spring Cloud Aliabab专题 - [Spring Cloud Alibaba与Spring Boot、Spring Cloud之间不得不说的版本关系](http://blog.didispace.com/spring-cloud-alibaba-version/) - [说说我为什么看好Spring Cloud Alibaba](http://blog.didispace.com/spring-cloud-alibaba-significance/) - [Spring Cloud Alibaba到底坑不坑?](http://blog.didispace.com/bo-kengdie-spring-cloud-alibaba/) *注册中心与配置中心:Nacos* - [Spring Cloud Alibaba基础教程:使用Nacos实现服务注册与发现](http://blog.didispace.com/spring-cloud-alibaba-1/) - [Spring Cloud Alibaba基础教程:Nacos 生产级版本 0.8.0](http://blog.didispace.com/spring-cloud-alibaba-nacos-1/) - [Spring Cloud Alibaba基础教程:支持的几种服务消费方式(RestTemplate、WebClient、Feign)](http://blog.didispace.com/spring-cloud-alibaba-2/) - [Spring Cloud Alibaba基础教程:使用Nacos作为配置中心](http://blog.didispace.com/spring-cloud-alibaba-3/) - [Spring Cloud Alibaba基础教程:Nacos配置的加载规则详解](http://blog.didispace.com/spring-cloud-alibaba-nacos-config-1/) - [Spring Cloud Alibaba基础教程:Nacos配置的多环境管理](http://blog.didispace.com/spring-cloud-alibaba-nacos-config-2/) - [Spring Cloud Alibaba基础教程:Nacos配置的多文件加载与共享配置](http://blog.didispace.com/spring-cloud-alibaba-nacos-config-3/) - [Spring Cloud Alibaba基础教程:Nacos的数据持久化](http://blog.didispace.com/spring-cloud-alibaba-4/) - [Spring Cloud Alibaba基础教程:Nacos的集群部署](http://blog.didispace.com/spring-cloud-alibaba-5/) *分布式流量防卫兵:Sentinel* - [Spring Cloud Alibaba基础教程:使用Sentinel实现接口限流](http://blog.didispace.com/spring-cloud-alibaba-sentinel-1/) - [Spring Cloud Alibaba基础教程:Sentinel使用Nacos存储规则](http://blog.didispace.com/spring-cloud-alibaba-sentinel-2-1/) - [Spring Cloud Alibaba基础教程:Sentinel使用Apollo存储规则](http://blog.didispace.com/spring-cloud-alibaba-sentinel-2-2/) - [Spring Cloud Alibaba基础教程:Sentinel Dashboard中修改规则同步到Apollo](http://blog.didispace.com/spring-cloud-alibaba-sentinel-2-3/) - [Spring Cloud Alibaba基础教程:Sentinel Dashboard中修改规则同步到Nacos](http://blog.didispace.com/spring-cloud-alibaba-sentinel-2-4/) - [Spring Cloud Alibaba基础教程:@SentinelResource注解使用详解](http://blog.didispace.com/spring-cloud-alibaba-sentinel-2-5/) *国内使用最多的RPC框架整合:Dubbo* - [Spring Cloud Alibaba基础教程:与Dubbo的完美融合](http://blog.didispace.com/spring-cloud-alibaba-dubbo-1/) ##### Spring Cloud Stream专题补充 - [Spring Cloud Stream如何消费自己生产的消息](http://blog.didispace.com/spring-cloud-starter-finchley-7-1) - [Spring Cloud Stream同一通道根据消息内容分发不同的消费逻辑](http://blog.didispace.com/spring-cloud-starter-finchley-7-6) - [Spring Cloud Stream使用延迟消息实现定时任务(RabbitMQ)](http://blog.didispace.com/spring-cloud-starter-finchley-7-7) - [Spring Cloud Stream消费失败后的处理策略(一):自动重试](http://blog.didispace.com/spring-cloud-starter-finchley-7-2) - [Spring Cloud Stream消费失败后的处理策略(二):自定义错误处理逻辑](http://blog.didispace.com/spring-cloud-starter-finchley-7-3) - [Spring Cloud Stream消费失败后的处理策略(三):使用DLQ队列(RabbitMQ)](http://blog.didispace.com/spring-cloud-starter-finchley-7-4) - [Spring Cloud Stream消费失败后的处理策略(四):重新入队(RabbitMQ)](http://blog.didispace.com/spring-cloud-starter-finchley-7-5) #### Edgware版 > 本系列主要是对Dalston版的补充,包含Edgware版的主要新增或变动的内容,对于Spring Cloud的基础使用依然建议参考Dalston版教程 - [分布式配置中心(数据库存储)](http://blog.didispace.com/spring-cloud-starter-edgware-3-1) #### Dalston版 - [服务注册与发现(Eureka、Consul)](http://blog.didispace.com/spring-cloud-starter-dalston-1/) - [服务消费者(基础)](http://blog.didispace.com/spring-cloud-starter-dalston-2-1/) - [服务消费者(Ribbon)](http://blog.didispace.com/spring-cloud-starter-dalston-2-2/) - [服务消费者(Feign)](http://blog.didispace.com/spring-cloud-starter-dalston-2-3/) - [服务消费者(Feign)传文件](http://blog.didispace.com/spring-cloud-starter-dalston-2-4/) - [分布式配置中心](http://blog.didispace.com/spring-cloud-starter-dalston-3) - [服务容错保护(Hystrix服务降级)](http://blog.didispace.com/spring-cloud-starter-dalston-4-1) - [服务容错保护(Hystrix依赖隔离)](http://blog.didispace.com/spring-cloud-starter-dalston-4-2) - [服务容错保护(Hystrix断路器)](http://blog.didispace.com/spring-cloud-starter-dalston-4-3) - [Hystrix监控面板](http://blog.didispace.com/spring-cloud-starter-dalston-5-1/) - [Hystrix监控数据聚合](http://blog.didispace.com/spring-cloud-starter-dalston-5-2/) - [服务网关(基础)](http://blog.didispace.com/spring-cloud-starter-dalston-6-1/) - [服务网关(路由配置)](http://blog.didispace.com/spring-cloud-starter-dalston-6-2/) - [服务网关(过滤器)](http://blog.didispace.com/spring-cloud-starter-dalston-6-3/) - [服务网关(API文档汇总)](http://blog.didispace.com/Spring-Cloud-Zuul-use-Swagger-API-doc/) - [消息驱动的微服务(入门)](http://blog.didispace.com/spring-cloud-starter-dalston-7-1/) - [消息驱动的微服务(核心概念)](http://blog.didispace.com/spring-cloud-starter-dalston-7-2/) - [消息驱动的微服务(消费组)](http://blog.didispace.com/spring-cloud-starter-dalston-7-3/) - [消息驱动的微服务(消费组案例:解决消息重复消费)](http://blog.didispace.com/spring-cloud-starter-dalston-7-5/) - [消息驱动的微服务(消息分区)](http://blog.didispace.com/spring-cloud-starter-dalston-7-4/) - [分布式服务跟踪(入门)](http://blog.didispace.com/spring-cloud-starter-dalston-8-1/) - [分布式服务跟踪(跟踪原理)](http://blog.didispace.com/spring-cloud-starter-dalston-8-2/) - [分布式服务跟踪(整合logstash)](http://blog.didispace.com/spring-cloud-starter-dalston-8-3/) - [分布式服务跟踪(整合zipkin)](http://blog.didispace.com/spring-cloud-starter-dalston-8-4/) - [分布式服务跟踪(收集原理)](http://blog.didispace.com/spring-cloud-starter-dalston-8-5/) - [分布式服务跟踪(抽样收集)](http://blog.didispace.com/spring-cloud-starter-dalston-8-6/) #### Brixton版 - 1-Brixton版教程示例/chapter1-1-1:[Spring Cloud构建微服务架构(一)服务注册与发现](http://blog.didispace.com/springcloud1/) - 1-Brixton版教程示例/chapter1-1-2:[Spring Cloud构建微服务架构(二)服务消费者](http://blog.didispace.com/springcloud2/) - 1-Brixton版教程示例/chapter1-1-3:[Spring Cloud构建微服务架构(三)断路器](http://blog.didispace.com/springcloud3/) - 1-Brixton版教程示例/chapter1-1-4:[Spring Cloud构建微服务架构(四)分布式配置中心](http://blog.didispace.com/springcloud4/) - 1-Brixton版教程示例/chapter1-1-8:[Spring Cloud构建微服务架构(四)分布式配置中心(续)](http://blog.didispace.com/springcloud4-2/) - 1-Brixton版教程示例/chapter1-1-5:[Spring Cloud构建微服务架构(五)服务网关](http://blog.didispace.com/springcloud5/) - 1-Brixton版教程示例/chapter1-1-6:[Spring Cloud构建微服务架构(六)高可用服务注册中心](http://blog.didispace.com/springcloud6/) - 1-Brixton版教程示例/chapter1-1-7:[Spring Cloud构建微服务架构(七)消息总线(Rabbit)](http://blog.didispace.com/springcloud7/) - 1-Brixton版教程示例/chapter1-1-7:[Spring Cloud构建微服务架构(七)消息总线(Kafka)](http://blog.didispace.com/springcloud7-2/) ### 《Spring Cloud源码分析》系列博文 - [Spring Cloud源码分析(一)Eureka](http://blog.didispace.com/springcloud-sourcecode-eureka/) - [Spring Cloud源码分析(二)Ribbon](http://blog.didispace.com/springcloud-sourcecode-ribbon/) - [Spring Cloud源码分析(二)Ribbon](http://blog.didispace.com/springcloud-sourcecode-ribbon/) - [Spring Cloud源码分析(四)Zuul:核心过滤器](http://blog.didispace.com/spring-cloud-source-zuul/) - 未完待续 ### 《Spring Cloud实战小贴士》系列博文 - [Spring Cloud实战小贴士:版本依赖关系](http://blog.didispace.com/spring-cloud-tips-1/) - [Spring Cloud实战小贴士:随机端口](http://blog.didispace.com/spring-cloud-tips-2/) - [Spring Cloud实战小贴士:健康检查](http://blog.didispace.com/spring-cloud-tips-3/) - [Spring Cloud实战小贴士:Zuul处理Cookie和重定向](http://blog.didispace.com/spring-cloud-zuul-cookie-redirect/) - [Spring Cloud实战小贴士:Zuul统一异常处理(一)](http://blog.didispace.com/spring-cloud-zuul-exception/) - [Spring Cloud实战小贴士:Zuul统一异常处理(二)](http://blog.didispace.com/spring-cloud-zuul-exception-2/) - [Spring Cloud实战小贴士:Zuul统一异常处理(三)【Dalston版】](http://blog.didispace.com/spring-cloud-zuul-exception-3/) - [Spring Cloud实战小贴士:Turbine如何聚合设置了context-path的Hystrix数据](http://blog.didispace.com/spring-cloud-tips-4/) - [Spring Cloud实战小贴士:Feign的继承特性(伪RPC模式)](http://blog.didispace.com/spring-cloud-tips-feign-rpc/) - [Spring Cloud实战小贴士:Ribbon的饥饿加载(eager-load)模式](http://blog.didispace.com/spring-cloud-tips-ribbon-eager/) - [Spring Cloud实战小贴士:Zuul的饥饿加载(eager-load)使用](http://blog.didispace.com/spring-cloud-tips-zuul-eager/) #### 其他文章 - [使用Intellij中的Spring Initializr来快速构建Spring Boot/Cloud工程](http://blog.didispace.com/spring-initializr-in-intellij/) - [为Spring Cloud Ribbon配置请求重试(Camden.SR2+)](http://blog.didispace.com/spring-cloud-ribbon-failed-retry/) - [Consul注销实例时候的问题](http://blog.didispace.com/consul-deregister/) - [使用Spring Boot Actuator、Jolokia和Grafana实现准实时监控](http://blog.didispace.com/spring-boot-jolokia-grafana-monitor/) - [Netflix Zuul与Nginx的性能对比](http://blog.didispace.com/zuul-vs-nginx-performance/) - [基于Consul的分布式锁实现](http://blog.didispace.com/spring-cloud-consul-lock-and-semphore/) - [基于Consul的分布式信号量实现](http://blog.didispace.com/spring-cloud-consul-lock-and-semphore-2/) ## 我的公众号 ![](http://git.oschina.net/uploads/images/2017/0105/082137_85109d07_437188.jpeg "在这里输入图片标题") ## 推荐我的书 ![](https://git.oschina.net/uploads/images/2017/0416/233656_dd3bce94_437188.png "在这里输入图片标题") ## 其他推荐 - [Spring Boot基础教程](http://blog.didispace.com/Spring-Boot%E5%9F%BA%E7%A1%80%E6%95%99%E7%A8%8B/) - [Spring Boot 2.0新特性](http://blog.didispace.com/Spring-Boot-2-0-feature/) - [Spring Cloud基础教程](http://blog.didispace.com/Spring-Cloud%E5%9F%BA%E7%A1%80%E6%95%99%E7%A8%8B/) - [微服务架构专题](http://blog.didispace.com/micro-serivces-arch/) ================================================ FILE: 4-Finchley/alibaba-dubbo-api/pom.xml ================================================ 4.0.0 com.didispace alibaba-dubbo-api 0.0.1-SNAPSHOT jar UTF-8 UTF-8 1.8 ================================================ FILE: 4-Finchley/alibaba-dubbo-api/src/main/java/com/didispace/alibaba/dubbo/api/HelloService.java ================================================ package com.didispace.alibaba.dubbo.api; /** * Created by 程序猿DD/翟永超 on 2019/8/5. *

* Blog: http://blog.didispace.com/ * Github: https://github.com/dyc87112/ */ public interface HelloService { String hello(String name); } ================================================ FILE: 4-Finchley/alibaba-dubbo-client/pom.xml ================================================ 4.0.0 com.didispace alibaba-dubbo-client 0.0.1-SNAPSHOT jar org.springframework.boot spring-boot-starter-parent 2.0.5.RELEASE UTF-8 UTF-8 1.8 com.didispace alibaba-dubbo-api 0.0.1-SNAPSHOT org.springframework.boot spring-boot-starter-actuator org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-dubbo org.springframework.cloud spring-cloud-starter-alibaba-nacos-discovery org.projectlombok lombok 1.18.2 true org.springframework.boot spring-boot-starter-test test org.springframework.cloud spring-cloud-dependencies Finchley.SR1 pom import org.springframework.cloud spring-cloud-alibaba-dependencies 0.2.2.RELEASE pom import org.springframework.boot spring-boot-maven-plugin ================================================ FILE: 4-Finchley/alibaba-dubbo-client/src/main/java/com/didispace/alibaba/dubbo/client/DubboClientApplication.java ================================================ package com.didispace.alibaba.dubbo.client; import com.didispace.alibaba.dubbo.api.HelloService; import lombok.extern.slf4j.Slf4j; import org.apache.dubbo.config.annotation.Reference; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @EnableDiscoveryClient @SpringBootApplication public class DubboClientApplication { public static void main(String[] args) { SpringApplication.run(DubboClientApplication.class, args); } @Slf4j @RestController static class TestController { @Reference HelloService helloService; @GetMapping("/test") public String test() { return helloService.hello("didispace.com"); } } } ================================================ FILE: 4-Finchley/alibaba-dubbo-client/src/main/resources/application.properties ================================================ spring.application.name=alibaba-dubbo-client server.port=8002 spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848 dubbo.protocol.name=dubbo dubbo.protocol.port=-1 dubbo.registry.address=spring-cloud://localhost dubbo.cloud.subscribed-services=alibaba-dubbo-server ================================================ FILE: 4-Finchley/alibaba-dubbo-server/pom.xml ================================================ 4.0.0 com.didispace alibaba-dubbo-server 0.0.1-SNAPSHOT jar org.springframework.boot spring-boot-starter-parent 2.0.5.RELEASE UTF-8 UTF-8 1.8 com.didispace alibaba-dubbo-api 0.0.1-SNAPSHOT org.springframework.boot spring-boot-starter-actuator org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-dubbo org.springframework.cloud spring-cloud-starter-alibaba-nacos-discovery org.projectlombok lombok 1.18.2 true org.springframework.boot spring-boot-starter-test test org.springframework.cloud spring-cloud-dependencies Finchley.SR1 pom import org.springframework.cloud spring-cloud-alibaba-dependencies 0.2.2.RELEASE pom import org.springframework.boot spring-boot-maven-plugin ================================================ FILE: 4-Finchley/alibaba-dubbo-server/src/main/java/com/didispace/alibaba/dubbo/server/DubboServerApplication.java ================================================ package com.didispace.alibaba.dubbo.server; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; @EnableDiscoveryClient @SpringBootApplication public class DubboServerApplication { public static void main(String[] args) { SpringApplication.run(DubboServerApplication.class, args); } } ================================================ FILE: 4-Finchley/alibaba-dubbo-server/src/main/java/com/didispace/alibaba/dubbo/server/HelloServiceImpl.java ================================================ package com.didispace.alibaba.dubbo.server; import com.didispace.alibaba.dubbo.api.HelloService; import org.apache.dubbo.config.annotation.Service; // 注意:是 org.apache.dubbo.config.annotation.Service 注解 @Service public class HelloServiceImpl implements HelloService { @Override public String hello(String name) { return "hello " + name; } } ================================================ FILE: 4-Finchley/alibaba-dubbo-server/src/main/resources/application.properties ================================================ spring.application.name=alibaba-dubbo-server server.port=8001 spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848 # 指定 Dubbo 服务实现类的扫描基准包 dubbo.scan.base-packages=com.didispace.alibaba.dubbo.server dubbo.protocol.name=dubbo dubbo.protocol.port=-1 dubbo.registry.address=spring-cloud://localhost ================================================ FILE: 4-Finchley/alibaba-nacos-config-client/pom.xml ================================================ 4.0.0 com.didispace alibaba-nacos-config-client 0.0.1-SNAPSHOT jar org.springframework.boot spring-boot-starter-parent 2.0.5.RELEASE UTF-8 UTF-8 1.8 org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-alibaba-nacos-config org.projectlombok lombok 1.18.2 true org.springframework.boot spring-boot-starter-test test org.springframework.cloud spring-cloud-dependencies Finchley.SR1 pom import org.springframework.cloud spring-cloud-alibaba-dependencies 0.2.2.RELEASE pom import org.springframework.boot spring-boot-maven-plugin ================================================ FILE: 4-Finchley/alibaba-nacos-config-client/src/main/java/com/didispace/alibaba/nacos/config/client/TestApplication.java ================================================ package com.didispace.alibaba.nacos.config.client; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.context.config.annotation.RefreshScope; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @SpringBootApplication public class TestApplication { public static void main(String[] args) { SpringApplication.run(TestApplication.class, args); } @Slf4j @RestController @RefreshScope static class TestController { @Value("${didispace.title:}") private String title; @GetMapping("/test") public String hello() { return title; } } /** * 多文件加载的例子使用的验证接口 * * blog: http://blog.didispace.com/spring-cloud-alibaba-nacos-config-3/ */ @Slf4j @RestController @RefreshScope static class Test2Controller { @Value("${didispace.title:}") private String title; @Value("${aaa:}") private String aaa; @Value("${bbb:}") private String bbb; @GetMapping("/test2") public String test2() { return title + ", " + aaa + ", " + bbb; } } } ================================================ FILE: 4-Finchley/alibaba-nacos-config-client/src/main/resources/bootstrap.properties ================================================ spring.application.name=alibaba-nacos-config-client server.port=8001 spring.cloud.nacos.config.server-addr=127.0.0.1:8848 # blog: http://blog.didispace.com/spring-cloud-alibaba-nacos-config-1/ #spring.cloud.nacos.config.prefix=example #spring.cloud.nacos.config.file-extension=properties #spring.cloud.nacos.config.group=DEFAULT_GROUP # blog: http://blog.didispace.com/spring-cloud-alibaba-nacos-config-2/ #spring.profiles.active=DEV #spring.cloud.nacos.config.group=DEV_GROUP #spring.cloud.nacos.config.namespace=83eed625-d166-4619-b923-93df2088883a # blog: http://blog.didispace.com/spring-cloud-alibaba-nacos-config-3/ #spring.cloud.nacos.config.ext-config[0].data-id=actuator.properties #spring.cloud.nacos.config.ext-config[0].group=DEFAULT_GROUP #spring.cloud.nacos.config.ext-config[0].refresh=true #spring.cloud.nacos.config.ext-config[1].data-id=log.properties #spring.cloud.nacos.config.ext-config[1].group=DEFAULT_GROUP #spring.cloud.nacos.config.ext-config[1].refresh=true #spring.cloud.nacos.config.shared-dataids=actuator.properties,log.properties #spring.cloud.nacos.config.refreshable-dataids=actuator.properties,log.properties ================================================ FILE: 4-Finchley/alibaba-nacos-discovery-client-common/pom.xml ================================================ 4.0.0 com.didispace alibaba-nacos-discovery-client-common 0.0.1-SNAPSHOT jar org.springframework.boot spring-boot-starter-parent 2.0.5.RELEASE UTF-8 UTF-8 1.8 org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-alibaba-nacos-discovery org.projectlombok lombok 1.18.2 true org.springframework.boot spring-boot-starter-test test org.springframework.cloud spring-cloud-dependencies Finchley.SR1 pom import org.springframework.cloud spring-cloud-alibaba-dependencies 0.2.2.RELEASE pom import org.springframework.boot spring-boot-maven-plugin ================================================ FILE: 4-Finchley/alibaba-nacos-discovery-client-common/src/main/java/com/didispace/alibaba/nacos/discovery/client/TestApplication.java ================================================ package com.didispace.alibaba.nacos.discovery.client; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.client.loadbalancer.LoadBalancerClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; @EnableDiscoveryClient @SpringBootApplication public class TestApplication { public static void main(String[] args) { SpringApplication.run(TestApplication.class, args); } @Slf4j @RestController static class TestController { @Autowired LoadBalancerClient loadBalancerClient; @GetMapping("/test") public String test() { // 通过spring cloud common中的负载均衡接口选取服务提供节点实现接口调用 ServiceInstance serviceInstance = loadBalancerClient.choose("alibaba-nacos-discovery-server"); String url = serviceInstance.getUri() + "/hello?name=" + "didi"; RestTemplate restTemplate = new RestTemplate(); String result = restTemplate.getForObject(url, String.class); return "Invoke : " + url + ", return : " + result; } } } ================================================ FILE: 4-Finchley/alibaba-nacos-discovery-client-common/src/main/resources/application.properties ================================================ spring.application.name=alibaba-nacos-discovery-client-common server.port=9000 spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848 ================================================ FILE: 4-Finchley/alibaba-nacos-discovery-client-feign/pom.xml ================================================ 4.0.0 com.didispace alibaba-nacos-discovery-client-feign 0.0.1-SNAPSHOT jar org.springframework.boot spring-boot-starter-parent 2.0.5.RELEASE UTF-8 UTF-8 1.8 org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-alibaba-nacos-discovery org.springframework.cloud spring-cloud-starter-openfeign org.projectlombok lombok 1.18.2 true org.springframework.boot spring-boot-starter-test test org.springframework.cloud spring-cloud-dependencies Finchley.SR1 pom import org.springframework.cloud spring-cloud-alibaba-dependencies 0.2.2.RELEASE pom import org.springframework.boot spring-boot-maven-plugin ================================================ FILE: 4-Finchley/alibaba-nacos-discovery-client-feign/src/main/java/com/didispace/alibaba/nacos/discovery/client/TestApplication.java ================================================ package com.didispace.alibaba.nacos.discovery.client; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.context.annotation.Bean; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; @EnableDiscoveryClient @SpringBootApplication @EnableFeignClients public class TestApplication { public static void main(String[] args) { SpringApplication.run(TestApplication.class, args); } @Slf4j @RestController static class TestController { @Autowired Client client; @GetMapping("/test") public String test() { String result = client.hello("didi"); return "Return : " + result; } } @FeignClient("alibaba-nacos-discovery-server") interface Client { @GetMapping("/hello") String hello(@RequestParam(name = "name") String name); } } ================================================ FILE: 4-Finchley/alibaba-nacos-discovery-client-feign/src/main/resources/application.properties ================================================ spring.application.name=alibaba-nacos-discovery-client-feign server.port=9000 spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848 ================================================ FILE: 4-Finchley/alibaba-nacos-discovery-client-resttemplate/pom.xml ================================================ 4.0.0 com.didispace alibaba-nacos-discovery-client-resttemplate 0.0.1-SNAPSHOT jar org.springframework.boot spring-boot-starter-parent 2.0.5.RELEASE UTF-8 UTF-8 1.8 org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-alibaba-nacos-discovery org.projectlombok lombok 1.18.2 true org.springframework.boot spring-boot-starter-test test org.springframework.cloud spring-cloud-dependencies Finchley.SR1 pom import org.springframework.cloud spring-cloud-alibaba-dependencies 0.2.2.RELEASE pom import org.springframework.boot spring-boot-maven-plugin ================================================ FILE: 4-Finchley/alibaba-nacos-discovery-client-resttemplate/src/main/java/com/didispace/alibaba/nacos/discovery/client/TestApplication.java ================================================ package com.didispace.alibaba.nacos.discovery.client; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.context.annotation.Bean; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; @EnableDiscoveryClient @SpringBootApplication public class TestApplication { public static void main(String[] args) { SpringApplication.run(TestApplication.class, args); } @Slf4j @RestController static class TestController { @Autowired RestTemplate restTemplate; @GetMapping("/test") public String test() { String result = restTemplate.getForObject("http://alibaba-nacos-discovery-server/hello?name=didi", String.class); return "Return : " + result; } } @Bean @LoadBalanced public RestTemplate restTemplate() { return new RestTemplate(); } } ================================================ FILE: 4-Finchley/alibaba-nacos-discovery-client-resttemplate/src/main/resources/application.properties ================================================ spring.application.name=alibaba-nacos-discovery-client-resttemplate server.port=9000 spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848 ================================================ FILE: 4-Finchley/alibaba-nacos-discovery-client-webclient/pom.xml ================================================ 4.0.0 com.didispace alibaba-nacos-discovery-client-webclient 0.0.1-SNAPSHOT jar org.springframework.boot spring-boot-starter-parent 2.0.5.RELEASE UTF-8 UTF-8 1.8 org.springframework.boot spring-boot-starter-webflux org.springframework.cloud spring-cloud-starter-alibaba-nacos-discovery org.projectlombok lombok 1.18.2 true org.springframework.boot spring-boot-starter-test test org.springframework.cloud spring-cloud-dependencies Finchley.SR1 pom import org.springframework.cloud spring-cloud-alibaba-dependencies 0.2.2.RELEASE pom import org.springframework.boot spring-boot-maven-plugin ================================================ FILE: 4-Finchley/alibaba-nacos-discovery-client-webclient/src/main/java/com/didispace/alibaba/nacos/discovery/client/TestApplication.java ================================================ package com.didispace.alibaba.nacos.discovery.client; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.context.annotation.Bean; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.reactive.function.client.WebClient; import reactor.core.publisher.Mono; @EnableDiscoveryClient @SpringBootApplication public class TestApplication { public static void main(String[] args) { SpringApplication.run(TestApplication.class, args); } @Slf4j @RestController static class TestController { @Autowired private WebClient.Builder webClientBuilder; @GetMapping("/test") public Mono test() { Mono result = webClientBuilder.build() .get() .uri("http://alibaba-nacos-discovery-server/hello?name=didi") .retrieve() .bodyToMono(String.class); return result; } } @Bean @LoadBalanced public WebClient.Builder loadBalancedWebClientBuilder() { return WebClient.builder(); } } ================================================ FILE: 4-Finchley/alibaba-nacos-discovery-client-webclient/src/main/resources/application.properties ================================================ spring.application.name=alibaba-nacos-discovery-client-webclient server.port=9000 spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848 ================================================ FILE: 4-Finchley/alibaba-nacos-discovery-server/pom.xml ================================================ 4.0.0 com.didispace alibaba-nacos-discovery-server 0.0.1-SNAPSHOT jar org.springframework.boot spring-boot-starter-parent 2.0.5.RELEASE UTF-8 UTF-8 1.8 org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-alibaba-nacos-discovery org.projectlombok lombok 1.18.2 true org.springframework.boot spring-boot-starter-test test org.springframework.cloud spring-cloud-dependencies Finchley.SR1 pom import org.springframework.cloud spring-cloud-alibaba-dependencies 0.2.2.RELEASE pom import org.springframework.boot spring-boot-maven-plugin ================================================ FILE: 4-Finchley/alibaba-nacos-discovery-server/src/main/java/com/didispace/alibaba/nacos/discovery/server/TestApplication.java ================================================ package com.didispace.alibaba.nacos.discovery.server; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.client.loadbalancer.LoadBalancerClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @EnableDiscoveryClient @SpringBootApplication public class TestApplication { public static void main(String[] args) { SpringApplication.run(TestApplication.class, args); } @Slf4j @RestController static class TestController { @GetMapping("/hello") public String hello(@RequestParam String name) { log.info("invoked name = " + name); return "hello " + name; } } } ================================================ FILE: 4-Finchley/alibaba-nacos-discovery-server/src/main/resources/application.properties ================================================ spring.application.name=alibaba-nacos-discovery-server server.port=8001 spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848 ================================================ FILE: 4-Finchley/alibaba-sentinel-annotation/pom.xml ================================================ 4.0.0 com.didispace alibaba-sentinel-annotation 0.0.1-SNAPSHOT jar org.springframework.boot spring-boot-starter-parent 2.0.5.RELEASE UTF-8 UTF-8 1.8 org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-alibaba-sentinel org.projectlombok lombok 1.18.2 true org.springframework.boot spring-boot-starter-test test org.springframework.cloud spring-cloud-dependencies Finchley.SR1 pom import org.springframework.cloud spring-cloud-alibaba-dependencies 0.2.2.RELEASE pom import org.springframework.boot spring-boot-maven-plugin ================================================ FILE: 4-Finchley/alibaba-sentinel-annotation/src/main/java/com/didispace/alibaba/sentinel/TestApplication.java ================================================ package com.didispace.alibaba.sentinel; import com.alibaba.csp.sentinel.annotation.aspectj.SentinelResourceAspect; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; @SpringBootApplication public class TestApplication { public static void main(String[] args) { SpringApplication.run(TestApplication.class, args); } @Bean public SentinelResourceAspect sentinelResourceAspect() { return new SentinelResourceAspect(); } } ================================================ FILE: 4-Finchley/alibaba-sentinel-annotation/src/main/java/com/didispace/alibaba/sentinel/service/TestService.java ================================================ package com.didispace.alibaba.sentinel.service; import com.alibaba.csp.sentinel.annotation.SentinelResource; import com.alibaba.csp.sentinel.slots.block.BlockException; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @Slf4j @Service public class TestService { // 限流与阻塞处理 @SentinelResource(value = "doSomeThing", blockHandler = "exceptionHandler") public void doSomeThing(String str) { log.info(str); } public void exceptionHandler(String str, BlockException ex) { log.error("blockHandler:" + str, ex); } // 熔断与降级处理 @SentinelResource(value = "doSomeThing2", fallback = "fallbackHandler") public void doSomeThing2(String str) { log.info(str); throw new RuntimeException("发生异常"); } public void fallbackHandler(String str) { log.error("fallbackHandler:" + str); } } ================================================ FILE: 4-Finchley/alibaba-sentinel-annotation/src/main/java/com/didispace/alibaba/sentinel/web/TestController.java ================================================ package com.didispace.alibaba.sentinel.web; import com.didispace.alibaba.sentinel.service.TestService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import java.util.Date; @Slf4j @RestController public class TestController { @Autowired private TestService testService; @GetMapping("/hello") public String hello() { testService.doSomeThing("hello " + new Date()); return "didispace.com"; } @GetMapping("/hello2") public String hello2() { testService.doSomeThing2("hello2 " + new Date()); return "didispace.com"; } } ================================================ FILE: 4-Finchley/alibaba-sentinel-annotation/src/main/resources/application.properties ================================================ spring.application.name=alibaba-sentinel-annotation server.port=8001 # sentinel dashboard spring.cloud.sentinel.transport.dashboard=localhost:8080 ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/pom.xml ================================================ 4.0.0 com.alibaba.csp sentinel-parent 1.6.0 alibaba-sentinel-dashboard-apollo jar 1.8 1.8 2.0.5.RELEASE com.alibaba.csp sentinel-core com.alibaba.csp sentinel-web-servlet ${project.version} com.alibaba.csp sentinel-transport-simple-http com.alibaba.csp sentinel-parameter-flow-control ${project.version} org.springframework.boot spring-boot-starter-web ${spring.boot.version} org.springframework.boot spring-boot-starter-logging ${spring.boot.version} org.springframework.boot spring-boot-starter-test ${spring.boot.version} test log4j log4j 1.2.14 commons-lang commons-lang 2.6 org.apache.httpcomponents httpclient 4.5.3 org.apache.httpcomponents httpcore 4.4.5 org.apache.httpcomponents httpasyncclient 4.1.3 org.apache.httpcomponents httpcore-nio 4.4.6 com.alibaba fastjson com.alibaba.csp sentinel-datasource-nacos test com.ctrip.framework.apollo apollo-openapi 1.2.0 junit junit test org.mockito mockito-core test com.github.stefanbirkner system-rules 1.16.1 test sentinel-dashboard org.springframework.boot spring-boot-maven-plugin true com.alibaba.csp.sentinel.dashboard.DashboardApplication repackage org.apache.maven.plugins maven-compiler-plugin ${maven.compiler.source} ${maven.compiler.target} org.apache.maven.plugins maven-deploy-plugin ${maven.deploy.version} true src/main/resources src/main/webapp/ resources/node_modules/** ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/DashboardApplication.java ================================================ /* * 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; import com.alibaba.csp.sentinel.init.InitExecutor; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; /** * Sentinel dashboard application. * * @author Carpenter Lee */ @SpringBootApplication public class DashboardApplication { public static void main(String[] args) { triggerSentinelInit(); SpringApplication.run(DashboardApplication.class, args); } private static void triggerSentinelInit() { new Thread(() -> InitExecutor.doInit()).start(); } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/auth/AuthService.java ================================================ /* * 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; /** * Interface for authentication and authorization. * * @author Carpenter Lee * @since 1.5.0 */ public interface AuthService { /** * Get the authentication user. * * @param request the request contains the user information * @return the auth user represent the current user, when the user is illegal, a null value will return. */ AuthUser getAuthUser(R request); /** * Privilege type. */ enum PrivilegeType { /** * Read rule */ READ_RULE, /** * Create or modify rule */ WRITE_RULE, /** * Delete rule */ DELETE_RULE, /** * Read metrics */ READ_METRIC, /** * Add machine */ ADD_MACHINE, /** * All privileges above are granted. */ ALL } /** * Represents the current user. */ interface AuthUser { /** * Query whether current user has the specific privilege to the target, the target * may be an app name or an ip address, or other destination. *

* This method will use return value to represent whether user has the specific * privileges to the target, but to throw a RuntimeException to represent no auth * is also a good way. *

* * @param target the target to check * @param privilegeType the privilege type to check * @return if current user has the specific privileges to the target, return true, * otherwise return false. */ boolean authTarget(String target, PrivilegeType privilegeType); /** * Check whether current user is a super-user. * * @return if current user is super user return true, else return false. */ boolean isSuperUser(); /** * Get current user's nick name. * * @return current user's nick name. */ String getNickName(); /** * Get current user's login name. * * @return current user's login name. */ String getLoginName(); /** * Get current user's ID. * * @return ID of current user */ String getId(); } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/auth/FakeAuthServiceImpl.java ================================================ /* * 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 javax.servlet.http.HttpServletRequest; import org.springframework.stereotype.Component; /** * A fake AuthService implementation, which will pass all user auth checking. * * @author Carpenter Lee * @since 1.5.0 */ @Component public class FakeAuthServiceImpl implements AuthService { @Override public AuthUser getAuthUser(HttpServletRequest request) { return new AuthUserImpl(); } static final class AuthUserImpl implements AuthUser { @Override public boolean authTarget(String target, PrivilegeType privilegeType) { // fake implementation, always return true return true; } @Override public boolean isSuperUser() { // fake implementation, always return true return true; } @Override public String getNickName() { return "FAKE_NICK_NAME"; } @Override public String getLoginName() { return "FAKE_LOGIN_NAME"; } @Override public String getId() { return "FAKE_EMP_ID"; } } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/auth/SimpleWebAuthServiceImpl.java ================================================ /* * 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.springframework.context.annotation.Primary; import org.springframework.stereotype.Component; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; /** * @author cdfive * @since 1.6.0 */ @Primary @Component public class SimpleWebAuthServiceImpl implements AuthService { public static final String WEB_SESSTION_KEY = "session_sentinel_admin"; @Override public AuthUser getAuthUser(HttpServletRequest request) { HttpSession session = request.getSession(); Object sentinelUserObj = session.getAttribute(SimpleWebAuthServiceImpl.WEB_SESSTION_KEY); if (sentinelUserObj != null && sentinelUserObj instanceof AuthUser) { return (AuthUser) sentinelUserObj; } return null; } public static final class SimpleWebAuthUserImpl implements AuthUser { private String username; public SimpleWebAuthUserImpl(String username) { this.username = username; } @Override public boolean authTarget(String target, PrivilegeType privilegeType) { return true; } @Override public boolean isSuperUser() { return true; } @Override public String getNickName() { return username; } @Override public String getLoginName() { return username; } @Override public String getId() { return username; } } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/client/CommandFailedException.java ================================================ /* * 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.client; /** * @author Eric Zhao */ public class CommandFailedException extends RuntimeException { public CommandFailedException() {} public CommandFailedException(String message) { super(message); } @Override public synchronized Throwable fillInStackTrace() { return this; } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/client/CommandNotFoundException.java ================================================ /* * 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.client; /** * @author Eric Zhao * @since 0.2.1 */ public class CommandNotFoundException extends Exception { public CommandNotFoundException() { } public CommandNotFoundException(String message) { super(message); } @Override public synchronized Throwable fillInStackTrace() { return this; } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/client/SentinelApiClient.java ================================================ /* * 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.client; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Optional; import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.stream.Collectors; import com.alibaba.csp.sentinel.command.CommandConstants; import com.alibaba.csp.sentinel.config.SentinelConfig; import com.alibaba.csp.sentinel.command.vo.NodeVo; import com.alibaba.csp.sentinel.dashboard.util.AsyncUtils; import com.alibaba.csp.sentinel.slots.block.Rule; import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRule; import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule; import com.alibaba.csp.sentinel.slots.block.flow.FlowRule; import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule; import com.alibaba.csp.sentinel.slots.system.SystemRule; import com.alibaba.csp.sentinel.util.AssertUtil; import com.alibaba.csp.sentinel.util.StringUtil; import com.alibaba.fastjson.JSON; import com.alibaba.csp.sentinel.dashboard.datasource.entity.SentinelVersion; import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.AuthorityRuleEntity; import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.DegradeRuleEntity; import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity; import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.ParamFlowRuleEntity; import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.RuleEntity; import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.SystemRuleEntity; import com.alibaba.csp.sentinel.dashboard.discovery.AppManagement; import com.alibaba.csp.sentinel.dashboard.domain.cluster.ClusterClientInfoVO; import com.alibaba.csp.sentinel.dashboard.domain.cluster.state.ClusterServerStateVO; import com.alibaba.csp.sentinel.dashboard.domain.cluster.state.ClusterStateSimpleEntity; import com.alibaba.csp.sentinel.dashboard.domain.cluster.config.ClusterClientConfig; import com.alibaba.csp.sentinel.dashboard.domain.cluster.config.ServerFlowConfig; import com.alibaba.csp.sentinel.dashboard.domain.cluster.config.ServerTransportConfig; import com.alibaba.csp.sentinel.dashboard.util.VersionUtils; import org.apache.http.HttpResponse; import org.apache.http.NameValuePair; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.concurrent.FutureCallback; import org.apache.http.entity.ContentType; import org.apache.http.impl.client.DefaultRedirectStrategy; import org.apache.http.impl.nio.client.CloseableHttpAsyncClient; import org.apache.http.impl.nio.client.HttpAsyncClients; import org.apache.http.impl.nio.reactor.IOReactorConfig; import org.apache.http.message.BasicNameValuePair; import org.apache.http.util.EntityUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.lang.Nullable; import org.springframework.stereotype.Component; /** * Communicate with Sentinel client. * * @author leyou */ @Component public class SentinelApiClient { private static Logger logger = LoggerFactory.getLogger(SentinelApiClient.class); private static final Charset DEFAULT_CHARSET = Charset.forName(SentinelConfig.charset()); private static final String RESOURCE_URL_PATH = "jsonTree"; private static final String CLUSTER_NODE_PATH = "clusterNode"; private static final String GET_RULES_PATH = "getRules"; private static final String SET_RULES_PATH = "setRules"; private static final String GET_PARAM_RULE_PATH = "getParamFlowRules"; private static final String SET_PARAM_RULE_PATH = "setParamFlowRules"; private static final String FETCH_CLUSTER_MODE_PATH = "getClusterMode"; private static final String MODIFY_CLUSTER_MODE_PATH = "setClusterMode"; private static final String FETCH_CLUSTER_CLIENT_CONFIG_PATH = "cluster/client/fetchConfig"; private static final String MODIFY_CLUSTER_CLIENT_CONFIG_PATH = "cluster/client/modifyConfig"; private static final String FETCH_CLUSTER_SERVER_ALL_CONFIG_PATH = "cluster/server/fetchConfig"; private static final String FETCH_CLUSTER_SERVER_BASIC_INFO_PATH = "cluster/server/info"; private static final String MODIFY_CLUSTER_SERVER_TRANSPORT_CONFIG_PATH = "cluster/server/modifyTransportConfig"; private static final String MODIFY_CLUSTER_SERVER_FLOW_CONFIG_PATH = "cluster/server/modifyFlowConfig"; private static final String MODIFY_CLUSTER_SERVER_NAMESPACE_SET_PATH = "cluster/server/modifyNamespaceSet"; private static final String FLOW_RULE_TYPE = "flow"; private static final String DEGRADE_RULE_TYPE = "degrade"; private static final String SYSTEM_RULE_TYPE = "system"; private static final String AUTHORITY_TYPE = "authority"; private CloseableHttpAsyncClient httpClient; private static final SentinelVersion version160 = new SentinelVersion(1, 6, 0); @Autowired private AppManagement appManagement; public SentinelApiClient() { IOReactorConfig ioConfig = IOReactorConfig.custom().setConnectTimeout(3000).setSoTimeout(10000) .setIoThreadCount(Runtime.getRuntime().availableProcessors() * 2).build(); httpClient = HttpAsyncClients.custom().setRedirectStrategy(new DefaultRedirectStrategy() { @Override protected boolean isRedirectable(final String method) { return false; } }).setMaxConnTotal(4000).setMaxConnPerRoute(1000).setDefaultIOReactorConfig(ioConfig).build(); httpClient.start(); } private boolean isSuccess(int statusCode) { return statusCode >= 200 && statusCode < 300; } private boolean isCommandNotFound(int statusCode, String body) { return statusCode == 400 && StringUtil.isNotEmpty(body) && body.contains(CommandConstants.MSG_UNKNOWN_COMMAND_PREFIX); } private StringBuilder queryString(Map params) { StringBuilder queryStringBuilder = new StringBuilder(); for (Entry entry : params.entrySet()) { if (StringUtil.isEmpty(entry.getValue())) { continue; } String name = urlEncode(entry.getKey()); String value = urlEncode(entry.getValue()); if (name != null && value != null) { if (queryStringBuilder.length() > 0) { queryStringBuilder.append('&'); } queryStringBuilder.append(name).append('=').append(value); } } return queryStringBuilder; } private HttpUriRequest postRequest(String url, Map params) { HttpPost httpPost = new HttpPost(url); if (params != null && params.size() > 0) { List list = new ArrayList<>(params.size()); for (Entry entry : params.entrySet()) { list.add(new BasicNameValuePair(entry.getKey(), entry.getValue())); } try { httpPost.setEntity(new UrlEncodedFormEntity(list)); } catch (UnsupportedEncodingException e) { logger.warn("httpPostContent encode entity error: {}", params, e); return null; } } return httpPost; } private String urlEncode(String str) { try { return URLEncoder.encode(str, DEFAULT_CHARSET.name()); } catch (UnsupportedEncodingException e) { logger.info("encode string error: {}", str, e); return null; } } private String getBody(HttpResponse response) throws Exception { Charset charset = null; try { String contentTypeStr = response.getFirstHeader("Content-type").getValue(); if (StringUtil.isNotEmpty(contentTypeStr)) { ContentType contentType = ContentType.parse(contentTypeStr); charset = contentType.getCharset(); } } catch (Exception ignore) { } return EntityUtils.toString(response.getEntity(), charset != null ? charset : DEFAULT_CHARSET); } /** * With no param * * @param ip * @param port * @param api * @return */ private CompletableFuture executeCommand(String ip, int port, String api, boolean useHttpPost) { return executeCommand(ip, port, api, null, useHttpPost); } /** * No app specified, force to GET * * @param ip * @param port * @param api * @param params * @return */ private CompletableFuture executeCommand(String ip, int port, String api, Map params, boolean useHttpPost) { return executeCommand(null, ip, port, api, params, useHttpPost); } /** * Prefer to execute request using POST * * @param app * @param ip * @param port * @param api * @param params * @return */ private CompletableFuture executeCommand(String app, String ip, int port, String api, Map params, boolean useHttpPost) { CompletableFuture future = new CompletableFuture<>(); if (StringUtil.isBlank(ip) || StringUtil.isBlank(api)) { future.completeExceptionally(new IllegalArgumentException("Bad URL or command name")); return future; } StringBuilder urlBuilder = new StringBuilder(); urlBuilder.append("http://"); urlBuilder.append(ip).append(':').append(port).append('/').append(api); if (params == null) { params = Collections.emptyMap(); } boolean supportPost = StringUtil.isNotEmpty(app) && Optional.ofNullable(appManagement.getDetailApp(app)) .flatMap(e -> e.getMachine(ip, port)) .flatMap(m -> VersionUtils.parseVersion(m.getVersion()) .map(v -> v.greaterOrEqual(version160))) .orElse(false); if (!useHttpPost || !supportPost) { // Using GET in older versions, append parameters after url if (!params.isEmpty()) { if (urlBuilder.indexOf("?") == -1) { urlBuilder.append('?'); } else { urlBuilder.append('&'); } urlBuilder.append(queryString(params)); } return executeCommand(new HttpGet(urlBuilder.toString())); } else { // Using POST return executeCommand(postRequest(urlBuilder.toString(), params)); } } private CompletableFuture executeCommand(HttpUriRequest request) { CompletableFuture future = new CompletableFuture<>(); httpClient.execute(request, new FutureCallback() { @Override public void completed(final HttpResponse response) { int statusCode = response.getStatusLine().getStatusCode(); try { String value = getBody(response); if (isSuccess(statusCode)) { future.complete(value); } else { if (isCommandNotFound(statusCode, value)) { future.completeExceptionally(new CommandNotFoundException(request.getURI().getPath())); } else { future.completeExceptionally(new CommandFailedException(value)); } } } catch (Exception ex) { future.completeExceptionally(ex); logger.error("HTTP request failed: {}", request.getURI().toString(), ex); } } @Override public void failed(final Exception ex) { future.completeExceptionally(ex); logger.error("HTTP request failed: {}", request.getURI().toString(), ex); } @Override public void cancelled() { future.complete(null); } }); return future; } public void close() throws Exception { httpClient.close(); } @Nullable private CompletableFuture> fetchItemsAsync(String ip, int port, String api, String type, Class ruleType) { AssertUtil.notEmpty(ip, "Bad machine IP"); AssertUtil.isTrue(port > 0, "Bad machine port"); Map params = null; if (StringUtil.isNotEmpty(type)) { params = new HashMap<>(1); params.put("type", type); } return executeCommand(ip, port, api, params, false) .thenApply(json -> JSON.parseArray(json, ruleType)); } @Nullable private List fetchItems(String ip, int port, String api, String type, Class ruleType) { try { AssertUtil.notEmpty(ip, "Bad machine IP"); AssertUtil.isTrue(port > 0, "Bad machine port"); Map params = null; if (StringUtil.isNotEmpty(type)) { params = new HashMap<>(1); params.put("type", type); } return fetchItemsAsync(ip, port, api, type, ruleType).get(); } catch (InterruptedException | ExecutionException e) { logger.error("Error when fetching items from api: {} -> {}", api, type, e); return null; } catch (Exception e) { logger.error("Error when fetching items: {} -> {}", api, type, e); return null; } } private List fetchRules(String ip, int port, String type, Class ruleType) { return fetchItems(ip, port, GET_RULES_PATH, type, ruleType); } private boolean setRules(String app, String ip, int port, String type, List entities) { if (entities == null) { return true; } try { AssertUtil.notEmpty(app, "Bad app name"); AssertUtil.notEmpty(ip, "Bad machine IP"); AssertUtil.isTrue(port > 0, "Bad machine port"); String data = JSON.toJSONString( entities.stream().map(r -> r.toRule()).collect(Collectors.toList())); Map params = new HashMap<>(2); params.put("type", type); params.put("data", data); String result = executeCommand(app, ip, port, SET_RULES_PATH, params, true).get(); logger.info("setRules: {}", result); return true; } catch (InterruptedException | ExecutionException e) { logger.warn("setRules api failed: {}", type, e); return false; } catch (Exception e) { logger.warn("setRules failed", e); return false; } } public List fetchResourceOfMachine(String ip, int port, String type) { return fetchItems(ip, port, RESOURCE_URL_PATH, type, NodeVo.class); } /** * Fetch cluster node. * * @param ip ip to fetch * @param port port of the ip * @param includeZero whether zero value should in the result list. * @return */ public List fetchClusterNodeOfMachine(String ip, int port, boolean includeZero) { String type = "noZero"; if (includeZero) { type = "zero"; } return fetchItems(ip, port, CLUSTER_NODE_PATH, type, NodeVo.class); } public List fetchFlowRuleOfMachine(String app, String ip, int port) { List rules = fetchRules(ip, port, FLOW_RULE_TYPE, FlowRule.class); if (rules != null) { return rules.stream().map(rule -> FlowRuleEntity.fromFlowRule(app, ip, port, rule)) .collect(Collectors.toList()); } else { return null; } } public List fetchDegradeRuleOfMachine(String app, String ip, int port) { List rules = fetchRules(ip, port, DEGRADE_RULE_TYPE, DegradeRule.class); if (rules != null) { return rules.stream().map(rule -> DegradeRuleEntity.fromDegradeRule(app, ip, port, rule)) .collect(Collectors.toList()); } else { return null; } } public List fetchSystemRuleOfMachine(String app, String ip, int port) { List rules = fetchRules(ip, port, SYSTEM_RULE_TYPE, SystemRule.class); if (rules != null) { return rules.stream().map(rule -> SystemRuleEntity.fromSystemRule(app, ip, port, rule)) .collect(Collectors.toList()); } else { return null; } } /** * Fetch all parameter flow rules from provided machine. * * @param app application name * @param ip machine client IP * @param port machine client port * @return all retrieved parameter flow rules * @since 0.2.1 */ public CompletableFuture> fetchParamFlowRulesOfMachine(String app, String ip, int port) { try { AssertUtil.notEmpty(app, "Bad app name"); 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)) .collect(Collectors.toList()) ); } catch (Exception e) { logger.error("Error when fetching parameter flow rules", e); return AsyncUtils.newFailedFuture(e); } } /** * Fetch all authority rules from provided machine. * * @param app application name * @param ip machine client IP * @param port machine client port * @return all retrieved authority rules * @since 0.2.1 */ public List fetchAuthorityRulesOfMachine(String app, String ip, int port) { AssertUtil.notEmpty(app, "Bad app name"); AssertUtil.notEmpty(ip, "Bad machine IP"); AssertUtil.isTrue(port > 0, "Bad machine port"); Map params = new HashMap<>(1); params.put("type", AUTHORITY_TYPE); List rules = fetchRules(ip, port, AUTHORITY_TYPE, AuthorityRule.class); return Optional.ofNullable(rules).map(r -> r.stream() .map(e -> AuthorityRuleEntity.fromAuthorityRule(app, ip, port, e)) .collect(Collectors.toList()) ).orElse(null); } /** * set rules of the machine. rules == null will return immediately; * rules.isEmpty() means setting the rules to empty. * * @param app * @param ip * @param port * @param rules * @return whether successfully set the rules. */ public boolean setFlowRuleOfMachine(String app, String ip, int port, List rules) { return setRules(app, ip, port, FLOW_RULE_TYPE, rules); } /** * set rules of the machine. rules == null will return immediately; * rules.isEmpty() means setting the rules to empty. * * @param app * @param ip * @param port * @param rules * @return whether successfully set the rules. */ public boolean setDegradeRuleOfMachine(String app, String ip, int port, List rules) { return setRules(app, ip, port, DEGRADE_RULE_TYPE, rules); } /** * set rules of the machine. rules == null will return immediately; * rules.isEmpty() means setting the rules to empty. * * @param app * @param ip * @param port * @param rules * @return whether successfully set the rules. */ public boolean setSystemRuleOfMachine(String app, String ip, int port, List rules) { return setRules(app, ip, port, SYSTEM_RULE_TYPE, rules); } public boolean setAuthorityRuleOfMachine(String app, String ip, int port, List rules) { return setRules(app, ip, port, AUTHORITY_TYPE, rules); } public CompletableFuture setParamFlowRuleOfMachine(String app, String ip, int port, List rules) { if (rules == null) { return CompletableFuture.completedFuture(null); } if (StringUtil.isBlank(ip) || port <= 0) { return AsyncUtils.newFailedFuture(new IllegalArgumentException("Invalid parameter")); } try { String data = JSON.toJSONString( rules.stream().map(ParamFlowRuleEntity::getRule).collect(Collectors.toList()) ); Map params = new HashMap<>(1); params.put("data", data); return executeCommand(app, ip, port, SET_PARAM_RULE_PATH, params, true) .thenCompose(e -> { if (CommandConstants.MSG_SUCCESS.equals(e)) { return CompletableFuture.completedFuture(null); } else { logger.warn("Push parameter flow rules to client failed: " + e); return AsyncUtils.newFailedFuture(new RuntimeException(e)); } }); } catch (Exception ex) { logger.warn("Error when setting parameter flow rule", ex); return AsyncUtils.newFailedFuture(ex); } } // Cluster related public CompletableFuture fetchClusterMode(String ip, int port) { if (StringUtil.isBlank(ip) || port <= 0) { return AsyncUtils.newFailedFuture(new IllegalArgumentException("Invalid parameter")); } try { return executeCommand(ip, port, FETCH_CLUSTER_MODE_PATH, false) .thenApply(r -> JSON.parseObject(r, ClusterStateSimpleEntity.class)); } catch (Exception ex) { logger.warn("Error when fetching cluster mode", ex); return AsyncUtils.newFailedFuture(ex); } } public CompletableFuture modifyClusterMode(String ip, int port, int mode) { if (StringUtil.isBlank(ip) || port <= 0) { return AsyncUtils.newFailedFuture(new IllegalArgumentException("Invalid parameter")); } try { Map params = new HashMap<>(1); params.put("mode", String.valueOf(mode)); return executeCommand(ip, port, MODIFY_CLUSTER_MODE_PATH, params, false) .thenCompose(e -> { if (CommandConstants.MSG_SUCCESS.equals(e)) { return CompletableFuture.completedFuture(null); } else { logger.warn("Error when modifying cluster mode: " + e); return AsyncUtils.newFailedFuture(new RuntimeException(e)); } }); } catch (Exception ex) { logger.warn("Error when modifying cluster mode", ex); return AsyncUtils.newFailedFuture(ex); } } public CompletableFuture fetchClusterClientInfoAndConfig(String ip, int port) { if (StringUtil.isBlank(ip) || port <= 0) { return AsyncUtils.newFailedFuture(new IllegalArgumentException("Invalid parameter")); } try { return executeCommand(ip, port, FETCH_CLUSTER_CLIENT_CONFIG_PATH, false) .thenApply(r -> JSON.parseObject(r, ClusterClientInfoVO.class)); } catch (Exception ex) { logger.warn("Error when fetching cluster client config", ex); return AsyncUtils.newFailedFuture(ex); } } public CompletableFuture modifyClusterClientConfig(String app, String ip, int port, ClusterClientConfig config) { if (StringUtil.isBlank(ip) || port <= 0) { return AsyncUtils.newFailedFuture(new IllegalArgumentException("Invalid parameter")); } try { Map params = new HashMap<>(1); params.put("data", JSON.toJSONString(config)); return executeCommand(app, ip, port, MODIFY_CLUSTER_CLIENT_CONFIG_PATH, params, true) .thenCompose(e -> { if (CommandConstants.MSG_SUCCESS.equals(e)) { return CompletableFuture.completedFuture(null); } else { logger.warn("Error when modifying cluster client config: " + e); return AsyncUtils.newFailedFuture(new RuntimeException(e)); } }); } catch (Exception ex) { logger.warn("Error when modifying cluster client config", ex); return AsyncUtils.newFailedFuture(ex); } } public CompletableFuture modifyClusterServerFlowConfig(String app, String ip, int port, ServerFlowConfig config) { if (StringUtil.isBlank(ip) || port <= 0) { return AsyncUtils.newFailedFuture(new IllegalArgumentException("Invalid parameter")); } try { Map params = new HashMap<>(1); params.put("data", JSON.toJSONString(config)); return executeCommand(app, ip, port, MODIFY_CLUSTER_SERVER_FLOW_CONFIG_PATH, params, true) .thenCompose(e -> { if (CommandConstants.MSG_SUCCESS.equals(e)) { return CompletableFuture.completedFuture(null); } else { logger.warn("Error when modifying cluster server flow config: " + e); return AsyncUtils.newFailedFuture(new RuntimeException(e)); } }); } catch (Exception ex) { logger.warn("Error when modifying cluster server flow config", ex); return AsyncUtils.newFailedFuture(ex); } } public CompletableFuture modifyClusterServerTransportConfig(String app, String ip, int port, ServerTransportConfig config) { if (StringUtil.isBlank(ip) || port <= 0) { return AsyncUtils.newFailedFuture(new IllegalArgumentException("Invalid parameter")); } try { Map params = new HashMap<>(2); params.put("port", config.getPort().toString()); params.put("idleSeconds", config.getIdleSeconds().toString()); return executeCommand(app, ip, port, MODIFY_CLUSTER_SERVER_TRANSPORT_CONFIG_PATH, params, false) .thenCompose(e -> { if (CommandConstants.MSG_SUCCESS.equals(e)) { return CompletableFuture.completedFuture(null); } else { logger.warn("Error when modifying cluster server transport config: " + e); return AsyncUtils.newFailedFuture(new RuntimeException(e)); } }); } catch (Exception ex) { logger.warn("Error when modifying cluster server transport config", ex); return AsyncUtils.newFailedFuture(ex); } } public CompletableFuture modifyClusterServerNamespaceSet(String app, String ip, int port, Set set) { if (StringUtil.isBlank(ip) || port <= 0) { return AsyncUtils.newFailedFuture(new IllegalArgumentException("Invalid parameter")); } try { Map params = new HashMap<>(1); params.put("data", JSON.toJSONString(set)); return executeCommand(app, ip, port, MODIFY_CLUSTER_SERVER_NAMESPACE_SET_PATH, params, true) .thenCompose(e -> { if (CommandConstants.MSG_SUCCESS.equals(e)) { return CompletableFuture.completedFuture(null); } else { logger.warn("Error when modifying cluster server NamespaceSet", e); return AsyncUtils.newFailedFuture(new RuntimeException(e)); } }); } catch (Exception ex) { logger.warn("Error when modifying cluster server NamespaceSet", ex); return AsyncUtils.newFailedFuture(ex); } } public CompletableFuture fetchClusterServerBasicInfo(String ip, int port) { if (StringUtil.isBlank(ip) || port <= 0) { return AsyncUtils.newFailedFuture(new IllegalArgumentException("Invalid parameter")); } try { return executeCommand(ip, port, FETCH_CLUSTER_SERVER_BASIC_INFO_PATH, false) .thenApply(r -> JSON.parseObject(r, ClusterServerStateVO.class)); } catch (Exception ex) { logger.warn("Error when fetching cluster sever all config and basic info", ex); return AsyncUtils.newFailedFuture(ex); } } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/config/DashboardConfig.java ================================================ /* * 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 java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.math.NumberUtils; import org.springframework.lang.NonNull; /** *

Dashboard local config support.

*

* Dashboard supports configuration loading by several ways by order:
* 1. System.properties
* 2. Env *

* * @author jason * @since 1.5.0 */ public class DashboardConfig { public static final int DEFAULT_MACHINE_HEALTHY_TIMEOUT_MS = 60_000; /** * Login username */ public static final String CONFIG_AUTH_USERNAME = "sentinel.dashboard.auth.username"; /** * Login password */ public static final String CONFIG_AUTH_PASSWORD = "sentinel.dashboard.auth.password"; /** * Hide application name in sidebar when it has no healthy machines after specific period in millisecond. */ public static final String CONFIG_HIDE_APP_NO_MACHINE_MILLIS = "sentinel.dashboard.app.hideAppNoMachineMillis"; /** * Remove application when it has no healthy machines after specific period in millisecond. */ public static final String CONFIG_REMOVE_APP_NO_MACHINE_MILLIS = "sentinel.dashboard.removeAppNoMachineMillis"; /** * Timeout */ public static final String CONFIG_UNHEALTHY_MACHINE_MILLIS = "sentinel.dashboard.unhealthyMachineMillis"; /** * Auto remove unhealthy machine after specific period in millisecond. */ public static final String CONFIG_AUTO_REMOVE_MACHINE_MILLIS = "sentinel.dashboard.autoRemoveMachineMillis"; private static final ConcurrentMap cacheMap = new ConcurrentHashMap<>(); @NonNull private static String getConfig(String name) { // env String val = System.getenv(name); if (StringUtils.isNotEmpty(val)) { return val; } // properties val = System.getProperty(name); if (StringUtils.isNotEmpty(val)) { return val; } return ""; } protected static String getConfigStr(String name) { if (cacheMap.containsKey(name)) { return (String) cacheMap.get(name); } String val = getConfig(name); if (StringUtils.isBlank(val)) { return null; } cacheMap.put(name, val); return val; } protected static int getConfigInt(String name, int defaultVal, int minVal) { if (cacheMap.containsKey(name)) { return (int)cacheMap.get(name); } int val = NumberUtils.toInt(getConfig(name)); if (val == 0) { val = defaultVal; } else if (val < minVal) { val = minVal; } cacheMap.put(name, val); return val; } public static String getAuthUsername() { return getConfigStr(CONFIG_AUTH_USERNAME); } public static String getAuthPassword() { return getConfigStr(CONFIG_AUTH_PASSWORD); } public static int getHideAppNoMachineMillis() { return getConfigInt(CONFIG_HIDE_APP_NO_MACHINE_MILLIS, 0, 60000); } public static int getRemoveAppNoMachineMillis() { return getConfigInt(CONFIG_REMOVE_APP_NO_MACHINE_MILLIS, 0, 120000); } public static int getAutoRemoveMachineMillis() { return getConfigInt(CONFIG_AUTO_REMOVE_MACHINE_MILLIS, 0, 300000); } public static int getUnhealthyMachineMillis() { return getConfigInt(CONFIG_UNHEALTHY_MACHINE_MILLIS, DEFAULT_MACHINE_HEALTHY_TIMEOUT_MS, 30000); } public static void clearCache() { cacheMap.clear(); } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/config/WebConfig.java ================================================ /* * 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.adapter.servlet.CommonFilter; import com.alibaba.csp.sentinel.dashboard.filter.AuthFilter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import javax.servlet.Filter; /** * @author leyou */ @Configuration public class WebConfig implements WebMvcConfigurer { private final Logger logger = LoggerFactory.getLogger(WebConfig.class); @Autowired private AuthFilter authFilter; @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/**").addResourceLocations("classpath:/resources/"); } @Override public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController("/").setViewName("forward:/index.htm"); } /** * Add {@link CommonFilter} to the server, this is the simplest way to use Sentinel * for Web application. */ @Bean public FilterRegistrationBean sentinelFilterRegistration() { FilterRegistrationBean registration = new FilterRegistrationBean<>(); registration.setFilter(new CommonFilter()); registration.addUrlPatterns("/*"); registration.setName("sentinelFilter"); registration.setOrder(1); logger.info("Sentinel servlet CommonFilter registered"); return registration; } @Bean public FilterRegistrationBean authenticationFilterRegistration() { FilterRegistrationBean registration = new FilterRegistrationBean<>(); registration.setFilter(authFilter); registration.addUrlPatterns("/*"); registration.setName("authenticationFilter"); registration.setOrder(0); return registration; } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/AppController.java ================================================ /* * 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.controller; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; import javax.servlet.http.HttpServletRequest; import com.alibaba.csp.sentinel.dashboard.discovery.AppInfo; import com.alibaba.csp.sentinel.dashboard.discovery.AppManagement; import com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo; import com.alibaba.csp.sentinel.dashboard.domain.Result; import com.alibaba.csp.sentinel.dashboard.domain.vo.MachineInfoVo; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; /** * @author Carpenter Lee */ @RestController @RequestMapping(value = "/app") public class AppController { @Autowired private AppManagement appManagement; @GetMapping("/names.json") public Result> queryApps(HttpServletRequest request) { return Result.ofSuccess(appManagement.getAppNames()); } @GetMapping("/briefinfos.json") public Result> queryAppInfos(HttpServletRequest request) { List list = new ArrayList<>(appManagement.getBriefApps()); Collections.sort(list, Comparator.comparing(AppInfo::getApp)); return Result.ofSuccess(list); } @GetMapping(value = "/{app}/machines.json") public Result> getMachinesByApp(@PathVariable("app") String app) { AppInfo appInfo = appManagement.getDetailApp(app); if (appInfo == null) { return Result.ofSuccess(null); } List list = new ArrayList<>(appInfo.getMachines()); Collections.sort(list, (o1, o2) -> { int t = o1.getApp().compareTo(o2.getApp()); if (t != 0) { return t; } t = o1.getIp().compareTo(o2.getIp()); if (t != 0) { return t; } return o1.getPort().compareTo(o2.getPort()); }); return Result.ofSuccess(MachineInfoVo.fromMachineInfoList(list)); } @RequestMapping(value = "/{app}/machine/remove.json") public Result removeMachineById( @PathVariable("app") String app, @RequestParam(name = "ip") String ip, @RequestParam(name = "port") int port) { AppInfo appInfo = appManagement.getDetailApp(app); if (appInfo == null) { return Result.ofSuccess(null); } if (appManagement.removeMachine(app, ip, port)) { return Result.ofSuccessMsg("success"); } else { return Result.ofFail(1, "remove failed"); } } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/AuthController.java ================================================ /* * 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.controller; import com.alibaba.csp.sentinel.dashboard.auth.AuthService; import com.alibaba.csp.sentinel.dashboard.auth.SimpleWebAuthServiceImpl; import com.alibaba.csp.sentinel.dashboard.config.DashboardConfig; import com.alibaba.csp.sentinel.dashboard.domain.Result; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; import javax.servlet.http.HttpServletRequest; /** * @author cdfive * @since 1.6.0 */ @RestController @RequestMapping("/auth") public class AuthController { private static Logger LOGGER = LoggerFactory.getLogger(AuthController.class); @Value("${auth.username:sentinel}") private String authUsername; @Value("${auth.password:sentinel}") private String authPassword; @PostMapping("/login") public Result login(HttpServletRequest request, String username, String password) { if (StringUtils.isNotBlank(DashboardConfig.getAuthUsername())) { authUsername = DashboardConfig.getAuthUsername(); } if (StringUtils.isNotBlank(DashboardConfig.getAuthPassword())) { authPassword = DashboardConfig.getAuthPassword(); } /* * If auth.username or auth.password is blank(set in application.properties or VM arguments), * auth will pass, as the front side validate the input which can't be blank, * so user can input any username or password(both are not blank) to login in that case. */ if (StringUtils.isNotBlank(authUsername) && !authUsername.equals(username) || StringUtils.isNotBlank(authPassword) && !authPassword.equals(password)) { LOGGER.error("Login failed: Invalid username or password, username=" + username + ", password=" + password); return Result.ofFail(-1, "Invalid username or password"); } AuthService.AuthUser authUser = new SimpleWebAuthServiceImpl.SimpleWebAuthUserImpl(username); request.getSession().setAttribute(SimpleWebAuthServiceImpl.WEB_SESSTION_KEY, authUser); return Result.ofSuccess(authUser); } @RequestMapping(value = "/logout", method = RequestMethod.POST) public Result logout(HttpServletRequest request) { request.getSession().invalidate(); return Result.ofSuccess(null); } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/AuthorityRuleController.java ================================================ /* * 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.controller; import java.util.Date; import java.util.List; import javax.servlet.http.HttpServletRequest; import com.alibaba.csp.sentinel.dashboard.client.SentinelApiClient; import com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo; import com.alibaba.csp.sentinel.dashboard.auth.AuthService; import com.alibaba.csp.sentinel.dashboard.auth.AuthService.AuthUser; import com.alibaba.csp.sentinel.dashboard.auth.AuthService.PrivilegeType; import com.alibaba.csp.sentinel.slots.block.RuleConstant; import com.alibaba.csp.sentinel.util.StringUtil; import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.AuthorityRuleEntity; import com.alibaba.csp.sentinel.dashboard.domain.Result; import com.alibaba.csp.sentinel.dashboard.repository.rule.RuleRepository; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; /** * @author Eric Zhao * @since 0.2.1 */ @RestController @RequestMapping(value = "/authority") public class AuthorityRuleController { private final Logger logger = LoggerFactory.getLogger(AuthorityRuleController.class); @Autowired private SentinelApiClient sentinelApiClient; @Autowired private RuleRepository repository; @Autowired private AuthService authService; @GetMapping("/rules") public Result> apiQueryAllRulesForMachine(HttpServletRequest request, @RequestParam String app, @RequestParam String ip, @RequestParam Integer port) { AuthUser authUser = authService.getAuthUser(request); authUser.authTarget(app, PrivilegeType.READ_RULE); if (StringUtil.isEmpty(app)) { return Result.ofFail(-1, "app cannot be null or empty"); } if (StringUtil.isEmpty(ip)) { return Result.ofFail(-1, "ip cannot be null or empty"); } if (port == null || port <= 0) { return Result.ofFail(-1, "Invalid parameter: port"); } try { List rules = sentinelApiClient.fetchAuthorityRulesOfMachine(app, ip, port); rules = repository.saveAll(rules); return Result.ofSuccess(rules); } catch (Throwable throwable) { logger.error("Error when querying authority rules", throwable); return Result.ofFail(-1, throwable.getMessage()); } } private Result checkEntityInternal(AuthorityRuleEntity entity) { if (entity == null) { return Result.ofFail(-1, "bad rule body"); } if (StringUtil.isBlank(entity.getApp())) { return Result.ofFail(-1, "app can't be null or empty"); } if (StringUtil.isBlank(entity.getIp())) { return Result.ofFail(-1, "ip can't be null or empty"); } if (entity.getPort() == null || entity.getPort() <= 0) { return Result.ofFail(-1, "port can't be null"); } if (entity.getRule() == null) { return Result.ofFail(-1, "rule can't be null"); } if (StringUtil.isBlank(entity.getResource())) { return Result.ofFail(-1, "resource name cannot be null or empty"); } if (StringUtil.isBlank(entity.getLimitApp())) { return Result.ofFail(-1, "limitApp should be valid"); } if (entity.getStrategy() != RuleConstant.AUTHORITY_WHITE && entity.getStrategy() != RuleConstant.AUTHORITY_BLACK) { return Result.ofFail(-1, "Unknown strategy (must be blacklist or whitelist)"); } return null; } @PostMapping("/rule") public Result apiAddAuthorityRule(HttpServletRequest request, @RequestBody AuthorityRuleEntity entity) { AuthUser authUser = authService.getAuthUser(request); authUser.authTarget(entity.getApp(), PrivilegeType.WRITE_RULE); Result checkResult = checkEntityInternal(entity); if (checkResult != null) { return checkResult; } entity.setId(null); Date date = new Date(); entity.setGmtCreate(date); entity.setGmtModified(date); try { entity = repository.save(entity); } catch (Throwable throwable) { logger.error("Failed to add authority rule", throwable); return Result.ofThrowable(-1, throwable); } if (!publishRules(entity.getApp(), entity.getIp(), entity.getPort())) { logger.info("Publish authority rules failed after rule add"); } return Result.ofSuccess(entity); } @PutMapping("/rule/{id}") public Result apiUpdateParamFlowRule(HttpServletRequest request, @PathVariable("id") Long id, @RequestBody AuthorityRuleEntity entity) { AuthUser authUser = authService.getAuthUser(request); authUser.authTarget(entity.getApp(), PrivilegeType.WRITE_RULE); if (id == null || id <= 0) { return Result.ofFail(-1, "Invalid id"); } Result checkResult = checkEntityInternal(entity); if (checkResult != null) { return checkResult; } entity.setId(id); Date date = new Date(); entity.setGmtCreate(null); entity.setGmtModified(date); try { entity = repository.save(entity); if (entity == null) { return Result.ofFail(-1, "Failed to save authority rule"); } } catch (Throwable throwable) { logger.error("Failed to save authority rule", throwable); return Result.ofThrowable(-1, throwable); } if (!publishRules(entity.getApp(), entity.getIp(), entity.getPort())) { logger.info("Publish authority rules failed after rule update"); } return Result.ofSuccess(entity); } @DeleteMapping("/rule/{id}") public Result apiDeleteRule(HttpServletRequest request, @PathVariable("id") Long id) { AuthUser authUser = authService.getAuthUser(request); if (id == null) { return Result.ofFail(-1, "id cannot be null"); } AuthorityRuleEntity oldEntity = repository.findById(id); if (oldEntity == null) { return Result.ofSuccess(null); } authUser.authTarget(oldEntity.getApp(), PrivilegeType.DELETE_RULE); try { repository.delete(id); } catch (Exception e) { return Result.ofFail(-1, e.getMessage()); } if (!publishRules(oldEntity.getApp(), oldEntity.getIp(), oldEntity.getPort())) { logger.error("Publish authority rules failed after rule delete"); } return Result.ofSuccess(id); } private boolean publishRules(String app, String ip, Integer port) { List rules = repository.findAllByMachine(MachineInfo.of(app, ip, port)); return sentinelApiClient.setAuthorityRuleOfMachine(app, ip, port, rules); } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/DegradeController.java ================================================ /* * 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.controller; import java.util.Date; import java.util.List; import javax.servlet.http.HttpServletRequest; import com.alibaba.csp.sentinel.dashboard.client.SentinelApiClient; import com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo; import com.alibaba.csp.sentinel.dashboard.auth.AuthService; import com.alibaba.csp.sentinel.dashboard.auth.AuthService.AuthUser; import com.alibaba.csp.sentinel.dashboard.auth.AuthService.PrivilegeType; import com.alibaba.csp.sentinel.slots.block.RuleConstant; import com.alibaba.csp.sentinel.util.StringUtil; import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.DegradeRuleEntity; import com.alibaba.csp.sentinel.dashboard.domain.Result; import com.alibaba.csp.sentinel.dashboard.repository.rule.InMemDegradeRuleStore; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; /** * @author leyou */ @Controller @RequestMapping(value = "/degrade", produces = MediaType.APPLICATION_JSON_VALUE) public class DegradeController { private final Logger logger = LoggerFactory.getLogger(DegradeController.class); @Autowired private InMemDegradeRuleStore repository; @Autowired private SentinelApiClient sentinelApiClient; @Autowired private AuthService authService; @ResponseBody @RequestMapping("/rules.json") public Result> queryMachineRules(HttpServletRequest request, String app, String ip, Integer port) { AuthUser authUser = authService.getAuthUser(request); authUser.authTarget(app, PrivilegeType.READ_RULE); if (StringUtil.isEmpty(app)) { return Result.ofFail(-1, "app can't be null or empty"); } if (StringUtil.isEmpty(ip)) { return Result.ofFail(-1, "ip can't be null or empty"); } if (port == null) { return Result.ofFail(-1, "port can't be null"); } try { List rules = sentinelApiClient.fetchDegradeRuleOfMachine(app, ip, port); rules = repository.saveAll(rules); return Result.ofSuccess(rules); } catch (Throwable throwable) { logger.error("queryApps error:", throwable); return Result.ofThrowable(-1, throwable); } } @ResponseBody @RequestMapping("/new.json") public Result add(HttpServletRequest request, String app, String ip, Integer port, String limitApp, String resource, Double count, Integer timeWindow, Integer grade) { AuthUser authUser = authService.getAuthUser(request); authUser.authTarget(app, PrivilegeType.WRITE_RULE); if (StringUtil.isBlank(app)) { return Result.ofFail(-1, "app can't be null or empty"); } if (StringUtil.isBlank(ip)) { return Result.ofFail(-1, "ip can't be null or empty"); } if (port == null) { return Result.ofFail(-1, "port can't be null"); } if (StringUtil.isBlank(limitApp)) { return Result.ofFail(-1, "limitApp can't be null or empty"); } if (StringUtil.isBlank(resource)) { return Result.ofFail(-1, "resource can't be null or empty"); } if (count == null) { return Result.ofFail(-1, "count can't be null"); } if (timeWindow == null) { return Result.ofFail(-1, "timeWindow can't be null"); } if (grade == null) { return Result.ofFail(-1, "grade can't be null"); } if (grade < RuleConstant.DEGRADE_GRADE_RT || grade > RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT) { return Result.ofFail(-1, "Invalid grade: " + grade); } DegradeRuleEntity entity = new DegradeRuleEntity(); entity.setApp(app.trim()); entity.setIp(ip.trim()); entity.setPort(port); entity.setLimitApp(limitApp.trim()); entity.setResource(resource.trim()); entity.setCount(count); entity.setTimeWindow(timeWindow); entity.setGrade(grade); Date date = new Date(); entity.setGmtCreate(date); entity.setGmtModified(date); try { entity = repository.save(entity); } catch (Throwable throwable) { logger.error("add error:", throwable); return Result.ofThrowable(-1, throwable); } if (!publishRules(app, ip, port)) { logger.info("publish degrade rules fail after rule add"); } return Result.ofSuccess(entity); } @ResponseBody @RequestMapping("/save.json") public Result updateIfNotNull(HttpServletRequest request, Long id, String app, String limitApp, String resource, Double count, Integer timeWindow, Integer grade) { AuthUser authUser = authService.getAuthUser(request); if (id == null) { return Result.ofFail(-1, "id can't be null"); } if (grade != null) { if (grade < RuleConstant.DEGRADE_GRADE_RT || grade > RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT) { return Result.ofFail(-1, "Invalid grade: " + grade); } } DegradeRuleEntity entity = repository.findById(id); if (entity == null) { return Result.ofFail(-1, "id " + id + " dose not exist"); } authUser.authTarget(entity.getApp(), PrivilegeType.WRITE_RULE); if (StringUtil.isNotBlank(app)) { entity.setApp(app.trim()); } if (StringUtil.isNotBlank(limitApp)) { entity.setLimitApp(limitApp.trim()); } if (StringUtil.isNotBlank(resource)) { entity.setResource(resource.trim()); } if (count != null) { entity.setCount(count); } if (timeWindow != null) { entity.setTimeWindow(timeWindow); } if (grade != null) { entity.setGrade(grade); } Date date = new Date(); entity.setGmtModified(date); try { entity = repository.save(entity); } catch (Throwable throwable) { logger.error("save error:", throwable); return Result.ofThrowable(-1, throwable); } if (!publishRules(entity.getApp(), entity.getIp(), entity.getPort())) { logger.info("publish degrade rules fail after rule update"); } return Result.ofSuccess(entity); } @ResponseBody @RequestMapping("/delete.json") public Result delete(HttpServletRequest request, Long id) { AuthUser authUser = authService.getAuthUser(request); if (id == null) { return Result.ofFail(-1, "id can't be null"); } DegradeRuleEntity oldEntity = repository.findById(id); if (oldEntity == null) { return Result.ofSuccess(null); } authUser.authTarget(oldEntity.getApp(), PrivilegeType.DELETE_RULE); try { repository.delete(id); } catch (Throwable throwable) { logger.error("delete error:", throwable); return Result.ofThrowable(-1, throwable); } if (!publishRules(oldEntity.getApp(), oldEntity.getIp(), oldEntity.getPort())) { logger.info("publish degrade rules fail after rule delete"); } return Result.ofSuccess(id); } private boolean publishRules(String app, String ip, Integer port) { List rules = repository.findAllByMachine(MachineInfo.of(app, ip, port)); return sentinelApiClient.setDegradeRuleOfMachine(app, ip, port, rules); } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/DemoController.java ================================================ /* * 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.controller; import java.util.Random; import java.util.concurrent.TimeUnit; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.MediaType; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import com.alibaba.csp.sentinel.Entry; import com.alibaba.csp.sentinel.EntryType; import com.alibaba.csp.sentinel.SphU; import com.alibaba.csp.sentinel.context.ContextUtil; import com.alibaba.csp.sentinel.slots.block.BlockException; @Controller @RequestMapping(value = "/demo", produces = MediaType.APPLICATION_JSON_VALUE) public class DemoController { Logger logger = LoggerFactory.getLogger(MachineRegistryController.class); @RequestMapping("/greeting") public String greeting() { return "index"; } @RequestMapping("/link") @ResponseBody public String link() throws BlockException { Entry entry = SphU.entry("head1", EntryType.IN); Entry entry1 = SphU.entry("head2", EntryType.IN); Entry entry2 = SphU.entry("head3", EntryType.IN); Entry entry3 = SphU.entry("head4", EntryType.IN); entry3.exit(); entry2.exit(); entry1.exit(); entry.exit(); return "successfully create a call link"; } @RequestMapping("/loop") @ResponseBody public String loop(String name, int time) throws BlockException { for (int i = 0; i < 10; i++) { Thread timer = new Thread(new RunTask(name, time, false)); timer.setName("false"); timer.start(); } return "successfully create a loop thread"; } @RequestMapping("/slow") @ResponseBody public String slow(String name, int time) throws BlockException { for (int i = 0; i < 10; i++) { Thread timer = new Thread(new RunTask(name, time, true)); timer.setName("false"); timer.start(); } return "successfully create a loop thread"; } static class RunTask implements Runnable { int time; boolean stop = false; String name; boolean slow = false; public RunTask(String name, int time, boolean slow) { super(); this.time = time; this.name = name; this.slow = slow; } @Override public void run() { long startTime = System.currentTimeMillis(); ContextUtil.enter(String.valueOf(startTime)); while (!stop) { long now = System.currentTimeMillis(); if (now - startTime > time * 1000) { stop = true; } Entry e1 = null; try { e1 = SphU.entry(name); if (slow == true) { TimeUnit.MILLISECONDS.sleep(3000); } } catch (Exception e) { } finally { if (e1 != null) { e1.exit(); } } Random random2 = new Random(); try { TimeUnit.MILLISECONDS.sleep(random2.nextInt(200)); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } ContextUtil.exit(); } } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/FlowControllerV1.java ================================================ /* * 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.controller; import java.util.Date; import java.util.List; import javax.servlet.http.HttpServletRequest; import com.alibaba.csp.sentinel.dashboard.auth.AuthService; import com.alibaba.csp.sentinel.dashboard.auth.AuthService.AuthUser; import com.alibaba.csp.sentinel.dashboard.auth.AuthService.PrivilegeType; import com.alibaba.csp.sentinel.util.StringUtil; import com.alibaba.csp.sentinel.dashboard.client.SentinelApiClient; import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity; import com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo; import com.alibaba.csp.sentinel.dashboard.domain.Result; import com.alibaba.csp.sentinel.dashboard.repository.rule.InMemoryRuleRepositoryAdapter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; /** * Flow rule controller. * * @author leyou * @author Eric Zhao */ @RestController @RequestMapping(value = "/v1/flow") public class FlowControllerV1 { private final Logger logger = LoggerFactory.getLogger(FlowControllerV1.class); @Autowired private InMemoryRuleRepositoryAdapter repository; @Autowired private AuthService authService; @Autowired private SentinelApiClient sentinelApiClient; @GetMapping("/rules") public Result> apiQueryMachineRules(HttpServletRequest request, @RequestParam String app, @RequestParam String ip, @RequestParam Integer port) { AuthUser authUser = authService.getAuthUser(request); authUser.authTarget(app, PrivilegeType.READ_RULE); if (StringUtil.isEmpty(app)) { return Result.ofFail(-1, "app can't be null or empty"); } if (StringUtil.isEmpty(ip)) { return Result.ofFail(-1, "ip can't be null or empty"); } if (port == null) { return Result.ofFail(-1, "port can't be null"); } try { List rules = sentinelApiClient.fetchFlowRuleOfMachine(app, ip, port); rules = repository.saveAll(rules); return Result.ofSuccess(rules); } catch (Throwable throwable) { logger.error("Error when querying flow rules", throwable); return Result.ofThrowable(-1, throwable); } } private Result checkEntityInternal(FlowRuleEntity entity) { if (StringUtil.isBlank(entity.getApp())) { return Result.ofFail(-1, "app can't be null or empty"); } if (StringUtil.isBlank(entity.getIp())) { return Result.ofFail(-1, "ip can't be null or empty"); } if (entity.getPort() == null) { return Result.ofFail(-1, "port can't be null"); } if (StringUtil.isBlank(entity.getLimitApp())) { return Result.ofFail(-1, "limitApp can't be null or empty"); } if (StringUtil.isBlank(entity.getResource())) { return Result.ofFail(-1, "resource can't be null or empty"); } if (entity.getGrade() == null) { return Result.ofFail(-1, "grade can't be null"); } if (entity.getGrade() != 0 && entity.getGrade() != 1) { return Result.ofFail(-1, "grade must be 0 or 1, but " + entity.getGrade() + " got"); } if (entity.getCount() == null || entity.getCount() < 0) { return Result.ofFail(-1, "count should be at lease zero"); } if (entity.getStrategy() == null) { return Result.ofFail(-1, "strategy can't be null"); } if (entity.getStrategy() != 0 && StringUtil.isBlank(entity.getRefResource())) { return Result.ofFail(-1, "refResource can't be null or empty when strategy!=0"); } if (entity.getControlBehavior() == null) { return Result.ofFail(-1, "controlBehavior can't be null"); } int controlBehavior = entity.getControlBehavior(); if (controlBehavior == 1 && entity.getWarmUpPeriodSec() == null) { return Result.ofFail(-1, "warmUpPeriodSec can't be null when controlBehavior==1"); } if (controlBehavior == 2 && entity.getMaxQueueingTimeMs() == null) { return Result.ofFail(-1, "maxQueueingTimeMs can't be null when controlBehavior==2"); } if (entity.isClusterMode() && entity.getClusterConfig() == null) { return Result.ofFail(-1, "cluster config should be valid"); } return null; } @PostMapping("/rule") public Result apiAddFlowRule(HttpServletRequest request, @RequestBody FlowRuleEntity entity) { AuthUser authUser = authService.getAuthUser(request); authUser.authTarget(entity.getApp(), PrivilegeType.WRITE_RULE); Result checkResult = checkEntityInternal(entity); if (checkResult != null) { return checkResult; } entity.setId(null); Date date = new Date(); entity.setGmtCreate(date); entity.setGmtModified(date); entity.setLimitApp(entity.getLimitApp().trim()); entity.setResource(entity.getResource().trim()); try { entity = repository.save(entity); } catch (Throwable throwable) { logger.error("Failed to add flow rule", throwable); return Result.ofThrowable(-1, throwable); } if (!publishRules(entity.getApp(), entity.getIp(), entity.getPort())) { logger.error("Publish flow rules failed after rule add"); } return Result.ofSuccess(entity); } @PutMapping("/save.json") public Result updateIfNotNull(HttpServletRequest request, Long id, String app, String limitApp, String resource, Integer grade, Double count, Integer strategy, String refResource, Integer controlBehavior, Integer warmUpPeriodSec, Integer maxQueueingTimeMs) { AuthUser authUser = authService.getAuthUser(request); authUser.authTarget(app, PrivilegeType.WRITE_RULE); if (id == null) { return Result.ofFail(-1, "id can't be null"); } FlowRuleEntity entity = repository.findById(id); if (entity == null) { return Result.ofFail(-1, "id " + id + " dose not exist"); } if (StringUtil.isNotBlank(app)) { entity.setApp(app.trim()); } if (StringUtil.isNotBlank(limitApp)) { entity.setLimitApp(limitApp.trim()); } if (StringUtil.isNotBlank(resource)) { entity.setResource(resource.trim()); } if (grade != null) { if (grade != 0 && grade != 1) { return Result.ofFail(-1, "grade must be 0 or 1, but " + grade + " got"); } entity.setGrade(grade); } if (count != null) { entity.setCount(count); } if (strategy != null) { if (strategy != 0 && strategy != 1 && strategy != 2) { return Result.ofFail(-1, "strategy must be in [0, 1, 2], but " + strategy + " got"); } entity.setStrategy(strategy); if (strategy != 0) { if (StringUtil.isBlank(refResource)) { return Result.ofFail(-1, "refResource can't be null or empty when strategy!=0"); } entity.setRefResource(refResource.trim()); } } if (controlBehavior != null) { if (controlBehavior != 0 && controlBehavior != 1 && controlBehavior != 2) { return Result.ofFail(-1, "controlBehavior must be in [0, 1, 2], but " + controlBehavior + " got"); } if (controlBehavior == 1 && warmUpPeriodSec == null) { return Result.ofFail(-1, "warmUpPeriodSec can't be null when controlBehavior==1"); } if (controlBehavior == 2 && maxQueueingTimeMs == null) { return Result.ofFail(-1, "maxQueueingTimeMs can't be null when controlBehavior==2"); } entity.setControlBehavior(controlBehavior); if (warmUpPeriodSec != null) { entity.setWarmUpPeriodSec(warmUpPeriodSec); } if (maxQueueingTimeMs != null) { entity.setMaxQueueingTimeMs(maxQueueingTimeMs); } } Date date = new Date(); entity.setGmtModified(date); try { entity = repository.save(entity); if (entity == null) { return Result.ofFail(-1, "save entity fail"); } } catch (Throwable throwable) { logger.error("save error:", throwable); return Result.ofThrowable(-1, throwable); } if (!publishRules(entity.getApp(), entity.getIp(), entity.getPort())) { logger.info("publish flow rules fail after rule update"); } return Result.ofSuccess(entity); } @DeleteMapping("/delete.json") public Result delete(HttpServletRequest request, Long id) { AuthUser authUser = authService.getAuthUser(request); if (id == null) { return Result.ofFail(-1, "id can't be null"); } FlowRuleEntity oldEntity = repository.findById(id); if (oldEntity == null) { return Result.ofSuccess(null); } authUser.authTarget(oldEntity.getApp(), PrivilegeType.DELETE_RULE); try { repository.delete(id); } catch (Exception e) { return Result.ofFail(-1, e.getMessage()); } if (!publishRules(oldEntity.getApp(), oldEntity.getIp(), oldEntity.getPort())) { logger.info("publish flow rules fail after rule delete"); } return Result.ofSuccess(id); } private boolean publishRules(String app, String ip, Integer port) { List rules = repository.findAllByMachine(MachineInfo.of(app, ip, port)); return sentinelApiClient.setFlowRuleOfMachine(app, ip, port, rules); } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/MachineRegistryController.java ================================================ /* * 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.controller; import com.alibaba.csp.sentinel.dashboard.discovery.AppManagement; import com.alibaba.csp.sentinel.util.StringUtil; 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 org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; @Controller @RequestMapping(value = "/registry", produces = MediaType.APPLICATION_JSON_VALUE) public class MachineRegistryController { private final Logger logger = LoggerFactory.getLogger(MachineRegistryController.class); @Autowired private AppManagement appManagement; @ResponseBody @RequestMapping("/machine") public Result receiveHeartBeat(String app, Long version, String v, String hostname, String ip, Integer port) { if (app == null) { app = MachineDiscovery.UNKNOWN_APP_NAME; } if (ip == null) { return Result.ofFail(-1, "ip can't be null"); } if (port == null) { return Result.ofFail(-1, "port can't be null"); } if (port == -1) { logger.info("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; version = version == null ? System.currentTimeMillis() : version; try { MachineInfo machineInfo = new MachineInfo(); machineInfo.setApp(app); machineInfo.setHostname(hostname); machineInfo.setIp(ip); machineInfo.setPort(port); machineInfo.setHeartbeatVersion(version); machineInfo.setLastHeartbeat(System.currentTimeMillis()); machineInfo.setVersion(sentinelVersion); appManagement.addMachine(machineInfo); return Result.ofSuccessMsg("success"); } catch (Exception e) { logger.error("Receive heartbeat error", e); return Result.ofFail(-1, e.getMessage()); } } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/MetricController.java ================================================ /* * 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.controller; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.TreeMap; import java.util.concurrent.ConcurrentHashMap; import com.alibaba.csp.sentinel.dashboard.domain.Result; import com.alibaba.csp.sentinel.dashboard.repository.metric.MetricsRepository; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import com.alibaba.csp.sentinel.util.StringUtil; import com.alibaba.csp.sentinel.dashboard.datasource.entity.MetricEntity; import com.alibaba.csp.sentinel.dashboard.domain.vo.MetricVo; /** * @author leyou */ @Controller @RequestMapping(value = "/metric", produces = MediaType.APPLICATION_JSON_VALUE) public class MetricController { private static Logger logger = LoggerFactory.getLogger(MetricController.class); private static final long maxQueryIntervalMs = 1000 * 60 * 60; @Autowired private MetricsRepository metricStore; @ResponseBody @RequestMapping("/queryTopResourceMetric.json") public Result queryTopResourceMetric(final String app, Integer pageIndex, Integer pageSize, Boolean desc, Long startTime, Long endTime, String searchKey) { if (StringUtil.isEmpty(app)) { return Result.ofFail(-1, "app can't be null or empty"); } if (pageIndex == null || pageIndex <= 0) { pageIndex = 1; } if (pageSize == null) { pageSize = 6; } if (pageSize >= 20) { pageSize = 20; } if (desc == null) { desc = true; } if (endTime == null) { endTime = System.currentTimeMillis(); } if (startTime == null) { startTime = endTime - 1000 * 60 * 5; } if (endTime - startTime > maxQueryIntervalMs) { return Result.ofFail(-1, "time intervalMs is too big, must <= 1h"); } List resources = metricStore.listResourcesOfApp(app); logger.debug("queryTopResourceMetric(), resources.size()={}", resources.size()); if (resources == null || resources.isEmpty()) { return Result.ofSuccess(null); } if (!desc) { Collections.reverse(resources); } if (StringUtil.isNotEmpty(searchKey)) { List searched = new ArrayList<>(); for (String resource : resources) { if (resource.contains(searchKey)) { searched.add(resource); } } resources = searched; } int totalPage = (resources.size() + pageSize - 1) / pageSize; List topResource = new ArrayList<>(); if (pageIndex <= totalPage) { topResource = resources.subList((pageIndex - 1) * pageSize, Math.min(pageIndex * pageSize, resources.size())); } final Map> map = new ConcurrentHashMap<>(); logger.debug("topResource={}", topResource); long time = System.currentTimeMillis(); for (final String resource : topResource) { List entities = metricStore.queryByAppAndResourceBetween( app, resource, startTime, endTime); logger.debug("resource={}, entities.size()={}", resource, entities == null ? "null" : entities.size()); List vos = MetricVo.fromMetricEntities(entities, resource); Iterable vosSorted = sortMetricVoAndDistinct(vos); map.put(resource, vosSorted); } logger.debug("queryTopResourceMetric() total query time={} ms", System.currentTimeMillis() - time); Map resultMap = new HashMap<>(16); resultMap.put("totalCount", resources.size()); resultMap.put("totalPage", totalPage); resultMap.put("pageIndex", pageIndex); resultMap.put("pageSize", pageSize); Map> map2 = new LinkedHashMap<>(); // order matters. for (String identity : topResource) { map2.put(identity, map.get(identity)); } resultMap.put("metric", map2); return Result.ofSuccess(resultMap); } @ResponseBody @RequestMapping("/queryByAppAndResource.json") public Result queryByAppAndResource(String app, String identity, Long startTime, Long endTime) { if (StringUtil.isEmpty(app)) { return Result.ofFail(-1, "app can't be null or empty"); } if (StringUtil.isEmpty(identity)) { return Result.ofFail(-1, "identity can't be null or empty"); } if (endTime == null) { endTime = System.currentTimeMillis(); } if (startTime == null) { startTime = endTime - 1000 * 60; } if (endTime - startTime > maxQueryIntervalMs) { return Result.ofFail(-1, "time intervalMs is too big, must <= 1h"); } List entities = metricStore.queryByAppAndResourceBetween( app, identity, startTime, endTime); List vos = MetricVo.fromMetricEntities(entities, identity); return Result.ofSuccess(sortMetricVoAndDistinct(vos)); } private Iterable sortMetricVoAndDistinct(List vos) { if (vos == null) { return null; } Map map = new TreeMap<>(); for (MetricVo vo : vos) { MetricVo oldVo = map.get(vo.getTimestamp()); if (oldVo == null || vo.getGmtCreate() > oldVo.getGmtCreate()) { map.put(vo.getTimestamp(), vo); } } return map.values(); } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/ParamFlowRuleController.java ================================================ /* * 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.controller; import java.util.Date; import java.util.List; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import javax.servlet.http.HttpServletRequest; import com.alibaba.csp.sentinel.dashboard.client.CommandNotFoundException; import com.alibaba.csp.sentinel.dashboard.client.SentinelApiClient; import com.alibaba.csp.sentinel.dashboard.discovery.AppManagement; import com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo; import com.alibaba.csp.sentinel.dashboard.auth.AuthService; import com.alibaba.csp.sentinel.dashboard.auth.AuthService.AuthUser; import com.alibaba.csp.sentinel.dashboard.auth.AuthService.PrivilegeType; import com.alibaba.csp.sentinel.slots.block.RuleConstant; import com.alibaba.csp.sentinel.util.StringUtil; import com.alibaba.csp.sentinel.dashboard.datasource.entity.SentinelVersion; import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.ParamFlowRuleEntity; import com.alibaba.csp.sentinel.dashboard.domain.Result; import com.alibaba.csp.sentinel.dashboard.repository.rule.RuleRepository; import com.alibaba.csp.sentinel.dashboard.util.VersionUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; /** * @author Eric Zhao * @since 0.2.1 */ @RestController @RequestMapping(value = "/paramFlow") public class ParamFlowRuleController { private final Logger logger = LoggerFactory.getLogger(ParamFlowRuleController.class); @Autowired private SentinelApiClient sentinelApiClient; @Autowired private AppManagement appManagement; @Autowired private RuleRepository repository; @Autowired private AuthService authService; private boolean checkIfSupported(String app, String ip, int port) { try { return Optional.ofNullable(appManagement.getDetailApp(app)) .flatMap(e -> e.getMachine(ip, port)) .flatMap(m -> VersionUtils.parseVersion(m.getVersion()) .map(v -> v.greaterOrEqual(version020))) .orElse(true); // If error occurred or cannot retrieve machine info, return true. } catch (Exception ex) { return true; } } @GetMapping("/rules") public Result> apiQueryAllRulesForMachine(HttpServletRequest request, @RequestParam String app, @RequestParam String ip, @RequestParam Integer port) { AuthUser authUser = authService.getAuthUser(request); authUser.authTarget(app, PrivilegeType.READ_RULE); if (StringUtil.isEmpty(app)) { return Result.ofFail(-1, "app cannot be null or empty"); } if (StringUtil.isEmpty(ip)) { return Result.ofFail(-1, "ip cannot be null or empty"); } if (port == null || port <= 0) { return Result.ofFail(-1, "Invalid parameter: port"); } if (!checkIfSupported(app, ip, port)) { return unsupportedVersion(); } try { return sentinelApiClient.fetchParamFlowRulesOfMachine(app, ip, port) .thenApply(repository::saveAll) .thenApply(Result::ofSuccess) .get(); } catch (ExecutionException ex) { logger.error("Error when querying parameter flow rules", ex.getCause()); if (isNotSupported(ex.getCause())) { return unsupportedVersion(); } else { return Result.ofThrowable(-1, ex.getCause()); } } catch (Throwable throwable) { logger.error("Error when querying parameter flow rules", throwable); return Result.ofFail(-1, throwable.getMessage()); } } private boolean isNotSupported(Throwable ex) { return ex instanceof CommandNotFoundException; } @PostMapping("/rule") public Result apiAddParamFlowRule(HttpServletRequest request, @RequestBody ParamFlowRuleEntity entity) { AuthUser authUser = authService.getAuthUser(request); authUser.authTarget(entity.getApp(), PrivilegeType.WRITE_RULE); Result checkResult = checkEntityInternal(entity); if (checkResult != null) { return checkResult; } if (!checkIfSupported(entity.getApp(), entity.getIp(), entity.getPort())) { return unsupportedVersion(); } entity.setId(null); entity.getRule().setResource(entity.getResource().trim()); Date date = new Date(); entity.setGmtCreate(date); entity.setGmtModified(date); try { entity = repository.save(entity); publishRules(entity.getApp(), entity.getIp(), entity.getPort()).get(); return Result.ofSuccess(entity); } catch (ExecutionException ex) { logger.error("Error when adding new parameter flow rules", ex.getCause()); if (isNotSupported(ex.getCause())) { return unsupportedVersion(); } else { return Result.ofThrowable(-1, ex.getCause()); } } catch (Throwable throwable) { logger.error("Error when adding new parameter flow rules", throwable); return Result.ofFail(-1, throwable.getMessage()); } } private Result checkEntityInternal(ParamFlowRuleEntity entity) { if (entity == null) { return Result.ofFail(-1, "bad rule body"); } if (StringUtil.isBlank(entity.getApp())) { return Result.ofFail(-1, "app can't be null or empty"); } if (StringUtil.isBlank(entity.getIp())) { return Result.ofFail(-1, "ip can't be null or empty"); } if (entity.getPort() == null || entity.getPort() <= 0) { return Result.ofFail(-1, "port can't be null"); } if (entity.getRule() == null) { return Result.ofFail(-1, "rule can't be null"); } if (StringUtil.isBlank(entity.getResource())) { return Result.ofFail(-1, "resource name cannot be null or empty"); } if (entity.getCount() < 0) { return Result.ofFail(-1, "count should be valid"); } if (entity.getGrade() != RuleConstant.FLOW_GRADE_QPS) { return Result.ofFail(-1, "Unknown mode (blockGrade) for parameter flow control"); } if (entity.getParamIdx() == null || entity.getParamIdx() < 0) { return Result.ofFail(-1, "paramIdx should be valid"); } return null; } @PutMapping("/rule/{id}") public Result apiUpdateParamFlowRule(HttpServletRequest request, @PathVariable("id") Long id, @RequestBody ParamFlowRuleEntity entity) { AuthUser authUser = authService.getAuthUser(request); if (id == null || id <= 0) { return Result.ofFail(-1, "Invalid id"); } ParamFlowRuleEntity oldEntity = repository.findById(id); if (oldEntity == null) { return Result.ofFail(-1, "id " + id + " does not exist"); } authUser.authTarget(oldEntity.getApp(), PrivilegeType.WRITE_RULE); Result checkResult = checkEntityInternal(entity); if (checkResult != null) { return checkResult; } if (!checkIfSupported(entity.getApp(), entity.getIp(), entity.getPort())) { return unsupportedVersion(); } entity.setId(id); Date date = new Date(); entity.setGmtCreate(oldEntity.getGmtCreate()); entity.setGmtModified(date); try { entity = repository.save(entity); publishRules(entity.getApp(), entity.getIp(), entity.getPort()).get(); return Result.ofSuccess(entity); } catch (ExecutionException ex) { logger.error("Error when updating parameter flow rules, id=" + id, ex.getCause()); if (isNotSupported(ex.getCause())) { return unsupportedVersion(); } else { return Result.ofThrowable(-1, ex.getCause()); } } catch (Throwable throwable) { logger.error("Error when updating parameter flow rules, id=" + id, throwable); return Result.ofFail(-1, throwable.getMessage()); } } @DeleteMapping("/rule/{id}") public Result apiDeleteRule(HttpServletRequest request, @PathVariable("id") Long id) { AuthUser authUser = authService.getAuthUser(request); if (id == null) { return Result.ofFail(-1, "id cannot be null"); } ParamFlowRuleEntity oldEntity = repository.findById(id); if (oldEntity == null) { return Result.ofSuccess(null); } authUser.authTarget(oldEntity.getApp(), PrivilegeType.DELETE_RULE); try { repository.delete(id); publishRules(oldEntity.getApp(), oldEntity.getIp(), oldEntity.getPort()).get(); return Result.ofSuccess(id); } catch (ExecutionException ex) { logger.error("Error when deleting parameter flow rules", ex.getCause()); if (isNotSupported(ex.getCause())) { return unsupportedVersion(); } else { return Result.ofThrowable(-1, ex.getCause()); } } catch (Throwable throwable) { logger.error("Error when deleting parameter flow rules", throwable); return Result.ofFail(-1, throwable.getMessage()); } } private CompletableFuture publishRules(String app, String ip, Integer port) { List rules = repository.findAllByMachine(MachineInfo.of(app, ip, port)); return sentinelApiClient.setParamFlowRuleOfMachine(app, ip, port, rules); } private Result unsupportedVersion() { return Result.ofFail(4041, "Sentinel client not supported for parameter flow control (unsupported version or dependency absent)"); } private final SentinelVersion version020 = new SentinelVersion().setMinorVersion(2); } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/ResourceController.java ================================================ /* * 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.controller; import java.util.List; import java.util.stream.Collectors; import com.alibaba.csp.sentinel.util.StringUtil; import com.alibaba.csp.sentinel.command.vo.NodeVo; import com.alibaba.csp.sentinel.dashboard.domain.ResourceTreeNode; import com.alibaba.csp.sentinel.dashboard.client.SentinelApiClient; import com.alibaba.csp.sentinel.dashboard.domain.Result; import com.alibaba.csp.sentinel.dashboard.domain.vo.ResourceVo; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * @author Carpenter Lee */ @RestController @RequestMapping(value = "/resource") public class ResourceController { private static Logger logger = LoggerFactory.getLogger(ResourceController.class); @Autowired private SentinelApiClient httpFetcher; /** * Fetch real time statistics info of the machine. * * @param ip ip to fetch * @param port port of the ip * @param type one of [root, default, cluster], 'root' means fetching from tree root node, 'default' means * fetching from tree default node, 'cluster' means fetching from cluster node. * @param searchKey key to search * @return node statistics info. */ @GetMapping("/machineResource.json") public Result> fetchResourceChainListOfMachine(String ip, Integer port, String type, String searchKey) { if (StringUtil.isEmpty(ip) || port == null) { return Result.ofFail(-1, "invalid param, give ip, port"); } final String ROOT = "root"; final String DEFAULT = "default"; if (StringUtil.isEmpty(type)) { type = ROOT; } if (ROOT.equalsIgnoreCase(type) || DEFAULT.equalsIgnoreCase(type)) { List nodeVos = httpFetcher.fetchResourceOfMachine(ip, port, type); if (nodeVos == null) { return Result.ofSuccess(null); } ResourceTreeNode treeNode = ResourceTreeNode.fromNodeVoList(nodeVos); treeNode.searchIgnoreCase(searchKey); return Result.ofSuccess(ResourceVo.fromResourceTreeNode(treeNode)); } else { // Normal (cluster node). List nodeVos = httpFetcher.fetchClusterNodeOfMachine(ip, port, true); if (nodeVos == null) { return Result.ofSuccess(null); } if (StringUtil.isNotEmpty(searchKey)) { nodeVos = nodeVos.stream().filter(node -> node.getResource() .toLowerCase().contains(searchKey.toLowerCase())) .collect(Collectors.toList()); } return Result.ofSuccess(ResourceVo.fromNodeVoList(nodeVos)); } } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/SystemController.java ================================================ /* * 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.controller; import java.util.Date; import java.util.List; import javax.servlet.http.HttpServletRequest; import com.alibaba.csp.sentinel.dashboard.auth.AuthService; import com.alibaba.csp.sentinel.dashboard.auth.AuthService.AuthUser; import com.alibaba.csp.sentinel.dashboard.auth.AuthService.PrivilegeType; import com.alibaba.csp.sentinel.util.StringUtil; import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.SystemRuleEntity; import com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo; import com.alibaba.csp.sentinel.dashboard.client.SentinelApiClient; import com.alibaba.csp.sentinel.dashboard.domain.Result; import com.alibaba.csp.sentinel.dashboard.repository.rule.InMemSystemRuleStore; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; /** * @author leyou(lihao) */ @Controller @RequestMapping(value = "/system", produces = MediaType.APPLICATION_JSON_VALUE) public class SystemController { private static Logger logger = LoggerFactory.getLogger(SystemController.class); @Autowired private InMemSystemRuleStore repository; @Autowired private SentinelApiClient sentinelApiClient; @Autowired private AuthService authService; @ResponseBody @RequestMapping("/rules.json") Result> queryMachineRules(HttpServletRequest request, String app, String ip, Integer port) { AuthUser authUser = authService.getAuthUser(request); authUser.authTarget(app, PrivilegeType.READ_RULE); if (StringUtil.isEmpty(app)) { return Result.ofFail(-1, "app can't be null or empty"); } if (StringUtil.isEmpty(ip)) { return Result.ofFail(-1, "ip can't be null or empty"); } if (port == null) { return Result.ofFail(-1, "port can't be null"); } try { List rules = sentinelApiClient.fetchSystemRuleOfMachine(app, ip, port); rules = repository.saveAll(rules); return Result.ofSuccess(rules); } catch (Throwable throwable) { logger.error("queryApps error:", throwable); return Result.ofThrowable(-1, throwable); } } private int countNotNullAndNotNegative(Number... values) { int notNullCount = 0; for (int i = 0; i < values.length; i++) { if (values[i] != null && values[i].doubleValue() >= 0) { notNullCount++; } } return notNullCount; } @ResponseBody @RequestMapping("/new.json") Result add(HttpServletRequest request, String app, String ip, Integer port, Double avgLoad, Long avgRt, Long maxThread, Double qps) { AuthUser authUser = authService.getAuthUser(request); authUser.authTarget(app, PrivilegeType.WRITE_RULE); if (StringUtil.isBlank(app)) { return Result.ofFail(-1, "app can't be null or empty"); } if (StringUtil.isBlank(ip)) { return Result.ofFail(-1, "ip can't be null or empty"); } if (port == null) { return Result.ofFail(-1, "port can't be null"); } int notNullCount = countNotNullAndNotNegative(avgLoad, avgRt, maxThread, qps); if (notNullCount != 1) { return Result.ofFail(-1, "only one of [avgLoad, avgRt, maxThread, qps] " + "value must be set >= 0, but " + notNullCount + " values get"); } SystemRuleEntity entity = new SystemRuleEntity(); entity.setApp(app.trim()); entity.setIp(ip.trim()); entity.setPort(port); // -1 is a fake value if (avgLoad != null) { entity.setAvgLoad(avgLoad); } else { entity.setAvgLoad(-1D); } if (avgRt != null) { entity.setAvgRt(avgRt); } else { entity.setAvgRt(-1L); } if (maxThread != null) { entity.setMaxThread(maxThread); } else { entity.setMaxThread(-1L); } if (qps != null) { entity.setQps(qps); } else { entity.setQps(-1D); } Date date = new Date(); entity.setGmtCreate(date); entity.setGmtModified(date); try { entity = repository.save(entity); } catch (Throwable throwable) { logger.error("add error:", throwable); return Result.ofThrowable(-1, throwable); } if (!publishRules(app, ip, port)) { logger.info("publish system rules fail after rule add"); } return Result.ofSuccess(entity); } @ResponseBody @RequestMapping("/save.json") Result updateIfNotNull(HttpServletRequest request, Long id, String app, Double avgLoad, Long avgRt, Long maxThread, Double qps) { AuthUser authUser = authService.getAuthUser(request); if (id == null) { return Result.ofFail(-1, "id can't be null"); } SystemRuleEntity entity = repository.findById(id); if (entity == null) { return Result.ofFail(-1, "id " + id + " dose not exist"); } authUser.authTarget(entity.getApp(), PrivilegeType.WRITE_RULE); if (StringUtil.isNotBlank(app)) { entity.setApp(app.trim()); } if (avgLoad != null) { if (avgLoad < 0) { return Result.ofFail(-1, "avgLoad must >= 0"); } entity.setAvgLoad(avgLoad); } if (avgRt != null) { if (avgRt < 0) { return Result.ofFail(-1, "avgRt must >= 0"); } entity.setAvgRt(avgRt); } if (maxThread != null) { if (maxThread < 0) { return Result.ofFail(-1, "maxThread must >= 0"); } entity.setMaxThread(maxThread); } if (qps != null) { if (qps < 0) { return Result.ofFail(-1, "qps must >= 0"); } entity.setQps(qps); } Date date = new Date(); entity.setGmtModified(date); try { entity = repository.save(entity); } catch (Throwable throwable) { logger.error("save error:", throwable); return Result.ofThrowable(-1, throwable); } if (!publishRules(entity.getApp(), entity.getIp(), entity.getPort())) { logger.info("publish system rules fail after rule update"); } return Result.ofSuccess(entity); } @ResponseBody @RequestMapping("/delete.json") Result delete(HttpServletRequest request, Long id) { AuthUser authUser = authService.getAuthUser(request); if (id == null) { return Result.ofFail(-1, "id can't be null"); } SystemRuleEntity oldEntity = repository.findById(id); if (oldEntity == null) { return Result.ofSuccess(null); } authUser.authTarget(oldEntity.getApp(), PrivilegeType.DELETE_RULE); try { repository.delete(id); } catch (Throwable throwable) { logger.error("delete error:", throwable); return Result.ofThrowable(-1, throwable); } if (!publishRules(oldEntity.getApp(), oldEntity.getIp(), oldEntity.getPort())) { logger.info("publish system rules fail after rule delete"); } return Result.ofSuccess(id); } private boolean publishRules(String app, String ip, Integer port) { List rules = repository.findAllByMachine(MachineInfo.of(app, ip, port)); return sentinelApiClient.setSystemRuleOfMachine(app, ip, port, rules); } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/cluster/ClusterAssignController.java ================================================ /* * 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.controller.cluster; import java.util.Collections; import java.util.Set; import com.alibaba.csp.sentinel.util.StringUtil; import com.alibaba.csp.sentinel.dashboard.domain.cluster.ClusterAppFullAssignRequest; import com.alibaba.csp.sentinel.dashboard.domain.cluster.ClusterAppAssignResultVO; import com.alibaba.csp.sentinel.dashboard.domain.cluster.ClusterAppSingleServerAssignRequest; import com.alibaba.csp.sentinel.dashboard.service.ClusterAssignService; import com.alibaba.csp.sentinel.dashboard.domain.Result; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * @author Eric Zhao * @since 1.4.1 */ @RestController @RequestMapping("/cluster/assign") public class ClusterAssignController { private final Logger logger = LoggerFactory.getLogger(ClusterAssignController.class); @Autowired private ClusterAssignService clusterAssignService; @PostMapping("/all_server/{app}") public Result apiAssignAllClusterServersOfApp(@PathVariable String app, @RequestBody ClusterAppFullAssignRequest assignRequest) { if (StringUtil.isEmpty(app)) { return Result.ofFail(-1, "app cannot be null or empty"); } if (assignRequest == null || assignRequest.getClusterMap() == null || assignRequest.getRemainingList() == null) { return Result.ofFail(-1, "bad request body"); } try { return Result.ofSuccess(clusterAssignService.applyAssignToApp(app, assignRequest.getClusterMap(), assignRequest.getRemainingList())); } catch (Throwable throwable) { logger.error("Error when assigning full cluster servers for app: " + app, throwable); return Result.ofFail(-1, throwable.getMessage()); } } @PostMapping("/single_server/{app}") public Result apiAssignSingleClusterServersOfApp(@PathVariable String app, @RequestBody ClusterAppSingleServerAssignRequest assignRequest) { if (StringUtil.isEmpty(app)) { return Result.ofFail(-1, "app cannot be null or empty"); } if (assignRequest == null || assignRequest.getClusterMap() == null) { return Result.ofFail(-1, "bad request body"); } try { return Result.ofSuccess(clusterAssignService.applyAssignToApp(app, Collections.singletonList(assignRequest.getClusterMap()), assignRequest.getRemainingList())); } catch (Throwable throwable) { logger.error("Error when assigning single cluster servers for app: " + app, throwable); return Result.ofFail(-1, throwable.getMessage()); } } @PostMapping("/unbind_server/{app}") public Result apiUnbindClusterServersOfApp(@PathVariable String app, @RequestBody Set machineIds) { if (StringUtil.isEmpty(app)) { return Result.ofFail(-1, "app cannot be null or empty"); } if (machineIds == null || machineIds.isEmpty()) { return Result.ofFail(-1, "bad request body"); } try { return Result.ofSuccess(clusterAssignService.unbindClusterServers(app, machineIds)); } catch (Throwable throwable) { logger.error("Error when unbinding cluster server {} for app <{}>", machineIds, app, throwable); return Result.ofFail(-1, throwable.getMessage()); } } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/cluster/ClusterConfigController.java ================================================ /* * 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.controller.cluster; import java.util.List; import java.util.Optional; import java.util.concurrent.ExecutionException; import com.alibaba.csp.sentinel.cluster.ClusterStateManager; import com.alibaba.csp.sentinel.dashboard.client.CommandNotFoundException; import com.alibaba.csp.sentinel.dashboard.discovery.AppManagement; import com.alibaba.csp.sentinel.util.StringUtil; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.alibaba.csp.sentinel.dashboard.datasource.entity.SentinelVersion; import com.alibaba.csp.sentinel.dashboard.domain.cluster.request.ClusterClientModifyRequest; import com.alibaba.csp.sentinel.dashboard.domain.cluster.request.ClusterModifyRequest; import com.alibaba.csp.sentinel.dashboard.domain.cluster.request.ClusterServerModifyRequest; import com.alibaba.csp.sentinel.dashboard.domain.cluster.state.AppClusterClientStateWrapVO; import com.alibaba.csp.sentinel.dashboard.domain.cluster.state.AppClusterServerStateWrapVO; import com.alibaba.csp.sentinel.dashboard.domain.cluster.state.ClusterUniversalStatePairVO; import com.alibaba.csp.sentinel.dashboard.domain.cluster.state.ClusterUniversalStateVO; import com.alibaba.csp.sentinel.dashboard.service.ClusterConfigService; import com.alibaba.csp.sentinel.dashboard.util.ClusterEntityUtils; import com.alibaba.csp.sentinel.dashboard.util.VersionUtils; import com.alibaba.csp.sentinel.dashboard.domain.Result; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; /** * @author Eric Zhao * @since 1.4.0 */ @RestController @RequestMapping(value = "/cluster") public class ClusterConfigController { private final Logger logger = LoggerFactory.getLogger(ClusterConfigController.class); private final SentinelVersion version140 = new SentinelVersion().setMajorVersion(1).setMinorVersion(4); @Autowired private AppManagement appManagement; @Autowired private ClusterConfigService clusterConfigService; @PostMapping("/config/modify_single") public Result apiModifyClusterConfig(@RequestBody String payload) { if (StringUtil.isBlank(payload)) { return Result.ofFail(-1, "empty request body"); } try { JSONObject body = JSON.parseObject(payload); if (body.containsKey(KEY_MODE)) { int mode = body.getInteger(KEY_MODE); switch (mode) { case ClusterStateManager.CLUSTER_CLIENT: ClusterClientModifyRequest data = JSON.parseObject(payload, ClusterClientModifyRequest.class); Result res = checkValidRequest(data); if (res != null) { return res; } clusterConfigService.modifyClusterClientConfig(data).get(); return Result.ofSuccess(true); case ClusterStateManager.CLUSTER_SERVER: ClusterServerModifyRequest d = JSON.parseObject(payload, ClusterServerModifyRequest.class); Result r = checkValidRequest(d); if (r != null) { return r; } // TODO: bad design here, should refactor! clusterConfigService.modifyClusterServerConfig(d).get(); return Result.ofSuccess(true); default: return Result.ofFail(-1, "invalid mode"); } } return Result.ofFail(-1, "invalid parameter"); } catch (ExecutionException ex) { logger.error("Error when modifying cluster config", ex.getCause()); return errorResponse(ex); } catch (Throwable ex) { logger.error("Error when modifying cluster config", ex); return Result.ofFail(-1, ex.getMessage()); } } private Result errorResponse(ExecutionException ex) { if (isNotSupported(ex.getCause())) { return unsupportedVersion(); } else { return Result.ofThrowable(-1, ex.getCause()); } } @GetMapping("/state_single") public Result apiGetClusterState(@RequestParam String app, @RequestParam String ip, @RequestParam Integer port) { if (StringUtil.isEmpty(app)) { return Result.ofFail(-1, "app cannot be null or empty"); } if (StringUtil.isEmpty(ip)) { return Result.ofFail(-1, "ip cannot be null or empty"); } if (port == null || port <= 0) { return Result.ofFail(-1, "Invalid parameter: port"); } if (!checkIfSupported(app, ip, port)) { return unsupportedVersion(); } try { return clusterConfigService.getClusterUniversalState(app, ip, port) .thenApply(Result::ofSuccess) .get(); } catch (ExecutionException ex) { logger.error("Error when fetching cluster state", ex.getCause()); return errorResponse(ex); } catch (Throwable throwable) { logger.error("Error when fetching cluster state", throwable); return Result.ofFail(-1, throwable.getMessage()); } } @GetMapping("/server_state/{app}") public Result> apiGetClusterServerStateOfApp(@PathVariable String app) { if (StringUtil.isEmpty(app)) { return Result.ofFail(-1, "app cannot be null or empty"); } try { return clusterConfigService.getClusterUniversalState(app) .thenApply(ClusterEntityUtils::wrapToAppClusterServerState) .thenApply(Result::ofSuccess) .get(); } catch (ExecutionException ex) { logger.error("Error when fetching cluster server state of app: " + app, ex.getCause()); return errorResponse(ex); } catch (Throwable throwable) { logger.error("Error when fetching cluster server state of app: " + app, throwable); return Result.ofFail(-1, throwable.getMessage()); } } @GetMapping("/client_state/{app}") public Result> apiGetClusterClientStateOfApp(@PathVariable String app) { if (StringUtil.isEmpty(app)) { return Result.ofFail(-1, "app cannot be null or empty"); } try { return clusterConfigService.getClusterUniversalState(app) .thenApply(ClusterEntityUtils::wrapToAppClusterClientState) .thenApply(Result::ofSuccess) .get(); } catch (ExecutionException ex) { logger.error("Error when fetching cluster token client state of app: " + app, ex.getCause()); return errorResponse(ex); } catch (Throwable throwable) { logger.error("Error when fetching cluster token client state of app: " + app, throwable); return Result.ofFail(-1, throwable.getMessage()); } } @GetMapping("/state/{app}") public Result> apiGetClusterStateOfApp(@PathVariable String app) { if (StringUtil.isEmpty(app)) { return Result.ofFail(-1, "app cannot be null or empty"); } try { return clusterConfigService.getClusterUniversalState(app) .thenApply(Result::ofSuccess) .get(); } catch (ExecutionException ex) { logger.error("Error when fetching cluster state of app: " + app, ex.getCause()); return errorResponse(ex); } catch (Throwable throwable) { logger.error("Error when fetching cluster state of app: " + app, throwable); return Result.ofFail(-1, throwable.getMessage()); } } private boolean isNotSupported(Throwable ex) { return ex instanceof CommandNotFoundException; } private boolean checkIfSupported(String app, String ip, int port) { try { return Optional.ofNullable(appManagement.getDetailApp(app)) .flatMap(e -> e.getMachine(ip, port)) .flatMap(m -> VersionUtils.parseVersion(m.getVersion()) .map(v -> v.greaterOrEqual(version140))) .orElse(true); // If error occurred or cannot retrieve machine info, return true. } catch (Exception ex) { return true; } } private Result checkValidRequest(ClusterModifyRequest request) { if (StringUtil.isEmpty(request.getApp())) { return Result.ofFail(-1, "app cannot be empty"); } if (StringUtil.isEmpty(request.getIp())) { return Result.ofFail(-1, "ip cannot be empty"); } if (request.getPort() == null || request.getPort() < 0) { return Result.ofFail(-1, "invalid port"); } if (request.getMode() == null || request.getMode() < 0) { return Result.ofFail(-1, "invalid mode"); } if (!checkIfSupported(request.getApp(), request.getIp(), request.getPort())) { return unsupportedVersion(); } return null; } private Result unsupportedVersion() { return Result.ofFail(4041, "Sentinel client not supported for cluster flow control (unsupported version or dependency absent)"); } private static final String KEY_MODE = "mode"; } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/v2/FlowControllerV2.java ================================================ /* * 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.controller.v2; import java.util.Date; import java.util.List; import javax.servlet.http.HttpServletRequest; import com.alibaba.csp.sentinel.dashboard.auth.AuthService; import com.alibaba.csp.sentinel.dashboard.auth.AuthService.AuthUser; import com.alibaba.csp.sentinel.dashboard.auth.AuthService.PrivilegeType; import com.alibaba.csp.sentinel.util.StringUtil; import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity; import com.alibaba.csp.sentinel.dashboard.repository.rule.InMemoryRuleRepositoryAdapter; import com.alibaba.csp.sentinel.dashboard.rule.DynamicRuleProvider; import com.alibaba.csp.sentinel.dashboard.rule.DynamicRulePublisher; import com.alibaba.csp.sentinel.dashboard.domain.Result; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; /** * Flow rule controller (v2). * * @author Eric Zhao * @since 1.4.0 */ @RestController @RequestMapping(value = "/v2/flow") public class FlowControllerV2 { private final Logger logger = LoggerFactory.getLogger(FlowControllerV2.class); @Autowired private InMemoryRuleRepositoryAdapter repository; @Autowired @Qualifier("flowRuleApolloProvider") private DynamicRuleProvider> ruleProvider; @Autowired @Qualifier("flowRuleApolloPublisher") private DynamicRulePublisher> rulePublisher; @Autowired private AuthService authService; @GetMapping("/rules") public Result> apiQueryMachineRules(HttpServletRequest request, @RequestParam String app) { AuthUser authUser = authService.getAuthUser(request); authUser.authTarget(app, PrivilegeType.READ_RULE); if (StringUtil.isEmpty(app)) { return Result.ofFail(-1, "app can't be null or empty"); } try { List rules = ruleProvider.getRules(app); if (rules != null && !rules.isEmpty()) { for (FlowRuleEntity entity : rules) { entity.setApp(app); if (entity.getClusterConfig() != null && entity.getClusterConfig().getFlowId() != null) { entity.setId(entity.getClusterConfig().getFlowId()); } } } rules = repository.saveAll(rules); return Result.ofSuccess(rules); } catch (Throwable throwable) { logger.error("Error when querying flow rules", throwable); return Result.ofThrowable(-1, throwable); } } private Result checkEntityInternal(FlowRuleEntity entity) { if (entity == null) { return Result.ofFail(-1, "invalid body"); } if (StringUtil.isBlank(entity.getApp())) { return Result.ofFail(-1, "app can't be null or empty"); } if (StringUtil.isBlank(entity.getLimitApp())) { return Result.ofFail(-1, "limitApp can't be null or empty"); } if (StringUtil.isBlank(entity.getResource())) { return Result.ofFail(-1, "resource can't be null or empty"); } if (entity.getGrade() == null) { return Result.ofFail(-1, "grade can't be null"); } if (entity.getGrade() != 0 && entity.getGrade() != 1) { return Result.ofFail(-1, "grade must be 0 or 1, but " + entity.getGrade() + " got"); } if (entity.getCount() == null || entity.getCount() < 0) { return Result.ofFail(-1, "count should be at lease zero"); } if (entity.getStrategy() == null) { return Result.ofFail(-1, "strategy can't be null"); } if (entity.getStrategy() != 0 && StringUtil.isBlank(entity.getRefResource())) { return Result.ofFail(-1, "refResource can't be null or empty when strategy!=0"); } if (entity.getControlBehavior() == null) { return Result.ofFail(-1, "controlBehavior can't be null"); } int controlBehavior = entity.getControlBehavior(); if (controlBehavior == 1 && entity.getWarmUpPeriodSec() == null) { return Result.ofFail(-1, "warmUpPeriodSec can't be null when controlBehavior==1"); } if (controlBehavior == 2 && entity.getMaxQueueingTimeMs() == null) { return Result.ofFail(-1, "maxQueueingTimeMs can't be null when controlBehavior==2"); } if (entity.isClusterMode() && entity.getClusterConfig() == null) { return Result.ofFail(-1, "cluster config should be valid"); } return null; } @PostMapping("/rule") public Result apiAddFlowRule(HttpServletRequest request, @RequestBody FlowRuleEntity entity) { AuthUser authUser = authService.getAuthUser(request); authUser.authTarget(entity.getApp(), PrivilegeType.WRITE_RULE); Result checkResult = checkEntityInternal(entity); if (checkResult != null) { return checkResult; } entity.setId(null); Date date = new Date(); entity.setGmtCreate(date); entity.setGmtModified(date); entity.setLimitApp(entity.getLimitApp().trim()); entity.setResource(entity.getResource().trim()); try { entity = repository.save(entity); publishRules(entity.getApp()); } catch (Throwable throwable) { logger.error("Failed to add flow rule", throwable); return Result.ofThrowable(-1, throwable); } return Result.ofSuccess(entity); } @PutMapping("/rule/{id}") public Result apiUpdateFlowRule(HttpServletRequest request, @PathVariable("id") Long id, @RequestBody FlowRuleEntity entity) { AuthUser authUser = authService.getAuthUser(request); if (id == null || id <= 0) { return Result.ofFail(-1, "Invalid id"); } FlowRuleEntity oldEntity = repository.findById(id); if (oldEntity == null) { return Result.ofFail(-1, "id " + id + " does not exist"); } if (entity == null) { return Result.ofFail(-1, "invalid body"); } authUser.authTarget(oldEntity.getApp(), PrivilegeType.WRITE_RULE); entity.setApp(oldEntity.getApp()); entity.setIp(oldEntity.getIp()); entity.setPort(oldEntity.getPort()); Result checkResult = checkEntityInternal(entity); if (checkResult != null) { return checkResult; } entity.setId(id); Date date = new Date(); entity.setGmtCreate(oldEntity.getGmtCreate()); entity.setGmtModified(date); try { entity = repository.save(entity); if (entity == null) { return Result.ofFail(-1, "save entity fail"); } publishRules(oldEntity.getApp()); } catch (Throwable throwable) { logger.error("Failed to update flow rule", throwable); return Result.ofThrowable(-1, throwable); } return Result.ofSuccess(entity); } @DeleteMapping("/rule/{id}") public Result apiDeleteRule(HttpServletRequest request, @PathVariable("id") Long id) { AuthUser authUser = authService.getAuthUser(request); if (id == null || id <= 0) { return Result.ofFail(-1, "Invalid id"); } FlowRuleEntity oldEntity = repository.findById(id); if (oldEntity == null) { return Result.ofSuccess(null); } authUser.authTarget(oldEntity.getApp(), PrivilegeType.DELETE_RULE); try { repository.delete(id); publishRules(oldEntity.getApp()); } catch (Exception e) { return Result.ofFail(-1, e.getMessage()); } return Result.ofSuccess(id); } private void publishRules(/*@NonNull*/ String app) throws Exception { List rules = repository.findAllByApp(app); rulePublisher.publish(app, rules); } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/datasource/entity/ApplicationEntity.java ================================================ /* * 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.datasource.entity; import java.util.Date; import com.alibaba.csp.sentinel.dashboard.discovery.AppInfo; /** * @author leyou */ public class ApplicationEntity { private Long id; private Date gmtCreate; private Date gmtModified; private String app; private String activeConsole; private Date lastFetch; public long getId() { return id; } public void setId(long id) { this.id = id; } public Date getGmtCreate() { return gmtCreate; } public void setGmtCreate(Date gmtCreate) { this.gmtCreate = gmtCreate; } public Date getGmtModified() { return gmtModified; } public void setGmtModified(Date gmtModified) { this.gmtModified = gmtModified; } public String getApp() { return app; } public void setApp(String app) { this.app = app; } public String getActiveConsole() { return activeConsole; } public Date getLastFetch() { return lastFetch; } public void setLastFetch(Date lastFetch) { this.lastFetch = lastFetch; } public void setActiveConsole(String activeConsole) { this.activeConsole = activeConsole; } public AppInfo toAppInfo() { return new AppInfo(app); } @Override public String toString() { return "ApplicationEntity{" + "id=" + id + ", gmtCreate=" + gmtCreate + ", gmtModified=" + gmtModified + ", app='" + app + '\'' + ", activeConsole='" + activeConsole + '\'' + ", lastFetch=" + lastFetch + '}'; } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/datasource/entity/MachineEntity.java ================================================ /* * 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.datasource.entity; import java.util.Date; import com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo; /** * @author leyou */ public class MachineEntity { private Long id; private Date gmtCreate; private Date gmtModified; private String app; private String ip; private String hostname; private Date timestamp; private Integer port; public long getId() { return id; } public void setId(long id) { this.id = id; } public Date getGmtCreate() { return gmtCreate; } public void setGmtCreate(Date gmtCreate) { this.gmtCreate = gmtCreate; } public Date getGmtModified() { return gmtModified; } public void setGmtModified(Date gmtModified) { this.gmtModified = gmtModified; } public String getApp() { return app; } public void setApp(String app) { this.app = app; } public String getIp() { return ip; } public void setIp(String ip) { this.ip = ip; } public String getHostname() { return hostname; } public void setHostname(String hostname) { this.hostname = hostname; } public Date getTimestamp() { return timestamp; } public void setTimestamp(Date timestamp) { this.timestamp = timestamp; } public Integer getPort() { return port; } public void setPort(Integer port) { this.port = port; } public MachineInfo toMachineInfo() { MachineInfo machineInfo = new MachineInfo(); machineInfo.setApp(app); machineInfo.setHostname(hostname); machineInfo.setIp(ip); machineInfo.setPort(port); machineInfo.setLastHeartbeat(timestamp.getTime()); machineInfo.setHeartbeatVersion(timestamp.getTime()); return machineInfo; } @Override public String toString() { return "MachineEntity{" + "id=" + id + ", gmtCreate=" + gmtCreate + ", gmtModified=" + gmtModified + ", app='" + app + '\'' + ", ip='" + ip + '\'' + ", hostname='" + hostname + '\'' + ", timestamp=" + timestamp + ", port=" + port + '}'; } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/datasource/entity/MetricEntity.java ================================================ /* * 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.datasource.entity; import java.util.Date; /** * @author leyou */ public class MetricEntity { private Long id; private Date gmtCreate; private Date gmtModified; private String app; /** * 监控信息的时间戳 */ private Date timestamp; private String resource; private Long passQps; private Long successQps; private Long blockQps; private Long exceptionQps; /** * summary rt of all success exit qps. */ private double rt; /** * 本次聚合的总条数 */ private int count; private int resourceCode; public static MetricEntity copyOf(MetricEntity oldEntity) { MetricEntity entity = new MetricEntity(); entity.setId(oldEntity.getId()); entity.setGmtCreate(oldEntity.getGmtCreate()); entity.setGmtModified(oldEntity.getGmtModified()); entity.setApp(oldEntity.getApp()); entity.setTimestamp(oldEntity.getTimestamp()); entity.setResource(oldEntity.getResource()); entity.setPassQps(oldEntity.getPassQps()); entity.setBlockQps(oldEntity.getBlockQps()); entity.setSuccessQps(oldEntity.getSuccessQps()); entity.setExceptionQps(oldEntity.getExceptionQps()); entity.setRt(oldEntity.getRt()); entity.setCount(oldEntity.getCount()); entity.setResource(oldEntity.getResource()); return entity; } public synchronized void addPassQps(Long passQps) { this.passQps += passQps; } public synchronized void addBlockQps(Long blockQps) { this.blockQps += blockQps; } public synchronized void addExceptionQps(Long exceptionQps) { this.exceptionQps += exceptionQps; } public synchronized void addCount(int count) { this.count += count; } public synchronized void addRtAndSuccessQps(double avgRt, Long successQps) { this.rt += avgRt * successQps; this.successQps += successQps; } /** * {@link #rt} = {@code avgRt * successQps} * * @param avgRt average rt of {@code successQps} * @param successQps */ public synchronized void setRtAndSuccessQps(double avgRt, Long successQps) { this.rt = avgRt * successQps; this.successQps = successQps; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public Date getGmtCreate() { return gmtCreate; } public void setGmtCreate(Date gmtCreate) { this.gmtCreate = gmtCreate; } public Date getGmtModified() { return gmtModified; } public void setGmtModified(Date gmtModified) { this.gmtModified = gmtModified; } public String getApp() { return app; } public void setApp(String app) { this.app = app; } public Date getTimestamp() { return timestamp; } public void setTimestamp(Date timestamp) { this.timestamp = timestamp; } public String getResource() { return resource; } public void setResource(String resource) { this.resource = resource; this.resourceCode = resource.hashCode(); } public Long getPassQps() { return passQps; } public void setPassQps(Long passQps) { this.passQps = passQps; } public Long getBlockQps() { return blockQps; } public void setBlockQps(Long blockQps) { this.blockQps = blockQps; } public Long getExceptionQps() { return exceptionQps; } public void setExceptionQps(Long exceptionQps) { this.exceptionQps = exceptionQps; } public double getRt() { return rt; } public void setRt(double rt) { this.rt = rt; } public int getCount() { return count; } public void setCount(int count) { this.count = count; } public int getResourceCode() { return resourceCode; } public Long getSuccessQps() { return successQps; } public void setSuccessQps(Long successQps) { this.successQps = successQps; } @Override public String toString() { return "MetricEntity{" + "id=" + id + ", gmtCreate=" + gmtCreate + ", gmtModified=" + gmtModified + ", app='" + app + '\'' + ", timestamp=" + timestamp + ", resource='" + resource + '\'' + ", passQps=" + passQps + ", blockQps=" + blockQps + ", successQps=" + successQps + ", exceptionQps=" + exceptionQps + ", rt=" + rt + ", count=" + count + ", resourceCode=" + resourceCode + '}'; } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/datasource/entity/MetricPositionEntity.java ================================================ /* * 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.datasource.entity; import java.util.Date; /** * @author leyou */ public class MetricPositionEntity { private long id; private Date gmtCreate; private Date gmtModified; private String app; private String ip; /** * Sentinel在该应用上使用的端口 */ private int port; /** * 机器名,冗余字段 */ private String hostname; /** * 上一次拉取的最晚时间戳 */ private Date lastFetch; public long getId() { return id; } public void setId(long id) { this.id = id; } public Date getGmtCreate() { return gmtCreate; } public void setGmtCreate(Date gmtCreate) { this.gmtCreate = gmtCreate; } public Date getGmtModified() { return gmtModified; } public void setGmtModified(Date gmtModified) { this.gmtModified = gmtModified; } public String getApp() { return app; } public void setApp(String app) { this.app = app; } public String getIp() { return ip; } public void setIp(String ip) { this.ip = ip; } public int getPort() { return port; } public void setPort(int port) { this.port = port; } public String getHostname() { return hostname; } public void setHostname(String hostname) { this.hostname = hostname; } public Date getLastFetch() { return lastFetch; } public void setLastFetch(Date lastFetch) { this.lastFetch = lastFetch; } @Override public String toString() { return "MetricPositionEntity{" + "id=" + id + ", gmtCreate=" + gmtCreate + ", gmtModified=" + gmtModified + ", app='" + app + '\'' + ", ip='" + ip + '\'' + ", port=" + port + ", hostname='" + hostname + '\'' + ", lastFetch=" + lastFetch + '}'; } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/datasource/entity/SentinelVersion.java ================================================ /* * 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.datasource.entity; /** * @author Eric Zhao * @since 0.2.1 */ public class SentinelVersion { private int majorVersion; private int minorVersion; private int fixVersion; private String postfix; public SentinelVersion() { this(0, 0, 0); } public SentinelVersion(int major, int minor, int fix) { this(major, minor, fix, null); } public SentinelVersion(int major, int minor, int fix, String postfix) { this.majorVersion = major; this.minorVersion = minor; this.fixVersion = fix; this.postfix = postfix; } /** * 000, 000, 000 */ public int getFullVersion() { return majorVersion * 1000000 + minorVersion * 1000 + fixVersion; } public int getMajorVersion() { return majorVersion; } public SentinelVersion setMajorVersion(int majorVersion) { this.majorVersion = majorVersion; return this; } public int getMinorVersion() { return minorVersion; } public SentinelVersion setMinorVersion(int minorVersion) { this.minorVersion = minorVersion; return this; } public int getFixVersion() { return fixVersion; } public SentinelVersion setFixVersion(int fixVersion) { this.fixVersion = fixVersion; return this; } public String getPostfix() { return postfix; } public SentinelVersion setPostfix(String postfix) { this.postfix = postfix; return this; } public boolean greaterThan(SentinelVersion version) { if (version == null) { return true; } return getFullVersion() > version.getFullVersion(); } public boolean greaterOrEqual(SentinelVersion version) { if (version == null) { return true; } return getFullVersion() >= version.getFullVersion(); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } SentinelVersion that = (SentinelVersion)o; if (getFullVersion() != that.getFullVersion()) { return false; } return postfix != null ? postfix.equals(that.postfix) : that.postfix == null; } @Override public int hashCode() { int result = majorVersion; result = 31 * result + minorVersion; result = 31 * result + fixVersion; result = 31 * result + (postfix != null ? postfix.hashCode() : 0); return result; } @Override public String toString() { return "SentinelVersion{" + "majorVersion=" + majorVersion + ", minorVersion=" + minorVersion + ", fixVersion=" + fixVersion + ", postfix='" + postfix + '\'' + '}'; } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/datasource/entity/rule/AbstractRuleEntity.java ================================================ /* * 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.datasource.entity.rule; import java.util.Date; import com.alibaba.csp.sentinel.slots.block.AbstractRule; /** * @author Eric Zhao * @since 0.2.1 */ public abstract class AbstractRuleEntity implements RuleEntity { protected Long id; protected String app; protected String ip; protected Integer port; protected T rule; private Date gmtCreate; private Date gmtModified; @Override public Long getId() { return id; } @Override public void setId(Long id) { this.id = id; } @Override public String getApp() { return app; } public AbstractRuleEntity setApp(String app) { this.app = app; return this; } @Override public String getIp() { return ip; } public AbstractRuleEntity setIp(String ip) { this.ip = ip; return this; } @Override public Integer getPort() { return port; } public AbstractRuleEntity setPort(Integer port) { this.port = port; return this; } public T getRule() { return rule; } public AbstractRuleEntity setRule(T rule) { this.rule = rule; return this; } @Override public Date getGmtCreate() { return gmtCreate; } public AbstractRuleEntity setGmtCreate(Date gmtCreate) { this.gmtCreate = gmtCreate; return this; } public Date getGmtModified() { return gmtModified; } public AbstractRuleEntity setGmtModified(Date gmtModified) { this.gmtModified = gmtModified; return this; } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/datasource/entity/rule/AuthorityRuleEntity.java ================================================ /* * 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.datasource.entity.rule; import com.alibaba.csp.sentinel.slots.block.Rule; import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRule; import com.alibaba.csp.sentinel.util.AssertUtil; import com.fasterxml.jackson.annotation.JsonIgnore; /** * @author Eric Zhao * @since 0.2.1 */ public class AuthorityRuleEntity extends AbstractRuleEntity { public AuthorityRuleEntity() {} public AuthorityRuleEntity(AuthorityRule authorityRule) { AssertUtil.notNull(authorityRule, "Authority rule should not be null"); this.rule = authorityRule; } public static AuthorityRuleEntity fromAuthorityRule(String app, String ip, Integer port, AuthorityRule rule) { AuthorityRuleEntity entity = new AuthorityRuleEntity(rule); entity.setApp(app); entity.setIp(ip); entity.setPort(port); return entity; } @JsonIgnore public String getLimitApp() { return rule.getLimitApp(); } @JsonIgnore public String getResource() { return rule.getResource(); } @JsonIgnore public int getStrategy() { return rule.getStrategy(); } @Override public Rule toRule() { return rule; } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/datasource/entity/rule/DegradeRuleEntity.java ================================================ /* * 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.datasource.entity.rule; import java.util.Date; import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule; /** * @author leyou */ public class DegradeRuleEntity implements RuleEntity { private Long id; private String app; private String ip; private Integer port; private String resource; private String limitApp; private Double count; private Integer timeWindow; /** * 0 rt 限流; 1为异常; */ private Integer grade; private Date gmtCreate; private Date gmtModified; public static DegradeRuleEntity fromDegradeRule(String app, String ip, Integer port, DegradeRule rule) { DegradeRuleEntity entity = new DegradeRuleEntity(); entity.setApp(app); entity.setIp(ip); entity.setPort(port); entity.setResource(rule.getResource()); entity.setLimitApp(rule.getLimitApp()); entity.setCount(rule.getCount()); entity.setTimeWindow(rule.getTimeWindow()); entity.setGrade(rule.getGrade()); return entity; } @Override public String getIp() { return ip; } public void setIp(String ip) { this.ip = ip; } @Override public Integer getPort() { return port; } public void setPort(Integer port) { this.port = port; } @Override public Long getId() { return id; } @Override public void setId(Long id) { this.id = id; } @Override public String getApp() { return app; } public void setApp(String app) { this.app = app; } public String getResource() { return resource; } public void setResource(String resource) { this.resource = resource; } public String getLimitApp() { return limitApp; } public void setLimitApp(String limitApp) { this.limitApp = limitApp; } public Double getCount() { return count; } public void setCount(Double count) { this.count = count; } public Integer getTimeWindow() { return timeWindow; } public void setTimeWindow(Integer timeWindow) { this.timeWindow = timeWindow; } public Integer getGrade() { return grade; } public void setGrade(Integer grade) { this.grade = grade; } @Override public Date getGmtCreate() { return gmtCreate; } public void setGmtCreate(Date gmtCreate) { this.gmtCreate = gmtCreate; } public Date getGmtModified() { return gmtModified; } public void setGmtModified(Date gmtModified) { this.gmtModified = gmtModified; } @Override public DegradeRule toRule() { DegradeRule rule = new DegradeRule(); rule.setResource(resource); rule.setLimitApp(limitApp); rule.setCount(count); rule.setTimeWindow(timeWindow); rule.setGrade(grade); return rule; } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/datasource/entity/rule/FlowRuleEntity.java ================================================ /* * 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.datasource.entity.rule; import java.util.Date; import com.alibaba.csp.sentinel.slots.block.flow.ClusterFlowConfig; import com.alibaba.csp.sentinel.slots.block.flow.FlowRule; /** * @author leyou */ public class FlowRuleEntity implements RuleEntity { private Long id; private String app; private String ip; private Integer port; private String limitApp; private String resource; /** * 0为线程数;1为qps */ private Integer grade; private Double count; /** * 0为直接限流;1为关联限流;2为链路限流 ***/ private Integer strategy; private String refResource; /** * 0. default, 1. warm up, 2. rate limiter */ private Integer controlBehavior; private Integer warmUpPeriodSec; /** * max queueing time in rate limiter behavior */ private Integer maxQueueingTimeMs; private boolean clusterMode; /** * Flow rule config for cluster mode. */ private ClusterFlowConfig clusterConfig; private Date gmtCreate; private Date gmtModified; public static FlowRuleEntity fromFlowRule(String app, String ip, Integer port, FlowRule rule) { FlowRuleEntity entity = new FlowRuleEntity(); entity.setApp(app); entity.setIp(ip); entity.setPort(port); entity.setLimitApp(rule.getLimitApp()); entity.setResource(rule.getResource()); entity.setGrade(rule.getGrade()); entity.setCount(rule.getCount()); entity.setStrategy(rule.getStrategy()); entity.setRefResource(rule.getRefResource()); entity.setControlBehavior(rule.getControlBehavior()); entity.setWarmUpPeriodSec(rule.getWarmUpPeriodSec()); entity.setMaxQueueingTimeMs(rule.getMaxQueueingTimeMs()); entity.setClusterMode(rule.isClusterMode()); entity.setClusterConfig(rule.getClusterConfig()); return entity; } @Override public String getIp() { return ip; } public void setIp(String ip) { this.ip = ip; } @Override public Integer getPort() { return port; } public void setPort(Integer port) { this.port = port; } @Override public String getApp() { return app; } public void setApp(String app) { this.app = app; } @Override public Long getId() { return id; } @Override public void setId(Long id) { this.id = id; } public String getLimitApp() { return limitApp; } public void setLimitApp(String limitApp) { this.limitApp = limitApp; } public String getResource() { return resource; } public void setResource(String resource) { this.resource = resource; } public Integer getGrade() { return grade; } public void setGrade(Integer grade) { this.grade = grade; } public Double getCount() { return count; } public void setCount(Double count) { this.count = count; } public Integer getStrategy() { return strategy; } public void setStrategy(Integer strategy) { this.strategy = strategy; } public String getRefResource() { return refResource; } public void setRefResource(String refResource) { this.refResource = refResource; } public Integer getControlBehavior() { return controlBehavior; } public void setControlBehavior(Integer controlBehavior) { this.controlBehavior = controlBehavior; } public Integer getWarmUpPeriodSec() { return warmUpPeriodSec; } public void setWarmUpPeriodSec(Integer warmUpPeriodSec) { this.warmUpPeriodSec = warmUpPeriodSec; } public Integer getMaxQueueingTimeMs() { return maxQueueingTimeMs; } public void setMaxQueueingTimeMs(Integer maxQueueingTimeMs) { this.maxQueueingTimeMs = maxQueueingTimeMs; } public boolean isClusterMode() { return clusterMode; } public FlowRuleEntity setClusterMode(boolean clusterMode) { this.clusterMode = clusterMode; return this; } public ClusterFlowConfig getClusterConfig() { return clusterConfig; } public FlowRuleEntity setClusterConfig(ClusterFlowConfig clusterConfig) { this.clusterConfig = clusterConfig; return this; } @Override public Date getGmtCreate() { return gmtCreate; } public void setGmtCreate(Date gmtCreate) { this.gmtCreate = gmtCreate; } public Date getGmtModified() { return gmtModified; } public void setGmtModified(Date gmtModified) { this.gmtModified = gmtModified; } @Override public FlowRule toRule() { FlowRule flowRule = new FlowRule(); flowRule.setCount(this.count); flowRule.setGrade(this.grade); flowRule.setResource(this.resource); flowRule.setLimitApp(this.limitApp); flowRule.setRefResource(this.refResource); flowRule.setStrategy(this.strategy); if (this.controlBehavior != null) { flowRule.setControlBehavior(controlBehavior); } if (this.warmUpPeriodSec != null) { flowRule.setWarmUpPeriodSec(warmUpPeriodSec); } if (this.maxQueueingTimeMs != null) { flowRule.setMaxQueueingTimeMs(maxQueueingTimeMs); } flowRule.setClusterMode(clusterMode); flowRule.setClusterConfig(clusterConfig); return flowRule; } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/datasource/entity/rule/ParamFlowRuleEntity.java ================================================ /* * 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.datasource.entity.rule; import java.util.List; import com.alibaba.csp.sentinel.slots.block.Rule; import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowClusterConfig; import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowItem; import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule; import com.alibaba.csp.sentinel.util.AssertUtil; import com.fasterxml.jackson.annotation.JsonIgnore; /** * @author Eric Zhao * @since 0.2.1 */ public class ParamFlowRuleEntity extends AbstractRuleEntity { public ParamFlowRuleEntity() {} public ParamFlowRuleEntity(ParamFlowRule rule) { AssertUtil.notNull(rule, "Authority rule should not be null"); this.rule = rule; } public static ParamFlowRuleEntity fromAuthorityRule(String app, String ip, Integer port, ParamFlowRule rule) { ParamFlowRuleEntity entity = new ParamFlowRuleEntity(rule); entity.setApp(app); entity.setIp(ip); entity.setPort(port); return entity; } @JsonIgnore public String getLimitApp() { return rule.getLimitApp(); } @JsonIgnore public String getResource() { return rule.getResource(); } @JsonIgnore public int getGrade() { return rule.getGrade(); } @JsonIgnore public Integer getParamIdx() { return rule.getParamIdx(); } @JsonIgnore public double getCount() { return rule.getCount(); } @JsonIgnore public List getParamFlowItemList() { return rule.getParamFlowItemList(); } @JsonIgnore public boolean isClusterMode() { return rule.isClusterMode(); } @JsonIgnore public ParamFlowClusterConfig getClusterConfig() { return rule.getClusterConfig(); } @Override public Rule toRule() { return rule; } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/datasource/entity/rule/RuleEntity.java ================================================ /* * 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.datasource.entity.rule; import java.util.Date; import com.alibaba.csp.sentinel.slots.block.Rule; /** * @author leyou */ public interface RuleEntity { Long getId(); void setId(Long id); String getApp(); String getIp(); Integer getPort(); Date getGmtCreate(); Rule toRule(); } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/datasource/entity/rule/SystemRuleEntity.java ================================================ /* * 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.datasource.entity.rule; import java.util.Date; import com.alibaba.csp.sentinel.slots.system.SystemRule; /** * @author leyou */ public class SystemRuleEntity implements RuleEntity { private Long id; private String app; private String ip; private Integer port; private Double avgLoad; private Long avgRt; private Long maxThread; private Double qps; private Date gmtCreate; private Date gmtModified; public static SystemRuleEntity fromSystemRule(String app, String ip, Integer port, SystemRule rule) { SystemRuleEntity entity = new SystemRuleEntity(); entity.setApp(app); entity.setIp(ip); entity.setPort(port); entity.setAvgLoad(rule.getHighestSystemLoad()); entity.setAvgRt(rule.getAvgRt()); entity.setMaxThread(rule.getMaxThread()); entity.setQps(rule.getQps()); return entity; } @Override public String getIp() { return ip; } public void setIp(String ip) { this.ip = ip; } @Override public Integer getPort() { return port; } public void setPort(Integer port) { this.port = port; } @Override public Long getId() { return id; } @Override public void setId(Long id) { this.id = id; } @Override public String getApp() { return app; } public void setApp(String app) { this.app = app; } public Double getAvgLoad() { return avgLoad; } public void setAvgLoad(Double avgLoad) { this.avgLoad = avgLoad; } public Long getAvgRt() { return avgRt; } public void setAvgRt(Long avgRt) { this.avgRt = avgRt; } public Long getMaxThread() { return maxThread; } public void setMaxThread(Long maxThread) { this.maxThread = maxThread; } public Double getQps() { return qps; } public void setQps(Double qps) { this.qps = qps; } @Override public Date getGmtCreate() { return gmtCreate; } public void setGmtCreate(Date gmtCreate) { this.gmtCreate = gmtCreate; } public Date getGmtModified() { return gmtModified; } public void setGmtModified(Date gmtModified) { this.gmtModified = gmtModified; } @Override public SystemRule toRule() { SystemRule rule = new SystemRule(); rule.setHighestSystemLoad(avgLoad); rule.setAvgRt(avgRt); rule.setMaxThread(maxThread); rule.setQps(qps); return rule; } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/discovery/AppInfo.java ================================================ /* * 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.discovery; import java.util.Comparator; import java.util.HashSet; import java.util.Optional; import java.util.Iterator; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import com.alibaba.csp.sentinel.dashboard.config.DashboardConfig; public class AppInfo { private String app = ""; private Set machines = ConcurrentHashMap.newKeySet(); public AppInfo() {} public AppInfo(String app) { this.app = app; } public String getApp() { return app; } public void setApp(String app) { this.app = app; } /** * Get the current machines. * * @return a new copy of the current machines. */ public Set getMachines() { return new HashSet<>(machines); } @Override public String toString() { return "AppInfo{" + "app='" + app + ", machines=" + machines + '}'; } public boolean addMachine(MachineInfo machineInfo) { machines.remove(machineInfo); return machines.add(machineInfo); } public synchronized boolean removeMachine(String ip, int port) { Iterator it = machines.iterator(); while (it.hasNext()) { MachineInfo machine = it.next(); if (machine.getIp().equals(ip) && machine.getPort() == port) { it.remove(); return true; } } return false; } public Optional getMachine(String ip, int port) { return machines.stream() .filter(e -> e.getIp().equals(ip) && e.getPort().equals(port)) .findFirst(); } private boolean heartbeatJudge(final int threshold) { if (machines.size() == 0) { return false; } if (threshold > 0) { long healthyCount = machines.stream() .filter(MachineInfo::isHealthy) .count(); if (healthyCount == 0) { // No healthy machines. return machines.stream() .max(Comparator.comparingLong(MachineInfo::getLastHeartbeat)) .map(e -> System.currentTimeMillis() - e.getLastHeartbeat() < threshold) .orElse(false); } } return true; } /** * Check whether current application has no healthy machines and should not be displayed. * * @return true if the application should be displayed in the sidebar, otherwise false */ public boolean isShown() { return heartbeatJudge(DashboardConfig.getHideAppNoMachineMillis()); } /** * Check whether current application has no healthy machines and should be removed. * * @return true if the application is dead and should be removed, otherwise false */ public boolean isDead() { return !heartbeatJudge(DashboardConfig.getRemoveAppNoMachineMillis()); } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/discovery/AppManagement.java ================================================ /* * 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.discovery; import java.util.List; import java.util.Set; import javax.annotation.PostConstruct; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.stereotype.Component; @Component public class AppManagement implements MachineDiscovery { @Autowired private ApplicationContext context; private MachineDiscovery machineDiscovery; @PostConstruct public void init() { machineDiscovery = context.getBean(SimpleMachineDiscovery.class); } @Override public Set getBriefApps() { return machineDiscovery.getBriefApps(); } @Override public long addMachine(MachineInfo machineInfo) { return machineDiscovery.addMachine(machineInfo); } @Override public boolean removeMachine(String app, String ip, int port) { return machineDiscovery.removeMachine(app, ip, port); } @Override public List getAppNames() { return machineDiscovery.getAppNames(); } @Override public AppInfo getDetailApp(String app) { return machineDiscovery.getDetailApp(app); } @Override public void removeApp(String app) { machineDiscovery.removeApp(app); } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/discovery/MachineDiscovery.java ================================================ /* * 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.discovery; import java.util.List; import java.util.Set; public interface MachineDiscovery { String UNKNOWN_APP_NAME = "CLUSTER_NOT_STARTED"; List getAppNames(); Set getBriefApps(); AppInfo getDetailApp(String app); /** * Remove the given app from the application registry. * * @param app application name * @since 1.5.0 */ void removeApp(String app); long addMachine(MachineInfo machineInfo); /** * Remove the given machine instance from the application registry. * * @param app the application name of the machine * @param ip machine IP * @param port machine port * @return true if removed, otherwise false * @since 1.5.0 */ boolean removeMachine(String app, String ip, int port); } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/discovery/MachineInfo.java ================================================ /* * 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.discovery; import java.util.Objects; import com.alibaba.csp.sentinel.dashboard.config.DashboardConfig; import com.alibaba.csp.sentinel.util.StringUtil; public class MachineInfo implements Comparable { private String app = ""; private String hostname = ""; private String ip = ""; private Integer port = -1; private long lastHeartbeat; private long heartbeatVersion; /** * Indicates the version of Sentinel client (since 0.2.0). */ private String version; public static MachineInfo of(String app, String ip, Integer port) { MachineInfo machineInfo = new MachineInfo(); machineInfo.setApp(app); machineInfo.setIp(ip); machineInfo.setPort(port); return machineInfo; } public String toHostPort() { return ip + ":" + port; } public Integer getPort() { return port; } public void setPort(Integer port) { this.port = port; } public String getApp() { return app; } public void setApp(String app) { this.app = app; } public String getHostname() { return hostname; } public void setHostname(String hostname) { this.hostname = hostname; } public String getIp() { return ip; } public void setIp(String ip) { this.ip = ip; } public long getHeartbeatVersion() { return heartbeatVersion; } public void setHeartbeatVersion(long heartbeatVersion) { this.heartbeatVersion = heartbeatVersion; } public String getVersion() { return version; } public MachineInfo setVersion(String version) { this.version = version; return this; } public boolean isHealthy() { long delta = System.currentTimeMillis() - lastHeartbeat; return delta < DashboardConfig.getUnhealthyMachineMillis(); } /** * whether dead should be removed * * @return */ public boolean isDead() { if (DashboardConfig.getAutoRemoveMachineMillis() > 0) { long delta = System.currentTimeMillis() - lastHeartbeat; return delta > DashboardConfig.getAutoRemoveMachineMillis(); } return false; } public long getLastHeartbeat() { return lastHeartbeat; } public void setLastHeartbeat(long lastHeartbeat) { this.lastHeartbeat = lastHeartbeat; } @Override public int compareTo(MachineInfo o) { if (this == o) { return 0; } if (!port.equals(o.getPort())) { return port.compareTo(o.getPort()); } if (!StringUtil.equals(app, o.getApp())) { return app.compareToIgnoreCase(o.getApp()); } return ip.compareToIgnoreCase(o.getIp()); } @Override public String toString() { return new StringBuilder("MachineInfo {") .append("app='").append(app).append('\'') .append(", hostname='").append(hostname).append('\'') .append(", ip='").append(ip).append('\'') .append(", port=").append(port) .append(", heartbeatVersion=").append(heartbeatVersion) .append(", lastHeartbeat=").append(lastHeartbeat) .append(", version='").append(version).append('\'') .append(", healthy=").append(isHealthy()) .append('}').toString(); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof MachineInfo)) { return false; } MachineInfo that = (MachineInfo)o; return Objects.equals(app, that.app) && Objects.equals(ip, that.ip) && Objects.equals(port, that.port); } @Override public int hashCode() { return Objects.hash(app, ip, port); } /** * Information for log * * @return */ public String toLogString() { return app + "|" + ip + "|" + port + "|" + version; } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/discovery/SimpleMachineDiscovery.java ================================================ /* * 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.discovery; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import com.alibaba.csp.sentinel.util.AssertUtil; import org.springframework.stereotype.Component; /** * @author leyou */ @Component public class SimpleMachineDiscovery implements MachineDiscovery { private final ConcurrentMap apps = new ConcurrentHashMap<>(); @Override public long addMachine(MachineInfo machineInfo) { AssertUtil.notNull(machineInfo, "machineInfo cannot be null"); AppInfo appInfo = apps.computeIfAbsent(machineInfo.getApp(), AppInfo::new); appInfo.addMachine(machineInfo); return 1; } @Override public boolean removeMachine(String app, String ip, int port) { AssertUtil.assertNotBlank(app, "app name cannot be blank"); AppInfo appInfo = apps.get(app); if (appInfo != null) { return appInfo.removeMachine(ip, port); } return false; } @Override public List getAppNames() { return new ArrayList<>(apps.keySet()); } @Override public AppInfo getDetailApp(String app) { AssertUtil.assertNotBlank(app, "app name cannot be blank"); return apps.get(app); } @Override public Set getBriefApps() { return new HashSet<>(apps.values()); } @Override public void removeApp(String app) { AssertUtil.assertNotBlank(app, "app name cannot be blank"); apps.remove(app); } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/ResourceTreeNode.java ================================================ /* * 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.domain; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import com.alibaba.csp.sentinel.command.vo.NodeVo; /** * @author leyou */ public class ResourceTreeNode { private String id; private String parentId; private String resource; private Integer threadNum; private Long passQps; private Long blockQps; private Long totalQps; private Long averageRt; private Long successQps; private Long exceptionQps; private Long oneMinutePass; private Long oneMinuteBlock; private Long oneMinuteException; private Long oneMinuteTotal; private boolean visible = true; private List children = new ArrayList<>(); public static ResourceTreeNode fromNodeVoList(List nodeVos) { if (nodeVos == null || nodeVos.isEmpty()) { return null; } ResourceTreeNode root = null; Map map = new HashMap<>(); for (NodeVo vo : nodeVos) { ResourceTreeNode node = fromNodeVo(vo); map.put(node.id, node); // real root if (node.parentId == null) { root = node; } else if (map.containsKey(node.parentId)) { map.get(node.parentId).children.add(node); } else { // impossible } } return root; } public static ResourceTreeNode fromNodeVo(NodeVo vo) { ResourceTreeNode node = new ResourceTreeNode(); node.id = vo.getId(); node.parentId = vo.getParentId(); node.resource = vo.getResource(); node.threadNum = vo.getThreadNum(); node.passQps = vo.getPassQps(); node.blockQps = vo.getBlockQps(); node.totalQps = vo.getTotalQps(); node.averageRt = vo.getAverageRt(); node.successQps = vo.getSuccessQps(); node.exceptionQps = vo.getExceptionQps(); node.oneMinutePass = vo.getOneMinutePass(); node.oneMinuteBlock = vo.getOneMinuteBlock(); node.oneMinuteException = vo.getOneMinuteException(); node.oneMinuteTotal = vo.getOneMinuteTotal(); return node; } public void searchIgnoreCase(String searchKey) { search(this, searchKey); } /** * This node is visible only when searchKey matches this.resource or at least * one of this's children is visible */ private boolean search(ResourceTreeNode node, String searchKey) { // empty matches all if (searchKey == null || searchKey.isEmpty() || node.resource.toLowerCase().contains(searchKey.toLowerCase())) { node.visible = true; } else { node.visible = false; } boolean found = false; for (ResourceTreeNode c : node.children) { found |= search(c, searchKey); } node.visible |= found; return node.visible; } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getParentId() { return parentId; } public void setParentId(String parentId) { this.parentId = parentId; } public String getResource() { return resource; } public void setResource(String resource) { this.resource = resource; } public Integer getThreadNum() { return threadNum; } public void setThreadNum(Integer threadNum) { this.threadNum = threadNum; } public Long getPassQps() { return passQps; } public void setPassQps(Long passQps) { this.passQps = passQps; } public Long getBlockQps() { return blockQps; } public void setBlockQps(Long blockQps) { this.blockQps = blockQps; } public Long getTotalQps() { return totalQps; } public void setTotalQps(Long totalQps) { this.totalQps = totalQps; } public Long getAverageRt() { return averageRt; } public void setAverageRt(Long averageRt) { this.averageRt = averageRt; } public Long getSuccessQps() { return successQps; } public void setSuccessQps(Long successQps) { this.successQps = successQps; } public Long getExceptionQps() { return exceptionQps; } public void setExceptionQps(Long exceptionQps) { this.exceptionQps = exceptionQps; } public Long getOneMinutePass() { return oneMinutePass; } public void setOneMinutePass(Long oneMinutePass) { this.oneMinutePass = oneMinutePass; } public Long getOneMinuteBlock() { return oneMinuteBlock; } public void setOneMinuteBlock(Long oneMinuteBlock) { this.oneMinuteBlock = oneMinuteBlock; } public Long getOneMinuteException() { return oneMinuteException; } public void setOneMinuteException(Long oneMinuteException) { this.oneMinuteException = oneMinuteException; } public Long getOneMinuteTotal() { return oneMinuteTotal; } public void setOneMinuteTotal(Long oneMinuteTotal) { this.oneMinuteTotal = oneMinuteTotal; } public boolean isVisible() { return visible; } public void setVisible(boolean visible) { this.visible = visible; } public List getChildren() { return children; } public void setChildren(List children) { this.children = children; } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/Result.java ================================================ /* * 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.domain; /** * @author leyou * @author Eric Zhao */ public class Result { private boolean success; private int code; private String msg; private R data; public static Result ofSuccess(R data) { return new Result() .setSuccess(true) .setMsg("success") .setData(data); } public static Result ofSuccessMsg(String msg) { return new Result() .setSuccess(true) .setMsg(msg); } public static Result ofFail(int code, String msg) { Result result = new Result<>(); result.setSuccess(false); result.setCode(code); result.setMsg(msg); return result; } public static Result ofThrowable(int code, Throwable throwable) { Result result = new Result<>(); result.setSuccess(false); result.setCode(code); result.setMsg(throwable.getClass().getName() + ", " + throwable.getMessage()); return result; } public boolean isSuccess() { return success; } public Result setSuccess(boolean success) { this.success = success; return this; } public int getCode() { return code; } public Result setCode(int code) { this.code = code; return this; } public String getMsg() { return msg; } public Result setMsg(String msg) { this.msg = msg; return this; } public R getData() { return data; } public Result setData(R data) { this.data = data; return this; } @Override public String toString() { return "Result{" + "success=" + success + ", code=" + code + ", msg='" + msg + '\'' + ", data=" + data + '}'; } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/cluster/ClusterAppAssignResultVO.java ================================================ /* * 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.domain.cluster; import java.util.Set; /** * @author Eric Zhao * @since 1.4.1 */ public class ClusterAppAssignResultVO { private Set failedServerSet; private Set failedClientSet; private Integer totalCount; public Set getFailedServerSet() { return failedServerSet; } public ClusterAppAssignResultVO setFailedServerSet(Set failedServerSet) { this.failedServerSet = failedServerSet; return this; } public Set getFailedClientSet() { return failedClientSet; } public ClusterAppAssignResultVO setFailedClientSet(Set failedClientSet) { this.failedClientSet = failedClientSet; return this; } public Integer getTotalCount() { return totalCount; } public ClusterAppAssignResultVO setTotalCount(Integer totalCount) { this.totalCount = totalCount; return this; } @Override public String toString() { return "ClusterAppAssignResultVO{" + "failedServerSet=" + failedServerSet + ", failedClientSet=" + failedClientSet + ", totalCount=" + totalCount + '}'; } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/cluster/ClusterAppFullAssignRequest.java ================================================ /* * 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.domain.cluster; import java.util.List; import java.util.Set; import com.alibaba.csp.sentinel.dashboard.domain.cluster.request.ClusterAppAssignMap; /** * @author Eric Zhao * @since 1.4.1 */ public class ClusterAppFullAssignRequest { private List clusterMap; private Set remainingList; public List getClusterMap() { return clusterMap; } public ClusterAppFullAssignRequest setClusterMap( List clusterMap) { this.clusterMap = clusterMap; return this; } public Set getRemainingList() { return remainingList; } public ClusterAppFullAssignRequest setRemainingList(Set remainingList) { this.remainingList = remainingList; return this; } @Override public String toString() { return "ClusterAppFullAssignRequest{" + "clusterMap=" + clusterMap + ", remainingList=" + remainingList + '}'; } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/cluster/ClusterAppSingleServerAssignRequest.java ================================================ /* * 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.domain.cluster; import java.util.Set; import com.alibaba.csp.sentinel.dashboard.domain.cluster.request.ClusterAppAssignMap; /** * @author Eric Zhao * @since 1.4.1 */ public class ClusterAppSingleServerAssignRequest { private ClusterAppAssignMap clusterMap; private Set remainingList; public ClusterAppAssignMap getClusterMap() { return clusterMap; } public ClusterAppSingleServerAssignRequest setClusterMap(ClusterAppAssignMap clusterMap) { this.clusterMap = clusterMap; return this; } public Set getRemainingList() { return remainingList; } public ClusterAppSingleServerAssignRequest setRemainingList(Set remainingList) { this.remainingList = remainingList; return this; } @Override public String toString() { return "ClusterAppSingleServerAssignRequest{" + "clusterMap=" + clusterMap + ", remainingList=" + remainingList + '}'; } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/cluster/ClusterClientInfoVO.java ================================================ /* * 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.domain.cluster; /** * @author Eric Zhao * @since 1.4.1 */ public class ClusterClientInfoVO { private String serverHost; private Integer serverPort; private Integer clientState; private Integer requestTimeout; public String getServerHost() { return serverHost; } public ClusterClientInfoVO setServerHost(String serverHost) { this.serverHost = serverHost; return this; } public Integer getServerPort() { return serverPort; } public ClusterClientInfoVO setServerPort(Integer serverPort) { this.serverPort = serverPort; return this; } public Integer getClientState() { return clientState; } public ClusterClientInfoVO setClientState(Integer clientState) { this.clientState = clientState; return this; } public Integer getRequestTimeout() { return requestTimeout; } public ClusterClientInfoVO setRequestTimeout(Integer requestTimeout) { this.requestTimeout = requestTimeout; return this; } @Override public String toString() { return "ClusterClientInfoVO{" + "serverHost='" + serverHost + '\'' + ", serverPort=" + serverPort + ", clientState=" + clientState + ", requestTimeout=" + requestTimeout + '}'; } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/cluster/ClusterGroupEntity.java ================================================ /* * 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.domain.cluster; import java.util.HashSet; import java.util.Set; /** * @author Eric Zhao * @since 1.4.1 */ public class ClusterGroupEntity { private String machineId; private String ip; private Integer port; private Set clientSet = new HashSet<>(); private Boolean belongToApp; public String getMachineId() { return machineId; } public ClusterGroupEntity setMachineId(String machineId) { this.machineId = machineId; return this; } public String getIp() { return ip; } public ClusterGroupEntity setIp(String ip) { this.ip = ip; return this; } public Integer getPort() { return port; } public ClusterGroupEntity setPort(Integer port) { this.port = port; return this; } public Set getClientSet() { return clientSet; } public ClusterGroupEntity setClientSet(Set clientSet) { this.clientSet = clientSet; return this; } public Boolean getBelongToApp() { return belongToApp; } public ClusterGroupEntity setBelongToApp(Boolean belongToApp) { this.belongToApp = belongToApp; return this; } @Override public String toString() { return "ClusterGroupEntity{" + "machineId='" + machineId + '\'' + ", ip='" + ip + '\'' + ", port=" + port + ", clientSet=" + clientSet + ", belongToApp=" + belongToApp + '}'; } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/cluster/ClusterStateSingleVO.java ================================================ /* * 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.domain.cluster; /** * @author Eric Zhao * @since 1.4.1 */ public class ClusterStateSingleVO { private String address; private Integer mode; private String target; public String getAddress() { return address; } public ClusterStateSingleVO setAddress(String address) { this.address = address; return this; } public Integer getMode() { return mode; } public ClusterStateSingleVO setMode(Integer mode) { this.mode = mode; return this; } public String getTarget() { return target; } public ClusterStateSingleVO setTarget(String target) { this.target = target; return this; } @Override public String toString() { return "ClusterStateSingleVO{" + "address='" + address + '\'' + ", mode=" + mode + ", target='" + target + '\'' + '}'; } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/cluster/ConnectionDescriptorVO.java ================================================ /* * 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.domain.cluster; /** * @author Eric Zhao * @since 1.4.0 */ public class ConnectionDescriptorVO { private String address; private String host; public String getAddress() { return address; } public ConnectionDescriptorVO setAddress(String address) { this.address = address; return this; } public String getHost() { return host; } public ConnectionDescriptorVO setHost(String host) { this.host = host; return this; } @Override public String toString() { return "ConnectionDescriptorVO{" + "address='" + address + '\'' + ", host='" + host + '\'' + '}'; } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/cluster/ConnectionGroupVO.java ================================================ /* * 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.domain.cluster; import java.util.List; /** * @author Eric Zhao * @since 1.4.0 */ public class ConnectionGroupVO { private String namespace; private List connectionSet; private Integer connectedCount; public String getNamespace() { return namespace; } public ConnectionGroupVO setNamespace(String namespace) { this.namespace = namespace; return this; } public List getConnectionSet() { return connectionSet; } public ConnectionGroupVO setConnectionSet( List connectionSet) { this.connectionSet = connectionSet; return this; } public Integer getConnectedCount() { return connectedCount; } public ConnectionGroupVO setConnectedCount(Integer connectedCount) { this.connectedCount = connectedCount; return this; } @Override public String toString() { return "ConnectionGroupVO{" + "namespace='" + namespace + '\'' + ", connectionSet=" + connectionSet + ", connectedCount=" + connectedCount + '}'; } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/cluster/config/ClusterClientConfig.java ================================================ /* * 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.domain.cluster.config; /** * @author Eric Zhao * @since 1.4.0 */ public class ClusterClientConfig { private String serverHost; private Integer serverPort; private Integer requestTimeout; private Integer connectTimeout; public String getServerHost() { return serverHost; } public ClusterClientConfig setServerHost(String serverHost) { this.serverHost = serverHost; return this; } public Integer getServerPort() { return serverPort; } public ClusterClientConfig setServerPort(Integer serverPort) { this.serverPort = serverPort; return this; } public Integer getRequestTimeout() { return requestTimeout; } public ClusterClientConfig setRequestTimeout(Integer requestTimeout) { this.requestTimeout = requestTimeout; return this; } public Integer getConnectTimeout() { return connectTimeout; } public ClusterClientConfig setConnectTimeout(Integer connectTimeout) { this.connectTimeout = connectTimeout; return this; } @Override public String toString() { return "ClusterClientConfig{" + "serverHost='" + serverHost + '\'' + ", serverPort=" + serverPort + ", requestTimeout=" + requestTimeout + ", connectTimeout=" + connectTimeout + '}'; } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/cluster/config/ServerFlowConfig.java ================================================ /* * 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.domain.cluster.config; /** * @author Eric Zhao * @since 1.4.0 */ public class ServerFlowConfig { public static final double DEFAULT_EXCEED_COUNT = 1.0d; public static final double DEFAULT_MAX_OCCUPY_RATIO = 1.0d; public static final int DEFAULT_INTERVAL_MS = 1000; public static final int DEFAULT_SAMPLE_COUNT= 10; public static final double DEFAULT_MAX_ALLOWED_QPS= 30000; private final String namespace; private Double exceedCount = DEFAULT_EXCEED_COUNT; private Double maxOccupyRatio = DEFAULT_MAX_OCCUPY_RATIO; private Integer intervalMs = DEFAULT_INTERVAL_MS; private Integer sampleCount = DEFAULT_SAMPLE_COUNT; private Double maxAllowedQps = DEFAULT_MAX_ALLOWED_QPS; public ServerFlowConfig() { this("default"); } public ServerFlowConfig(String namespace) { this.namespace = namespace; } public String getNamespace() { return namespace; } public Double getExceedCount() { return exceedCount; } public ServerFlowConfig setExceedCount(Double exceedCount) { this.exceedCount = exceedCount; return this; } public Double getMaxOccupyRatio() { return maxOccupyRatio; } public ServerFlowConfig setMaxOccupyRatio(Double maxOccupyRatio) { this.maxOccupyRatio = maxOccupyRatio; return this; } public Integer getIntervalMs() { return intervalMs; } public ServerFlowConfig setIntervalMs(Integer intervalMs) { this.intervalMs = intervalMs; return this; } public Integer getSampleCount() { return sampleCount; } public ServerFlowConfig setSampleCount(Integer sampleCount) { this.sampleCount = sampleCount; return this; } public Double getMaxAllowedQps() { return maxAllowedQps; } public ServerFlowConfig setMaxAllowedQps(Double maxAllowedQps) { this.maxAllowedQps = maxAllowedQps; return this; } @Override public String toString() { return "ServerFlowConfig{" + "namespace='" + namespace + '\'' + ", exceedCount=" + exceedCount + ", maxOccupyRatio=" + maxOccupyRatio + ", intervalMs=" + intervalMs + ", sampleCount=" + sampleCount + ", maxAllowedQps=" + maxAllowedQps + '}'; } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/cluster/config/ServerTransportConfig.java ================================================ /* * 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.domain.cluster.config; /** * @author Eric Zhao * @since 1.4.0 */ public class ServerTransportConfig { public static final int DEFAULT_PORT = 18730; public static final int DEFAULT_IDLE_SECONDS = 600; private Integer port; private Integer idleSeconds; public ServerTransportConfig() { this(DEFAULT_PORT, DEFAULT_IDLE_SECONDS); } public ServerTransportConfig(Integer port, Integer idleSeconds) { this.port = port; this.idleSeconds = idleSeconds; } public Integer getPort() { return port; } public ServerTransportConfig setPort(Integer port) { this.port = port; return this; } public Integer getIdleSeconds() { return idleSeconds; } public ServerTransportConfig setIdleSeconds(Integer idleSeconds) { this.idleSeconds = idleSeconds; return this; } @Override public String toString() { return "ServerTransportConfig{" + "port=" + port + ", idleSeconds=" + idleSeconds + '}'; } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/cluster/request/ClusterAppAssignMap.java ================================================ /* * 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.domain.cluster.request; import java.util.Set; /** * @author Eric Zhao * @since 1.4.1 */ public class ClusterAppAssignMap { private String machineId; private String ip; private Integer port; private Boolean belongToApp; private Set clientSet; private Set namespaceSet; private Double maxAllowedQps; public String getMachineId() { return machineId; } public ClusterAppAssignMap setMachineId(String machineId) { this.machineId = machineId; return this; } public String getIp() { return ip; } public ClusterAppAssignMap setIp(String ip) { this.ip = ip; return this; } public Integer getPort() { return port; } public ClusterAppAssignMap setPort(Integer port) { this.port = port; return this; } public Set getClientSet() { return clientSet; } public ClusterAppAssignMap setClientSet(Set clientSet) { this.clientSet = clientSet; return this; } public Set getNamespaceSet() { return namespaceSet; } public ClusterAppAssignMap setNamespaceSet(Set namespaceSet) { this.namespaceSet = namespaceSet; return this; } public Boolean getBelongToApp() { return belongToApp; } public ClusterAppAssignMap setBelongToApp(Boolean belongToApp) { this.belongToApp = belongToApp; return this; } public Double getMaxAllowedQps() { return maxAllowedQps; } public ClusterAppAssignMap setMaxAllowedQps(Double maxAllowedQps) { this.maxAllowedQps = maxAllowedQps; return this; } @Override public String toString() { return "ClusterAppAssignMap{" + "machineId='" + machineId + '\'' + ", ip='" + ip + '\'' + ", port=" + port + ", belongToApp=" + belongToApp + ", clientSet=" + clientSet + ", namespaceSet=" + namespaceSet + ", maxAllowedQps=" + maxAllowedQps + '}'; } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/cluster/request/ClusterClientModifyRequest.java ================================================ /* * 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.domain.cluster.request; import com.alibaba.csp.sentinel.dashboard.domain.cluster.config.ClusterClientConfig; /** * @author Eric Zhao * @since 1.4.0 */ public class ClusterClientModifyRequest implements ClusterModifyRequest { private String app; private String ip; private Integer port; private Integer mode; private ClusterClientConfig clientConfig; @Override public String getApp() { return app; } public ClusterClientModifyRequest setApp(String app) { this.app = app; return this; } @Override public String getIp() { return ip; } public ClusterClientModifyRequest setIp(String ip) { this.ip = ip; return this; } @Override public Integer getPort() { return port; } public ClusterClientModifyRequest setPort(Integer port) { this.port = port; return this; } @Override public Integer getMode() { return mode; } public ClusterClientModifyRequest setMode(Integer mode) { this.mode = mode; return this; } public ClusterClientConfig getClientConfig() { return clientConfig; } public ClusterClientModifyRequest setClientConfig( ClusterClientConfig clientConfig) { this.clientConfig = clientConfig; return this; } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/cluster/request/ClusterModifyRequest.java ================================================ /* * 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.domain.cluster.request; /** * @author Eric Zhao * @since 1.4.0 */ public interface ClusterModifyRequest { String getApp(); String getIp(); Integer getPort(); Integer getMode(); } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/cluster/request/ClusterServerModifyRequest.java ================================================ /* * 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.domain.cluster.request; import java.util.Set; import com.alibaba.csp.sentinel.dashboard.domain.cluster.config.ServerFlowConfig; import com.alibaba.csp.sentinel.dashboard.domain.cluster.config.ServerTransportConfig; /** * @author Eric Zhao * @since 1.4.0 */ public class ClusterServerModifyRequest implements ClusterModifyRequest { private String app; private String ip; private Integer port; private Integer mode; private ServerFlowConfig flowConfig; private ServerTransportConfig transportConfig; private Set namespaceSet; @Override public String getApp() { return app; } public ClusterServerModifyRequest setApp(String app) { this.app = app; return this; } @Override public String getIp() { return ip; } public ClusterServerModifyRequest setIp(String ip) { this.ip = ip; return this; } @Override public Integer getPort() { return port; } public ClusterServerModifyRequest setPort(Integer port) { this.port = port; return this; } @Override public Integer getMode() { return mode; } public ClusterServerModifyRequest setMode(Integer mode) { this.mode = mode; return this; } public ServerFlowConfig getFlowConfig() { return flowConfig; } public ClusterServerModifyRequest setFlowConfig( ServerFlowConfig flowConfig) { this.flowConfig = flowConfig; return this; } public ServerTransportConfig getTransportConfig() { return transportConfig; } public ClusterServerModifyRequest setTransportConfig( ServerTransportConfig transportConfig) { this.transportConfig = transportConfig; return this; } public Set getNamespaceSet() { return namespaceSet; } public ClusterServerModifyRequest setNamespaceSet(Set namespaceSet) { this.namespaceSet = namespaceSet; return this; } @Override public String toString() { return "ClusterServerModifyRequest{" + "app='" + app + '\'' + ", ip='" + ip + '\'' + ", port=" + port + ", mode=" + mode + ", flowConfig=" + flowConfig + ", transportConfig=" + transportConfig + ", namespaceSet=" + namespaceSet + '}'; } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/cluster/state/AppClusterClientStateWrapVO.java ================================================ /* * 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.domain.cluster.state; /** * @author Eric Zhao * @since 1.4.1 */ public class AppClusterClientStateWrapVO { /** * {ip}@{transport_command_port}. */ private String id; private Integer commandPort; private String ip; private ClusterClientStateVO state; public String getId() { return id; } public AppClusterClientStateWrapVO setId(String id) { this.id = id; return this; } public String getIp() { return ip; } public AppClusterClientStateWrapVO setIp(String ip) { this.ip = ip; return this; } public ClusterClientStateVO getState() { return state; } public AppClusterClientStateWrapVO setState(ClusterClientStateVO state) { this.state = state; return this; } public Integer getCommandPort() { return commandPort; } public AppClusterClientStateWrapVO setCommandPort(Integer commandPort) { this.commandPort = commandPort; return this; } @Override public String toString() { return "AppClusterClientStateWrapVO{" + "id='" + id + '\'' + ", commandPort=" + commandPort + ", ip='" + ip + '\'' + ", state=" + state + '}'; } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/cluster/state/AppClusterServerStateWrapVO.java ================================================ /* * 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.domain.cluster.state; /** * @author Eric Zhao * @since 1.4.1 */ public class AppClusterServerStateWrapVO { /** * {ip}@{transport_command_port}. */ private String id; private String ip; private Integer port; private Integer connectedCount; private Boolean belongToApp; private ClusterServerStateVO state; public String getId() { return id; } public AppClusterServerStateWrapVO setId(String id) { this.id = id; return this; } public String getIp() { return ip; } public AppClusterServerStateWrapVO setIp(String ip) { this.ip = ip; return this; } public Integer getPort() { return port; } public AppClusterServerStateWrapVO setPort(Integer port) { this.port = port; return this; } public Boolean getBelongToApp() { return belongToApp; } public AppClusterServerStateWrapVO setBelongToApp(Boolean belongToApp) { this.belongToApp = belongToApp; return this; } public Integer getConnectedCount() { return connectedCount; } public AppClusterServerStateWrapVO setConnectedCount(Integer connectedCount) { this.connectedCount = connectedCount; return this; } public ClusterServerStateVO getState() { return state; } public AppClusterServerStateWrapVO setState(ClusterServerStateVO state) { this.state = state; return this; } @Override public String toString() { return "AppClusterServerStateWrapVO{" + "id='" + id + '\'' + ", ip='" + ip + '\'' + ", port='" + port + '\'' + ", belongToApp=" + belongToApp + ", state=" + state + '}'; } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/cluster/state/ClusterClientStateVO.java ================================================ /* * 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.domain.cluster.state; import com.alibaba.csp.sentinel.dashboard.domain.cluster.ClusterClientInfoVO; /** * @author Eric Zhao * @since 1.4.0 */ public class ClusterClientStateVO { /** * Cluster token client state. */ private ClusterClientInfoVO clientConfig; public ClusterClientInfoVO getClientConfig() { return clientConfig; } public ClusterClientStateVO setClientConfig(ClusterClientInfoVO clientConfig) { this.clientConfig = clientConfig; return this; } @Override public String toString() { return "ClusterClientStateVO{" + "clientConfig=" + clientConfig + '}'; } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/cluster/state/ClusterRequestLimitVO.java ================================================ /* * 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.domain.cluster.state; /** * @author Eric Zhao * @since 1.4.1 */ public class ClusterRequestLimitVO { private String namespace; private Double currentQps; private Double maxAllowedQps; public String getNamespace() { return namespace; } public ClusterRequestLimitVO setNamespace(String namespace) { this.namespace = namespace; return this; } public Double getCurrentQps() { return currentQps; } public ClusterRequestLimitVO setCurrentQps(Double currentQps) { this.currentQps = currentQps; return this; } public Double getMaxAllowedQps() { return maxAllowedQps; } public ClusterRequestLimitVO setMaxAllowedQps(Double maxAllowedQps) { this.maxAllowedQps = maxAllowedQps; return this; } @Override public String toString() { return "ClusterRequestLimitVO{" + "namespace='" + namespace + '\'' + ", currentQps=" + currentQps + ", maxAllowedQps=" + maxAllowedQps + '}'; } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/cluster/state/ClusterServerStateVO.java ================================================ /* * 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.domain.cluster.state; import java.util.List; import java.util.Set; import com.alibaba.csp.sentinel.dashboard.domain.cluster.ConnectionGroupVO; import com.alibaba.csp.sentinel.dashboard.domain.cluster.config.ServerFlowConfig; import com.alibaba.csp.sentinel.dashboard.domain.cluster.config.ServerTransportConfig; /** * @author Eric Zhao * @since 1.4.0 */ public class ClusterServerStateVO { private String appName; private ServerTransportConfig transport; private ServerFlowConfig flow; private Set namespaceSet; private Integer port; private List connection; private List requestLimitData; private Boolean embedded; public String getAppName() { return appName; } public ClusterServerStateVO setAppName(String appName) { this.appName = appName; return this; } public ServerTransportConfig getTransport() { return transport; } public ClusterServerStateVO setTransport(ServerTransportConfig transport) { this.transport = transport; return this; } public ServerFlowConfig getFlow() { return flow; } public ClusterServerStateVO setFlow(ServerFlowConfig flow) { this.flow = flow; return this; } public Set getNamespaceSet() { return namespaceSet; } public ClusterServerStateVO setNamespaceSet(Set namespaceSet) { this.namespaceSet = namespaceSet; return this; } public Integer getPort() { return port; } public ClusterServerStateVO setPort(Integer port) { this.port = port; return this; } public List getConnection() { return connection; } public ClusterServerStateVO setConnection(List connection) { this.connection = connection; return this; } public List getRequestLimitData() { return requestLimitData; } public ClusterServerStateVO setRequestLimitData(List requestLimitData) { this.requestLimitData = requestLimitData; return this; } public Boolean getEmbedded() { return embedded; } public ClusterServerStateVO setEmbedded(Boolean embedded) { this.embedded = embedded; return this; } @Override public String toString() { return "ClusterServerStateVO{" + "appName='" + appName + '\'' + ", transport=" + transport + ", flow=" + flow + ", namespaceSet=" + namespaceSet + ", port=" + port + ", connection=" + connection + ", requestLimitData=" + requestLimitData + ", embedded=" + embedded + '}'; } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/cluster/state/ClusterStateSimpleEntity.java ================================================ /* * 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.domain.cluster.state; /** * @author Eric Zhao * @since 1.4.0 */ public class ClusterStateSimpleEntity { private Integer mode; private Long lastModified; private Boolean clientAvailable; private Boolean serverAvailable; public Integer getMode() { return mode; } public ClusterStateSimpleEntity setMode(Integer mode) { this.mode = mode; return this; } public Long getLastModified() { return lastModified; } public ClusterStateSimpleEntity setLastModified(Long lastModified) { this.lastModified = lastModified; return this; } public Boolean getClientAvailable() { return clientAvailable; } public ClusterStateSimpleEntity setClientAvailable(Boolean clientAvailable) { this.clientAvailable = clientAvailable; return this; } public Boolean getServerAvailable() { return serverAvailable; } public ClusterStateSimpleEntity setServerAvailable(Boolean serverAvailable) { this.serverAvailable = serverAvailable; return this; } @Override public String toString() { return "ClusterStateSimpleEntity{" + "mode=" + mode + ", lastModified=" + lastModified + ", clientAvailable=" + clientAvailable + ", serverAvailable=" + serverAvailable + '}'; } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/cluster/state/ClusterUniversalStatePairVO.java ================================================ /* * 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.domain.cluster.state; /** * @author Eric Zhao * @since 1.4.1 */ public class ClusterUniversalStatePairVO { private String ip; private Integer commandPort; private ClusterUniversalStateVO state; public ClusterUniversalStatePairVO() {} public ClusterUniversalStatePairVO(String ip, Integer commandPort, ClusterUniversalStateVO state) { this.ip = ip; this.commandPort = commandPort; this.state = state; } public String getIp() { return ip; } public ClusterUniversalStatePairVO setIp(String ip) { this.ip = ip; return this; } public Integer getCommandPort() { return commandPort; } public ClusterUniversalStatePairVO setCommandPort(Integer commandPort) { this.commandPort = commandPort; return this; } public ClusterUniversalStateVO getState() { return state; } public ClusterUniversalStatePairVO setState(ClusterUniversalStateVO state) { this.state = state; return this; } @Override public String toString() { return "ClusterUniversalStatePairVO{" + "ip='" + ip + '\'' + ", commandPort=" + commandPort + ", state=" + state + '}'; } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/cluster/state/ClusterUniversalStateVO.java ================================================ /* * 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.domain.cluster.state; /** * @author Eric Zhao * @since 1.4.0 */ public class ClusterUniversalStateVO { private ClusterStateSimpleEntity stateInfo; private ClusterClientStateVO client; private ClusterServerStateVO server; public ClusterClientStateVO getClient() { return client; } public ClusterUniversalStateVO setClient(ClusterClientStateVO client) { this.client = client; return this; } public ClusterServerStateVO getServer() { return server; } public ClusterUniversalStateVO setServer(ClusterServerStateVO server) { this.server = server; return this; } public ClusterStateSimpleEntity getStateInfo() { return stateInfo; } public ClusterUniversalStateVO setStateInfo( ClusterStateSimpleEntity stateInfo) { this.stateInfo = stateInfo; return this; } @Override public String toString() { return "ClusterUniversalStateVO{" + "stateInfo=" + stateInfo + ", client=" + client + ", server=" + server + '}'; } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/vo/MachineInfoVo.java ================================================ /* * 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.domain.vo; import java.util.ArrayList; import java.util.List; import com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo; /** * @author leyou */ public class MachineInfoVo { private String app; private String hostname; private String ip; private int port; private long heartbeatVersion; private long lastHeartbeat; private boolean healthy; private String version; public static List fromMachineInfoList(List machines) { List list = new ArrayList<>(); for (MachineInfo machine : machines) { list.add(fromMachineInfo(machine)); } return list; } public static MachineInfoVo fromMachineInfo(MachineInfo machine) { MachineInfoVo vo = new MachineInfoVo(); vo.setApp(machine.getApp()); vo.setHostname(machine.getHostname()); vo.setIp(machine.getIp()); vo.setPort(machine.getPort()); vo.setLastHeartbeat(machine.getLastHeartbeat()); vo.setHeartbeatVersion(machine.getHeartbeatVersion()); vo.setVersion(machine.getVersion()); vo.setHealthy(machine.isHealthy()); return vo; } public String getApp() { return app; } public void setApp(String app) { this.app = app; } public String getHostname() { return hostname; } public void setHostname(String hostname) { this.hostname = hostname; } public String getIp() { return ip; } public void setIp(String ip) { this.ip = ip; } public int getPort() { return port; } public void setPort(int port) { this.port = port; } public long getLastHeartbeat() { return lastHeartbeat; } public void setLastHeartbeat(long lastHeartbeat) { this.lastHeartbeat = lastHeartbeat; } public void setHeartbeatVersion(long heartbeatVersion) { this.heartbeatVersion = heartbeatVersion; } public long getHeartbeatVersion() { return heartbeatVersion; } public String getVersion() { return version; } public MachineInfoVo setVersion(String version) { this.version = version; return this; } public boolean isHealthy() { return healthy; } public void setHealthy(boolean healthy) { this.healthy = healthy; } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/vo/MetricVo.java ================================================ /* * 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.domain.vo; import java.util.ArrayList; import java.util.Collection; import java.util.List; import com.alibaba.csp.sentinel.dashboard.datasource.entity.MetricEntity; /** * @author leyou */ public class MetricVo implements Comparable { private Long id; private String app; private Long timestamp; private Long gmtCreate = System.currentTimeMillis(); private String resource; private Long passQps; private Long blockQps; private Long successQps; private Long exceptionQps; /** * average rt */ private Double rt; private Integer count; public MetricVo() { } public static List fromMetricEntities(Collection entities) { List list = new ArrayList<>(); if (entities != null) { for (MetricEntity entity : entities) { list.add(fromMetricEntity(entity)); } } return list; } /** * 保留资源名为identity的结果。 * * @param entities 通过hashCode查找到的MetricEntities * @param identity 真正需要查找的资源名 * @return */ public static List fromMetricEntities(Collection entities, String identity) { List list = new ArrayList<>(); if (entities != null) { for (MetricEntity entity : entities) { if (entity.getResource().equals(identity)) { list.add(fromMetricEntity(entity)); } } } return list; } public static MetricVo fromMetricEntity(MetricEntity entity) { MetricVo vo = new MetricVo(); vo.id = entity.getId(); vo.app = entity.getApp(); vo.timestamp = entity.getTimestamp().getTime(); vo.gmtCreate = entity.getGmtCreate().getTime(); vo.resource = entity.getResource(); vo.passQps = entity.getPassQps(); vo.blockQps = entity.getBlockQps(); vo.successQps = entity.getSuccessQps(); vo.exceptionQps = entity.getExceptionQps(); if (entity.getSuccessQps() != 0) { vo.rt = entity.getRt() / entity.getSuccessQps(); } else { vo.rt = 0D; } vo.count = entity.getCount(); return vo; } public static MetricVo parse(String line) { String[] strs = line.split("\\|"); long timestamp = Long.parseLong(strs[0]); String identity = strs[1]; long passQps = Long.parseLong(strs[2]); long blockQps = Long.parseLong(strs[3]); long exception = Long.parseLong(strs[4]); double rt = Double.parseDouble(strs[5]); long successQps = Long.parseLong(strs[6]); MetricVo vo = new MetricVo(); vo.timestamp = timestamp; vo.resource = identity; vo.passQps = passQps; vo.blockQps = blockQps; vo.successQps = successQps; vo.exceptionQps = exception; vo.rt = rt; vo.count = 1; return vo; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getApp() { return app; } public void setApp(String app) { this.app = app; } public Long getTimestamp() { return timestamp; } public void setTimestamp(Long timestamp) { this.timestamp = timestamp; } public Long getGmtCreate() { return gmtCreate; } public void setGmtCreate(Long gmtCreate) { this.gmtCreate = gmtCreate; } public String getResource() { return resource; } public void setResource(String resource) { this.resource = resource; } public Long getPassQps() { return passQps; } public void setPassQps(Long passQps) { this.passQps = passQps; } public Long getBlockQps() { return blockQps; } public void setBlockQps(Long blockQps) { this.blockQps = blockQps; } public Long getSuccessQps() { return successQps; } public void setSuccessQps(Long successQps) { this.successQps = successQps; } public Long getExceptionQps() { return exceptionQps; } public void setExceptionQps(Long exceptionQps) { this.exceptionQps = exceptionQps; } public Double getRt() { return rt; } public void setRt(Double rt) { this.rt = rt; } public Integer getCount() { return count; } public void setCount(Integer count) { this.count = count; } @Override public int compareTo(MetricVo o) { return Long.compare(this.timestamp, o.timestamp); } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/vo/ResourceVo.java ================================================ /* * 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.domain.vo; import java.util.ArrayList; import java.util.List; import com.alibaba.csp.sentinel.command.vo.NodeVo; import com.alibaba.csp.sentinel.dashboard.domain.ResourceTreeNode; /** * @author leyou */ public class ResourceVo { private String parentTtId; private String ttId; private String resource; private Integer threadNum; private Long passQps; private Long blockQps; private Long totalQps; private Long averageRt; private Long passRequestQps; private Long exceptionQps; private Long oneMinutePass; private Long oneMinuteBlock; private Long oneMinuteException; private Long oneMinuteTotal; private boolean visible = true; public ResourceVo() { } public static List fromNodeVoList(List nodeVos) { if (nodeVos == null) { return null; } List list = new ArrayList<>(); for (NodeVo nodeVo : nodeVos) { ResourceVo vo = new ResourceVo(); vo.parentTtId = nodeVo.getParentId(); vo.ttId = nodeVo.getId(); vo.resource = nodeVo.getResource(); vo.threadNum = nodeVo.getThreadNum(); vo.passQps = nodeVo.getPassQps(); vo.blockQps = nodeVo.getBlockQps(); vo.totalQps = nodeVo.getTotalQps(); vo.averageRt = nodeVo.getAverageRt(); vo.exceptionQps = nodeVo.getExceptionQps(); vo.oneMinutePass = nodeVo.getOneMinutePass(); vo.oneMinuteBlock = nodeVo.getOneMinuteBlock(); vo.oneMinuteException = nodeVo.getOneMinuteException(); vo.oneMinuteTotal = nodeVo.getOneMinuteTotal(); list.add(vo); } return list; } public static List fromResourceTreeNode(ResourceTreeNode root) { if (root == null) { return null; } List list = new ArrayList<>(); visit(root, list, false, true); //if(!list.isEmpty()){ // list.remove(0); //} return list; } /** * This node is visible when this.visible==true or one of this's parents is visible, * root node is always invisible. */ private static void visit(ResourceTreeNode node, List list, boolean parentVisible, boolean isRoot) { boolean visible = !isRoot && (node.isVisible() || parentVisible); //boolean visible = node.isVisible(); if (visible) { ResourceVo vo = new ResourceVo(); vo.parentTtId = node.getParentId(); vo.ttId = node.getId(); vo.resource = node.getResource(); vo.threadNum = node.getThreadNum(); vo.passQps = node.getPassQps(); vo.blockQps = node.getBlockQps(); vo.totalQps = node.getTotalQps(); vo.averageRt = node.getAverageRt(); vo.exceptionQps = node.getExceptionQps(); vo.oneMinutePass = node.getOneMinutePass(); vo.oneMinuteBlock = node.getOneMinuteBlock(); vo.oneMinuteException = node.getOneMinuteException(); vo.oneMinuteTotal = node.getOneMinuteTotal(); vo.visible = node.isVisible(); list.add(vo); } for (ResourceTreeNode c : node.getChildren()) { visit(c, list, visible, false); } } public String getParentTtId() { return parentTtId; } public void setParentTtId(String parentTtId) { this.parentTtId = parentTtId; } public String getTtId() { return ttId; } public void setTtId(String ttId) { this.ttId = ttId; } public String getResource() { return resource; } public void setResource(String resource) { this.resource = resource; } public Integer getThreadNum() { return threadNum; } public void setThreadNum(Integer threadNum) { this.threadNum = threadNum; } public Long getPassQps() { return passQps; } public void setPassQps(Long passQps) { this.passQps = passQps; } public Long getBlockQps() { return blockQps; } public void setBlockQps(Long blockQps) { this.blockQps = blockQps; } public Long getTotalQps() { return totalQps; } public void setTotalQps(Long totalQps) { this.totalQps = totalQps; } public Long getAverageRt() { return averageRt; } public void setAverageRt(Long averageRt) { this.averageRt = averageRt; } public Long getPassRequestQps() { return passRequestQps; } public void setPassRequestQps(Long passRequestQps) { this.passRequestQps = passRequestQps; } public Long getExceptionQps() { return exceptionQps; } public void setExceptionQps(Long exceptionQps) { this.exceptionQps = exceptionQps; } public Long getOneMinuteException() { return oneMinuteException; } public void setOneMinuteException(Long oneMinuteException) { this.oneMinuteException = oneMinuteException; } public Long getOneMinutePass() { return oneMinutePass; } public void setOneMinutePass(Long oneMinutePass) { this.oneMinutePass = oneMinutePass; } public Long getOneMinuteBlock() { return oneMinuteBlock; } public void setOneMinuteBlock(Long oneMinuteBlock) { this.oneMinuteBlock = oneMinuteBlock; } public Long getOneMinuteTotal() { return oneMinuteTotal; } public void setOneMinuteTotal(Long oneMinuteTotal) { this.oneMinuteTotal = oneMinuteTotal; } public boolean isVisible() { return visible; } public void setVisible(boolean visible) { this.visible = visible; } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/filter/AuthFilter.java ================================================ /* * 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.filter; import com.alibaba.csp.sentinel.dashboard.auth.AuthService; 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 javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.List; /** * Servlet Filter that authenticate requests. * * Note: * Some urls are excluded as they needn't auth, such as: * * Index url: / * Authentication request url: /login,logout * Used for client: /registry/machine * Static resources: htm,html,js and so on. * * The excluded urls and urlSuffixes are configured in application.properties * * @author cdfive * @since 1.6.0 */ @Component public class AuthFilter implements Filter { private static final String URL_SUFFIX_DOT = "."; /**Some urls which needn't auth, such as /auth/login,/registry/machine and so on*/ @Value("#{'${auth.filter.exclude-urls}'.split(',')}") private List 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 authFilterExcludeUrlSuffixes; /**Authentication using AuthService interface*/ @Autowired private 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 requestURI = httpRequest.getRequestURI(); // Exclude the urls which needn't auth if (authFilterExcludeUrls.contains(requestURI)) { 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 (requestURI.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() { } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/metric/MetricFetcher.java ================================================ /* * 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.metric; import java.net.ConnectException; import java.net.SocketTimeoutException; import java.nio.charset.Charset; import java.util.Date; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.RejectedExecutionHandler; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.ThreadPoolExecutor.DiscardPolicy; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; import com.alibaba.csp.sentinel.concurrent.NamedThreadFactory; import com.alibaba.csp.sentinel.config.SentinelConfig; import com.alibaba.csp.sentinel.dashboard.datasource.entity.MetricEntity; import com.alibaba.csp.sentinel.dashboard.discovery.AppInfo; import com.alibaba.csp.sentinel.dashboard.discovery.AppManagement; import com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo; import com.alibaba.csp.sentinel.node.metric.MetricNode; import com.alibaba.csp.sentinel.util.StringUtil; import com.alibaba.csp.sentinel.dashboard.repository.metric.MetricsRepository; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.concurrent.FutureCallback; import org.apache.http.entity.ContentType; import org.apache.http.impl.client.DefaultRedirectStrategy; import org.apache.http.impl.nio.client.CloseableHttpAsyncClient; import org.apache.http.impl.nio.client.HttpAsyncClients; import org.apache.http.impl.nio.reactor.IOReactorConfig; import org.apache.http.protocol.HTTP; import org.apache.http.util.EntityUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; /** * Fetch metric of machines. * * @author leyou */ @Component public class MetricFetcher { public static final String NO_METRICS = "No metrics"; private static final int HTTP_OK = 200; private static final long MAX_LAST_FETCH_INTERVAL_MS = 1000 * 15; private static final long FETCH_INTERVAL_SECOND = 6; private static final Charset DEFAULT_CHARSET = Charset.forName(SentinelConfig.charset()); private final static String METRIC_URL_PATH = "metric"; private static Logger logger = LoggerFactory.getLogger(MetricFetcher.class); private final long intervalSecond = 1; private Map appLastFetchTime = new ConcurrentHashMap<>(); @Autowired private MetricsRepository metricStore; @Autowired private AppManagement appManagement; private CloseableHttpAsyncClient httpclient; @SuppressWarnings("PMD.ThreadPoolCreationRule") private ScheduledExecutorService fetchScheduleService = Executors.newScheduledThreadPool(1, new NamedThreadFactory("sentinel-dashboard-metrics-fetch-task")); private ExecutorService fetchService; private ExecutorService fetchWorker; public MetricFetcher() { int cores = Runtime.getRuntime().availableProcessors() * 2; long keepAliveTime = 0; 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); fetchWorker = new ThreadPoolExecutor(cores, cores, keepAliveTime, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(queueSize), new NamedThreadFactory("sentinel-dashboard-metrics-fetchWorker"), handler); IOReactorConfig ioConfig = IOReactorConfig.custom() .setConnectTimeout(3000) .setSoTimeout(3000) .setIoThreadCount(Runtime.getRuntime().availableProcessors() * 2) .build(); httpclient = HttpAsyncClients.custom() .setRedirectStrategy(new DefaultRedirectStrategy() { @Override protected boolean isRedirectable(final String method) { return false; } }).setMaxConnTotal(4000) .setMaxConnPerRoute(1000) .setDefaultIOReactorConfig(ioConfig) .build(); httpclient.start(); start(); } private void start() { fetchScheduleService.scheduleAtFixedRate(() -> { try { fetchAllApp(); } catch (Exception e) { logger.info("fetchAllApp error:", e); } }, 10, intervalSecond, TimeUnit.SECONDS); } private void writeMetric(Map map) { if (map.isEmpty()) { return; } Date date = new Date(); for (MetricEntity entity : map.values()) { entity.setGmtCreate(date); entity.setGmtModified(date); } metricStore.saveAll(map.values()); } /** * Traverse each APP, and then pull the metric of all machines for that APP. */ private void fetchAllApp() { List apps = appManagement.getAppNames(); if (apps == null) { return; } for (final String app : apps) { fetchService.submit(() -> { try { doFetchAppMetric(app); } catch (Exception e) { logger.error("fetchAppMetric error", e); } }); } } /** * fetch metric between [startTime, endTime], both side inclusive */ private void fetchOnce(String app, long startTime, long endTime, int maxWaitSeconds) { if (maxWaitSeconds <= 0) { throw new IllegalArgumentException("maxWaitSeconds must > 0, but " + maxWaitSeconds); } AppInfo appInfo = appManagement.getDetailApp(app); // auto remove for app if (appInfo.isDead()) { logger.info("Dead app removed: {}", app); appManagement.removeApp(app); return; } Set machines = appInfo.getMachines(); logger.debug("enter fetchOnce(" + app + "), machines.size()=" + machines.size() + ", time intervalMs [" + startTime + ", " + endTime + "]"); if (machines.isEmpty()) { return; } final String msg = "fetch"; AtomicLong unhealthy = new AtomicLong(); final AtomicLong success = new AtomicLong(); final AtomicLong fail = new AtomicLong(); long start = System.currentTimeMillis(); /** app_resource_timeSecond -> metric */ final Map metricMap = new ConcurrentHashMap<>(16); final CountDownLatch latch = new CountDownLatch(machines.size()); for (final MachineInfo machine : machines) { // auto remove if (machine.isDead()) { latch.countDown(); appManagement.getDetailApp(app).removeMachine(machine.getIp(), machine.getPort()); logger.info("Dead machine removed: {}:{} of {}", machine.getIp(), machine.getPort(), app); continue; } if (!machine.isHealthy()) { latch.countDown(); unhealthy.incrementAndGet(); continue; } final String url = "http://" + machine.getIp() + ":" + machine.getPort() + "/" + METRIC_URL_PATH + "?startTime=" + startTime + "&endTime=" + endTime + "&refetch=" + false; final HttpGet httpGet = new HttpGet(url); httpGet.setHeader(HTTP.CONN_DIRECTIVE, HTTP.CONN_CLOSE); httpclient.execute(httpGet, new FutureCallback() { @Override public void completed(final HttpResponse response) { try { handleResponse(response, machine, metricMap); success.incrementAndGet(); } catch (Exception e) { logger.error(msg + " metric " + url + " error:", e); } finally { latch.countDown(); } } @Override public void failed(final Exception ex) { latch.countDown(); fail.incrementAndGet(); httpGet.abort(); if (ex instanceof SocketTimeoutException) { logger.error("Failed to fetch metric from <{}>: socket timeout", url); } else if (ex instanceof ConnectException) { logger.error("Failed to fetch metric from <{}> (ConnectionException: {})", url, ex.getMessage()); } else { logger.error(msg + " metric " + url + " error", ex); } } @Override public void cancelled() { latch.countDown(); fail.incrementAndGet(); httpGet.abort(); } }); } try { latch.await(maxWaitSeconds, TimeUnit.SECONDS); } catch (Exception e) { logger.info(msg + " metric, wait http client error:", e); } long cost = System.currentTimeMillis() - start; //logger.info("finished " + msg + " metric for " + app + ", time intervalMs [" + startTime + ", " + endTime // + "], total machines=" + machines.size() + ", dead=" + dead + ", fetch success=" // + success + ", fetch fail=" + fail + ", time cost=" + cost + " ms"); writeMetric(metricMap); } private void doFetchAppMetric(final String app) { long now = System.currentTimeMillis(); long lastFetchMs = now - MAX_LAST_FETCH_INTERVAL_MS; if (appLastFetchTime.containsKey(app)) { lastFetchMs = Math.max(lastFetchMs, appLastFetchTime.get(app).get() + 1000); } // trim milliseconds lastFetchMs = lastFetchMs / 1000 * 1000; long endTime = lastFetchMs + FETCH_INTERVAL_SECOND * 1000; if (endTime > now - 1000 * 2) { // to near return; } // update last_fetch in advance. appLastFetchTime.computeIfAbsent(app, a -> new AtomicLong()).set(endTime); final long finalLastFetchMs = lastFetchMs; final long finalEndTime = endTime; try { // do real fetch async fetchWorker.submit(() -> { try { fetchOnce(app, finalLastFetchMs, finalEndTime, 5); } catch (Exception e) { logger.info("fetchOnce(" + app + ") error", e); } }); } catch (Exception e) { logger.info("submit fetchOnce(" + app + ") fail, intervalMs [" + lastFetchMs + ", " + endTime + "]", e); } } private void handleResponse(final HttpResponse response, MachineInfo machine, Map metricMap) throws Exception { int code = response.getStatusLine().getStatusCode(); if (code != HTTP_OK) { return; } Charset charset = null; try { String contentTypeStr = response.getFirstHeader("Content-type").getValue(); if (StringUtil.isNotEmpty(contentTypeStr)) { ContentType contentType = ContentType.parse(contentTypeStr); charset = contentType.getCharset(); } } catch (Exception ignore) { } String body = EntityUtils.toString(response.getEntity(), charset != null ? charset : DEFAULT_CHARSET); if (StringUtil.isEmpty(body) || body.startsWith(NO_METRICS)) { //logger.info(machine.getApp() + ":" + machine.getIp() + ":" + machine.getPort() + ", bodyStr is empty"); return; } String[] lines = body.split("\n"); //logger.info(machine.getApp() + ":" + machine.getIp() + ":" + machine.getPort() + // ", bodyStr.length()=" + body.length() + ", lines=" + lines.length); handleBody(lines, machine, metricMap); } private void handleBody(String[] lines, MachineInfo machine, Map map) { //logger.info("handleBody() lines=" + lines.length + ", machine=" + machine); if (lines.length < 1) { return; } for (String line : lines) { try { MetricNode node = MetricNode.fromThinString(line); /** * 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); } } catch (Exception e) { logger.warn("handleBody line exception, machine: {}, line: {}", machine.toLogString(), line); } } } private String buildMetricKey(String app, String resource, long timestamp) { return app + "__" + resource + "__" + (timestamp / 1000); } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/repository/metric/InMemoryMetricsRepository.java ================================================ /* * 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.repository.metric; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; import com.alibaba.csp.sentinel.dashboard.datasource.entity.MetricEntity; import com.alibaba.csp.sentinel.util.StringUtil; import com.googlecode.concurrentlinkedhashmap.ConcurrentLinkedHashMap; import org.springframework.stereotype.Component; /** * Caches metrics data in a period of time in memory. * * @author Carpenter Lee * @author Eric Zhao */ @Component public class InMemoryMetricsRepository implements MetricsRepository { private static final long MAX_METRIC_LIVE_TIME_MS = 1000 * 60 * 5; /** * {@code app -> resource -> timestamp -> metric} */ private Map>> allMetrics = new ConcurrentHashMap<>(); @Override public synchronized void save(MetricEntity entity) { if (entity == null || StringUtil.isBlank(entity.getApp())) { return; } allMetrics.computeIfAbsent(entity.getApp(), e -> new ConcurrentHashMap<>(16)) .computeIfAbsent(entity.getResource(), e -> new ConcurrentLinkedHashMap.Builder() .maximumWeightedCapacity(MAX_METRIC_LIVE_TIME_MS).weigher((key, value) -> { // Metric older than {@link #MAX_METRIC_LIVE_TIME_MS} will be removed. int weight = (int)(System.currentTimeMillis() - key); // weight must be a number greater than or equal to one return Math.max(weight, 1); }).build()).put(entity.getTimestamp().getTime(), entity); } @Override public synchronized void saveAll(Iterable metrics) { if (metrics == null) { return; } metrics.forEach(this::save); } @Override public synchronized List queryByAppAndResourceBetween(String app, String resource, long startTime, long endTime) { List results = new ArrayList<>(); if (StringUtil.isBlank(app)) { return results; } Map> resourceMap = allMetrics.get(app); if (resourceMap == null) { return results; } ConcurrentLinkedHashMap metricsMap = resourceMap.get(resource); if (metricsMap == null) { return results; } for (Entry entry : metricsMap.entrySet()) { if (entry.getKey() >= startTime && entry.getKey() <= endTime) { results.add(entry.getValue()); } } return results; } @Override public List listResourcesOfApp(String app) { List results = new ArrayList<>(); if (StringUtil.isBlank(app)) { return results; } // resource -> timestamp -> metric Map> resourceMap = allMetrics.get(app); if (resourceMap == null) { return results; } final long minTimeMs = System.currentTimeMillis() - 1000 * 60; Map resourceCount = new ConcurrentHashMap<>(32); for (Entry> resourceMetrics : resourceMap.entrySet()) { for (Entry metrics : resourceMetrics.getValue().entrySet()) { if (metrics.getKey() < minTimeMs) { continue; } MetricEntity newEntity = metrics.getValue(); if (resourceCount.containsKey(resourceMetrics.getKey())) { MetricEntity oldEntity = resourceCount.get(resourceMetrics.getKey()); oldEntity.addPassQps(newEntity.getPassQps()); oldEntity.addRtAndSuccessQps(newEntity.getRt(), newEntity.getSuccessQps()); oldEntity.addBlockQps(newEntity.getBlockQps()); oldEntity.addExceptionQps(newEntity.getExceptionQps()); oldEntity.addCount(1); } else { resourceCount.put(resourceMetrics.getKey(), MetricEntity.copyOf(newEntity)); } } } // Order by last minute b_qps DESC. return resourceCount.entrySet() .stream() .sorted((o1, o2) -> { MetricEntity e1 = o1.getValue(); MetricEntity e2 = o2.getValue(); int t = e2.getBlockQps().compareTo(e1.getBlockQps()); if (t != 0) { return t; } return e2.getPassQps().compareTo(e1.getPassQps()); }) .map(Entry::getKey) .collect(Collectors.toList()); } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/repository/metric/MetricsRepository.java ================================================ /* * 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.repository.metric; import java.util.List; /** * Repository interface for aggregated metrics data. * * @param type of metrics * @author Eric Zhao */ public interface MetricsRepository { /** * Save the metric to the storage repository. * * @param metric metric data to save */ void save(T metric); /** * Save all metrics to the storage repository. * * @param metrics metrics to save */ void saveAll(Iterable metrics); /** * Get all metrics by {@code appName} and {@code resourceName} between a period of time. * * @param app application name for Sentinel * @param resource resource name * @param startTime start timestamp * @param endTime end timestamp * @return all metrics in query conditions */ List queryByAppAndResourceBetween(String app, String resource, long startTime, long endTime); /** * List resource name of provided application name. * * @param app application name * @return list of resources */ List listResourcesOfApp(String app); } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/repository/rule/InMemAuthorityRuleStore.java ================================================ /* * 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.repository.rule; import java.util.concurrent.atomic.AtomicLong; import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.AuthorityRuleEntity; import org.springframework.stereotype.Component; /** * In-memory storage for authority rules. * * @author Eric Zhao * @since 0.2.1 */ @Component public class InMemAuthorityRuleStore extends InMemoryRuleRepositoryAdapter { private static AtomicLong ids = new AtomicLong(0); @Override protected long nextId() { return ids.incrementAndGet(); } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/repository/rule/InMemDegradeRuleStore.java ================================================ /* * 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.repository.rule; import java.util.concurrent.atomic.AtomicLong; import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.DegradeRuleEntity; import org.springframework.stereotype.Component; /** * @author leyou */ @Component public class InMemDegradeRuleStore extends InMemoryRuleRepositoryAdapter { private static AtomicLong ids = new AtomicLong(0); @Override protected long nextId() { return ids.incrementAndGet(); } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/repository/rule/InMemFlowRuleStore.java ================================================ /* * 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.repository.rule; import java.util.concurrent.atomic.AtomicLong; import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity; import com.alibaba.csp.sentinel.slots.block.flow.ClusterFlowConfig; import org.springframework.stereotype.Component; /** * Store {@link FlowRuleEntity} in memory. * * @author leyou */ @Component public class InMemFlowRuleStore extends InMemoryRuleRepositoryAdapter { private static AtomicLong ids = new AtomicLong(0); @Override protected long nextId() { return ids.incrementAndGet(); } @Override protected FlowRuleEntity preProcess(FlowRuleEntity entity) { if (entity != null && entity.isClusterMode()) { ClusterFlowConfig config = entity.getClusterConfig(); if (config == null) { config = new ClusterFlowConfig(); entity.setClusterConfig(config); } // Set cluster rule id. config.setFlowId(entity.getId()); } return entity; } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/repository/rule/InMemParamFlowRuleStore.java ================================================ /* * 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.repository.rule; import java.util.concurrent.atomic.AtomicLong; import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.ParamFlowRuleEntity; import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowClusterConfig; import org.springframework.stereotype.Component; /** * @author Eric Zhao * @since 0.2.1 */ @Component public class InMemParamFlowRuleStore extends InMemoryRuleRepositoryAdapter { private static AtomicLong ids = new AtomicLong(0); @Override protected long nextId() { return ids.incrementAndGet(); } @Override protected ParamFlowRuleEntity preProcess(ParamFlowRuleEntity entity) { if (entity != null && entity.isClusterMode()) { ParamFlowClusterConfig config = entity.getClusterConfig(); if (config == null) { config = new ParamFlowClusterConfig(); } // Set cluster rule id. config.setFlowId(entity.getId()); } return entity; } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/repository/rule/InMemSystemRuleStore.java ================================================ /* * 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.repository.rule; import java.util.concurrent.atomic.AtomicLong; import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.SystemRuleEntity; import org.springframework.stereotype.Component; /** * @author leyou */ @Component public class InMemSystemRuleStore extends InMemoryRuleRepositoryAdapter { private static AtomicLong ids = new AtomicLong(0); @Override protected long nextId() { return ids.incrementAndGet(); } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/repository/rule/InMemoryRuleRepositoryAdapter.java ================================================ /* * 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.repository.rule; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.RuleEntity; import com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo; import com.alibaba.csp.sentinel.util.AssertUtil; /** * @author leyou */ public abstract class InMemoryRuleRepositoryAdapter implements RuleRepository { /** * {@code >} */ private Map> machineRules = new ConcurrentHashMap<>(16); private Map allRules = new ConcurrentHashMap<>(16); private Map> appRules = new ConcurrentHashMap<>(16); private static final int MAX_RULES_SIZE = 10000; @Override public T save(T entity) { if (entity.getId() == null) { entity.setId(nextId()); } T processedEntity = preProcess(entity); if (processedEntity != null) { allRules.put(processedEntity.getId(), processedEntity); machineRules.computeIfAbsent(MachineInfo.of(processedEntity.getApp(), processedEntity.getIp(), processedEntity.getPort()), e -> new ConcurrentHashMap<>(32)) .put(processedEntity.getId(), processedEntity); appRules.computeIfAbsent(processedEntity.getApp(), v -> new ConcurrentHashMap<>(32)) .put(processedEntity.getId(), processedEntity); } return processedEntity; } @Override public List saveAll(List rules) { // TODO: check here. allRules.clear(); machineRules.clear(); appRules.clear(); if (rules == null) { return null; } List savedRules = new ArrayList<>(rules.size()); for (T rule : rules) { savedRules.add(save(rule)); } return savedRules; } @Override public T delete(Long id) { T entity = allRules.remove(id); if (entity != null) { if (appRules.get(entity.getApp()) != null) { appRules.get(entity.getApp()).remove(id); } machineRules.get(MachineInfo.of(entity.getApp(), entity.getIp(), entity.getPort())).remove(id); } return entity; } @Override public T findById(Long id) { return allRules.get(id); } @Override public List findAllByMachine(MachineInfo machineInfo) { Map entities = machineRules.get(machineInfo); if (entities == null) { return new ArrayList<>(); } return new ArrayList<>(entities.values()); } @Override public List findAllByApp(String appName) { AssertUtil.notEmpty(appName, "appName cannot be empty"); Map entities = appRules.get(appName); if (entities == null) { return new ArrayList<>(); } return new ArrayList<>(entities.values()); } protected T preProcess(T entity) { return entity; } /** * Get next unused id. * * @return next unused id */ abstract protected long nextId(); } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/repository/rule/RuleRepository.java ================================================ /* * 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.repository.rule; import java.util.List; import com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo; /** * Interface to store and find rules. * * @author leyou */ public interface RuleRepository { /** * Save one. * * @param entity * @return */ T save(T entity); /** * Save all. * * @param rules * @return rules saved. */ List saveAll(List rules); /** * Delete by id * * @param id * @return entity deleted */ T delete(ID id); /** * Find by id. * * @param id * @return */ T findById(ID id); /** * Find all by machine. * * @param machineInfo * @return */ List findAllByMachine(MachineInfo machineInfo); /** * Find all by application. * * @param appName valid app name * @return all rules of the application * @since 1.4.0 */ List findAllByApp(String appName); ///** // * Find all by app and enable switch. // * @param app // * @param enable // * @return // */ //List findAllByAppAndEnable(String app, boolean enable); } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/rule/DynamicRuleProvider.java ================================================ /* * 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.rule; /** * @author Eric Zhao * @since 1.4.0 */ public interface DynamicRuleProvider { T getRules(String appName) throws Exception; } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/rule/DynamicRulePublisher.java ================================================ /* * 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.rule; /** * @author Eric Zhao * @since 1.4.0 */ public interface DynamicRulePublisher { /** * Publish rules to remote rule configuration center for given application name. * * @param app app name * @param rules list of rules to push * @throws Exception if some error occurs */ void publish(String app, T rules) throws Exception; } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/rule/FlowRuleApiProvider.java ================================================ /* * 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.rule; import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; import com.alibaba.csp.sentinel.dashboard.client.SentinelApiClient; import com.alibaba.csp.sentinel.dashboard.discovery.AppManagement; import com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo; import com.alibaba.csp.sentinel.util.StringUtil; import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; /** * @author Eric Zhao */ @Component("flowRuleDefaultProvider") public class FlowRuleApiProvider implements DynamicRuleProvider> { @Autowired private SentinelApiClient sentinelApiClient; @Autowired private AppManagement appManagement; @Override public List getRules(String appName) throws Exception { if (StringUtil.isBlank(appName)) { return new ArrayList<>(); } List list = appManagement.getDetailApp(appName).getMachines() .stream() .filter(MachineInfo::isHealthy) .sorted((e1, e2) -> Long.compare(e2.getLastHeartbeat(), e1.getLastHeartbeat())).collect(Collectors.toList()); if (list.isEmpty()) { return new ArrayList<>(); } else { MachineInfo machine = list.get(0); return sentinelApiClient.fetchFlowRuleOfMachine(machine.getApp(), machine.getIp(), machine.getPort()); } } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/rule/FlowRuleApiPublisher.java ================================================ /* * 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.rule; import java.util.List; import java.util.Set; import com.alibaba.csp.sentinel.dashboard.client.SentinelApiClient; import com.alibaba.csp.sentinel.dashboard.discovery.AppManagement; import com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo; import com.alibaba.csp.sentinel.util.StringUtil; import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; /** * @author Eric Zhao * @since 1.4.0 */ @Component("flowRuleDefaultPublisher") public class FlowRuleApiPublisher implements DynamicRulePublisher> { @Autowired private SentinelApiClient sentinelApiClient; @Autowired private AppManagement appManagement; @Override public void publish(String app, List rules) throws Exception { if (StringUtil.isBlank(app)) { return; } if (rules == null) { return; } Set set = appManagement.getDetailApp(app).getMachines(); for (MachineInfo machine : set) { if (!machine.isHealthy()) { continue; } // TODO: parse the results sentinelApiClient.setFlowRuleOfMachine(app, machine.getIp(), machine.getPort(), rules); } } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/rule/apollo/ApolloConfig.java ================================================ /* * 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.rule.apollo; import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity; import com.alibaba.csp.sentinel.datasource.Converter; import com.alibaba.fastjson.JSON; import com.ctrip.framework.apollo.openapi.client.ApolloOpenApiClient; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.List; /** * @author hantianwei@gmail.com * @since 1.5.0 */ @Configuration public class ApolloConfig { @Bean public Converter, String> flowRuleEntityEncoder() { return JSON::toJSONString; } @Bean public Converter> flowRuleEntityDecoder() { return s -> JSON.parseArray(s, FlowRuleEntity.class); } @Bean public ApolloOpenApiClient apolloOpenApiClient() { ApolloOpenApiClient client = ApolloOpenApiClient.newBuilder() .withPortalUrl("https://apollo.xxx.com") // TODO 根据实际情况修改 .withToken("open api token") // TODO 根据实际情况修改 .build(); return client; } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/rule/apollo/FlowRuleApolloProvider.java ================================================ /* * 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.rule.apollo; import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity; import com.alibaba.csp.sentinel.dashboard.rule.DynamicRuleProvider; import com.alibaba.csp.sentinel.datasource.Converter; import com.alibaba.csp.sentinel.util.StringUtil; import com.ctrip.framework.apollo.openapi.client.ApolloOpenApiClient; import com.ctrip.framework.apollo.openapi.dto.OpenItemDTO; import com.ctrip.framework.apollo.openapi.dto.OpenNamespaceDTO; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import java.util.ArrayList; import java.util.List; /** * @author hantianwei@gmail.com * @since 1.5.0 */ @Component("flowRuleApolloProvider") public class FlowRuleApolloProvider implements DynamicRuleProvider> { @Autowired private ApolloOpenApiClient apolloOpenApiClient; @Autowired private Converter> converter; @Value("${env:DEV}") private String env; @Override public List getRules(String appName) throws Exception { String flowDataId = "sentinel.flowRules"; OpenNamespaceDTO openNamespaceDTO = apolloOpenApiClient.getNamespace(appName, env, "default", "application"); String rules = openNamespaceDTO .getItems() .stream() .filter(p -> p.getKey().equals(flowDataId)) .map(OpenItemDTO::getValue) .findFirst() .orElse(""); if (StringUtil.isEmpty(rules)) { return new ArrayList<>(); } return converter.convert(rules); } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/rule/apollo/FlowRuleApolloPublisher.java ================================================ /* * 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.rule.apollo; import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity; import com.alibaba.csp.sentinel.dashboard.rule.DynamicRulePublisher; import com.alibaba.csp.sentinel.datasource.Converter; import com.alibaba.csp.sentinel.util.AssertUtil; import com.ctrip.framework.apollo.openapi.client.ApolloOpenApiClient; import com.ctrip.framework.apollo.openapi.dto.NamespaceReleaseDTO; import com.ctrip.framework.apollo.openapi.dto.OpenItemDTO; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import java.util.List; /** * @author hantianwei@gmail.com * @since 1.5.0 */ @Component("flowRuleApolloPublisher") public class FlowRuleApolloPublisher implements DynamicRulePublisher> { @Autowired private ApolloOpenApiClient apolloOpenApiClient; @Autowired private Converter, String> converter; @Value("${env:DEV}") private String env; @Override public void publish(String app, List rules) throws Exception { String flowDataId = "sentinel.flowRules"; AssertUtil.notEmpty(app, "app name cannot be empty"); if (rules == null) { return; } // TODO 处理不兼容的字段,spring cloud alibaba 0.2.2版本中实现JSON转换的时候,不会忽略不存在的字段,会导致客户端出现出现异常 // 可以通过下面这段代码将这些字段不存入Apollo,以避免客户端加载的错误 for (FlowRuleEntity ruleEntity : rules) { ruleEntity.setId(null); ruleEntity.setApp(null); ruleEntity.setGmtModified(null); ruleEntity.setGmtCreate(null); ruleEntity.setIp(null); ruleEntity.setPort(null); } // 请视情况使用 OpenItemDTO openItemDTO = new OpenItemDTO(); openItemDTO.setKey(flowDataId); openItemDTO.setValue(converter.convert(rules)); openItemDTO.setComment("modify by sentinel-dashboard"); openItemDTO.setDataChangeCreatedBy("apollo"); apolloOpenApiClient.createOrUpdateItem(app, env, "default", "application", openItemDTO); // Release configuration NamespaceReleaseDTO namespaceReleaseDTO = new NamespaceReleaseDTO(); namespaceReleaseDTO.setEmergencyPublish(true); namespaceReleaseDTO.setReleaseComment("release by sentinel-dashboard"); namespaceReleaseDTO.setReleasedBy("apollo"); namespaceReleaseDTO.setReleaseTitle("release by sentinel-dashboard"); apolloOpenApiClient.publishNamespace(app, env, "default", "application", namespaceReleaseDTO); } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/service/ClusterAssignService.java ================================================ /* * 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.service; import java.util.List; import java.util.Set; import com.alibaba.csp.sentinel.dashboard.domain.cluster.ClusterAppAssignResultVO; import com.alibaba.csp.sentinel.dashboard.domain.cluster.request.ClusterAppAssignMap; /** * @author Eric Zhao * @since 1.4.1 */ public interface ClusterAssignService { /** * Unbind a specific cluster server and its clients. * * @param app app name * @param machineId valid machine ID ({@code host@commandPort}) * @return assign result */ ClusterAppAssignResultVO unbindClusterServer(String app, String machineId); /** * Unbind a set of cluster servers and its clients. * * @param app app name * @param machineIdSet set of valid machine ID ({@code host@commandPort}) * @return assign result */ ClusterAppAssignResultVO unbindClusterServers(String app, Set machineIdSet); /** * Apply cluster server and client assignment for provided app. * * @param app app name * @param clusterMap cluster assign map (server -> clients) * @param remainingSet unassigned set of machine ID * @return assign result */ ClusterAppAssignResultVO applyAssignToApp(String app, List clusterMap, Set remainingSet); } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/service/ClusterAssignServiceImpl.java ================================================ /* * 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.service; import java.util.HashSet; import java.util.List; import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import com.alibaba.csp.sentinel.cluster.ClusterStateManager; import com.alibaba.csp.sentinel.dashboard.domain.cluster.state.ClusterUniversalStatePairVO; import com.alibaba.csp.sentinel.util.AssertUtil; import com.alibaba.csp.sentinel.util.function.Tuple2; import com.alibaba.csp.sentinel.dashboard.client.SentinelApiClient; import com.alibaba.csp.sentinel.dashboard.domain.cluster.ClusterAppAssignResultVO; import com.alibaba.csp.sentinel.dashboard.domain.cluster.ClusterGroupEntity; import com.alibaba.csp.sentinel.dashboard.domain.cluster.config.ClusterClientConfig; import com.alibaba.csp.sentinel.dashboard.domain.cluster.config.ServerFlowConfig; import com.alibaba.csp.sentinel.dashboard.domain.cluster.config.ServerTransportConfig; import com.alibaba.csp.sentinel.dashboard.domain.cluster.request.ClusterAppAssignMap; import com.alibaba.csp.sentinel.dashboard.util.MachineUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; /** * @author Eric Zhao * @since 1.4.1 */ @Service public class ClusterAssignServiceImpl implements ClusterAssignService { private final Logger LOGGER = LoggerFactory.getLogger(ClusterAssignServiceImpl.class); @Autowired private SentinelApiClient sentinelApiClient; @Autowired private ClusterConfigService clusterConfigService; private boolean isMachineInApp(/*@NonEmpty*/ String machineId) { return machineId.contains(":"); } private ClusterAppAssignResultVO handleUnbindClusterServerNotInApp(String app, String machineId) { Set failedSet = new HashSet<>(); try { List list = clusterConfigService.getClusterUniversalState(app) .get(10, TimeUnit.SECONDS); Set toModifySet = list.stream() .filter(e -> e.getState().getStateInfo().getMode() == ClusterStateManager.CLUSTER_CLIENT) .filter(e -> machineId.equals(e.getState().getClient().getClientConfig().getServerHost() + ':' + e.getState().getClient().getClientConfig().getServerPort())) .map(e -> e.getIp() + '@' + e.getCommandPort()) .collect(Collectors.toSet()); // Modify mode to NOT-STARTED for all associated token clients. modifyToNonStarted(toModifySet, failedSet); } catch (Exception ex) { Throwable e = ex instanceof ExecutionException ? ex.getCause() : ex; LOGGER.error("Failed to unbind machine <{}>", machineId, e); failedSet.add(machineId); } return new ClusterAppAssignResultVO() .setFailedClientSet(failedSet) .setFailedServerSet(new HashSet<>()); } private void modifyToNonStarted(Set toModifySet, Set failedSet) { toModifySet.parallelStream() .map(MachineUtils::parseCommandIpAndPort) .filter(Optional::isPresent) .map(Optional::get) .map(e -> { CompletableFuture f = modifyMode(e.r1, e.r2, ClusterStateManager.CLUSTER_NOT_STARTED); return Tuple2.of(e.r1 + '@' + e.r2, f); }) .forEach(f -> handleFutureSync(f, failedSet)); } @Override public ClusterAppAssignResultVO unbindClusterServer(String app, String machineId) { AssertUtil.assertNotBlank(app, "app cannot be blank"); AssertUtil.assertNotBlank(machineId, "machineId cannot be blank"); if (isMachineInApp(machineId)) { return handleUnbindClusterServerNotInApp(app, machineId); } Set failedSet = new HashSet<>(); try { ClusterGroupEntity entity = clusterConfigService.getClusterUniversalStateForAppMachine(app, machineId) .get(10, TimeUnit.SECONDS); Set toModifySet = new HashSet<>(); toModifySet.add(machineId); if (entity.getClientSet() != null) { toModifySet.addAll(entity.getClientSet()); } // Modify mode to NOT-STARTED for all chosen token servers and associated token clients. modifyToNonStarted(toModifySet, failedSet); } catch (Exception ex) { Throwable e = ex instanceof ExecutionException ? ex.getCause() : ex; LOGGER.error("Failed to unbind machine <{}>", machineId, e); failedSet.add(machineId); } return new ClusterAppAssignResultVO() .setFailedClientSet(failedSet) .setFailedServerSet(new HashSet<>()); } @Override public ClusterAppAssignResultVO unbindClusterServers(String app, Set machineIdSet) { AssertUtil.assertNotBlank(app, "app cannot be blank"); AssertUtil.isTrue(machineIdSet != null && !machineIdSet.isEmpty(), "machineIdSet cannot be empty"); ClusterAppAssignResultVO result = new ClusterAppAssignResultVO() .setFailedClientSet(new HashSet<>()) .setFailedServerSet(new HashSet<>()); for (String machineId : machineIdSet) { ClusterAppAssignResultVO resultVO = unbindClusterServer(app, machineId); result.getFailedClientSet().addAll(resultVO.getFailedClientSet()); result.getFailedServerSet().addAll(resultVO.getFailedServerSet()); } return result; } @Override public ClusterAppAssignResultVO applyAssignToApp(String app, List clusterMap, Set remainingSet) { AssertUtil.assertNotBlank(app, "app cannot be blank"); AssertUtil.notNull(clusterMap, "clusterMap cannot be null"); Set failedServerSet = new HashSet<>(); Set failedClientSet = new HashSet<>(); // Assign server and apply config. clusterMap.stream() .filter(Objects::nonNull) .filter(ClusterAppAssignMap::getBelongToApp) .map(e -> { String ip = e.getIp(); int commandPort = parsePort(e); CompletableFuture f = modifyMode(ip, commandPort, ClusterStateManager.CLUSTER_SERVER) .thenCompose(v -> applyServerConfigChange(app, ip, commandPort, e)); return Tuple2.of(e.getMachineId(), f); }) .forEach(t -> handleFutureSync(t, failedServerSet)); // Assign client of servers and apply config. clusterMap.parallelStream() .filter(Objects::nonNull) .forEach(e -> applyAllClientConfigChange(app, e, failedClientSet)); // Unbind remaining (unassigned) machines. applyAllRemainingMachineSet(app, remainingSet, failedClientSet); return new ClusterAppAssignResultVO() .setFailedClientSet(failedClientSet) .setFailedServerSet(failedServerSet); } private void applyAllRemainingMachineSet(String app, Set remainingSet, Set failedSet) { if (remainingSet == null || remainingSet.isEmpty()) { return; } remainingSet.parallelStream() .filter(Objects::nonNull) .map(MachineUtils::parseCommandIpAndPort) .filter(Optional::isPresent) .map(Optional::get) .map(ipPort -> { String ip = ipPort.r1; int commandPort = ipPort.r2; CompletableFuture f = modifyMode(ip, commandPort, ClusterStateManager.CLUSTER_NOT_STARTED); return Tuple2.of(ip + '@' + commandPort, f); }) .forEach(t -> handleFutureSync(t, failedSet)); } private void applyAllClientConfigChange(String app, ClusterAppAssignMap assignMap, Set failedSet) { Set clientSet = assignMap.getClientSet(); if (clientSet == null || clientSet.isEmpty()) { return; } final String serverIp = assignMap.getIp(); final int serverPort = assignMap.getPort(); clientSet.stream() .map(MachineUtils::parseCommandIpAndPort) .filter(Optional::isPresent) .map(Optional::get) .map(ipPort -> { CompletableFuture f = sentinelApiClient .modifyClusterMode(ipPort.r1, ipPort.r2, ClusterStateManager.CLUSTER_CLIENT) .thenCompose(v -> sentinelApiClient.modifyClusterClientConfig(app, ipPort.r1, ipPort.r2, new ClusterClientConfig().setRequestTimeout(20) .setServerHost(serverIp) .setServerPort(serverPort) )); return Tuple2.of(ipPort.r1 + '@' + ipPort.r2, f); }) .forEach(t -> handleFutureSync(t, failedSet)); } private void handleFutureSync(Tuple2> t, Set failedSet) { try { t.r2.get(10, TimeUnit.SECONDS); } catch (Exception ex) { if (ex instanceof ExecutionException) { LOGGER.error("Request for <{}> failed", t.r1, ex.getCause()); } else { LOGGER.error("Request for <{}> failed", t.r1, ex); } failedSet.add(t.r1); } } private CompletableFuture applyServerConfigChange(String app, String ip, int commandPort, ClusterAppAssignMap assignMap) { ServerTransportConfig transportConfig = new ServerTransportConfig() .setPort(assignMap.getPort()) .setIdleSeconds(600); return sentinelApiClient.modifyClusterServerTransportConfig(app, ip, commandPort, transportConfig) .thenCompose(v -> applyServerFlowConfigChange(app, ip, commandPort, assignMap)) .thenCompose(v -> applyServerNamespaceSetConfig(app, ip, commandPort, assignMap)); } private CompletableFuture applyServerFlowConfigChange(String app, String ip, int commandPort, ClusterAppAssignMap assignMap) { Double maxAllowedQps = assignMap.getMaxAllowedQps(); if (maxAllowedQps == null || maxAllowedQps <= 0 || maxAllowedQps > 20_0000) { return CompletableFuture.completedFuture(null); } return sentinelApiClient.modifyClusterServerFlowConfig(app, ip, commandPort, new ServerFlowConfig().setMaxAllowedQps(maxAllowedQps)); } private CompletableFuture applyServerNamespaceSetConfig(String app, String ip, int commandPort, ClusterAppAssignMap assignMap) { Set namespaceSet = assignMap.getNamespaceSet(); if (namespaceSet == null || namespaceSet.isEmpty()) { return CompletableFuture.completedFuture(null); } return sentinelApiClient.modifyClusterServerNamespaceSet(app, ip, commandPort, namespaceSet); } private CompletableFuture modifyMode(String ip, int port, int mode) { return sentinelApiClient.modifyClusterMode(ip, port, mode); } private int parsePort(ClusterAppAssignMap assignMap) { return MachineUtils.parseCommandPort(assignMap.getMachineId()) .orElse(ServerTransportConfig.DEFAULT_PORT); } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/service/ClusterConfigService.java ================================================ /* * 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.service; import java.util.ArrayList; import java.util.List; import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.stream.Collectors; import com.alibaba.csp.sentinel.cluster.ClusterStateManager; import com.alibaba.csp.sentinel.dashboard.client.SentinelApiClient; import com.alibaba.csp.sentinel.dashboard.discovery.AppInfo; import com.alibaba.csp.sentinel.dashboard.discovery.AppManagement; import com.alibaba.csp.sentinel.dashboard.domain.cluster.request.ClusterServerModifyRequest; import com.alibaba.csp.sentinel.dashboard.util.AsyncUtils; import com.alibaba.csp.sentinel.dashboard.util.ClusterEntityUtils; import com.alibaba.csp.sentinel.util.StringUtil; import com.alibaba.csp.sentinel.dashboard.domain.cluster.ClusterGroupEntity; import com.alibaba.csp.sentinel.dashboard.domain.cluster.request.ClusterClientModifyRequest; import com.alibaba.csp.sentinel.dashboard.domain.cluster.state.ClusterClientStateVO; import com.alibaba.csp.sentinel.dashboard.domain.cluster.state.ClusterUniversalStatePairVO; import com.alibaba.csp.sentinel.dashboard.domain.cluster.state.ClusterUniversalStateVO; import com.alibaba.csp.sentinel.dashboard.domain.cluster.config.ClusterClientConfig; import com.alibaba.csp.sentinel.dashboard.domain.cluster.config.ServerFlowConfig; import com.alibaba.csp.sentinel.dashboard.domain.cluster.config.ServerTransportConfig; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; /** * @author Eric Zhao * @since 1.4.0 */ @Service public class ClusterConfigService { @Autowired private SentinelApiClient sentinelApiClient; @Autowired private AppManagement appManagement; public CompletableFuture modifyClusterClientConfig(ClusterClientModifyRequest request) { if (notClientRequestValid(request)) { throw new IllegalArgumentException("Invalid request"); } String app = request.getApp(); String ip = request.getIp(); int port = request.getPort(); return sentinelApiClient.modifyClusterClientConfig(app, ip, port, request.getClientConfig()) .thenCompose(v -> sentinelApiClient.modifyClusterMode(ip, port, ClusterStateManager.CLUSTER_CLIENT)); } private boolean notClientRequestValid(/*@NonNull */ ClusterClientModifyRequest request) { ClusterClientConfig config = request.getClientConfig(); return config == null || StringUtil.isEmpty(config.getServerHost()) || config.getServerPort() == null || config.getServerPort() <= 0 || config.getRequestTimeout() == null || config.getRequestTimeout() <= 0; } public CompletableFuture modifyClusterServerConfig(ClusterServerModifyRequest request) { ServerTransportConfig transportConfig = request.getTransportConfig(); ServerFlowConfig flowConfig = request.getFlowConfig(); Set namespaceSet = request.getNamespaceSet(); if (invalidTransportConfig(transportConfig)) { throw new IllegalArgumentException("Invalid transport config in request"); } if (invalidFlowConfig(flowConfig)) { throw new IllegalArgumentException("Invalid flow config in request"); } if (namespaceSet == null) { throw new IllegalArgumentException("namespace set cannot be null"); } String app = request.getApp(); String ip = request.getIp(); int port = request.getPort(); return sentinelApiClient.modifyClusterServerNamespaceSet(app, ip, port, namespaceSet) .thenCompose(v -> sentinelApiClient.modifyClusterServerTransportConfig(app, ip, port, transportConfig)) .thenCompose(v -> sentinelApiClient.modifyClusterServerFlowConfig(app, ip, port, flowConfig)) .thenCompose(v -> sentinelApiClient.modifyClusterMode(ip, port, ClusterStateManager.CLUSTER_SERVER)); } /** * Get cluster state list of all available machines of provided application. * * @param app application name * @return cluster state list of all available machines of the application * @since 1.4.1 */ public CompletableFuture> getClusterUniversalState(String app) { if (StringUtil.isBlank(app)) { return AsyncUtils.newFailedFuture(new IllegalArgumentException("app cannot be empty")); } AppInfo appInfo = appManagement.getDetailApp(app); if (appInfo == null || appInfo.getMachines() == null) { return CompletableFuture.completedFuture(new ArrayList<>()); } List> futures = appInfo.getMachines().stream() .filter(e -> e.isHealthy()) .map(machine -> getClusterUniversalState(app, machine.getIp(), machine.getPort()) .thenApply(e -> new ClusterUniversalStatePairVO(machine.getIp(), machine.getPort(), e))) .collect(Collectors.toList()); return AsyncUtils.sequenceSuccessFuture(futures); } public CompletableFuture getClusterUniversalStateForAppMachine(String app, String machineId) { if (StringUtil.isBlank(app)) { return AsyncUtils.newFailedFuture(new IllegalArgumentException("app cannot be empty")); } AppInfo appInfo = appManagement.getDetailApp(app); if (appInfo == null || appInfo.getMachines() == null) { return AsyncUtils.newFailedFuture(new IllegalArgumentException("app does not have machines")); } boolean machineOk = appInfo.getMachines().stream() .filter(e -> e.isHealthy()) .map(e -> e.getIp() + '@' + e.getPort()) .anyMatch(e -> e.equals(machineId)); if (!machineOk) { return AsyncUtils.newFailedFuture(new IllegalStateException("machine does not exist or disconnected")); } return getClusterUniversalState(app) .thenApply(ClusterEntityUtils::wrapToClusterGroup) .thenCompose(e -> e.stream() .filter(e1 -> e1.getMachineId().equals(machineId)) .findAny() .map(CompletableFuture::completedFuture) .orElse(AsyncUtils.newFailedFuture(new IllegalStateException("not a server: " + machineId))) ); } public CompletableFuture getClusterUniversalState(String app, String ip, int port) { return sentinelApiClient.fetchClusterMode(ip, port) .thenApply(e -> new ClusterUniversalStateVO().setStateInfo(e)) .thenCompose(vo -> { if (vo.getStateInfo().getClientAvailable()) { return sentinelApiClient.fetchClusterClientInfoAndConfig(ip, port) .thenApply(cc -> vo.setClient(new ClusterClientStateVO().setClientConfig(cc))); } else { return CompletableFuture.completedFuture(vo); } }).thenCompose(vo -> { if (vo.getStateInfo().getServerAvailable()) { return sentinelApiClient.fetchClusterServerBasicInfo(ip, port) .thenApply(vo::setServer); } else { return CompletableFuture.completedFuture(vo); } }); } private boolean invalidTransportConfig(ServerTransportConfig transportConfig) { return transportConfig == null || transportConfig.getPort() == null || transportConfig.getPort() <= 0 || transportConfig.getIdleSeconds() == null || transportConfig.getIdleSeconds() <= 0; } private boolean invalidFlowConfig(ServerFlowConfig flowConfig) { return flowConfig == null || flowConfig.getSampleCount() == null || flowConfig.getSampleCount() <= 0 || flowConfig.getIntervalMs() == null || flowConfig.getIntervalMs() <= 0 || flowConfig.getIntervalMs() % flowConfig.getSampleCount() != 0 || flowConfig.getMaxAllowedQps() == null || flowConfig.getMaxAllowedQps() < 0; } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/util/AsyncUtils.java ================================================ /* * 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.util; import java.util.List; import java.util.Objects; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author Eric Zhao * @since 1.4.1 */ public final class AsyncUtils { private static final Logger LOG = LoggerFactory.getLogger(AsyncUtils.class); public static CompletableFuture newFailedFuture(Throwable ex) { CompletableFuture future = new CompletableFuture<>(); future.completeExceptionally(ex); return future; } public static CompletableFuture> sequenceFuture(List> futures) { return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])) .thenApply(v -> futures.stream() .map(AsyncUtils::getValue) .filter(Objects::nonNull) .collect(Collectors.toList()) ); } public static CompletableFuture> sequenceSuccessFuture(List> futures) { return CompletableFuture.supplyAsync(() -> futures.parallelStream() .map(AsyncUtils::getValue) .filter(Objects::nonNull) .collect(Collectors.toList()) ); } public static T getValue(CompletableFuture future) { try { return future.get(10, TimeUnit.SECONDS); } catch (Exception ex) { LOG.error("getValue for async result failed", ex); } return null; } public static boolean isSuccessFuture(CompletableFuture future) { return future.isDone() && !future.isCompletedExceptionally() && !future.isCancelled(); } private AsyncUtils() {} } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/util/ClusterEntityUtils.java ================================================ /* * 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.util; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import com.alibaba.csp.sentinel.cluster.ClusterStateManager; import com.alibaba.csp.sentinel.util.StringUtil; import com.alibaba.csp.sentinel.dashboard.domain.cluster.ClusterGroupEntity; import com.alibaba.csp.sentinel.dashboard.domain.cluster.ConnectionGroupVO; import com.alibaba.csp.sentinel.dashboard.domain.cluster.state.AppClusterClientStateWrapVO; import com.alibaba.csp.sentinel.dashboard.domain.cluster.state.AppClusterServerStateWrapVO; import com.alibaba.csp.sentinel.dashboard.domain.cluster.state.ClusterClientStateVO; import com.alibaba.csp.sentinel.dashboard.domain.cluster.state.ClusterServerStateVO; import com.alibaba.csp.sentinel.dashboard.domain.cluster.state.ClusterUniversalStatePairVO; /** * @author Eric Zhao * @since 1.4.1 */ public final class ClusterEntityUtils { public static List wrapToAppClusterServerState( List list) { if (list == null || list.isEmpty()) { return new ArrayList<>(); } Map map = new HashMap<>(); Set tokenServerSet = new HashSet<>(); // Handle token servers that belong to current app. for (ClusterUniversalStatePairVO stateVO : list) { int mode = stateVO.getState().getStateInfo().getMode(); if (mode == ClusterStateManager.CLUSTER_SERVER) { String ip = stateVO.getIp(); String serverId = ip + '@' + stateVO.getCommandPort(); ClusterServerStateVO serverStateVO = stateVO.getState().getServer(); map.computeIfAbsent(serverId, v -> new AppClusterServerStateWrapVO() .setId(serverId) .setIp(ip) .setPort(serverStateVO.getPort()) .setState(serverStateVO) .setBelongToApp(true) .setConnectedCount(serverStateVO.getConnection().stream() .mapToInt(ConnectionGroupVO::getConnectedCount) .sum() ) ); tokenServerSet.add(ip + ":" + serverStateVO.getPort()); } } // Handle token servers from other app. for (ClusterUniversalStatePairVO stateVO : list) { int mode = stateVO.getState().getStateInfo().getMode(); if (mode == ClusterStateManager.CLUSTER_CLIENT) { ClusterClientStateVO clientState = stateVO.getState().getClient(); if (clientState == null) { continue; } String serverIp = clientState.getClientConfig().getServerHost(); int serverPort = clientState.getClientConfig().getServerPort(); if (tokenServerSet.contains(serverIp + ":" + serverPort)) { continue; } // We are not able to get the commandPort of foreign token server directly. String serverId = String.format("%s:%d", serverIp, serverPort); map.computeIfAbsent(serverId, v -> new AppClusterServerStateWrapVO() .setId(serverId) .setIp(serverIp) .setPort(serverPort) .setBelongToApp(false) ); } } return new ArrayList<>(map.values()); } public static List wrapToAppClusterClientState( List list) { if (list == null || list.isEmpty()) { return new ArrayList<>(); } Map map = new HashMap<>(); for (ClusterUniversalStatePairVO stateVO : list) { int mode = stateVO.getState().getStateInfo().getMode(); if (mode == ClusterStateManager.CLUSTER_CLIENT) { String ip = stateVO.getIp(); String clientId = ip + '@' + stateVO.getCommandPort(); ClusterClientStateVO clientStateVO = stateVO.getState().getClient(); map.computeIfAbsent(clientId, v -> new AppClusterClientStateWrapVO() .setId(clientId) .setIp(ip) .setState(clientStateVO) .setCommandPort(stateVO.getCommandPort()) ); } } return new ArrayList<>(map.values()); } public static List wrapToClusterGroup(List list) { if (list == null || list.isEmpty()) { return new ArrayList<>(); } Map map = new HashMap<>(); for (ClusterUniversalStatePairVO stateVO : list) { int mode = stateVO.getState().getStateInfo().getMode(); String ip = stateVO.getIp(); if (mode == ClusterStateManager.CLUSTER_SERVER) { String serverAddress = getIp(ip); int port = stateVO.getState().getServer().getPort(); map.computeIfAbsent(serverAddress, v -> new ClusterGroupEntity() .setBelongToApp(true).setMachineId(ip + '@' + stateVO.getCommandPort()) .setIp(ip).setPort(port) ); } } for (ClusterUniversalStatePairVO stateVO : list) { int mode = stateVO.getState().getStateInfo().getMode(); String ip = stateVO.getIp(); if (mode == ClusterStateManager.CLUSTER_CLIENT) { String targetServer = stateVO.getState().getClient().getClientConfig().getServerHost(); Integer targetPort = stateVO.getState().getClient().getClientConfig().getServerPort(); if (StringUtil.isBlank(targetServer) || targetPort == null || targetPort <= 0) { continue; } ClusterGroupEntity group = map.computeIfAbsent(targetServer, v -> new ClusterGroupEntity() .setBelongToApp(true).setMachineId(targetServer) .setIp(targetServer).setPort(targetPort) ); group.getClientSet().add(ip + '@' + stateVO.getCommandPort()); } } return new ArrayList<>(map.values()); } private static String getIp(String str) { if (str.contains(":")) { return str.split(":")[0]; } return str; } private ClusterEntityUtils() {} } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/util/MachineUtils.java ================================================ /* * 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.util; import java.util.Optional; import com.alibaba.csp.sentinel.util.StringUtil; import com.alibaba.csp.sentinel.util.function.Tuple2; /** * @author Eric Zhao */ public final class MachineUtils { public static Optional parseCommandPort(String machineIp) { try { if (!machineIp.contains("@")) { return Optional.empty(); } String[] str = machineIp.split("@"); if (str.length <= 1) { return Optional.empty(); } return Optional.of(Integer.parseInt(str[1])); } catch (Exception ex) { return Optional.empty(); } } public static Optional> parseCommandIpAndPort(String machineIp) { try { if (StringUtil.isEmpty(machineIp) || !machineIp.contains("@")) { return Optional.empty(); } String[] str = machineIp.split("@"); if (str.length <= 1) { return Optional.empty(); } return Optional.of(Tuple2.of(str[0], Integer.parseInt(str[1]))); } catch (Exception ex) { return Optional.empty(); } } private MachineUtils() {} } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/java/com/alibaba/csp/sentinel/dashboard/util/VersionUtils.java ================================================ /* * 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.util; import java.util.Optional; import com.alibaba.csp.sentinel.util.StringUtil; import com.alibaba.csp.sentinel.dashboard.datasource.entity.SentinelVersion; /** * Util class for parsing version. * * @author Eric Zhao * @since 0.2.1 */ public final class VersionUtils { /** * Parse version of Sentinel from raw string. * * @param versionFull version string * @return parsed {@link SentinelVersion} if the version is valid; empty if * there is something wrong with the format */ public static Optional parseVersion(String s) { if (StringUtil.isBlank(s)) { return Optional.empty(); } try { String versionFull = s; SentinelVersion version = new SentinelVersion(); // postfix int index = versionFull.indexOf("-"); if (index == 0) { // Start with "-" return Optional.empty(); } if (index == versionFull.length() - 1) { // End with "-" } else if (index > 0) { version.setPostfix(versionFull.substring(index + 1)); } if (index >= 0) { versionFull = versionFull.substring(0, index); } // x.x.x int segment = 0; int[] ver = new int[3]; while (segment < ver.length) { index = versionFull.indexOf('.'); if (index < 0) { if (versionFull.length() > 0) { ver[segment] = Integer.valueOf(versionFull); } break; } ver[segment] = Integer.valueOf(versionFull.substring(0, index)); versionFull = versionFull.substring(index + 1); segment ++; } if (ver[0] < 1) { // Wrong format, return empty. return Optional.empty(); } else { return Optional.of(version .setMajorVersion(ver[0]) .setMinorVersion(ver[1]) .setFixVersion(ver[2])); } } catch (Exception ex) { // Parse fail, return empty. return Optional.empty(); } } private VersionUtils() {} } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/resources/application.properties ================================================ #spring settings spring.http.encoding.force=true spring.http.encoding.charset=UTF-8 spring.http.encoding.enabled=true #logging settings logging.level.org.springframework.web=INFO logging.file=${user.home}/logs/csp/sentinel-dashboard.log logging.pattern.file= %d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n #logging.pattern.console= %d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n #auth settings auth.filter.exclude-urls=/,/auth/login,/auth/logout,/registry/machine auth.filter.exclude-url-suffixes=htm,html,js,css,map,ico,ttf,woff,png auth.username=sentinel auth.password=sentinel ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/.gitignore ================================================ node_modules/ tmp/ ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/.jshintrc ================================================ { /* * ENVIRONMENTS * ================= */ // Define globals exposed by modern browsers. "browser": true, // Define globals exposed by jQuery. "jquery": true, // Define globals exposed by Node.js. "node": true, // Allow ES6. "esversion": 6, /* * ENFORCING OPTIONS * ================= */ // Force all variable names to use either camelCase style or UPPER_CASE // with underscores. "camelcase": true, // Prohibit use of == and != in favor of === and !==. "eqeqeq": true, // Enforce tab width of 2 spaces. "indent": 2, // Prohibit use of a variable before it is defined. "latedef": true, // Enforce line length to 100 characters "maxlen": 100, // Require capitalized names for constructor functions. "newcap": true, // Enforce use of single quotation marks for strings. "quotmark": "single", // Enforce placing 'use strict' at the top function scope // 前端项目中外层使用 strict 即可,覆盖此条规则 "strict": false, // Prohibit use of explicitly undeclared variables. "undef": true, // Warn when variables are defined but never used. "unused": true, /* * RELAXING OPTIONS * ================= */ // Suppress warnings about == null comparisons. "eqnull": true, "globals": { "$": false, "angular": false } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/README.md ================================================ # Sentinel Dashboard Frontend ## Env Requirement - Node.js > 6.x ## Code Guide - [Code Style Guide for HTML/CSS](https://codeguide.bootcss.com/) - [Airbnb JavaScript Style Guide](https://github.com/airbnb/javascript/tree/es5-deprecated/es5) ## Install Packages ``` npm install ``` ## Start Development ``` npm start ``` ## Build for production ``` npm run build ``` ## Credit - [sb-admin-angular](https://github.com/start-angular/sb-admin-angular) ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/README_zh.md ================================================ # Sentinel Dashboard Frontend ## 环境要求 - Node.js > 6.x ## 编码规范 - HTML/CSS 遵循 [Bootstrap 编码规范](https://codeguide.bootcss.com/) - JavaScript 遵循 [Airbnb JavaScript Style Guide](https://github.com/airbnb/javascript/tree/es5-deprecated/es5) 以及最新的 ES 6 标准 ## 安装依赖 ``` npm i ``` ## 开始本地开发 ``` npm start ``` ## 构建前端资源 ``` npm run build ``` ## Credit - [sb-admin-angular](https://github.com/start-angular/sb-admin-angular) ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/scripts/app.js ================================================ 'use strict'; /** * @ngdoc overview * @name sentinelDashboardApp * @description * # sentinelDashboardApp * * Main module of the application. */ angular .module('sentinelDashboardApp', [ 'oc.lazyLoad', 'ui.router', 'ui.bootstrap', 'angular-loading-bar', 'ngDialog', 'ui.bootstrap.datetimepicker', 'ui-notification', 'rzTable', 'angular-clipboard', 'selectize', 'angularUtils.directives.dirPagination' ]) .factory('AuthInterceptor', ['$window', '$state', function ($window, $state) { var authInterceptor = { 'responseError' : function(response) { if (response.status == 401) { // If not auth, clear session in localStorage and jump to the login page $window.localStorage.removeItem("session_sentinel_admin"); $state.go('login'); } return response; }, 'response' : function(response) { return response; }, 'request' : function(config) { return config; }, 'requestError' : function(config){ return config; } }; return authInterceptor; }]) .config(['$stateProvider', '$urlRouterProvider', '$ocLazyLoadProvider', '$httpProvider', function ($stateProvider, $urlRouterProvider, $ocLazyLoadProvider, $httpProvider) { $httpProvider.interceptors.push('AuthInterceptor'); $ocLazyLoadProvider.config({ debug: false, events: true, }); $urlRouterProvider.otherwise('/dashboard/home'); $stateProvider .state('login', { url: '/login', templateUrl: 'app/views/login.html', controller: 'LoginCtl', resolve: { loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) { return $ocLazyLoad.load({ name: 'sentinelDashboardApp', files: [ 'app/scripts/controllers/login.js', ] }); }] } }) .state('dashboard', { url: '/dashboard', templateUrl: 'app/views/dashboard/main.html', resolve: { loadMyDirectives: ['$ocLazyLoad', function ($ocLazyLoad) { return $ocLazyLoad.load( { name: 'sentinelDashboardApp', files: [ 'app/scripts/directives/header/header.js', 'app/scripts/directives/sidebar/sidebar.js', 'app/scripts/directives/sidebar/sidebar-search/sidebar-search.js', ] }); }] } }) .state('dashboard.home', { url: '/home', templateUrl: 'app/views/dashboard/home.html', resolve: { loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) { return $ocLazyLoad.load({ name: 'sentinelDashboardApp', files: [ 'app/scripts/controllers/main.js', ] }); }] } }) .state('dashboard.flowV1', { templateUrl: 'app/views/flow_v1.html', url: '/flow/:app', controller: 'FlowControllerV1', resolve: { loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) { return $ocLazyLoad.load({ name: 'sentinelDashboardApp', files: [ 'app/scripts/controllers/flow_v1.js', ] }); }] } }) .state('dashboard.flow', { templateUrl: 'app/views/flow_v2.html', url: '/v2/flow/:app', controller: 'FlowControllerV2', resolve: { loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) { return $ocLazyLoad.load({ name: 'sentinelDashboardApp', files: [ 'app/scripts/controllers/flow_v2.js', ] }); }] } }) .state('dashboard.paramFlow', { templateUrl: 'app/views/param_flow.html', url: '/paramFlow/:app', controller: 'ParamFlowController', resolve: { loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) { return $ocLazyLoad.load({ name: 'sentinelDashboardApp', files: [ 'app/scripts/controllers/param_flow.js', ] }); }] } }) .state('dashboard.clusterAppAssignManage', { templateUrl: 'app/views/cluster_app_assign_manage.html', url: '/cluster/assign_manage/:app', controller: 'SentinelClusterAppAssignManageController', resolve: { loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) { return $ocLazyLoad.load({ name: 'sentinelDashboardApp', files: [ 'app/scripts/controllers/cluster_app_assign_manage.js', ] }); }] } }) .state('dashboard.clusterAppServerList', { templateUrl: 'app/views/cluster_app_server_list.html', url: '/cluster/server/:app', controller: 'SentinelClusterAppServerListController', resolve: { loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) { return $ocLazyLoad.load({ name: 'sentinelDashboardApp', files: [ 'app/scripts/controllers/cluster_app_server_list.js', ] }); }] } }) .state('dashboard.clusterAppClientList', { templateUrl: 'app/views/cluster_app_client_list.html', url: '/cluster/client/:app', controller: 'SentinelClusterAppTokenClientListController', resolve: { loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) { return $ocLazyLoad.load({ name: 'sentinelDashboardApp', files: [ 'app/scripts/controllers/cluster_app_token_client_list.js', ] }); }] } }) .state('dashboard.clusterSingle', { templateUrl: 'app/views/cluster_single_config.html', url: '/cluster/single/:app', controller: 'SentinelClusterSingleController', resolve: { loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) { return $ocLazyLoad.load({ name: 'sentinelDashboardApp', files: [ 'app/scripts/controllers/cluster_single.js', ] }); }] } }) .state('dashboard.authority', { templateUrl: 'app/views/authority.html', url: '/authority/:app', controller: 'AuthorityRuleController', resolve: { loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) { return $ocLazyLoad.load({ name: 'sentinelDashboardApp', files: [ 'app/scripts/controllers/authority.js', ] }); }] } }) .state('dashboard.degrade', { templateUrl: 'app/views/degrade.html', url: '/degrade/:app', controller: 'DegradeCtl', resolve: { loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) { return $ocLazyLoad.load({ name: 'sentinelDashboardApp', files: [ 'app/scripts/controllers/degrade.js', ] }); }] } }) .state('dashboard.system', { templateUrl: 'app/views/system.html', url: '/system/:app', controller: 'SystemCtl', resolve: { loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) { return $ocLazyLoad.load({ name: 'sentinelDashboardApp', files: [ 'app/scripts/controllers/system.js', ] }); }] } }) .state('dashboard.machine', { templateUrl: 'app/views/machine.html', url: '/app/:app', controller: 'MachineCtl', resolve: { loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) { return $ocLazyLoad.load({ name: 'sentinelDashboardApp', files: [ 'app/scripts/controllers/machine.js', ] }); }] } }) .state('dashboard.identity', { templateUrl: 'app/views/identity.html', url: '/identity/:app', controller: 'IdentityCtl', resolve: { loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) { return $ocLazyLoad.load({ name: 'sentinelDashboardApp', files: [ 'app/scripts/controllers/identity.js', ] }); }] } }) .state('dashboard.metric', { templateUrl: 'app/views/metric.html', url: '/metric/:app', controller: 'MetricCtl', resolve: { loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) { return $ocLazyLoad.load({ name: 'sentinelDashboardApp', files: [ 'app/scripts/controllers/metric.js', ] }); }] } }); }]); ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/scripts/controllers/authority.js ================================================ /** * Authority rule controller. */ angular.module('sentinelDashboardApp').controller('AuthorityRuleController', ['$scope', '$stateParams', 'AuthorityRuleService', 'ngDialog', 'MachineService', function ($scope, $stateParams, AuthorityRuleService, ngDialog, MachineService) { $scope.app = $stateParams.app; $scope.rulesPageConfig = { pageSize: 10, currentPageIndex: 1, totalPage: 1, totalCount: 0, }; $scope.macsInputConfig = { searchField: ['text', 'value'], persist: true, create: false, maxItems: 1, render: { item: function (data, escape) { return '
' + escape(data.text) + '
'; } }, onChange: function (value, oldValue) { $scope.macInputModel = value; } }; function getMachineRules() { if (!$scope.macInputModel) { return; } let mac = $scope.macInputModel.split(':'); AuthorityRuleService.queryMachineRules($scope.app, mac[0], mac[1]) .success(function (data) { if (data.code === 0 && data.data) { $scope.loadError = undefined; $scope.rules = data.data; $scope.rulesPageConfig.totalCount = $scope.rules.length; } else { $scope.rules = []; $scope.rulesPageConfig.totalCount = 0; $scope.loadError = {message: data.msg}; } }) .error((data, header, config, status) => { $scope.loadError = {message: "未知错误"}; }); }; $scope.getMachineRules = getMachineRules; getMachineRules(); var authorityRuleDialog; $scope.editRule = function (rule) { $scope.currentRule = angular.copy(rule); $scope.authorityRuleDialog = { title: '编辑授权规则', type: 'edit', confirmBtnText: '保存', }; authorityRuleDialog = ngDialog.open({ template: '/app/views/dialog/authority-rule-dialog.html', width: 680, overlay: true, scope: $scope }); }; $scope.addNewRule = function () { var mac = $scope.macInputModel.split(':'); $scope.currentRule = { app: $scope.app, ip: mac[0], port: mac[1], rule: { strategy: 0, limitApp: '', } }; $scope.authorityRuleDialog = { title: '新增授权规则', type: 'add', confirmBtnText: '新增', showAdvanceButton: true, }; authorityRuleDialog = ngDialog.open({ template: '/app/views/dialog/authority-rule-dialog.html', width: 680, overlay: true, scope: $scope }); }; $scope.saveRule = function () { if (!AuthorityRuleService.checkRuleValid($scope.currentRule.rule)) { return; } if ($scope.authorityRuleDialog.type === 'add') { addNewRuleAndPush($scope.currentRule); } else if ($scope.authorityRuleDialog.type === 'edit') { saveRuleAndPush($scope.currentRule, true); } }; function addNewRuleAndPush(rule) { AuthorityRuleService.addNewRule(rule).success((data) => { if (data.success) { getMachineRules(); authorityRuleDialog.close(); } else { alert('添加规则失败:' + data.msg); } }).error((data) => { if (data) { alert('添加规则失败:' + data.msg); } else { alert("添加规则失败:未知错误"); } }); } function saveRuleAndPush(rule, edit) { AuthorityRuleService.saveRule(rule).success(function (data) { if (data.success) { alert("修改规则成功"); getMachineRules(); if (edit) { authorityRuleDialog.close(); } else { confirmDialog.close(); } } else { alert('修改规则失败:' + data.msg); } }).error((data) => { if (data) { alert('修改规则失败:' + data.msg); } else { alert("修改规则失败:未知错误"); } }); } function deleteRuleAndPush(entity) { if (entity.id === undefined || isNaN(entity.id)) { alert('规则 ID 不合法!'); return; } AuthorityRuleService.deleteRule(entity).success((data) => { if (data.code == 0) { getMachineRules(); confirmDialog.close(); } else { alert('删除规则失败:' + data.msg); } }).error((data) => { if (data) { alert('删除规则失败:' + data.msg); } else { alert("删除规则失败:未知错误"); } }); }; var confirmDialog; $scope.deleteRule = function (ruleEntity) { $scope.currentRule = ruleEntity; $scope.confirmDialog = { title: '删除授权规则', type: 'delete_rule', attentionTitle: '请确认是否删除如下授权限流规则', attention: '资源名: ' + ruleEntity.rule.resource + ', 流控应用: ' + ruleEntity.rule.limitApp + ', 类型: ' + (ruleEntity.rule.strategy === 0 ? '白名单' : '黑名单'), confirmBtnText: '删除', }; confirmDialog = ngDialog.open({ template: '/app/views/dialog/confirm-dialog.html', scope: $scope, overlay: true }); }; $scope.confirm = function () { if ($scope.confirmDialog.type === 'delete_rule') { deleteRuleAndPush($scope.currentRule); } else { console.error('error'); } }; queryAppMachines(); function queryAppMachines() { MachineService.getAppMachines($scope.app).success( function (data) { if (data.code == 0) { // $scope.machines = data.data; if (data.data) { $scope.machines = []; $scope.macsInputOptions = []; data.data.forEach(function (item) { if (item.healthy) { $scope.macsInputOptions.push({ text: item.ip + ':' + item.port, value: item.ip + ':' + item.port }); } }); } if ($scope.macsInputOptions.length > 0) { $scope.macInputModel = $scope.macsInputOptions[0].value; } } else { $scope.macsInputOptions = []; } } ); }; $scope.$watch('macInputModel', function () { if ($scope.macInputModel) { getMachineRules(); } }); }]); ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/scripts/controllers/cluster_app_assign_manage.js ================================================ var app = angular.module('sentinelDashboardApp'); app.controller('SentinelClusterAppAssignManageController', ['$scope', '$stateParams', 'ngDialog', 'MachineService', 'ClusterStateService', function ($scope, $stateParams, ngDialog, MachineService, ClusterStateService) { $scope.app = $stateParams.app; const UNSUPPORTED_CODE = 4041; const CLUSTER_MODE_CLIENT = 0; const CLUSTER_MODE_SERVER = 1; const DEFAULT_CLUSTER_SERVER_PORT = 18730; $scope.tmp = { curClientChosen: [], curRemainingClientChosen: [], curChosenServer: {}, }; function convertSetToString(set) { if (set === undefined) { return ''; } let s = ''; for (let i = 0; i < set.length; i++) { s = s + set[i]; if (i < set.length - 1) { s = s + ','; } } return s; } function convertStrToNamespaceSet(str) { if (str === undefined || str === '') { return []; } let arr = []; let spliced = str.split(','); spliced.forEach((v) => { arr.push(v.trim()); }); return arr; } function processAppSingleData(data) { if (data.state.server && data.state.server.namespaceSet) { data.state.server.namespaceSetStr = convertSetToString(data.state.server.namespaceSet); data.mode = data.state.stateInfo.mode; } } function removeFromArr(arr, v) { for (let i = 0; i < arr.length; i++) { if (arr[i] === v) { arr.splice(i, 1); break; } } } function resetChosen() { $scope.tmp.curClientChosen = []; $scope.tmp.curRemainingClientChosen = []; } function generateMachineId(e) { return e.ip + '@' + e.commandPort; } function applyClusterMap(appClusterMachineList) { if (!appClusterMachineList) { return; } let tmpMap = new Map(); $scope.clusterMap = []; $scope.remainingClientAddressList = []; let tmpServerList = []; let tmpClientList = []; appClusterMachineList.forEach((e) => { if (e.mode === CLUSTER_MODE_CLIENT) { tmpClientList.push(e); } else if (e.mode === CLUSTER_MODE_SERVER) { tmpServerList.push(e); } else { $scope.remainingClientAddressList.push(generateMachineId(e)); } }); tmpServerList.forEach((e) => { let ip = e.ip; let machineId = ip + '@' + e.commandPort; let group = { ip: ip, machineId: machineId, port: e.state.server.port, clientSet: [], namespaceSetStr: e.state.server.namespaceSetStr, belongToApp: true, }; if (!tmpMap.has(ip)) { tmpMap.set(ip, group); } }); tmpClientList.forEach((e) => { let ip = e.ip; let machineId = ip + '@' + e.commandPort; let targetServer = e.state.client.clientConfig.serverHost; let targetPort = e.state.client.clientConfig.serverPort; if (targetServer === undefined || targetServer === '' || targetPort === undefined || targetPort <= 0) { $scope.remainingClientAddressList.push(generateMachineId(e)); return; } if (!tmpMap.has(targetServer)) { let group = { ip: targetServer, machineId: targetServer, port: targetPort, clientSet: [machineId], belongToApp: false, }; tmpMap.set(targetServer, group); } else { let g = tmpMap.get(targetServer); g.clientSet.push(machineId); } }); tmpMap.forEach((v) => { if (v !== undefined) { $scope.clusterMap.push(v); } }); } $scope.onCurrentServerChange = () => { resetChosen(); }; $scope.remainingClientAddressList = []; $scope.moveToServerGroup = () => { let chosenServer = $scope.tmp.curChosenServer; if (!chosenServer || !chosenServer.machineId) { return; } $scope.tmp.curRemainingClientChosen.forEach(e => { chosenServer.clientSet.push(e); removeFromArr($scope.remainingClientAddressList, e); }); resetChosen(); }; $scope.moveToRemainingSharePool = () => { $scope.tmp.curClientChosen.forEach(e => { $scope.remainingClientAddressList.push(e); removeFromArr($scope.tmp.curChosenServer.clientSet, e); }); resetChosen(); }; function parseIpFromMachineId(machineId) { if (machineId.indexOf('@') === -1) { return machineId; } let arr = machineId.split('@'); return arr[0]; } $scope.addToServerList = () => { let group; $scope.tmp.curRemainingClientChosen.forEach(e => { group = { machineId: e, ip: parseIpFromMachineId(e), port: DEFAULT_CLUSTER_SERVER_PORT, clientSet: [], namespaceSetStr: 'default,' + $scope.app, belongToApp: true, }; $scope.clusterMap.push(group); removeFromArr($scope.remainingClientAddressList, e); $scope.tmp.curChosenServer = group; }); resetChosen(); }; $scope.removeFromServerList = () => { let chosenServer = $scope.tmp.curChosenServer; if (!chosenServer || !chosenServer.machineId) { return; } chosenServer.clientSet.forEach((e) => { if (e !== undefined) { $scope.remainingClientAddressList.push(e); } }); if (chosenServer.belongToApp || chosenServer.machineId.indexOf('@') !== -1) { $scope.remainingClientAddressList.push(chosenServer.machineId); } else { alert('提示:非本应用内机器将不会置回空闲列表中'); } removeFromArr($scope.clusterMap, chosenServer); resetChosen(); if ($scope.clusterMap.length > 0) { $scope.tmp.curChosenServer = $scope.clusterMap[0]; $scope.onCurrentServerChange(); } else { $scope.tmp.curChosenServer = {}; } }; function retrieveClusterAppInfo() { ClusterStateService.fetchClusterUniversalStateOfApp($scope.app).success(function (data) { if (data.code === 0 && data.data) { $scope.loadError = undefined; $scope.appClusterMachineList = data.data; $scope.appClusterMachineList.forEach(processAppSingleData); applyClusterMap($scope.appClusterMachineList); if ($scope.clusterMap.length > 0) { $scope.tmp.curChosenServer = $scope.clusterMap[0]; $scope.onCurrentServerChange(); } } else { $scope.appClusterMachineList = {}; if (data.code === UNSUPPORTED_CODE) { $scope.loadError = {message: '该应用的 Sentinel 客户端不支持集群限流,请升级至 1.4.0 以上版本并引入相关依赖。'} } else { $scope.loadError = {message: data.msg}; } } }).error(() => { $scope.loadError = {message: '未知错误'}; }); } retrieveClusterAppInfo(); $scope.saveAndApplyAssign = () => { let ok = confirm('是否确认执行变更?'); if (!ok) { return; } let cm = $scope.clusterMap; if (!cm) { cm = []; } cm.forEach((e) => { e.namespaceSet = convertStrToNamespaceSet(e.namespaceSetStr); }); cm.namespaceSet = convertStrToNamespaceSet(cm.namespaceSetStr); let request = { clusterMap: cm, remainingList: $scope.remainingClientAddressList, }; ClusterStateService.applyClusterFullAssignOfApp($scope.app, request).success((data) => { if (data.code === 0 && data.data) { let failedServerSet = data.data.failedServerSet; let failedClientSet = data.data.failedClientSet; if (failedClientSet.length === 0 && failedServerSet.length === 0) { alert('全部推送成功'); } else { alert('推送完毕。token server 失败列表:' + JSON.stringify(failedServerSet) + '; token client 失败列表:' + JSON.stringify(failedClientSet)); } retrieveClusterAppInfo(); } else { if (data.code === UNSUPPORTED_CODE) { alert('该应用的 Sentinel 客户端不支持集群限流,请升级至 1.4.0 以上版本并引入相关依赖。'); } else { alert('推送失败:' + data.msg); } } }).error(() => { alert('未知错误'); }); }; }]); ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/scripts/controllers/cluster_app_server_list.js ================================================ var app = angular.module('sentinelDashboardApp'); app.controller('SentinelClusterAppServerListController', ['$scope', '$stateParams', 'ngDialog', 'MachineService', 'ClusterStateService', function ($scope, $stateParams, ngDialog, MachineService, ClusterStateService) { $scope.app = $stateParams.app; const UNSUPPORTED_CODE = 4041; const CLUSTER_MODE_CLIENT = 0; const CLUSTER_MODE_SERVER = 1; const DEFAULT_CLUSTER_SERVER_PORT = 18730; const DEFAULT_NAMESPACE = 'default'; const DEFAULT_MAX_ALLOWED_QPS = 20000; // tmp for dialog temporary data. $scope.tmp = { curClientChosen: [], curRemainingClientChosen: [], curChosenServer: {}, }; $scope.remainingMachineList = []; function convertSetToString(set) { if (set === undefined) { return ''; } if (set.length === 1 && set[0] === DEFAULT_NAMESPACE) { return DEFAULT_NAMESPACE; } let s = ''; for (let i = 0; i < set.length; i++) { let ns = set[i]; if (ns !== DEFAULT_NAMESPACE) { s = s + ns; if (i < set.length - 1) { s = s + ','; } } } return s; } function convertStrToNamespaceSet(str) { if (str === undefined || str === '') { return []; } let arr = []; let spliced = str.split(','); spliced.forEach((v) => { arr.push(v.trim()); }); return arr; } function processAppSingleData(data) { if (data.state.server && data.state.server.namespaceSet) { data.state.server.namespaceSetStr = convertSetToString(data.state.server.namespaceSet); data.mode = data.state.stateInfo.mode; } } function removeFromArr(arr, v) { for (let i = 0; i < arr.length; i++) { if (arr[i] === v) { arr.splice(i, 1); break; } } } function removeFromArrIf(arr, f) { for (let i = 0; i < arr.length; i++) { if (f(arr[i]) === true) { arr.splice(i, 1); break; } } } function resetAssignDialogChosen() { $scope.tmp.curClientChosen = []; $scope.tmp.curRemainingClientChosen = []; } function generateMachineId(e) { return e.ip + '@' + e.commandPort; } function applyClusterMap(appClusterMachineList) { if (!appClusterMachineList) { return; } let tmpMap = new Map(); let serverCommandPortMap = new Map(); $scope.clusterMap = []; $scope.remainingMachineList = []; let tmpServerList = []; let tmpClientList = []; appClusterMachineList.forEach((e) => { if (e.mode === CLUSTER_MODE_CLIENT) { tmpClientList.push(e); } else if (e.mode === CLUSTER_MODE_SERVER) { tmpServerList.push(e); } else { $scope.remainingMachineList.push(generateMachineId(e)); } }); tmpServerList.forEach((e) => { let ip = e.ip; let machineId = ip + '@' + e.commandPort; let group = { ip: ip, machineId: machineId, port: e.state.server.port, clientSet: [], namespaceSetStr: e.state.server.namespaceSetStr, maxAllowedQps: e.state.server.flow.maxAllowedQps, belongToApp: true, }; if (!tmpMap.has(machineId)) { tmpMap.set(machineId, group); } serverCommandPortMap.set(ip + ':' + e.state.server.port, e.commandPort); }); tmpClientList.forEach((e) => { let ip = e.ip; let machineId = ip + '@' + e.commandPort; let targetServer = e.state.client.clientConfig.serverHost; let targetPort = e.state.client.clientConfig.serverPort; if (targetServer === undefined || targetServer === '' || targetPort === undefined || targetPort <= 0) { $scope.remainingMachineList.push(generateMachineId(e)); return; } let serverHostPort = targetServer + ':' + targetPort; if (serverCommandPortMap.has(serverHostPort)) { let serverCommandPort = serverCommandPortMap.get(serverHostPort); let g; if (serverCommandPort < 0) { // Not belong to this app. g = tmpMap.get(serverHostPort); } else { // Belong to this app. g = tmpMap.get(targetServer + '@' + serverCommandPort); } g.clientSet.push(machineId); } else { let group = { ip: targetServer, machineId: serverHostPort, port: targetPort, clientSet: [machineId], belongToApp: false, }; tmpMap.set(serverHostPort, group); // Indicates that it's not belonging to current app. serverCommandPortMap.set(serverHostPort, -1); } // if (!tmpMap.has(serverHostPort)) { // let group = { // ip: targetServer, // machineId: targetServer, // port: targetPort, // clientSet: [machineId], // belongToApp: false, // }; // tmpMap.set(targetServer, group); // } else { // let g = tmpMap.get(targetServer); // g.clientSet.push(machineId); // } }); tmpMap.forEach((v) => { if (v !== undefined) { $scope.clusterMap.push(v); } }); } $scope.notChosenServer = (id) => { return id !== $scope.serverAssignDialogData.serverData.currentServer; }; $scope.onCurrentServerChange = () => { resetAssignDialogChosen(); }; $scope.moveToServerGroup = () => { $scope.tmp.curRemainingClientChosen.forEach(e => { $scope.serverAssignDialogData.serverData.clientSet.push(e); removeFromArr($scope.remainingMachineList, e); }); resetAssignDialogChosen(); }; $scope.moveToRemainingSharePool = () => { $scope.tmp.curClientChosen.forEach(e => { $scope.remainingMachineList.push(e); removeFromArr($scope.serverAssignDialogData.serverData.clientSet, e); }); resetAssignDialogChosen(); }; function parseIpFromMachineId(machineId) { if (machineId.indexOf(':') !== -1) { return machineId.split(':')[0]; } if (machineId.indexOf('@') === -1) { return machineId; } let arr = machineId.split('@'); return arr[0]; } function retrieveClusterAssignInfoOfApp() { ClusterStateService.fetchClusterUniversalStateOfApp($scope.app).success(function (data) { if (data.code === 0 && data.data) { $scope.loadError = undefined; $scope.appClusterMachineList = data.data; $scope.appClusterMachineList.forEach(processAppSingleData); applyClusterMap($scope.appClusterMachineList); } else { $scope.appClusterMachineList = {}; if (data.code === UNSUPPORTED_CODE) { $scope.loadError = {message: '该应用的 Sentinel 客户端不支持集群限流,请升级至 1.4.0 以上版本并引入相关依赖。'} } else { $scope.loadError = {message: data.msg}; } } }).error(() => { $scope.loadError = {message: '未知错误'}; }); } $scope.newServerDialog = () => { retrieveClusterAssignInfoOfApp(); $scope.serverAssignDialogData = { title: '新增 Token Server', type: 'add', confirmBtnText: '保存', serverData: { serverType: 0, clientSet: [], serverPort: DEFAULT_CLUSTER_SERVER_PORT, maxAllowedQps: DEFAULT_MAX_ALLOWED_QPS, } }; $scope.serverAssignDialog = ngDialog.open({ template: '/app/views/dialog/cluster/cluster-server-assign-dialog.html', width: 1000, overlay: true, scope: $scope }); }; $scope.modifyServerAssignConfig = (serverVO) => { let id = serverVO.id; ClusterStateService.fetchClusterUniversalStateOfApp($scope.app).success(function (data) { if (data.code === 0 && data.data) { $scope.loadError = undefined; $scope.appClusterMachineList = data.data; $scope.appClusterMachineList.forEach(processAppSingleData); applyClusterMap($scope.appClusterMachineList); let clusterMap = $scope.clusterMap; let d; for (let i = 0; i < clusterMap.length; i++) { if (clusterMap[i].machineId === id) { d = clusterMap[i]; } } if (!d) { alert('状态错误'); return; } $scope.serverAssignDialogData = { title: 'Token Server 分配编辑', type: 'edit', confirmBtnText: '保存', serverData: { currentServer: d.machineId, belongToApp: serverVO.belongToApp, serverPort: d.port, clientSet: d.clientSet, } }; if (d.maxAllowedQps !== undefined) { $scope.serverAssignDialogData.serverData.maxAllowedQps = d.maxAllowedQps; } $scope.serverAssignDialog = ngDialog.open({ template: '/app/views/dialog/cluster/cluster-server-assign-dialog.html', width: 1000, overlay: true, scope: $scope }); } else { if (data.code === UNSUPPORTED_CODE) { $scope.loadError = {message: '该应用的 Sentinel 客户端不支持集群限流,请升级至 1.4.0 以上版本并引入相关依赖。'} } else { $scope.loadError = {message: data.msg}; } } }).error(() => { $scope.loadError = {message: '未知错误'}; }); }; function getRemainingMachineList() { return $scope.remainingMachineList.filter((e) => $scope.notChosenServer(e)); } function doApplyNewSingleServerAssign() { let ok = confirm('是否确认执行变更?'); if (!ok) { return; } let serverData = $scope.serverAssignDialogData.serverData; let belongToApp = serverData.serverType == 0; // don't modify here! let machineId = serverData.currentServer; let request = { clusterMap: { machineId: machineId, ip: parseIpFromMachineId(machineId), port: serverData.serverPort, clientSet: serverData.clientSet, belongToApp: belongToApp, maxAllowedQps: serverData.maxAllowedQps, }, remainingList: getRemainingMachineList(), }; ClusterStateService.applyClusterSingleServerAssignOfApp($scope.app, request).success((data) => { if (data.code === 0 && data.data) { let failedServerSet = data.data.failedServerSet; let failedClientSet = data.data.failedClientSet; if (failedClientSet.length === 0 && failedServerSet.length === 0) { alert('全部推送成功'); } else { let failedSet = []; if (failedServerSet) { failedServerSet.forEach((e) => { failedSet.push(e); }); } if (failedClientSet) { failedClientSet.forEach((e) => { failedSet.push(e); }); } alert('推送完毕。失败机器列表:' + JSON.stringify(failedSet)); } location.reload(); } else { if (data.code === UNSUPPORTED_CODE) { alert('该应用的 Sentinel 客户端不支持集群限流,请升级至 1.4.0 以上版本并引入相关依赖。'); } else { alert('推送失败:' + data.msg); } } }).error(() => { alert('未知错误'); }); } function doApplySingleServerAssignEdit() { let ok = confirm('是否确认执行变更?'); if (!ok) { return; } let serverData = $scope.serverAssignDialogData.serverData; let machineId = serverData.currentServer; let request = { clusterMap: { machineId: machineId, ip: parseIpFromMachineId(machineId), port: serverData.serverPort, clientSet: serverData.clientSet, belongToApp: serverData.belongToApp, }, remainingList: $scope.remainingMachineList, }; if (serverData.maxAllowedQps !== undefined) { request.clusterMap.maxAllowedQps = serverData.maxAllowedQps; } ClusterStateService.applyClusterSingleServerAssignOfApp($scope.app, request).success((data) => { if (data.code === 0 && data.data) { let failedServerSet = data.data.failedServerSet; let failedClientSet = data.data.failedClientSet; if (failedClientSet.length === 0 && failedServerSet.length === 0) { alert('全部推送成功'); } else { let failedSet = []; failedServerSet.forEach(failedSet.push); failedClientSet.forEach(failedSet.push); alert('推送完毕。失败机器列表:' + JSON.stringify(failedSet)); } location.reload(); } else { if (data.code === UNSUPPORTED_CODE) { alert('该应用的 Sentinel 客户端不支持集群限流,请升级至 1.4.0 以上版本并引入相关依赖。'); } else { alert('推送失败:' + data.msg); } } }).error(() => { alert('未知错误'); }); } $scope.saveAssignForDialog = () => { if (!checkAssignDialogValid()) { return; } if ($scope.serverAssignDialogData.type === 'add') { doApplyNewSingleServerAssign(); } else if ($scope.serverAssignDialogData.type === 'edit') { doApplySingleServerAssignEdit(); } else { alert('未知的操作'); } }; function checkAssignDialogValid() { let serverData = $scope.serverAssignDialogData.serverData; if (serverData.currentServer === undefined || serverData.currentServer === '') { alert('请指定有效的 Token Server'); return false; } if (serverData.serverPort === undefined || serverData.serverPort <= 0 || serverData.serverPort > 65535) { alert('请输入合法的端口值'); return false; } if (serverData.maxAllowedQps !== undefined && serverData.maxAllowedQps < 0) { alert('请输入合法的最大允许 QPS'); return false; } return true; } $scope.viewConnectionDetail = (serverVO) => { $scope.connectionDetailDialogData = { serverData: serverVO }; $scope.connectionDetailDialog = ngDialog.open({ template: '/app/views/dialog/cluster/cluster-server-connection-detail-dialog.html', width: 700, overlay: true, scope: $scope }); }; function generateRequestLimitDataStr(limitData) { if (limitData.length === 1 && limitData[0].namespace === DEFAULT_NAMESPACE) { return 'default: ' + limitData[0].currentQps + ' / ' + limitData[0].maxAllowedQps; } for (let i = 0; i < limitData.length; i++) { let crl = limitData[i]; if (crl.namespace === $scope.app) { return '' + crl.currentQps + ' / ' + crl.maxAllowedQps; } } return '0'; } function processServerListData(serverVO) { if (serverVO.state && serverVO.state.namespaceSet) { serverVO.state.namespaceSetStr = convertSetToString(serverVO.state.namespaceSet); } if (serverVO.state && serverVO.state.requestLimitData) { serverVO.state.requestLimitDataStr = generateRequestLimitDataStr(serverVO.state.requestLimitData); } } $scope.generateConnectionSet = (data) => { let connectionSet = data; let s = ''; if (connectionSet) { s = s + '['; for (let i = 0; i < connectionSet.length; i++) { s = s + connectionSet[i].address; if (i < connectionSet.length - 1) { s = s + ', '; } } s = s + ']'; } else { s = '[]'; } return s; }; function retrieveClusterServerInfo() { ClusterStateService.fetchClusterServerStateOfApp($scope.app).success(function (data) { if (data.code === 0 && data.data) { $scope.loadError = undefined; $scope.serverVOList = data.data; $scope.serverVOList.forEach(processServerListData); } else { $scope.serverVOList = {}; if (data.code === UNSUPPORTED_CODE) { $scope.loadError = {message: '该应用的 Sentinel 客户端不支持集群限流,请升级至 1.4.0 以上版本并引入相关依赖。'} } else { $scope.loadError = {message: data.msg}; } } }).error(() => { $scope.loadError = {message: '未知错误'}; }); } retrieveClusterServerInfo(); let confirmUnbindServerDialog; $scope.unbindServer = (id) => { $scope.pendingUnbindIds = [id]; $scope.confirmDialog = { title: '移除 Token Server', type: 'unbind_token_server', attentionTitle: '请确认是否移除以下 Token Server(该 server 下的 client 也会解除分配)', attention: id + '', confirmBtnText: '移除', }; confirmUnbindServerDialog = ngDialog.open({ template: '/app/views/dialog/confirm-dialog.html', scope: $scope, overlay: true }); }; function apiUnbindServerAssign(ids) { ClusterStateService.applyClusterServerBatchUnbind($scope.app, ids).success((data) => { if (data.code === 0 && data.data) { let failedServerSet = data.data.failedServerSet; let failedClientSet = data.data.failedClientSet; if (failedClientSet.length === 0 && failedServerSet.length === 0) { alert('成功'); } else { alert('操作推送完毕,部分失败机器列表:' + JSON.stringify(failedClientSet)); } location.reload(); } else { if (data.code === UNSUPPORTED_CODE) { alert('该应用的 Sentinel 客户端不支持集群限流,请升级至 1.4.0 以上版本并引入相关依赖。'); } else { alert('推送失败:' + data.msg); } } }).error(() => { alert('未知错误'); }); // confirmUnbindServerDialog.close(); } // Confirm function for confirm dialog. $scope.confirm = () => { if ($scope.confirmDialog.type === 'unbind_token_server') { apiUnbindServerAssign($scope.pendingUnbindIds); } else { console.error('Error dialog when unbinding token server'); } }; }]); ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/scripts/controllers/cluster_app_server_manage.js ================================================ var app = angular.module('sentinelDashboardApp'); app.controller('SentinelClusterAppAssignManageController', ['$scope', '$stateParams', 'ngDialog', 'MachineService', 'ClusterStateService', function ($scope, $stateParams, ngDialog, MachineService, ClusterStateService) { $scope.app = $stateParams.app; const UNSUPPORTED_CODE = 4041; const CLUSTER_MODE_CLIENT = 0; const CLUSTER_MODE_SERVER = 1; const DEFAULT_CLUSTER_SERVER_PORT = 18730; $scope.tmp = { curClientChosen: [], curRemainingClientChosen: [], curChosenServer: {}, }; function convertSetToString(set) { if (set === undefined) { return ''; } let s = ''; for (let i = 0; i < set.length; i++) { s = s + set[i]; if (i < set.length - 1) { s = s + ','; } } return s; } function convertStrToNamespaceSet(str) { if (str === undefined || str === '') { return []; } let arr = []; let spliced = str.split(','); spliced.forEach((v) => { arr.push(v.trim()); }); return arr; } function processAppSingleData(data) { if (data.state.server && data.state.server.namespaceSet) { data.state.server.namespaceSetStr = convertSetToString(data.state.server.namespaceSet); data.mode = data.state.stateInfo.mode; } } function removeFromArr(arr, v) { for (let i = 0; i < arr.length; i++) { if (arr[i] === v) { arr.splice(i, 1); break; } } } function resetChosen() { $scope.tmp.curClientChosen = []; $scope.tmp.curRemainingClientChosen = []; } function generateMachineId(e) { return e.ip + '@' + e.commandPort; } function applyClusterMap(appClusterMachineList) { if (!appClusterMachineList) { return; } let tmpMap = new Map(); $scope.clusterMap = []; $scope.remainingClientAddressList = []; let tmpServerList = []; let tmpClientList = []; appClusterMachineList.forEach((e) => { if (e.mode === CLUSTER_MODE_CLIENT) { tmpClientList.push(e); } else if (e.mode === CLUSTER_MODE_SERVER) { tmpServerList.push(e); } else { $scope.remainingClientAddressList.push(generateMachineId(e)); } }); tmpServerList.forEach((e) => { let ip = e.ip; let machineId = ip + '@' + e.commandPort; let group = { ip: ip, machineId: machineId, port: e.state.server.port, clientSet: [], namespaceSetStr: e.state.server.namespaceSetStr, belongToApp: true, }; if (!tmpMap.has(ip)) { tmpMap.set(ip, group); } }); tmpClientList.forEach((e) => { let ip = e.ip; let machineId = ip + '@' + e.commandPort; let targetServer = e.state.client.clientConfig.serverHost; let targetPort = e.state.client.clientConfig.serverPort; if (targetServer === undefined || targetServer === '' || targetPort === undefined || targetPort <= 0) { $scope.remainingClientAddressList.push(generateMachineId(e)); return; } if (!tmpMap.has(targetServer)) { let group = { ip: targetServer, machineId: targetServer, port: targetPort, clientSet: [machineId], belongToApp: false, }; tmpMap.set(targetServer, group); } else { let g = tmpMap.get(targetServer); g.clientSet.push(machineId); } }); tmpMap.forEach((v) => { if (v !== undefined) { $scope.clusterMap.push(v); } }); } $scope.onCurrentServerChange = () => { resetChosen(); }; $scope.remainingClientAddressList = []; $scope.moveToServerGroup = () => { let chosenServer = $scope.tmp.curChosenServer; if (!chosenServer || !chosenServer.machineId) { return; } $scope.tmp.curRemainingClientChosen.forEach(e => { chosenServer.clientSet.push(e); removeFromArr($scope.remainingClientAddressList, e); }); resetChosen(); }; $scope.moveToRemainingSharePool = () => { $scope.tmp.curClientChosen.forEach(e => { $scope.remainingClientAddressList.push(e); removeFromArr($scope.tmp.curChosenServer.clientSet, e); }); resetChosen(); }; function parseIpFromMachineId(machineId) { if (machineId.indexOf('@') === -1) { return machineId; } let arr = machineId.split('@'); return arr[0]; } $scope.addToServerList = () => { let group; $scope.tmp.curRemainingClientChosen.forEach(e => { group = { machineId: e, ip: parseIpFromMachineId(e), port: DEFAULT_CLUSTER_SERVER_PORT, clientSet: [], namespaceSetStr: 'default,' + $scope.app, belongToApp: true, }; $scope.clusterMap.push(group); removeFromArr($scope.remainingClientAddressList, e); $scope.tmp.curChosenServer = group; }); resetChosen(); }; $scope.removeFromServerList = () => { let chosenServer = $scope.tmp.curChosenServer; if (!chosenServer || !chosenServer.machineId) { return; } chosenServer.clientSet.forEach((e) => { if (e !== undefined) { $scope.remainingClientAddressList.push(e); } }); if (chosenServer.belongToApp || chosenServer.machineId.indexOf('@') !== -1) { $scope.remainingClientAddressList.push(chosenServer.machineId); } else { alert('提示:非本应用内机器将不会置回空闲列表中'); } removeFromArr($scope.clusterMap, chosenServer); resetChosen(); if ($scope.clusterMap.length > 0) { $scope.tmp.curChosenServer = $scope.clusterMap[0]; $scope.onCurrentServerChange(); } else { $scope.tmp.curChosenServer = {}; } }; function retrieveClusterAppInfo() { ClusterStateService.fetchClusterUniversalStateOfApp($scope.app).success(function (data) { if (data.code === 0 && data.data) { $scope.loadError = undefined; $scope.appClusterMachineList = data.data; $scope.appClusterMachineList.forEach(processAppSingleData); applyClusterMap($scope.appClusterMachineList); if ($scope.clusterMap.length > 0) { $scope.tmp.curChosenServer = $scope.clusterMap[0]; $scope.onCurrentServerChange(); } } else { $scope.appClusterMachineList = {}; if (data.code === UNSUPPORTED_CODE) { $scope.loadError = {message: '该应用的 Sentinel 客户端不支持集群限流,请升级至 1.4.0 以上版本并引入相关依赖。'} } else { $scope.loadError = {message: data.msg}; } } }).error(() => { $scope.loadError = {message: '未知错误'}; }); } retrieveClusterAppInfo(); $scope.saveAndApplyAssign = () => { let ok = confirm('是否确认执行变更?'); if (!ok) { return; } let cm = $scope.clusterMap; if (!cm) { cm = []; } cm.forEach((e) => { e.namespaceSet = convertStrToNamespaceSet(e.namespaceSetStr); }); cm.namespaceSet = convertStrToNamespaceSet(cm.namespaceSetStr); let request = { clusterMap: cm, remainingList: $scope.remainingClientAddressList, }; ClusterStateService.applyClusterFullAssignOfApp($scope.app, request).success((data) => { if (data.code === 0 && data.data) { let failedServerSet = data.data.failedServerSet; let failedClientSet = data.data.failedClientSet; if (failedClientSet.length === 0 && failedServerSet.length === 0) { alert('全部推送成功'); } else { alert('推送完毕。token server 失败列表:' + JSON.stringify(failedServerSet) + '; token client 失败列表:' + JSON.stringify(failedClientSet)); } retrieveClusterAppInfo(); } else { if (data.code === UNSUPPORTED_CODE) { alert('该应用的 Sentinel 客户端不支持集群限流,请升级至 1.4.0 以上版本并引入相关依赖。'); } else { alert('推送失败:' + data.msg); } } }).error(() => { alert('未知错误'); }); }; }]); ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/scripts/controllers/cluster_app_server_monitor.js ================================================ var app = angular.module('sentinelDashboardApp'); app.controller('SentinelClusterAppServerMonitorController', ['$scope', '$stateParams', 'ngDialog', 'MachineService', 'ClusterStateService', function ($scope, $stateParams, ngDialog, MachineService, ClusterStateService) { $scope.app = $stateParams.app; const UNSUPPORTED_CODE = 4041; const CLUSTER_MODE_SERVER = 1; $scope.tmp = { curChosenServer: {}, }; function convertSetToString(set) { if (set === undefined) { return ''; } let s = ''; for (let i = 0; i < set.length; i++) { s = s + set[i]; if (i < set.length - 1) { s = s + ','; } } return s; } function processServerData(serverVO) { if (serverVO.state && serverVO.state.namespaceSet) { serverVO.state.namespaceSetStr = convertSetToString(serverVO.state.namespaceSet); } } $scope.generateConnectionSet = (data) => { let connectionSet = data; let s = ''; if (connectionSet) { s = s + '['; for (let i = 0; i < connectionSet.length; i++) { s = s + connectionSet[i].address; if (i < connectionSet.length - 1) { s = s + ', '; } } s = s + ']'; } else { s = '[]'; } return s; }; $scope.onChosenServerChange = () => { }; function retrieveClusterServerInfo() { ClusterStateService.fetchClusterServerStateOfApp($scope.app).success(function (data) { if (data.code === 0 && data.data) { $scope.loadError = undefined; $scope.serverVOList = data.data; $scope.serverVOList.forEach(processServerData); if ($scope.serverVOList.length > 0) { $scope.tmp.curChosenServer = $scope.serverVOList[0]; $scope.onChosenServerChange(); } } else { $scope.serverVOList = {}; if (data.code === UNSUPPORTED_CODE) { $scope.loadError = {message: '该应用的 Sentinel 客户端不支持集群限流,请升级至 1.4.0 以上版本并引入相关依赖。'} } else { $scope.loadError = {message: data.msg}; } } }).error(() => { $scope.loadError = {message: '未知错误'}; }); } retrieveClusterServerInfo(); $scope.macsInputConfig = { searchField: ['text', 'value'], persist: true, create: false, maxItems: 1, render: { item: function (data, escape) { return '
' + escape(data.text) + '
'; } }, onChange: function (value, oldValue) { $scope.macInputModel = value; } }; }]); ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/scripts/controllers/cluster_app_token_client_list.js ================================================ var app = angular.module('sentinelDashboardApp'); app.controller('SentinelClusterAppTokenClientListController', ['$scope', '$stateParams', 'ngDialog', 'MachineService', 'ClusterStateService', function ($scope, $stateParams, ngDialog, MachineService, ClusterStateService) { $scope.app = $stateParams.app; const UNSUPPORTED_CODE = 4041; const CLUSTER_MODE_CLIENT = 0; const CLUSTER_MODE_SERVER = 1; function processClientData(clientVO) { } $scope.modifyClientConfigDialog = (clientVO) => { if (!clientVO) { return; } $scope.ccDialogData = { ip: clientVO.ip, commandPort: clientVO.commandPort, clientId: clientVO.id, serverHost: clientVO.state.clientConfig.serverHost, serverPort: clientVO.state.clientConfig.serverPort, requestTimeout: clientVO.state.clientConfig.requestTimeout, }; $scope.ccDialog = ngDialog.open({ template: '/app/views/dialog/cluster/cluster-client-config-dialog.html', width: 700, overlay: true, scope: $scope }); }; function checkValidClientConfig(config) { if (!config.serverHost || config.serverHost.trim() == '') { alert('请输入有效的 Token Server IP'); return false; } if (config.serverPort === undefined || config.serverPort <= 0 || config.serverPort > 65535) { alert('请输入有效的 Token Server 端口'); return false; } if (config.requestTimeout === undefined || config.requestTimeout <= 0) { alert('请输入有效的请求超时时长'); return false; } return true; } $scope.doModifyClientConfig = () => { if (!checkValidClientConfig($scope.ccDialogData)) { return; } let id = $scope.ccDialogData.id; let request = { app: $scope.app, ip: $scope.ccDialogData.ip, port: $scope.ccDialogData.commandPort, mode: CLUSTER_MODE_CLIENT, clientConfig: { serverHost: $scope.ccDialogData.serverHost, serverPort: $scope.ccDialogData.serverPort, requestTimeout: $scope.ccDialogData.requestTimeout, } }; ClusterStateService.modifyClusterConfig(request).success((data) => { if (data.code === 0 && data.data) { alert('修改 Token Client 配置成功'); window.location.reload(); } else { if (data.code === UNSUPPORTED_CODE) { alert('机器 ' + id + ' 的 Sentinel 没有引入集群限流客户端,请升级至 1.4.0 以上版本并引入相关依赖。'); } else { alert('修改失败:' + data.msg); } } }).error((data, header, config, status) => { alert('未知错误'); }); }; function retrieveClusterTokenClientInfo() { ClusterStateService.fetchClusterClientStateOfApp($scope.app) .success((data) => { if (data.code === 0 && data.data) { $scope.loadError = undefined; $scope.clientVOList = data.data; $scope.clientVOList.forEach(processClientData); } else { $scope.clientVOList = []; if (data.code === UNSUPPORTED_CODE) { $scope.loadError = {message: '该应用的 Sentinel 客户端不支持集群限流,请升级至 1.4.0 以上版本并引入相关依赖。'} } else { $scope.loadError = {message: data.msg}; } } }) .error(() => { $scope.loadError = {message: '未知错误'}; }); } retrieveClusterTokenClientInfo(); $scope.macsInputConfig = { searchField: ['text', 'value'], persist: true, create: false, maxItems: 1, render: { item: function (data, escape) { return '
' + escape(data.text) + '
'; } }, onChange: function (value, oldValue) { $scope.macInputModel = value; } }; }]); ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/scripts/controllers/cluster_single.js ================================================ var app = angular.module('sentinelDashboardApp'); app.controller('SentinelClusterSingleController', ['$scope', '$stateParams', 'ngDialog', 'MachineService', 'ClusterStateService', function ($scope, $stateParams, ngDialog, MachineService, ClusterStateService) { $scope.app = $stateParams.app; const UNSUPPORTED_CODE = 4041; const CLUSTER_MODE_CLIENT = 0; const CLUSTER_MODE_SERVER = 1; $scope.macsInputConfig = { searchField: ['text', 'value'], persist: true, create: false, maxItems: 1, render: { item: function (data, escape) { return '
' + escape(data.text) + '
'; } }, onChange: function (value, oldValue) { $scope.macInputModel = value; } }; function convertSetToString(set) { if (set === undefined) { return ''; } let s = ''; for (let i = 0; i < set.length; i++) { s = s + set[i]; if (i < set.length - 1) { s = s + ','; } } return s; } function convertStrToNamespaceSet(str) { if (str === undefined || str === '') { return []; } let arr = []; let spliced = str.split(','); spliced.forEach((v) => { arr.push(v.trim()); }); return arr; } function fetchMachineClusterState() { if (!$scope.macInputModel || $scope.macInputModel === '') { return; } let mac = $scope.macInputModel.split(':'); ClusterStateService.fetchClusterUniversalStateSingle($scope.app, mac[0], mac[1]).success(function (data) { if (data.code == 0 && data.data) { $scope.loadError = undefined; $scope.stateVO = data.data; $scope.stateVO.currentMode = $scope.stateVO.stateInfo.mode; if ($scope.stateVO.server && $scope.stateVO.server.namespaceSet) { $scope.stateVO.server.namespaceSetStr = convertSetToString($scope.stateVO.server.namespaceSet); } } else { $scope.stateVO = {}; if (data.code === UNSUPPORTED_CODE) { $scope.loadError = {message: '机器 ' + mac[0] + ':' + mac[1] + ' 的 Sentinel 客户端版本不支持集群限流,请升级至 1.4.0 以上版本并引入相关依赖。'} } else { $scope.loadError = {message: data.msg}; } } }).error((data, header, config, status) => { $scope.loadError = {message: '未知错误'}; }); } fetchMachineClusterState(); function checkValidClientConfig(stateVO) { if (!stateVO.client || !stateVO.client.clientConfig) { alert('不合法的配置'); return false; } let config = stateVO.client.clientConfig; if (!config.serverHost || config.serverHost.trim() == '') { alert('请输入有效的 Token Server IP'); return false; } if (config.serverPort === undefined || config.serverPort <= 0 || config.serverPort > 65535) { alert('请输入有效的 Token Server 端口'); return false; } if (config.requestTimeout === undefined || config.requestTimeout <= 0) { alert('请输入有效的请求超时时长'); return false; } return true; } function sendClusterClientRequest(stateVO) { if (!checkValidClientConfig(stateVO)) { return; } if (!$scope.macInputModel) { return; } let mac = $scope.macInputModel.split(':'); let request = { app: $scope.app, ip: mac[0], port: mac[1], }; request.mode = CLUSTER_MODE_CLIENT; request.clientConfig = stateVO.client.clientConfig; ClusterStateService.modifyClusterConfig(request).success(function (data) { if (data.code == 0 && data.data) { alert('修改集群限流客户端配置成功'); window.location.reload(); } else { if (data.code === UNSUPPORTED_CODE) { alert('机器 ' + mac[0] + ':' + mac[1] + ' 的 Sentinel 客户端版本不支持集群限流客户端,请升级至 1.4.0 以上版本并引入相关依赖。'); } else { alert('修改失败:' + data.msg); } } }).error((data, header, config, status) => { alert('未知错误'); }); } function checkValidServerConfig(stateVO) { if (!stateVO.server || !stateVO.server.transport) { alert('不合法的配置'); return false; } if (stateVO.server.namespaceSetStr === undefined || stateVO.server.namespaceSetStr == '') { alert('请输入有效的命名空间集合(多个 namespace 以 , 分隔)'); return false; } let transportConfig = stateVO.server.transport; if (transportConfig.port === undefined || transportConfig.port <= 0 || transportConfig.port > 65535) { alert('请输入有效的 Token Server 端口'); return false; } let flowConfig = stateVO.server.flow; if (flowConfig.maxAllowedQps === undefined || flowConfig.maxAllowedQps < 0) { alert('请输入有效的最大允许 QPS'); return false; } // if (transportConfig.idleSeconds === undefined || transportConfig.idleSeconds <= 0) { // alert('请输入有效的连接清理时长 (idleSeconds)'); // return false; // } return true; } function sendClusterServerRequest(stateVO) { if (!checkValidServerConfig(stateVO)) { return; } if (!$scope.macInputModel) { return; } let mac = $scope.macInputModel.split(':'); let request = { app: $scope.app, ip: mac[0], port: mac[1], }; request.mode = CLUSTER_MODE_SERVER; request.flowConfig = stateVO.server.flow; request.transportConfig = stateVO.server.transport; request.namespaceSet = convertStrToNamespaceSet(stateVO.server.namespaceSetStr); ClusterStateService.modifyClusterConfig(request).success(function (data) { if (data.code == 0 && data.data) { alert('修改集群限流服务端配置成功'); window.location.reload(); } else { if (data.code === UNSUPPORTED_CODE) { alert('机器 ' + mac[0] + ':' + mac[1] + ' 的 Sentinel 客户端版本不支持集群限流服务端,请升级至 1.4.0 以上版本并引入相关依赖。'); } else { alert('修改失败:' + data.msg); } } }).error((data, header, config, status) => { alert('未知错误'); }); } $scope.saveConfig = () => { let ok = confirm('是否确定修改集群限流配置?'); if (!ok) { return; } let mode = $scope.stateVO.stateInfo.mode; if (mode != 1 && mode != 0) { alert('未知的集群限流模式'); return; } if (mode == 0) { sendClusterClientRequest($scope.stateVO); } else { sendClusterServerRequest($scope.stateVO); } }; function queryAppMachines() { MachineService.getAppMachines($scope.app).success( function (data) { if (data.code === 0) { // $scope.machines = data.data; if (data.data) { $scope.machines = []; $scope.macsInputOptionsOrigin = []; $scope.macsInputOptions = []; data.data.forEach(function (item) { if (item.healthy) { $scope.macsInputOptionsOrigin.push({ text: item.ip + ':' + item.port, value: item.ip + ':' + item.port }); } }); $scope.macsInputOptions = $scope.macsInputOptionsOrigin; } if ($scope.macsInputOptions.length > 0) { $scope.macInputModel = $scope.macsInputOptions[0].value; } } else { $scope.macsInputOptions = []; } } ); } queryAppMachines(); $scope.$watch('searchKey', function () { if (!$scope.macsInputOptions) { return; } if ($scope.searchKey) { $scope.macsInputOptions = $scope.macsInputOptionsOrigin .filter((e) => e.value.indexOf($scope.searchKey) !== -1); } else { $scope.macsInputOptions = $scope.macsInputOptionsOrigin; } if ($scope.macsInputOptions.length > 0) { $scope.macInputModel = $scope.macsInputOptions[0].value; } else { $scope.macInputModel = ''; } }); $scope.$watch('macInputModel', function () { if ($scope.macInputModel) { fetchMachineClusterState(); } }); }]); ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/scripts/controllers/degrade.js ================================================ var app = angular.module('sentinelDashboardApp'); app.controller('DegradeCtl', ['$scope', '$stateParams', 'DegradeService', 'ngDialog', 'MachineService', function ($scope, $stateParams, DegradeService, ngDialog, MachineService) { //初始化 $scope.app = $stateParams.app; $scope.rulesPageConfig = { pageSize: 10, currentPageIndex: 1, totalPage: 1, totalCount: 0, }; $scope.macsInputConfig = { searchField: ['text', 'value'], persist: true, create: false, maxItems: 1, render: { item: function (data, escape) { return '
' + escape(data.text) + '
'; } }, onChange: function (value, oldValue) { $scope.macInputModel = value; } }; getMachineRules(); function getMachineRules() { if (!$scope.macInputModel) { return; } var mac = $scope.macInputModel.split(':'); DegradeService.queryMachineRules($scope.app, mac[0], mac[1]).success( function (data) { if (data.code == 0 && data.data) { $scope.rules = data.data; $scope.rulesPageConfig.totalCount = $scope.rules.length; } else { $scope.rules = []; $scope.rulesPageConfig.totalCount = 0; } }); }; $scope.getMachineRules = getMachineRules; var degradeRuleDialog; $scope.editRule = function (rule) { $scope.currentRule = angular.copy(rule); $scope.degradeRuleDialog = { title: '编辑降级规则', type: 'edit', confirmBtnText: '保存' }; degradeRuleDialog = ngDialog.open({ template: '/app/views/dialog/degrade-rule-dialog.html', width: 680, overlay: true, scope: $scope }); }; $scope.addNewRule = function () { var mac = $scope.macInputModel.split(':'); $scope.currentRule = { grade: 0, app: $scope.app, ip: mac[0], port: mac[1], limitApp: 'default' }; $scope.degradeRuleDialog = { title: '新增降级规则', type: 'add', confirmBtnText: '新增' }; degradeRuleDialog = ngDialog.open({ template: '/app/views/dialog/degrade-rule-dialog.html', width: 680, overlay: true, scope: $scope }); }; $scope.saveRule = function () { if (!DegradeService.checkRuleValid($scope.currentRule)) { return; } if ($scope.degradeRuleDialog.type === 'add') { addNewRule($scope.currentRule); } else if ($scope.degradeRuleDialog.type === 'edit') { saveRule($scope.currentRule, true); } }; function parseDegradeMode(grade) { switch (grade) { case 0: return 'RT'; case 1: return '异常比例'; case 2: return '异常数'; default: return '未知'; } } var confirmDialog; $scope.deleteRule = function (rule) { $scope.currentRule = rule; $scope.confirmDialog = { title: '删除降级规则', type: 'delete_rule', attentionTitle: '请确认是否删除如下降级规则', attention: '资源名: ' + rule.resource + ', 降级模式: ' + parseDegradeMode(rule.grade) + ', 阈值: ' + rule.count, confirmBtnText: '删除', }; confirmDialog = ngDialog.open({ template: '/app/views/dialog/confirm-dialog.html', scope: $scope, overlay: true }); }; $scope.confirm = function () { if ($scope.confirmDialog.type == 'delete_rule') { deleteRule($scope.currentRule); } else { console.error('error'); } }; function deleteRule(rule) { DegradeService.deleteRule(rule).success(function (data) { if (data.code == 0) { getMachineRules(); confirmDialog.close(); } else { alert('失败!'); } }); }; function addNewRule(rule) { DegradeService.newRule(rule).success(function (data) { if (data.code == 0) { getMachineRules(); degradeRuleDialog.close(); } else { alert('失败!'); } }); }; function saveRule(rule, edit) { DegradeService.saveRule(rule).success(function (data) { if (data.code == 0) { getMachineRules(); if (edit) { degradeRuleDialog.close(); } else { confirmDialog.close(); } } else { alert('失败!'); } }); } queryAppMachines(); function queryAppMachines() { MachineService.getAppMachines($scope.app).success( function (data) { if (data.code == 0) { // $scope.machines = data.data; if (data.data) { $scope.machines = []; $scope.macsInputOptions = []; data.data.forEach(function (item) { if (item.healthy) { $scope.macsInputOptions.push({ text: item.ip + ':' + item.port, value: item.ip + ':' + item.port }); } }); } if ($scope.macsInputOptions.length > 0) { $scope.macInputModel = $scope.macsInputOptions[0].value; } } else { $scope.macsInputOptions = []; } } ); }; $scope.$watch('macInputModel', function () { if ($scope.macInputModel) { getMachineRules(); } }); }]); ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/scripts/controllers/flow_v1.js ================================================ var app = angular.module('sentinelDashboardApp'); app.controller('FlowControllerV1', ['$scope', '$stateParams', 'FlowServiceV1', 'ngDialog', 'MachineService', function ($scope, $stateParams, FlowService, ngDialog, MachineService) { $scope.app = $stateParams.app; $scope.rulesPageConfig = { pageSize: 10, currentPageIndex: 1, totalPage: 1, totalCount: 0, }; $scope.macsInputConfig = { searchField: ['text', 'value'], persist: true, create: false, maxItems: 1, render: { item: function (data, escape) { return '
' + escape(data.text) + '
'; } }, onChange: function (value, oldValue) { $scope.macInputModel = value; } }; $scope.generateThresholdTypeShow = (rule) => { if (!rule.clusterMode) { return '单机'; } if (rule.clusterConfig.thresholdType === 0) { return '集群均摊'; } else if (rule.clusterConfig.thresholdType === 1) { return '集群总体'; } else { return '集群'; } }; getMachineRules(); function getMachineRules() { if (!$scope.macInputModel) { return; } var mac = $scope.macInputModel.split(':'); FlowService.queryMachineRules($scope.app, mac[0], mac[1]).success( function (data) { if (data.code == 0 && data.data) { $scope.rules = data.data; $scope.rulesPageConfig.totalCount = $scope.rules.length; } else { $scope.rules = []; $scope.rulesPageConfig.totalCount = 0; } }); }; $scope.getMachineRules = getMachineRules; var flowRuleDialog; $scope.editRule = function (rule) { $scope.currentRule = angular.copy(rule); $scope.flowRuleDialog = { title: '编辑流控规则', type: 'edit', confirmBtnText: '保存', showAdvanceButton: rule.controlBehavior == 0 && rule.strategy == 0 }; flowRuleDialog = ngDialog.open({ template: '/app/views/dialog/flow-rule-dialog.html', width: 680, overlay: true, scope: $scope }); }; $scope.addNewRule = function () { var mac = $scope.macInputModel.split(':'); $scope.currentRule = { grade: 1, strategy: 0, controlBehavior: 0, app: $scope.app, ip: mac[0], port: mac[1], limitApp: 'default', clusterMode: false, clusterConfig: { thresholdType: 0 } }; $scope.flowRuleDialog = { title: '新增流控规则', type: 'add', confirmBtnText: '新增', showAdvanceButton: true, }; flowRuleDialog = ngDialog.open({ template: '/app/views/dialog/flow-rule-dialog.html', width: 680, overlay: true, scope: $scope }); }; $scope.saveRule = function () { if (!FlowService.checkRuleValid($scope.currentRule)) { return; } if ($scope.flowRuleDialog.type === 'add') { addNewRule($scope.currentRule); } else if ($scope.flowRuleDialog.type === 'edit') { saveRule($scope.currentRule, true); } }; var confirmDialog; $scope.deleteRule = function (rule) { $scope.currentRule = rule; $scope.confirmDialog = { title: '删除流控规则', type: 'delete_rule', attentionTitle: '请确认是否删除如下流控规则', attention: '资源名: ' + rule.resource + ', 流控应用: ' + rule.limitApp + ', 阈值类型: ' + (rule.grade == 0 ? '线程数' : 'QPS') + ', 阈值: ' + rule.count, confirmBtnText: '删除', }; confirmDialog = ngDialog.open({ template: '/app/views/dialog/confirm-dialog.html', scope: $scope, overlay: true }); }; $scope.confirm = function () { if ($scope.confirmDialog.type === 'delete_rule') { deleteRule($scope.currentRule); } else { console.error('error'); } }; function deleteRule(rule) { FlowService.deleteRule(rule).success(function (data) { if (data.code == 0) { getMachineRules(); confirmDialog.close(); } else { alert('失败!'); } }); }; function addNewRule(rule) { FlowService.newRule(rule).success(function (data) { if (data.code == 0) { getMachineRules(); flowRuleDialog.close(); } else { alert('失败!'); } }); }; $scope.onOpenAdvanceClick = function () { $scope.flowRuleDialog.showAdvanceButton = false; }; $scope.onCloseAdvanceClick = function () { $scope.flowRuleDialog.showAdvanceButton = true; }; function saveRule(rule, edit) { FlowService.saveRule(rule).success(function (data) { if (data.code == 0) { getMachineRules(); if (edit) { flowRuleDialog.close(); } else { confirmDialog.close(); } } else { alert('失败!'); } }); } queryAppMachines(); function queryAppMachines() { MachineService.getAppMachines($scope.app).success( function (data) { if (data.code == 0) { // $scope.machines = data.data; if (data.data) { $scope.machines = []; $scope.macsInputOptions = []; data.data.forEach(function (item) { if (item.healthy) { $scope.macsInputOptions.push({ text: item.ip + ':' + item.port, value: item.ip + ':' + item.port }); } }); } if ($scope.macsInputOptions.length > 0) { $scope.macInputModel = $scope.macsInputOptions[0].value; } } else { $scope.macsInputOptions = []; } } ); }; $scope.$watch('macInputModel', function () { if ($scope.macInputModel) { getMachineRules(); } }); }]); ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/scripts/controllers/flow_v2.js ================================================ var app = angular.module('sentinelDashboardApp'); app.controller('FlowControllerV2', ['$scope', '$stateParams', 'FlowServiceV2', 'ngDialog', 'MachineService', function ($scope, $stateParams, FlowService, ngDialog, MachineService) { $scope.app = $stateParams.app; $scope.rulesPageConfig = { pageSize: 10, currentPageIndex: 1, totalPage: 1, totalCount: 0, }; $scope.macsInputConfig = { searchField: ['text', 'value'], persist: true, create: false, maxItems: 1, render: { item: function (data, escape) { return '
' + escape(data.text) + '
'; } }, onChange: function (value, oldValue) { $scope.macInputModel = value; } }; $scope.generateThresholdTypeShow = (rule) => { if (!rule.clusterMode) { return '单机'; } if (rule.clusterConfig.thresholdType === 0) { return '集群均摊'; } else if (rule.clusterConfig.thresholdType === 1) { return '集群总体'; } else { return '集群'; } }; getMachineRules(); function getMachineRules() { if (!$scope.macInputModel) { return; } var mac = $scope.macInputModel.split(':'); FlowService.queryMachineRules($scope.app, mac[0], mac[1]).success( function (data) { if (data.code == 0 && data.data) { $scope.rules = data.data; $scope.rulesPageConfig.totalCount = $scope.rules.length; } else { $scope.rules = []; $scope.rulesPageConfig.totalCount = 0; } }); }; $scope.getMachineRules = getMachineRules; var flowRuleDialog; $scope.editRule = function (rule) { $scope.currentRule = angular.copy(rule); $scope.flowRuleDialog = { title: '编辑流控规则', type: 'edit', confirmBtnText: '保存', showAdvanceButton: rule.controlBehavior == 0 && rule.strategy == 0 }; flowRuleDialog = ngDialog.open({ template: '/app/views/dialog/flow-rule-dialog.html', width: 680, overlay: true, scope: $scope }); }; $scope.addNewRule = function () { var mac = $scope.macInputModel.split(':'); $scope.currentRule = { grade: 1, strategy: 0, controlBehavior: 0, app: $scope.app, ip: mac[0], port: mac[1], limitApp: 'default', clusterMode: false, clusterConfig: { thresholdType: 0, fallbackToLocalWhenFail: true } }; $scope.flowRuleDialog = { title: '新增流控规则', type: 'add', confirmBtnText: '新增', showAdvanceButton: true, }; flowRuleDialog = ngDialog.open({ template: '/app/views/dialog/flow-rule-dialog.html', width: 680, overlay: true, scope: $scope }); }; $scope.saveRule = function () { if (!FlowService.checkRuleValid($scope.currentRule)) { return; } if ($scope.flowRuleDialog.type === 'add') { addNewRule($scope.currentRule); } else if ($scope.flowRuleDialog.type === 'edit') { saveRule($scope.currentRule, true); } }; var confirmDialog; $scope.deleteRule = function (rule) { $scope.currentRule = rule; $scope.confirmDialog = { title: '删除流控规则', type: 'delete_rule', attentionTitle: '请确认是否删除如下流控规则', attention: '资源名: ' + rule.resource + ', 流控应用: ' + rule.limitApp + ', 阈值类型: ' + (rule.grade == 0 ? '线程数' : 'QPS') + ', 阈值: ' + rule.count, confirmBtnText: '删除', }; confirmDialog = ngDialog.open({ template: '/app/views/dialog/confirm-dialog.html', scope: $scope, overlay: true }); }; $scope.confirm = function () { if ($scope.confirmDialog.type === 'delete_rule') { deleteRule($scope.currentRule); } else { console.error('error'); } }; function deleteRule(rule) { FlowService.deleteRule(rule).success(function (data) { if (data.code == 0) { getMachineRules(); confirmDialog.close(); } else { alert('失败!'); } }); }; function addNewRule(rule) { FlowService.newRule(rule).success(function (data) { if (data.code == 0) { getMachineRules(); flowRuleDialog.close(); } else { alert('失败!'); } }); }; $scope.onOpenAdvanceClick = function () { $scope.flowRuleDialog.showAdvanceButton = false; }; $scope.onCloseAdvanceClick = function () { $scope.flowRuleDialog.showAdvanceButton = true; }; function saveRule(rule, edit) { FlowService.saveRule(rule).success(function (data) { if (data.code == 0) { getMachineRules(); if (edit) { flowRuleDialog.close(); } else { confirmDialog.close(); } } else { alert('失败!'); } }); } queryAppMachines(); function queryAppMachines() { MachineService.getAppMachines($scope.app).success( function (data) { if (data.code == 0) { // $scope.machines = data.data; if (data.data) { $scope.machines = []; $scope.macsInputOptions = []; data.data.forEach(function (item) { if (item.healthy) { $scope.macsInputOptions.push({ text: item.ip + ':' + item.port, value: item.ip + ':' + item.port }); } }); } if ($scope.macsInputOptions.length > 0) { $scope.macInputModel = $scope.macsInputOptions[0].value; } } else { $scope.macsInputOptions = []; } } ); }; $scope.$watch('macInputModel', function () { if ($scope.macInputModel) { getMachineRules(); } }); }]); ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/scripts/controllers/home.js ================================================ /** * @ngdoc function * @name sentinelDashboardApp.controller:MainCtrl * @description * # MainCtrl * Controller of the sentinelDashboardApp */ angular.module('sentinelDashboardApp') .controller('HomeCtrl', ['$scope', '$position', function ($scope, $position) { // do noting }]); ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/scripts/controllers/identity.js ================================================ var app = angular.module('sentinelDashboardApp'); app.controller('IdentityCtl', ['$scope', '$stateParams', 'IdentityService', 'ngDialog', 'FlowServiceV1', 'DegradeService', 'AuthorityRuleService', 'ParamFlowService', 'MachineService', '$interval', '$location', '$timeout', function ($scope, $stateParams, IdentityService, ngDialog, FlowService, DegradeService, AuthorityRuleService, ParamFlowService, MachineService, $interval, $location, $timeout) { $scope.app = $stateParams.app; $scope.currentPage = 1; $scope.pageSize = 16; $scope.totalPage = 1; $scope.totalCount = 0; $scope.identities = []; // 数据自动刷新频率, 默认10s var DATA_REFRESH_INTERVAL = 30; $scope.isExpand = true; $scope.searchKey = ''; $scope.firstExpandAll = false; $scope.isTreeView = true; $scope.macsInputConfig = { searchField: ['text', 'value'], persist: true, create: false, maxItems: 1, render: { item: function (data, escape) { return '
' + escape(data.text) + '
'; } }, onChange: function (value, oldValue) { $scope.macInputModel = value; } }; $scope.table = null; var flowRuleDialog; var flowRuleDialogScope; $scope.addNewFlowRule = function (resource) { if (!$scope.macInputModel) { return; } var mac = $scope.macInputModel.split(':'); flowRuleDialogScope = $scope.$new(true); flowRuleDialogScope.currentRule = { enable: false, strategy: 0, grade: 1, controlBehavior: 0, resource: resource, limitApp: 'default', clusterMode: false, clusterConfig: { thresholdType: 0 }, app: $scope.app, ip: mac[0], port: mac[1] }; flowRuleDialogScope.flowRuleDialog = { title: '新增流控规则', type: 'add', confirmBtnText: '新增', saveAndContinueBtnText: '新增并继续添加', showAdvanceButton: true }; // $scope.flowRuleDialog = { // showAdvanceButton : true // }; flowRuleDialogScope.saveRule = saveFlowRule; flowRuleDialogScope.saveRuleAndContinue = saveFlowRuleAndContinue; flowRuleDialogScope.onOpenAdvanceClick = function () { flowRuleDialogScope.flowRuleDialog.showAdvanceButton = false; }; flowRuleDialogScope.onCloseAdvanceClick = function () { flowRuleDialogScope.flowRuleDialog.showAdvanceButton = true; }; flowRuleDialog = ngDialog.open({ template: '/app/views/dialog/flow-rule-dialog.html', width: 680, overlay: true, scope: flowRuleDialogScope }); }; function saveFlowRule() { if (!FlowService.checkRuleValid(flowRuleDialogScope.currentRule)) { return; } FlowService.newRule(flowRuleDialogScope.currentRule).success(function (data) { if (data.code === 0) { flowRuleDialog.close(); let url = '/dashboard/flow/' + $scope.app; $location.path(url); } else { alert('失败!'); } }).error((data, header, config, status) => { alert('未知错误'); }); } function saveFlowRuleAndContinue() { if (!FlowService.checkRuleValid(flowRuleDialogScope.currentRule)) { return; } FlowService.newRule(flowRuleDialogScope.currentRule).success(function (data) { if (data.code == 0) { flowRuleDialog.close(); } else { alert('失败!'); } }); } var degradeRuleDialog; var degradeRuleDialogScope; $scope.addNewDegradeRule = function (resource) { if (!$scope.macInputModel) { return; } var mac = $scope.macInputModel.split(':'); degradeRuleDialogScope = $scope.$new(true); degradeRuleDialogScope.currentRule = { enable: false, grade: 0, strategy: 0, resource: resource, limitApp: 'default', app: $scope.app, ip: mac[0], port: mac[1] }; degradeRuleDialogScope.degradeRuleDialog = { title: '新增降级规则', type: 'add', confirmBtnText: '新增', saveAndContinueBtnText: '新增并继续添加' }; degradeRuleDialogScope.saveRule = saveDegradeRule; degradeRuleDialogScope.saveRuleAndContinue = saveDegradeRuleAndContinue; degradeRuleDialog = ngDialog.open({ template: '/app/views/dialog/degrade-rule-dialog.html', width: 680, overlay: true, scope: degradeRuleDialogScope }); }; function saveDegradeRule() { if (!DegradeService.checkRuleValid(degradeRuleDialogScope.currentRule)) { return; } DegradeService.newRule(degradeRuleDialogScope.currentRule).success(function (data) { if (data.code == 0) { degradeRuleDialog.close(); var url = '/dashboard/degrade/' + $scope.app; $location.path(url); } else { alert('失败!'); } }); } function saveDegradeRuleAndContinue() { if (!DegradeService.checkRuleValid(degradeRuleDialogScope.currentRule)) { return; } DegradeService.newRule(degradeRuleDialogScope.currentRule).success(function (data) { if (data.code == 0) { degradeRuleDialog.close(); } else { alert('失败!'); } }); } let authorityRuleDialog; let authorityRuleDialogScope; function saveAuthorityRule() { let ruleEntity = authorityRuleDialogScope.currentRule; if (!AuthorityRuleService.checkRuleValid(ruleEntity.rule)) { return; } AuthorityRuleService.addNewRule(ruleEntity).success((data) => { if (data.success) { authorityRuleDialog.close(); let url = '/dashboard/authority/' + $scope.app; $location.path(url); } else { alert('添加规则失败:' + data.msg); } }).error((data) => { if (data) { alert('添加规则失败:' + data.msg); } else { alert("添加规则失败:未知错误"); } }); } function saveAuthorityRuleAndContinue() { let ruleEntity = authorityRuleDialogScope.currentRule; if (!AuthorityRuleService.checkRuleValid(ruleEntity.rule)) { return; } AuthorityRuleService.addNewRule(ruleEntity).success((data) => { if (data.success) { authorityRuleDialog.close(); } else { alert('添加规则失败:' + data.msg); } }).error((data) => { if (data) { alert('添加规则失败:' + data.msg); } else { alert("添加规则失败:未知错误"); } }); } $scope.addNewAuthorityRule = function (resource) { if (!$scope.macInputModel) { return; } let mac = $scope.macInputModel.split(':'); authorityRuleDialogScope = $scope.$new(true); authorityRuleDialogScope.currentRule = { app: $scope.app, ip: mac[0], port: mac[1], rule: { resource: resource, strategy: 0, limitApp: '', } }; authorityRuleDialogScope.authorityRuleDialog = { title: '新增授权规则', type: 'add', confirmBtnText: '新增', saveAndContinueBtnText: '新增并继续添加' }; authorityRuleDialogScope.saveRule = saveAuthorityRule; authorityRuleDialogScope.saveRuleAndContinue = saveAuthorityRuleAndContinue; authorityRuleDialog = ngDialog.open({ template: '/app/views/dialog/authority-rule-dialog.html', width: 680, overlay: true, scope: authorityRuleDialogScope }); }; let paramFlowRuleDialog; let paramFlowRuleDialogScope; function saveParamFlowRule() { let ruleEntity = paramFlowRuleDialogScope.currentRule; if (!ParamFlowService.checkRuleValid(ruleEntity.rule)) { return; } ParamFlowService.addNewRule(ruleEntity).success((data) => { if (data.success) { paramFlowRuleDialog.close(); let url = '/dashboard/paramFlow/' + $scope.app; $location.path(url); } else { alert('添加热点规则失败:' + data.msg); } }).error((data) => { if (data) { alert('添加热点规则失败:' + data.msg); } else { alert("添加热点规则失败:未知错误"); } }); } function saveParamFlowRuleAndContinue() { let ruleEntity = paramFlowRuleDialogScope.currentRule; if (!ParamFlowService.checkRuleValid(ruleEntity.rule)) { return; } ParamFlowService.addNewRule(ruleEntity).success((data) => { if (data.success) { paramFlowRuleDialog.close(); } else { alert('添加热点规则失败:' + data.msg); } }).error((data) => { if (data) { alert('添加热点规则失败:' + data.msg); } else { alert("添加热点规则失败:未知错误"); } }); } $scope.addNewParamFlowRule = function (resource) { if (!$scope.macInputModel) { return; } let mac = $scope.macInputModel.split(':'); paramFlowRuleDialogScope = $scope.$new(true); paramFlowRuleDialogScope.currentRule = { app: $scope.app, ip: mac[0], port: mac[1], rule: { resource: resource, grade: 1, paramFlowItemList: [], count: 0, limitApp: 'default', } }; paramFlowRuleDialogScope.paramFlowRuleDialog = { title: '新增热点规则', type: 'add', confirmBtnText: '新增', saveAndContinueBtnText: '新增并继续添加', supportAdvanced: false, showAdvanceButton: true }; paramFlowRuleDialogScope.saveRule = saveParamFlowRule; paramFlowRuleDialogScope.saveRuleAndContinue = saveParamFlowRuleAndContinue; // paramFlowRuleDialogScope.onOpenAdvanceClick = function () { // paramFlowRuleDialogScope.paramFlowRuleDialog.showAdvanceButton = false; // }; // paramFlowRuleDialogScope.onCloseAdvanceClick = function () { // paramFlowRuleDialogScope.paramFlowRuleDialog.showAdvanceButton = true; // }; paramFlowRuleDialog = ngDialog.open({ template: '/app/views/dialog/param-flow-rule-dialog.html', width: 680, overlay: true, scope: paramFlowRuleDialogScope }); }; var searchHandler; $scope.searchChange = function (searchKey) { $timeout.cancel(searchHandler); searchHandler = $timeout(function () { $scope.searchKey = searchKey; $scope.isExpand = true; $scope.firstExpandAll = true; reInitIdentityDatas(); $scope.firstExpandAll = false; }, 600); }; $scope.initTreeTable = function () { // if (!$scope.table) { com_github_culmat_jsTreeTable.register(window); $scope.table = window.treeTable($('#identities')); // } }; $scope.expandAll = function () { $scope.isExpand = true; }; $scope.collapseAll = function () { $scope.isExpand = false; }; $scope.treeView = function () { $scope.isTreeView = true; queryIdentities(); }; $scope.listView = function () { $scope.isTreeView = false; queryIdentities(); }; function queryAppMachines() { MachineService.getAppMachines($scope.app).success( function (data) { if (data.code === 0) { if (data.data) { $scope.machines = []; $scope.macsInputOptions = []; data.data.forEach(function (item) { if (item.healthy) { $scope.macsInputOptions.push({ text: item.ip + ':' + item.port, value: item.ip + ':' + item.port }); } }); } if ($scope.macsInputOptions.length > 0) { $scope.macInputModel = $scope.macsInputOptions[0].value; } } else { $scope.macsInputOptions = []; } } ); } // Fetch all machines by current app name. queryAppMachines(); $scope.$watch('macInputModel', function () { if ($scope.macInputModel) { reInitIdentityDatas(); } }); $scope.$on('$destroy', function () { $interval.cancel(intervalId); }); var intervalId; function reInitIdentityDatas() { // $interval.cancel(intervalId); queryIdentities(); // intervalId = $interval(function () { // queryIdentities(); // }, DATA_REFRESH_INTERVAL * 1000); }; function queryIdentities() { var mac = $scope.macInputModel.split(':'); if (mac == null || mac.length < 2) { return; } if ($scope.isTreeView) { IdentityService.fetchIdentityOfMachine(mac[0], mac[1], $scope.searchKey).success( function (data) { if (data.code == 0 && data.data) { $scope.identities = data.data; $scope.totalCount = $scope.identities.length; } else { $scope.identities = []; $scope.totalCount = 0; } } ); } else { IdentityService.fetchClusterNodeOfMachine(mac[0], mac[1], $scope.searchKey).success( function (data) { if (data.code == 0 && data.data) { $scope.identities = data.data; $scope.totalCount = $scope.identities.length; } else { $scope.identities = []; $scope.totalCount = 0; } } ); } }; $scope.queryIdentities = queryIdentities; }]); ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/scripts/controllers/login.js ================================================ var app = angular.module('sentinelDashboardApp'); app.controller('LoginCtl', ['$scope', '$state', '$window', 'AuthService', function ($scope, $state, $window, LoginService) { // If auth, jump to the index page directly if ($window.localStorage.getItem('session_sentinel_admin')) { $state.go('dashboard'); } $scope.login = function () { if (!$scope.username) { alert('请输入用户名'); return; } if (!$scope.password) { alert('请输入密码'); return; } var param = {"username": $scope.username, "password": $scope.password}; LoginService.login(param).success(function (data) { if (data.code == 0) { $window.localStorage.setItem('session_sentinel_admin', { username: data.data }); $state.go('dashboard'); } else { alert(data.msg); } }); }; }] ); ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/scripts/controllers/machine.js ================================================ var app = angular.module('sentinelDashboardApp'); app.controller('MachineCtl', ['$scope', '$stateParams', 'MachineService', function ($scope, $stateParams, MachineService) { $scope.app = $stateParams.app; $scope.propertyName = ''; $scope.reverse = false; $scope.currentPage = 1; $scope.machines = []; $scope.machinesPageConfig = { pageSize: 10, currentPageIndex: 1, totalPage: 1, totalCount: 0, }; $scope.sortBy = function (propertyName) { // console.log('machine sortBy ' + propertyName); $scope.reverse = ($scope.propertyName === propertyName) ? !$scope.reverse : false; $scope.propertyName = propertyName; }; $scope.reloadMachines = function() { MachineService.getAppMachines($scope.app).success( function (data) { // console.log('get machines: ' + data.data[0].hostname) if (data.code == 0 && data.data) { $scope.machines = data.data; var healthy = 0; $scope.machines.forEach(function (item) { if (item.healthy) { healthy++; } if (!item.hostname) { item.hostname = '未知' } }) $scope.healthyCount = healthy; $scope.machinesPageConfig.totalCount = $scope.machines.length; } else { $scope.machines = []; $scope.healthyCount = 0; } } ); }; $scope.removeMachine = function(ip, port) { if (!confirm("confirm to remove machine [" + ip + ":" + port + "]?")) { return; } MachineService.removeAppMachine($scope.app, ip, port).success( function(data) { if (data.code == 0) { $scope.reloadMachines(); } else { alert("remove failed"); } } ); }; $scope.reloadMachines(); }]); ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/scripts/controllers/main.js ================================================ /** * @ngdoc function * @name sentinelDashboardApp.controller:MainCtrl * @description * # MainCtrl * Controller of the sentinelDashboardApp */ angular.module('sentinelDashboardApp') .controller('DashboardCtrl', ['$scope', '$position', function ($scope, $position) { }]); ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/scripts/controllers/metric.js ================================================ var app = angular.module('sentinelDashboardApp'); app.controller('MetricCtl', ['$scope', '$stateParams', 'MetricService', '$interval', '$timeout', function ($scope, $stateParams, MetricService, $interval, $timeout) { $scope.endTime = new Date(); $scope.startTime = new Date(); $scope.startTime.setMinutes($scope.endTime.getMinutes() - 30); $scope.startTimeFmt = formatDate($scope.startTime); $scope.endTimeFmt = formatDate($scope.endTime); function formatDate(date) { return moment(date).format('YYYY/MM/DD HH:mm:ss'); } $scope.changeStartTime = function (startTime) { $scope.startTime = new Date(startTime); $scope.startTimeFmt = formatDate(startTime); }; $scope.changeEndTime = function (endTime) { $scope.endTime = new Date(endTime); $scope.endTimeFmt = formatDate(endTime); }; $scope.app = $stateParams.app; // 数据自动刷新频率 var DATA_REFRESH_INTERVAL = 1000 * 10; $scope.servicePageConfig = { pageSize: 6, currentPageIndex: 1, totalPage: 1, totalCount: 0, }; $scope.servicesChartConfigs = []; $scope.pageChanged = function (newPageNumber) { $scope.servicePageConfig.currentPageIndex = newPageNumber; reInitIdentityDatas(); }; var searchT; $scope.searchService = function () { $timeout.cancel(searchT); searchT = $timeout(function () { reInitIdentityDatas(); }, 600); } var intervalId; reInitIdentityDatas(); function reInitIdentityDatas() { $interval.cancel(intervalId); queryIdentityDatas(); intervalId = $interval(function () { queryIdentityDatas(); }, DATA_REFRESH_INTERVAL); }; $scope.$on('$destroy', function () { $interval.cancel(intervalId); }); $scope.initAllChart = function () { $.each($scope.metrics, function (idx, metric) { if (idx == $scope.metrics.length - 1) { return; } const chart = new G2.Chart({ container: 'chart' + idx, forceFit: true, width: 100, height: 250, padding: [10, 30, 70, 50] }); var maxQps = 0; for (var i in metric.data) { var item = metric.data[i]; if (item.passQps > maxQps) { maxQps = item.passQps; } if (item.blockQps > maxQps) { maxQps = item.blockQps; } } chart.source(metric.data); chart.scale('timestamp', { type: 'time', mask: 'YYYY-MM-DD HH:mm:ss' }); chart.scale('passQps', { min: 0, max: maxQps, fine: true, alias: '通过 QPS' // max: 10 }); chart.scale('blockQps', { min: 0, max: maxQps, fine: true, alias: '拒绝 QPS', }); chart.scale('rt', { min: 0, fine: true, }); chart.axis('rt', { grid: null, label: null }); chart.axis('blockQps', { grid: null, label: null }); chart.axis('timestamp', { label: { textStyle: { textAlign: 'center', // 文本对齐方向,可取值为: start center end fill: '#404040', // 文本的颜色 fontSize: '11', // 文本大小 //textBaseline: 'top', // 文本基准线,可取 top middle bottom,默认为middle }, autoRotate: false, formatter: function (text, item, index) { return text.substring(11, 11 + 5); } } }); chart.legend({ custom: true, position: 'bottom', allowAllCanceled: true, itemFormatter: function (val) { if ('passQps' === val) { return '通过 QPS'; } if ('blockQps' === val) { return '拒绝 QPS'; } return val; }, items: [ { value: 'passQps', marker: { symbol: 'hyphen', stroke: 'green', radius: 5, lineWidth: 2 } }, { value: 'blockQps', marker: { symbol: 'hyphen', stroke: 'blue', radius: 5, lineWidth: 2 } }, //{ value: 'rt', marker: {symbol: 'hyphen', stroke: 'gray', radius: 5, lineWidth: 2} }, ], onClick: function (ev) { const item = ev.item; const value = item.value; const checked = ev.checked; const geoms = chart.getAllGeoms(); for (var i = 0; i < geoms.length; i++) { const geom = geoms[i]; if (geom.getYScale().field === value) { if (checked) { geom.show(); } else { geom.hide(); } } } } }); chart.line().position('timestamp*passQps').size(1).color('green').shape('smooth'); chart.line().position('timestamp*blockQps').size(1).color('blue').shape('smooth'); //chart.line().position('timestamp*rt').size(1).color('gray').shape('smooth'); G2.track(false); chart.render(); }); }; $scope.metrics = []; $scope.emptyObjs = []; function queryIdentityDatas() { var params = { app: $scope.app, pageIndex: $scope.servicePageConfig.currentPageIndex, pageSize: $scope.servicePageConfig.pageSize, desc: $scope.isDescOrder, searchKey: $scope.serviceQuery }; MetricService.queryAppSortedIdentities(params).success(function (data) { $scope.metrics = []; $scope.emptyObjs = []; if (data.code === 0 && data.data) { var metricsObj = data.data.metric; var identityNames = Object.keys(metricsObj); if (identityNames.length < 1) { $scope.emptyServices = true; } else { $scope.emptyServices = false; } $scope.servicePageConfig.totalPage = data.data.totalPage; $scope.servicePageConfig.pageSize = data.data.pageSize; var totalCount = data.data.totalCount; $scope.servicePageConfig.totalCount = totalCount; for (i = 0; i < totalCount; i++) { $scope.emptyObjs.push({}); } $.each(identityNames, function (idx, identityName) { var identityDatas = metricsObj[identityName]; var metrics = {}; metrics.resource = identityName; // metrics.data = identityDatas; metrics.data = fillZeros(identityDatas); metrics.shortData = lastOfArray(identityDatas, 6); $scope.metrics.push(metrics); }); // push an empty element in the last, for ng-init reasons. $scope.metrics.push([]); } else { $scope.emptyServices = true; console.log(data.msg); } }); }; function fillZeros(metricData) { if (!metricData || metricData.length == 0) { return []; } var filledData = []; filledData.push(metricData[0]); var lastTime = metricData[0].timestamp / 1000; for (var i = 1; i < metricData.length; i++) { var curTime = metricData[i].timestamp / 1000; if (curTime > lastTime + 1) { for (var j = lastTime + 1; j < curTime; j++) { filledData.push({ "timestamp": j * 1000, "passQps": 0, "blockQps": 0, "successQps": 0, "exception": 0, "rt": 0, "count": 0 }) } } filledData.push(metricData[i]); lastTime = curTime; } return filledData; } function lastOfArray(arr, n) { if (!arr.length) { return []; } var rs = []; for (i = 0; i < n && i < arr.length; i++) { rs.push(arr[arr.length - 1 - i]); } return rs; } $scope.isDescOrder = true; $scope.setDescOrder = function () { $scope.isDescOrder = true; reInitIdentityDatas(); } $scope.setAscOrder = function () { $scope.isDescOrder = false; reInitIdentityDatas(); } }]); ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/scripts/controllers/param_flow.js ================================================ /** * Parameter flow control controller. * * @author Eric Zhao */ angular.module('sentinelDashboardApp').controller('ParamFlowController', ['$scope', '$stateParams', 'ParamFlowService', 'ngDialog', 'MachineService', function ($scope, $stateParams, ParamFlowService, ngDialog, MachineService) { const UNSUPPORTED_CODE = 4041; $scope.app = $stateParams.app; $scope.curExItem = {}; $scope.paramItemClassTypeList = [ 'int', 'double', 'java.lang.String', 'long', 'float', 'char', 'byte' ]; $scope.rulesPageConfig = { pageSize: 10, currentPageIndex: 1, totalPage: 1, totalCount: 0, }; $scope.macsInputConfig = { searchField: ['text', 'value'], persist: true, create: false, maxItems: 1, render: { item: function (data, escape) { return '
' + escape(data.text) + '
'; } }, onChange: function (value, oldValue) { $scope.macInputModel = value; } }; function updateSingleParamItem(arr, v, t, c) { for (let i = 0; i < arr.length; i++) { if (arr[i].object === v && arr[i].classType === t) { arr[i].count = c; return; } } arr.push({object: v, classType: t, count: c}); } function removeSingleParamItem(arr, v, t) { for (let i = 0; i < arr.length; i++) { if (arr[i].object === v && arr[i].classType === t) { arr.splice(i, 1); break; } } } function isNumberClass(classType) { return classType === 'int' || classType === 'double' || classType === 'float' || classType === 'long' || classType === 'short'; } function isByteClass(classType) { return classType === 'byte'; } function notNumberAtLeastZero(num) { return num === undefined || num === '' || isNaN(num) || num < 0; } function notGoodNumber(num) { return num === undefined || num === '' || isNaN(num); } function notGoodNumberBetweenExclusive(num, l ,r) { return num === undefined || num === '' || isNaN(num) || num < l || num > r; } $scope.notValidParamItem = (curExItem) => { if (isNumberClass(curExItem.classType) && notGoodNumber(curExItem.object)) { return true; } if (isByteClass(curExItem.classType) && notGoodNumberBetweenExclusive(curExItem.object, -128, 127)) { return true; } return curExItem.object === undefined || curExItem.classType === undefined || notNumberAtLeastZero(curExItem.count); }; $scope.addParamItem = () => { updateSingleParamItem($scope.currentRule.rule.paramFlowItemList, $scope.curExItem.object, $scope.curExItem.classType, $scope.curExItem.count); let oldItem = $scope.curExItem; $scope.curExItem = {classType: oldItem.classType}; }; $scope.removeParamItem = (v, t) => { removeSingleParamItem($scope.currentRule.rule.paramFlowItemList, v, t); }; function getMachineRules() { if (!$scope.macInputModel) { return; } let mac = $scope.macInputModel.split(':'); ParamFlowService.queryMachineRules($scope.app, mac[0], mac[1]) .success(function (data) { if (data.code === 0 && data.data) { $scope.loadError = undefined; $scope.rules = data.data; $scope.rulesPageConfig.totalCount = $scope.rules.length; } else { $scope.rules = []; $scope.rulesPageConfig.totalCount = 0; if (data.code === UNSUPPORTED_CODE) { $scope.loadError = {message: "机器 " + mac[0] + ":" + mac[1] + " 的 Sentinel 客户端版本不支持热点参数限流功能,请升级至 0.2.0 以上版本并引入 sentinel-parameter-flow-control 依赖。"} } else { $scope.loadError = {message: data.msg} } } }) .error((data, header, config, status) => { $scope.loadError = {message: "未知错误"} }); } $scope.getMachineRules = getMachineRules; getMachineRules(); var paramFlowRuleDialog; $scope.editRule = function (rule) { $scope.currentRule = angular.copy(rule); $scope.paramFlowRuleDialog = { title: '编辑热点规则', type: 'edit', confirmBtnText: '保存', supportAdvanced: true, showAdvanceButton: rule.rule.paramFlowItemList === undefined || rule.rule.paramFlowItemList.length <= 0 }; paramFlowRuleDialog = ngDialog.open({ template: '/app/views/dialog/param-flow-rule-dialog.html', width: 680, overlay: true, scope: $scope }); $scope.curExItem = {}; }; $scope.addNewRule = function () { var mac = $scope.macInputModel.split(':'); $scope.currentRule = { app: $scope.app, ip: mac[0], port: mac[1], rule: { grade: 1, paramFlowItemList: [], count: 0, limitApp: 'default', clusterMode: false, clusterConfig: { thresholdType: 0 } } }; $scope.paramFlowRuleDialog = { title: '新增热点规则', type: 'add', confirmBtnText: '新增', showAdvanceButton: true, }; paramFlowRuleDialog = ngDialog.open({ template: '/app/views/dialog/param-flow-rule-dialog.html', width: 680, overlay: true, scope: $scope }); $scope.curExItem = {}; }; $scope.onOpenAdvanceClick = function () { $scope.paramFlowRuleDialog.showAdvanceButton = false; }; $scope.onCloseAdvanceClick = function () { $scope.paramFlowRuleDialog.showAdvanceButton = true; }; $scope.saveRule = function () { if (!ParamFlowService.checkRuleValid($scope.currentRule.rule)) { return; } if ($scope.paramFlowRuleDialog.type === 'add') { addNewRuleAndPush($scope.currentRule); } else if ($scope.paramFlowRuleDialog.type === 'edit') { saveRuleAndPush($scope.currentRule, true); } }; function addNewRuleAndPush(rule) { ParamFlowService.addNewRule(rule).success((data) => { if (data.success) { getMachineRules(); paramFlowRuleDialog.close(); } else { alert('添加规则失败:' + data.msg); } }).error((data) => { if (data) { alert('添加规则失败:' + data.msg); } else { alert("添加规则失败:未知错误"); } }); } function saveRuleAndPush(rule, edit) { ParamFlowService.saveRule(rule).success(function (data) { if (data.success) { alert("修改规则成功"); getMachineRules(); if (edit) { paramFlowRuleDialog.close(); } else { confirmDialog.close(); } } else { alert('修改规则失败:' + data.msg); } }).error((data) => { if (data) { alert('修改规则失败:' + data.msg); } else { alert("修改规则失败:未知错误"); } }); } function deleteRuleAndPush(entity) { if (entity.id === undefined || isNaN(entity.id)) { alert('规则 ID 不合法!'); return; } ParamFlowService.deleteRule(entity).success((data) => { if (data.code == 0) { getMachineRules(); confirmDialog.close(); } else { alert('删除规则失败:' + data.msg); } }).error((data) => { if (data) { alert('删除规则失败:' + data.msg); } else { alert("删除规则失败:未知错误"); } }); }; var confirmDialog; $scope.deleteRule = function (ruleEntity) { $scope.currentRule = ruleEntity; console.log('deleting: ' + ruleEntity); $scope.confirmDialog = { title: '删除热点规则', type: 'delete_rule', attentionTitle: '请确认是否删除如下热点参数限流规则', attention: '资源名: ' + ruleEntity.rule.resource + ', 热点参数索引: ' + ruleEntity.rule.paramIdx + ', 限流模式: ' + (ruleEntity.rule.grade === 1 ? 'QPS' : '未知') + ', 限流阈值: ' + ruleEntity.rule.count, confirmBtnText: '删除', }; confirmDialog = ngDialog.open({ template: '/app/views/dialog/confirm-dialog.html', scope: $scope, overlay: true }); }; $scope.confirm = function () { if ($scope.confirmDialog.type === 'delete_rule') { deleteRuleAndPush($scope.currentRule); } else { console.error('error'); } }; queryAppMachines(); function queryAppMachines() { MachineService.getAppMachines($scope.app).success( function (data) { if (data.code == 0) { // $scope.machines = data.data; if (data.data) { $scope.machines = []; $scope.macsInputOptions = []; data.data.forEach(function (item) { if (item.healthy) { $scope.macsInputOptions.push({ text: item.ip + ':' + item.port, value: item.ip + ':' + item.port }); } }); } if ($scope.macsInputOptions.length > 0) { $scope.macInputModel = $scope.macsInputOptions[0].value; } } else { $scope.macsInputOptions = []; } } ); }; $scope.$watch('macInputModel', function () { if ($scope.macInputModel) { getMachineRules(); } }); }]); ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/scripts/controllers/system.js ================================================ var app = angular.module('sentinelDashboardApp'); app.controller('SystemCtl', ['$scope', '$stateParams', 'SystemService', 'ngDialog', 'MachineService', function ($scope, $stateParams, SystemService, ngDialog, MachineService) { //初始化 $scope.app = $stateParams.app; $scope.rulesPageConfig = { pageSize: 10, currentPageIndex: 1, totalPage: 1, totalCount: 0, }; $scope.macsInputConfig = { searchField: ['text', 'value'], persist: true, create: false, maxItems: 1, render: { item: function (data, escape) { return '
' + escape(data.text) + '
'; } }, onChange: function (value, oldValue) { $scope.macInputModel = value; } }; getMachineRules(); function getMachineRules() { if (!$scope.macInputModel) { return; } var mac = $scope.macInputModel.split(':'); SystemService.queryMachineRules($scope.app, mac[0], mac[1]).success( function (data) { if (data.code == 0 && data.data) { $scope.rules = data.data; $.each($scope.rules, function (idx, rule) { // rule.orginEnable = rule.enable; if (rule.avgLoad >= 0) { rule.grade = 0; } else if (rule.avgRt >= 0) { rule.grade = 1; } else if (rule.maxThread >= 0) { rule.grade = 2; } else if (rule.qps >= 0) { rule.grade = 3; } }); $scope.rulesPageConfig.totalCount = $scope.rules.length; } else { $scope.rules = []; $scope.rulesPageConfig.totalCount = 0; } }); }; $scope.getMachineRules = getMachineRules; var systemRuleDialog; $scope.editRule = function (rule) { $scope.currentRule = angular.copy(rule); $scope.systemRuleDialog = { title: '编辑系统保护规则', type: 'edit', confirmBtnText: '保存' }; systemRuleDialog = ngDialog.open({ template: '/app/views/dialog/system-rule-dialog.html', width: 680, overlay: true, scope: $scope }); }; $scope.addNewRule = function () { var mac = $scope.macInputModel.split(':'); $scope.currentRule = { grade: 0, app: $scope.app, ip: mac[0], port: mac[1], }; $scope.systemRuleDialog = { title: '新增系统保护规则', type: 'add', confirmBtnText: '新增' }; systemRuleDialog = ngDialog.open({ template: '/app/views/dialog/system-rule-dialog.html', width: 680, overlay: true, scope: $scope }); }; $scope.saveRule = function () { if ($scope.systemRuleDialog.type == 'add') { addNewRule($scope.currentRule); } else if ($scope.systemRuleDialog.type == 'edit') { saveRule($scope.currentRule, true); } } var confirmDialog; $scope.deleteRule = function (rule) { $scope.currentRule = rule; var ruleTypeDesc = ''; var ruleTypeCount = null; if (rule.avgLoad != -1) { ruleTypeDesc = 'LOAD'; ruleTypeCount = rule.avgLoad; } else if (rule.avgRt != -1) { ruleTypeDesc = 'RT'; ruleTypeCount = rule.avgRt; } else if (rule.maxThread != -1) { ruleTypeDesc = '线程数'; ruleTypeCount = rule.maxThread; } else if (rule.qps != -1) { ruleTypeDesc = 'QPS'; ruleTypeCount = rule.qps; } $scope.confirmDialog = { title: '删除系统保护规则', type: 'delete_rule', attentionTitle: '请确认是否删除如下系统保护规则', attention: '阈值类型: ' + ruleTypeDesc + ', 阈值: ' + ruleTypeCount, confirmBtnText: '删除', }; confirmDialog = ngDialog.open({ template: '/app/views/dialog/confirm-dialog.html', scope: $scope, overlay: true }); }; $scope.confirm = function () { if ($scope.confirmDialog.type == 'delete_rule') { deleteRule($scope.currentRule); // } else if ($scope.confirmDialog.type == 'enable_rule') { // $scope.currentRule.enable = true; // saveRule($scope.currentRule); // } else if ($scope.confirmDialog.type == 'disable_rule') { // $scope.currentRule.enable = false; // saveRule($scope.currentRule); // } else if ($scope.confirmDialog.type == 'enable_all') { // enableAll($scope.app); // } else if ($scope.confirmDialog.type == 'disable_all') { // disableAll($scope.app); } else { console.error('error'); } }; function deleteRule(rule) { SystemService.deleteRule(rule).success(function (data) { if (data.code == 0) { getMachineRules(); confirmDialog.close(); } else { alert } }); }; function addNewRule(rule) { SystemService.newRule(rule).success(function (data) { if (data.code == 0) { getMachineRules(); systemRuleDialog.close(); } else { alert('失败!'); } }); }; function saveRule(rule, edit) { SystemService.saveRule(rule).success(function (data) { if (data.code == 0) { // if (rule.enable) { // rule.orginEnable = true; // } else { // rule.orginEnable = false; // } getMachineRules(); if (edit) { systemRuleDialog.close(); } else { confirmDialog.close(); } } else { alert('失败!'); } }); } queryAppMachines(); function queryAppMachines() { MachineService.getAppMachines($scope.app).success( function (data) { if (data.code == 0) { // $scope.machines = data.data; if (data.data) { $scope.machines = []; $scope.macsInputOptions = []; data.data.forEach(function (item) { if (item.healthy) { $scope.macsInputOptions.push({ text: item.ip + ':' + item.port, value: item.ip + ':' + item.port }); } }); } if ($scope.macsInputOptions.length > 0) { $scope.macInputModel = $scope.macsInputOptions[0].value; } } else { $scope.macsInputOptions = []; } } ); }; $scope.$watch('macInputModel', function () { if ($scope.macInputModel) { getMachineRules(); } }); }]); ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/scripts/directives/header/header.html ================================================
================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/scripts/directives/header/header.js ================================================ /** * @ngdoc directive * @name izzyposWebApp.directive:adminPosHeader * @description * # adminPosHeader */ angular.module('sentinelDashboardApp') .directive('header', ['AuthService', function () { return { templateUrl: 'app/scripts/directives/header/header.html', restrict: 'E', replace: true, controller: function ($scope, $state, $window, AuthService) { if (!$window.localStorage.getItem('session_sentinel_admin')) { $state.go('login'); } $scope.logout = function () { AuthService.logout().success(function (data) { if (data.code == 0) { $window.localStorage.removeItem("session_sentinel_admin"); $state.go('login'); } else { alert('logout error'); } }); } } } }]); ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/scripts/directives/sidebar/sidebar-search/sidebar-search.html ================================================ ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/scripts/directives/sidebar/sidebar-search/sidebar-search.js ================================================ /** * @ngdoc directive * @name izzyposWebApp.directive:adminPosHeader * @description * # adminPosHeader */ angular.module('sentinelDashboardApp') .directive('sidebarSearch', function () { return { templateUrl: 'app/scripts/directives/sidebar/sidebar-search/sidebar-search.html', restrict: 'E', replace: true, scope: { }, controller: function ($scope) { $scope.selectedMenu = 'home'; } } }); ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/scripts/directives/sidebar/sidebar.html ================================================ ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/scripts/directives/sidebar/sidebar.js ================================================ /** * @ngdoc directive * @name izzyposWebApp.directive:adminPosHeader * @description # adminPosHeader */ angular.module('sentinelDashboardApp') .directive('sidebar', ['$location', '$stateParams', 'AppService', function () { return { templateUrl: 'app/scripts/directives/sidebar/sidebar.html', restrict: 'E', replace: true, scope: { }, controller: function ($scope, $stateParams, $location, AppService) { $scope.app = $stateParams.app; $scope.collapseVar = 0; // app AppService.getApps().success( function (data) { if (data.code === 0) { let initHashApp = $location.path().split('/')[3]; $scope.apps = data.data; $scope.apps = $scope.apps.map(function (item) { if (item.app === initHashApp) { item.active = true; } var heathyCount = 0; for (var i in item.machines) { if (item.machines[i].healthy) { heathyCount++; } } item.heathyCount = heathyCount; if (item.shown) { return item; } }); } } ); // toggle side bar $scope.click = function ($event) { let entry = angular.element($event.target).scope().entry; entry.active = !entry.active;// toggle this clicked app bar $scope.apps.forEach(function (item) {// collapse other app bars if (item != entry) { item.active = false; } }); }; /** * @deprecated */ $scope.addSearchApp = function () { let findApp = false; for (let i = 0; i < $scope.apps.length; i++) { if ($scope.apps[i].app === $scope.searchApp) { findApp = true; break; } } if (!findApp) { $scope.apps.push({ app: $scope.searchApp }); } }; } }; }]); ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/scripts/filters/filters.js ================================================ var app = angular.module('sentinelDashboardApp'); app.filter('range', [function () { return function (input, length) { if (isNaN(length) || length <= 0) { return []; } input = []; for (var index = 1; index <= length; index++) { input.push(index); } return input; }; }]); ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/scripts/libs/treeTable.js ================================================ var com_github_culmat_jsTreeTable = (function(){ function depthFirst(tree, func, childrenAttr) { childrenAttr = childrenAttr || 'children' function i_depthFirst(node) { if (node[childrenAttr]) { $.each(node[childrenAttr], function(i, child) { i_depthFirst(child) }) } func(node) } $.each(tree, function(i, root) { i_depthFirst(root) }) return tree } /* * make a deep copy of the object */ function copy(data){ return JSON.parse(JSON.stringify(data)) } function makeTree (data, idAttr, refAttr, childrenAttr) { var data_tmp = data idAttr = idAttr || 'id' refAttr = refAttr || 'parent' childrenAttr = childrenAttr || 'children' var byName = [] $.each(data_tmp, function(i, entry) { byName[entry[idAttr]] = entry }) var tree = [] $.each(data_tmp, function(i, entry) { var parents = entry[refAttr] if(!$.isArray(parents)){ parents = [parents] } if(parents.length == 0){ tree.push(entry) } else { var inTree = false; $.each(parents, function(i,parentID){ var parent = byName[parentID] if (parent) { if (!parent[childrenAttr]) { parent[childrenAttr] = [] } if($.inArray(entry, parent[childrenAttr])< 0) parent[childrenAttr].push(entry) inTree = true } }) if(!inTree){ tree.push(entry) } } }) return tree } function renderTree(tree, childrenAttr, idAttr, attrs, renderer, tableAttributes) { childrenAttr = childrenAttr || 'children' idAttr = idAttr || 'id' tableAttributes = tableAttributes || {} var maxLevel = 0; var ret = [] var table = $("") $.each(tableAttributes, function(key, value){ if(key == 'class' && value != 'jsTT') { table.addClass(value) } else { table.attr(key, value) } }) var thead = $("") var tr = $("") var tbody = $("") table.append(thead) thead.append(tr) table.append(tbody) if (attrs) { $.each(attrs, function(attr, desc) { $(tr).append($('')) }) } else { $(tr).append($('')) $.each(tree[0], function(key, value) { if (key != childrenAttr && key != idAttr) $(tr).append($('')) }) } function render(node, parent) { var tr = $("") $(tr).attr('data-tt-id', node[idAttr]) $(tr).attr('data-tt-level', node['data-tt-level']) if(!node[childrenAttr] || node[childrenAttr].length == 0) $(tr).attr('data-tt-isleaf', true) else $(tr).attr('data-tt-isnode', true) if (parent) { $(tr).attr('data-tt-parent-id', parent[idAttr]) } if (renderer) { renderer($(tr), node) }else if (attrs) { $.each(attrs, function(attr, desc) { $(tr).append($('')) }) } else { $(tr).append($('')) $.each(node, function(key, value) { if (key != childrenAttr && key != idAttr && key != 'data-tt-level') $(tr).append($('')) }) } tbody.append(tr) } function i_renderTree(subTree, childrenAttr, level, parent) { maxLevel = Math.max(maxLevel, level) $.each(subTree, function(i, node) { node['data-tt-level'] = level render(node, parent) if (node[childrenAttr]) { $.each(node[childrenAttr], function(i, child) { i_renderTree([ child ], childrenAttr, level + 1, node) }) } }) } i_renderTree(tree, childrenAttr, 1) if (tree[0]) tree[0].maxLevel = maxLevel return table } function attr2attr(nodes, attrs){ $.each(nodes, function(i, node) { $.each(attrs, function(j, at) { node[at] = $(node).attr(at) }) }) return nodes } function treeTable(table){ table.addClass('jsTT') table.expandLevel = function (n) { $("tr[data-tt-level]", table).each(function(index) { var level = parseInt($(this).attr('data-tt-level')) if (level > n-1) { this.trCollapse(true) } else if (level == n-1){ this.trExpand(true) } }) } function getLevel(node){ var level = node.attr('data-tt-level') if(level != undefined ) return parseInt(level) var parentID = node.attr('data-tt-parent-id') if( parentID == undefined){ return 0 } else { return getLevel($('tr[data-tt-id="'+parentID+'"]', table).first()) + 1 } } $("tr[data-tt-id]", table).each(function(i,node){ node = $(node) node.attr('data-tt-level', getLevel(node)) }) var dat = $("tr[data-tt-level]", table).get() $.each(dat, function(j, d) { d.trChildrenVisible = true d.trChildren = [] }) dat = attr2attr(dat, ['data-tt-id', 'data-tt-parent-id']) dat = makeTree(dat, 'data-tt-id', 'data-tt-parent-id', 'trChildren') var imgExpand = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAsTAAALEwEAmpwYAAAKT2lDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVNnVFPpFj333vRCS4iAlEtvUhUIIFJCi4AUkSYqIQkQSoghodkVUcERRUUEG8igiAOOjoCMFVEsDIoK2AfkIaKOg6OIisr74Xuja9a89+bN/rXXPues852zzwfACAyWSDNRNYAMqUIeEeCDx8TG4eQuQIEKJHAAEAizZCFz/SMBAPh+PDwrIsAHvgABeNMLCADATZvAMByH/w/qQplcAYCEAcB0kThLCIAUAEB6jkKmAEBGAYCdmCZTAKAEAGDLY2LjAFAtAGAnf+bTAICd+Jl7AQBblCEVAaCRACATZYhEAGg7AKzPVopFAFgwABRmS8Q5ANgtADBJV2ZIALC3AMDOEAuyAAgMADBRiIUpAAR7AGDIIyN4AISZABRG8lc88SuuEOcqAAB4mbI8uSQ5RYFbCC1xB1dXLh4ozkkXKxQ2YQJhmkAuwnmZGTKBNA/g88wAAKCRFRHgg/P9eM4Ors7ONo62Dl8t6r8G/yJiYuP+5c+rcEAAAOF0ftH+LC+zGoA7BoBt/qIl7gRoXgugdfeLZrIPQLUAoOnaV/Nw+H48PEWhkLnZ2eXk5NhKxEJbYcpXff5nwl/AV/1s+X48/Pf14L7iJIEyXYFHBPjgwsz0TKUcz5IJhGLc5o9H/LcL//wd0yLESWK5WCoU41EScY5EmozzMqUiiUKSKcUl0v9k4t8s+wM+3zUAsGo+AXuRLahdYwP2SycQWHTA4vcAAPK7b8HUKAgDgGiD4c93/+8//UegJQCAZkmScQAAXkQkLlTKsz/HCAAARKCBKrBBG/TBGCzABhzBBdzBC/xgNoRCJMTCQhBCCmSAHHJgKayCQiiGzbAdKmAv1EAdNMBRaIaTcA4uwlW4Dj1wD/phCJ7BKLyBCQRByAgTYSHaiAFiilgjjggXmYX4IcFIBBKLJCDJiBRRIkuRNUgxUopUIFVIHfI9cgI5h1xGupE7yAAygvyGvEcxlIGyUT3UDLVDuag3GoRGogvQZHQxmo8WoJvQcrQaPYw2oefQq2gP2o8+Q8cwwOgYBzPEbDAuxsNCsTgsCZNjy7EirAyrxhqwVqwDu4n1Y8+xdwQSgUXACTYEd0IgYR5BSFhMWE7YSKggHCQ0EdoJNwkDhFHCJyKTqEu0JroR+cQYYjIxh1hILCPWEo8TLxB7iEPENyQSiUMyJ7mQAkmxpFTSEtJG0m5SI+ksqZs0SBojk8naZGuyBzmULCAryIXkneTD5DPkG+Qh8lsKnWJAcaT4U+IoUspqShnlEOU05QZlmDJBVaOaUt2ooVQRNY9aQq2htlKvUYeoEzR1mjnNgxZJS6WtopXTGmgXaPdpr+h0uhHdlR5Ol9BX0svpR+iX6AP0dwwNhhWDx4hnKBmbGAcYZxl3GK+YTKYZ04sZx1QwNzHrmOeZD5lvVVgqtip8FZHKCpVKlSaVGyovVKmqpqreqgtV81XLVI+pXlN9rkZVM1PjqQnUlqtVqp1Q61MbU2epO6iHqmeob1Q/pH5Z/YkGWcNMw09DpFGgsV/jvMYgC2MZs3gsIWsNq4Z1gTXEJrHN2Xx2KruY/R27iz2qqaE5QzNKM1ezUvOUZj8H45hx+Jx0TgnnKKeX836K3hTvKeIpG6Y0TLkxZVxrqpaXllirSKtRq0frvTau7aedpr1Fu1n7gQ5Bx0onXCdHZ4/OBZ3nU9lT3acKpxZNPTr1ri6qa6UbobtEd79up+6Ynr5egJ5Mb6feeb3n+hx9L/1U/W36p/VHDFgGswwkBtsMzhg8xTVxbzwdL8fb8VFDXcNAQ6VhlWGX4YSRudE8o9VGjUYPjGnGXOMk423GbcajJgYmISZLTepN7ppSTbmmKaY7TDtMx83MzaLN1pk1mz0x1zLnm+eb15vft2BaeFostqi2uGVJsuRaplnutrxuhVo5WaVYVVpds0atna0l1rutu6cRp7lOk06rntZnw7Dxtsm2qbcZsOXYBtuutm22fWFnYhdnt8Wuw+6TvZN9un2N/T0HDYfZDqsdWh1+c7RyFDpWOt6azpzuP33F9JbpL2dYzxDP2DPjthPLKcRpnVOb00dnF2e5c4PziIuJS4LLLpc+Lpsbxt3IveRKdPVxXeF60vWdm7Obwu2o26/uNu5p7ofcn8w0nymeWTNz0MPIQ+BR5dE/C5+VMGvfrH5PQ0+BZ7XnIy9jL5FXrdewt6V3qvdh7xc+9j5yn+M+4zw33jLeWV/MN8C3yLfLT8Nvnl+F30N/I/9k/3r/0QCngCUBZwOJgUGBWwL7+Hp8Ib+OPzrbZfay2e1BjKC5QRVBj4KtguXBrSFoyOyQrSH355jOkc5pDoVQfujW0Adh5mGLw34MJ4WHhVeGP45wiFga0TGXNXfR3ENz30T6RJZE3ptnMU85ry1KNSo+qi5qPNo3ujS6P8YuZlnM1VidWElsSxw5LiquNm5svt/87fOH4p3iC+N7F5gvyF1weaHOwvSFpxapLhIsOpZATIhOOJTwQRAqqBaMJfITdyWOCnnCHcJnIi/RNtGI2ENcKh5O8kgqTXqS7JG8NXkkxTOlLOW5hCepkLxMDUzdmzqeFpp2IG0yPTq9MYOSkZBxQqohTZO2Z+pn5mZ2y6xlhbL+xW6Lty8elQfJa7OQrAVZLQq2QqboVFoo1yoHsmdlV2a/zYnKOZarnivN7cyzytuQN5zvn//tEsIS4ZK2pYZLVy0dWOa9rGo5sjxxedsK4xUFK4ZWBqw8uIq2Km3VT6vtV5eufr0mek1rgV7ByoLBtQFr6wtVCuWFfevc1+1dT1gvWd+1YfqGnRs+FYmKrhTbF5cVf9go3HjlG4dvyr+Z3JS0qavEuWTPZtJm6ebeLZ5bDpaql+aXDm4N2dq0Dd9WtO319kXbL5fNKNu7g7ZDuaO/PLi8ZafJzs07P1SkVPRU+lQ27tLdtWHX+G7R7ht7vPY07NXbW7z3/T7JvttVAVVN1WbVZftJ+7P3P66Jqun4lvttXa1ObXHtxwPSA/0HIw6217nU1R3SPVRSj9Yr60cOxx++/p3vdy0NNg1VjZzG4iNwRHnk6fcJ3/ceDTradox7rOEH0x92HWcdL2pCmvKaRptTmvtbYlu6T8w+0dbq3nr8R9sfD5w0PFl5SvNUyWna6YLTk2fyz4ydlZ19fi753GDborZ752PO32oPb++6EHTh0kX/i+c7vDvOXPK4dPKy2+UTV7hXmq86X23qdOo8/pPTT8e7nLuarrlca7nuer21e2b36RueN87d9L158Rb/1tWeOT3dvfN6b/fF9/XfFt1+cif9zsu72Xcn7q28T7xf9EDtQdlD3YfVP1v+3Njv3H9qwHeg89HcR/cGhYPP/pH1jw9DBY+Zj8uGDYbrnjg+OTniP3L96fynQ89kzyaeF/6i/suuFxYvfvjV69fO0ZjRoZfyl5O/bXyl/erA6xmv28bCxh6+yXgzMV70VvvtwXfcdx3vo98PT+R8IH8o/2j5sfVT0Kf7kxmTk/8EA5jz/GMzLdsAAAAgY0hSTQAAeiUAAICDAAD5/wAAgOkAAHUwAADqYAAAOpgAABdvkl/FRgAAAHlJREFUeNrcU1sNgDAQ6wgmcAM2MICGGlg1gJnNzWQcvwQGy1j4oUl/7tH0mpwzM7SgQyO+EZAUWh2MkkzSWhJwuRAlHYsJwEwyvs1gABDuzqoJcTw5qxaIJN0bgQRgIjnlmn1heSO5PE6Y2YXe+5Cr5+h++gs12AcAS6FS+7YOsj4AAAAASUVORK5CYII=" var imgCollapse = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAsTAAALEwEAmpwYAAAKT2lDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVNnVFPpFj333vRCS4iAlEtvUhUIIFJCi4AUkSYqIQkQSoghodkVUcERRUUEG8igiAOOjoCMFVEsDIoK2AfkIaKOg6OIisr74Xuja9a89+bN/rXXPues852zzwfACAyWSDNRNYAMqUIeEeCDx8TG4eQuQIEKJHAAEAizZCFz/SMBAPh+PDwrIsAHvgABeNMLCADATZvAMByH/w/qQplcAYCEAcB0kThLCIAUAEB6jkKmAEBGAYCdmCZTAKAEAGDLY2LjAFAtAGAnf+bTAICd+Jl7AQBblCEVAaCRACATZYhEAGg7AKzPVopFAFgwABRmS8Q5ANgtADBJV2ZIALC3AMDOEAuyAAgMADBRiIUpAAR7AGDIIyN4AISZABRG8lc88SuuEOcqAAB4mbI8uSQ5RYFbCC1xB1dXLh4ozkkXKxQ2YQJhmkAuwnmZGTKBNA/g88wAAKCRFRHgg/P9eM4Ors7ONo62Dl8t6r8G/yJiYuP+5c+rcEAAAOF0ftH+LC+zGoA7BoBt/qIl7gRoXgugdfeLZrIPQLUAoOnaV/Nw+H48PEWhkLnZ2eXk5NhKxEJbYcpXff5nwl/AV/1s+X48/Pf14L7iJIEyXYFHBPjgwsz0TKUcz5IJhGLc5o9H/LcL//wd0yLESWK5WCoU41EScY5EmozzMqUiiUKSKcUl0v9k4t8s+wM+3zUAsGo+AXuRLahdYwP2SycQWHTA4vcAAPK7b8HUKAgDgGiD4c93/+8//UegJQCAZkmScQAAXkQkLlTKsz/HCAAARKCBKrBBG/TBGCzABhzBBdzBC/xgNoRCJMTCQhBCCmSAHHJgKayCQiiGzbAdKmAv1EAdNMBRaIaTcA4uwlW4Dj1wD/phCJ7BKLyBCQRByAgTYSHaiAFiilgjjggXmYX4IcFIBBKLJCDJiBRRIkuRNUgxUopUIFVIHfI9cgI5h1xGupE7yAAygvyGvEcxlIGyUT3UDLVDuag3GoRGogvQZHQxmo8WoJvQcrQaPYw2oefQq2gP2o8+Q8cwwOgYBzPEbDAuxsNCsTgsCZNjy7EirAyrxhqwVqwDu4n1Y8+xdwQSgUXACTYEd0IgYR5BSFhMWE7YSKggHCQ0EdoJNwkDhFHCJyKTqEu0JroR+cQYYjIxh1hILCPWEo8TLxB7iEPENyQSiUMyJ7mQAkmxpFTSEtJG0m5SI+ksqZs0SBojk8naZGuyBzmULCAryIXkneTD5DPkG+Qh8lsKnWJAcaT4U+IoUspqShnlEOU05QZlmDJBVaOaUt2ooVQRNY9aQq2htlKvUYeoEzR1mjnNgxZJS6WtopXTGmgXaPdpr+h0uhHdlR5Ol9BX0svpR+iX6AP0dwwNhhWDx4hnKBmbGAcYZxl3GK+YTKYZ04sZx1QwNzHrmOeZD5lvVVgqtip8FZHKCpVKlSaVGyovVKmqpqreqgtV81XLVI+pXlN9rkZVM1PjqQnUlqtVqp1Q61MbU2epO6iHqmeob1Q/pH5Z/YkGWcNMw09DpFGgsV/jvMYgC2MZs3gsIWsNq4Z1gTXEJrHN2Xx2KruY/R27iz2qqaE5QzNKM1ezUvOUZj8H45hx+Jx0TgnnKKeX836K3hTvKeIpG6Y0TLkxZVxrqpaXllirSKtRq0frvTau7aedpr1Fu1n7gQ5Bx0onXCdHZ4/OBZ3nU9lT3acKpxZNPTr1ri6qa6UbobtEd79up+6Ynr5egJ5Mb6feeb3n+hx9L/1U/W36p/VHDFgGswwkBtsMzhg8xTVxbzwdL8fb8VFDXcNAQ6VhlWGX4YSRudE8o9VGjUYPjGnGXOMk423GbcajJgYmISZLTepN7ppSTbmmKaY7TDtMx83MzaLN1pk1mz0x1zLnm+eb15vft2BaeFostqi2uGVJsuRaplnutrxuhVo5WaVYVVpds0atna0l1rutu6cRp7lOk06rntZnw7Dxtsm2qbcZsOXYBtuutm22fWFnYhdnt8Wuw+6TvZN9un2N/T0HDYfZDqsdWh1+c7RyFDpWOt6azpzuP33F9JbpL2dYzxDP2DPjthPLKcRpnVOb00dnF2e5c4PziIuJS4LLLpc+Lpsbxt3IveRKdPVxXeF60vWdm7Obwu2o26/uNu5p7ofcn8w0nymeWTNz0MPIQ+BR5dE/C5+VMGvfrH5PQ0+BZ7XnIy9jL5FXrdewt6V3qvdh7xc+9j5yn+M+4zw33jLeWV/MN8C3yLfLT8Nvnl+F30N/I/9k/3r/0QCngCUBZwOJgUGBWwL7+Hp8Ib+OPzrbZfay2e1BjKC5QRVBj4KtguXBrSFoyOyQrSH355jOkc5pDoVQfujW0Adh5mGLw34MJ4WHhVeGP45wiFga0TGXNXfR3ENz30T6RJZE3ptnMU85ry1KNSo+qi5qPNo3ujS6P8YuZlnM1VidWElsSxw5LiquNm5svt/87fOH4p3iC+N7F5gvyF1weaHOwvSFpxapLhIsOpZATIhOOJTwQRAqqBaMJfITdyWOCnnCHcJnIi/RNtGI2ENcKh5O8kgqTXqS7JG8NXkkxTOlLOW5hCepkLxMDUzdmzqeFpp2IG0yPTq9MYOSkZBxQqohTZO2Z+pn5mZ2y6xlhbL+xW6Lty8elQfJa7OQrAVZLQq2QqboVFoo1yoHsmdlV2a/zYnKOZarnivN7cyzytuQN5zvn//tEsIS4ZK2pYZLVy0dWOa9rGo5sjxxedsK4xUFK4ZWBqw8uIq2Km3VT6vtV5eufr0mek1rgV7ByoLBtQFr6wtVCuWFfevc1+1dT1gvWd+1YfqGnRs+FYmKrhTbF5cVf9go3HjlG4dvyr+Z3JS0qavEuWTPZtJm6ebeLZ5bDpaql+aXDm4N2dq0Dd9WtO319kXbL5fNKNu7g7ZDuaO/PLi8ZafJzs07P1SkVPRU+lQ27tLdtWHX+G7R7ht7vPY07NXbW7z3/T7JvttVAVVN1WbVZftJ+7P3P66Jqun4lvttXa1ObXHtxwPSA/0HIw6217nU1R3SPVRSj9Yr60cOxx++/p3vdy0NNg1VjZzG4iNwRHnk6fcJ3/ceDTradox7rOEH0x92HWcdL2pCmvKaRptTmvtbYlu6T8w+0dbq3nr8R9sfD5w0PFl5SvNUyWna6YLTk2fyz4ydlZ19fi753GDborZ752PO32oPb++6EHTh0kX/i+c7vDvOXPK4dPKy2+UTV7hXmq86X23qdOo8/pPTT8e7nLuarrlca7nuer21e2b36RueN87d9L158Rb/1tWeOT3dvfN6b/fF9/XfFt1+cif9zsu72Xcn7q28T7xf9EDtQdlD3YfVP1v+3Njv3H9qwHeg89HcR/cGhYPP/pH1jw9DBY+Zj8uGDYbrnjg+OTniP3L96fynQ89kzyaeF/6i/suuFxYvfvjV69fO0ZjRoZfyl5O/bXyl/erA6xmv28bCxh6+yXgzMV70VvvtwXfcdx3vo98PT+R8IH8o/2j5sfVT0Kf7kxmTk/8EA5jz/GMzLdsAAAAgY0hSTQAAeiUAAICDAAD5/wAAgOkAAHUwAADqYAAAOpgAABdvkl/FRgAAAHFJREFUeNpi/P//PwMlgImBQsA44C6gvhfa29v3MzAwOODRc6CystIRbxi0t7fjDJjKykpGYrwwi1hxnLHQ3t7+jIGBQRJJ6HllZaUUKYEYRYBPOB0gBShKwKGA////48VtbW3/8clTnBIH3gCKkzJgAGvBX0dDm0sCAAAAAElFTkSuQmCC" $("tr[data-tt-level]", table).each(function(index, tr) { var level = $(tr).attr('data-tt-level') var td = $("td",tr).first() if(tr.trChildren.length>0){ td.prepend($('')) } else { td.prepend($('')) } td.prepend($('')) // td.css('white-space','nowrap') tr.trExpand = function(changeState){ if(this.trChildren.length < 1) return if(changeState) { this.trChildrenVisible = true $('#state', this).get(0).src= imgCollapse } var doit = changeState || this.trChildrenVisible $.each(this.trChildren, function(i, ctr) { if(doit) $(ctr).css('display', 'table-row') ctr.trExpand() }) } tr.trCollapse = function(changeState){ if(this.trChildren.length < 1) return if(changeState) { this.trChildrenVisible = false $('#state', this).get(0).src= imgExpand } $.each(this.trChildren, function(i, ctr) { $(ctr).css('display', 'none') ctr.trCollapse() }) } $(tr).click(function() { this.trChildrenVisible ? this.trCollapse(true) : this.trExpand(true) }) }) return table } function appendTreetable(tree, options) { function inALine(nodes) { var tr = $('') $.each(nodes, function(i, node){ tr.append($('
' + desc + '' + idAttr + '' + key + '
' + node[attr] + '' + node[idAttr] + '' + value + '
').append(node)) }) return $('').append(tr) } options = options || {} options.idAttr = (options.idAttr || 'id') options.childrenAttr = (options.childrenAttr || 'children') var controls = (options.controls || []) if (!options.mountPoint) options.mountPoint = $('body') if (options.depthFirst) depthFirst(tree, options.depthFirst, options.childrenAttr) var rendered = renderTree(tree, options.childrenAttr, options.idAttr, options.renderedAttr, options.renderer, options.tableAttributes) treeTable(rendered) if (options.replaceContent) { options.mountPoint.html('') } var initialExpandLevel = options.initialExpandLevel ? parseInt(options.initialExpandLevel) : -1 initialExpandLevel = Math.min(initialExpandLevel, tree[0].maxLevel) rendered.expandLevel(initialExpandLevel) if(options.slider){ var slider = $('
') slider.width('200px') slider.slider({ min : 1, max : tree[0].maxLevel, range : "min", value : initialExpandLevel, slide : function(event, ui) { rendered.expandLevel(ui.value) } }) controls = [slider].concat(options.controls) } if(controls.length >0){ options.mountPoint.append(inALine(controls)) } options.mountPoint.append(rendered) return rendered } return { depthFirst : depthFirst, makeTree : makeTree, renderTree : renderTree, attr2attr : attr2attr, treeTable : treeTable, appendTreetable : appendTreetable, jsTreeTable : '1.0', register : function(target){ $.each(this, function(key, value){ if(key != 'register') target[key] = value}) } } })(); ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/scripts/services/appservice.js ================================================ var app = angular.module('sentinelDashboardApp'); app.service('AppService', ['$http', function ($http) { this.getApps = function () { return $http({ // url: 'app/mock_infos', url: 'app/briefinfos.json', method: 'GET' }); }; }]); ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/scripts/services/auth_service.js ================================================ var app = angular.module('sentinelDashboardApp'); app.service('AuthService', ['$http', function ($http) { this.login = function (param) { return $http({ url: '/auth/login', params: param, method: 'POST' }) } this.logout = function () { return $http({ url: '/auth/logout', method: 'POST' }) } }]); ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/scripts/services/authority_service.js ================================================ /** * Authority rule service. */ angular.module('sentinelDashboardApp').service('AuthorityRuleService', ['$http', function ($http) { this.queryMachineRules = function(app, ip, port) { var param = { app: app, ip: ip, port: port }; return $http({ url: '/authority/rules', params: param, method: 'GET' }); }; this.addNewRule = function(rule) { return $http({ url: '/authority/rule', data: rule, method: 'POST' }); }; this.saveRule = function (entity) { return $http({ url: '/authority/rule/' + entity.id, data: entity, method: 'PUT' }); }; this.deleteRule = function (entity) { return $http({ url: '/authority/rule/' + entity.id, method: 'DELETE' }); }; this.checkRuleValid = function checkRuleValid(rule) { if (rule.resource === undefined || rule.resource === '') { alert('资源名称不能为空'); return false; } if (rule.limitApp === undefined || rule.limitApp === '') { alert('流控针对应用不能为空'); return false; } if (rule.strategy === undefined) { alert('必须选择黑白名单模式'); return false; } return true; }; }]); ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/scripts/services/cluster_state_service.js ================================================ /** * Cluster state control service. * * @author Eric Zhao */ angular.module('sentinelDashboardApp').service('ClusterStateService', ['$http', function ($http) { this.fetchClusterUniversalStateSingle = function(app, ip, port) { var param = { app: app, ip: ip, port: port }; return $http({ url: '/cluster/state_single', params: param, method: 'GET' }); }; this.fetchClusterUniversalStateOfApp = function(app) { return $http({ url: '/cluster/state/' + app, method: 'GET' }); }; this.fetchClusterServerStateOfApp = function(app) { return $http({ url: '/cluster/server_state/' + app, method: 'GET' }); }; this.fetchClusterClientStateOfApp = function(app) { return $http({ url: '/cluster/client_state/' + app, method: 'GET' }); }; this.modifyClusterConfig = function(config) { return $http({ url: '/cluster/config/modify_single', data: config, method: 'POST' }); }; this.applyClusterFullAssignOfApp = function(app, clusterMap) { return $http({ url: '/cluster/assign/all_server/' + app, data: clusterMap, method: 'POST' }); }; this.applyClusterSingleServerAssignOfApp = function(app, request) { return $http({ url: '/cluster/assign/single_server/' + app, data: request, method: 'POST' }); }; this.applyClusterServerBatchUnbind = function(app, machineSet) { return $http({ url: '/cluster/assign/unbind_server/' + app, data: machineSet, method: 'POST' }); }; }]); ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/scripts/services/degradeservice.js ================================================ var app = angular.module('sentinelDashboardApp'); app.service('DegradeService', ['$http', function ($http) { this.queryMachineRules = function (app, ip, port) { var param = { app: app, ip: ip, port: port }; return $http({ url: 'degrade/rules.json', params: param, method: 'GET' }); }; this.newRule = function (rule) { var param = { id: rule.id, resource: rule.resource, limitApp: rule.limitApp, count: rule.count, timeWindow: rule.timeWindow, grade: rule.grade, app: rule.app, ip: rule.ip, port: rule.port }; return $http({ url: '/degrade/new.json', params: param, method: 'GET' }); }; this.saveRule = function (rule) { var param = { id: rule.id, resource: rule.resource, limitApp: rule.limitApp, grade: rule.grade, count: rule.count, timeWindow: rule.timeWindow, }; return $http({ url: '/degrade/save.json', params: param, method: 'GET' }); }; this.deleteRule = function (rule) { var param = { id: rule.id, app: rule.app }; return $http({ url: '/degrade/delete.json', params: param, method: 'GET' }); }; this.checkRuleValid = function (rule) { if (rule.resource === undefined || rule.resource === '') { alert('资源名称不能为空'); return false; } if (rule.grade === undefined || rule.grade < 0) { alert('未知的降级策略'); return false; } if (rule.count === undefined || rule.count === '' || rule.count < 0) { alert('降级阈值不能为空或小于 0'); return false; } if (rule.timeWindow === undefined || rule.timeWindow === '' || rule.timeWindow <= 0) { alert('降级时间窗口必须大于 0'); return false; } // 异常比率类型. if (rule.grade == 1 && rule.count > 1) { alert('异常比率超出范围:[0.0 - 1.0]'); return false; } return true; }; }]); ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/scripts/services/flow_service_v1.js ================================================ var app = angular.module('sentinelDashboardApp'); app.service('FlowServiceV1', ['$http', function ($http) { this.queryMachineRules = function (app, ip, port) { var param = { app: app, ip: ip, port: port }; return $http({ url: '/v1/flow/rules', params: param, method: 'GET' }); }; this.newRule = function (rule) { var param = { resource: rule.resource, limitApp: rule.limitApp, grade: rule.grade, count: rule.count, strategy: rule.strategy, refResource: rule.refResource, controlBehavior: rule.controlBehavior, warmUpPeriodSec: rule.warmUpPeriodSec, maxQueueingTimeMs: rule.maxQueueingTimeMs, app: rule.app, ip: rule.ip, port: rule.port }; return $http({ url: '/v1/flow/rule', data: rule, method: 'POST' }); }; this.saveRule = function (rule) { var param = { id: rule.id, resource: rule.resource, limitApp: rule.limitApp, grade: rule.grade, count: rule.count, strategy: rule.strategy, refResource: rule.refResource, controlBehavior: rule.controlBehavior, warmUpPeriodSec: rule.warmUpPeriodSec, maxQueueingTimeMs: rule.maxQueueingTimeMs, }; return $http({ url: '/v1/flow/save.json', params: param, method: 'PUT' }); }; this.deleteRule = function (rule) { var param = { id: rule.id, app: rule.app }; return $http({ url: '/v1/flow/delete.json', params: param, method: 'DELETE' }); }; function notNumberAtLeastZero(num) { return num === undefined || num === '' || isNaN(num) || num < 0; } function notNumberGreaterThanZero(num) { return num === undefined || num === '' || isNaN(num) || num <= 0; } this.checkRuleValid = function (rule) { if (rule.resource === undefined || rule.resource === '') { alert('资源名称不能为空'); return false; } if (rule.count === undefined || rule.count < 0) { alert('限流阈值必须大于等于 0'); return false; } if (rule.strategy === undefined || rule.strategy < 0) { alert('无效的流控模式'); return false; } if (rule.strategy == 1 || rule.strategy == 2) { if (rule.refResource === undefined || rule.refResource == '') { alert('请填写关联资源或入口'); return false; } } if (rule.controlBehavior === undefined || rule.controlBehavior < 0) { alert('无效的流控整形方式'); return false; } if (rule.controlBehavior == 1 && notNumberGreaterThanZero(rule.warmUpPeriodSec)) { alert('预热时长必须大于 0'); return false; } if (rule.controlBehavior == 2 && notNumberGreaterThanZero(rule.maxQueueingTimeMs)) { alert('排队超时时间必须大于 0'); return false; } if (rule.clusterMode && (rule.clusterConfig === undefined || rule.clusterConfig.thresholdType === undefined)) { alert('集群限流配置不正确'); return false; } return true; }; }]); ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/scripts/services/flow_service_v2.js ================================================ var app = angular.module('sentinelDashboardApp'); app.service('FlowServiceV2', ['$http', function ($http) { this.queryMachineRules = function (app, ip, port) { var param = { app: app, ip: ip, port: port }; return $http({ url: '/v2/flow/rules', params: param, method: 'GET' }); }; this.newRule = function (rule) { return $http({ url: '/v2/flow/rule', data: rule, method: 'POST' }); }; this.saveRule = function (rule) { return $http({ url: '/v2/flow/rule/' + rule.id, data: rule, method: 'PUT' }); }; this.deleteRule = function (rule) { return $http({ url: '/v2/flow/rule/' + rule.id, method: 'DELETE' }); }; function notNumberAtLeastZero(num) { return num === undefined || num === '' || isNaN(num) || num < 0; } function notNumberGreaterThanZero(num) { return num === undefined || num === '' || isNaN(num) || num <= 0; } this.checkRuleValid = function (rule) { if (rule.resource === undefined || rule.resource === '') { alert('资源名称不能为空'); return false; } if (rule.count === undefined || rule.count < 0) { alert('限流阈值必须大于等于 0'); return false; } if (rule.strategy === undefined || rule.strategy < 0) { alert('无效的流控模式'); return false; } if (rule.strategy == 1 || rule.strategy == 2) { if (rule.refResource === undefined || rule.refResource == '') { alert('请填写关联资源或入口'); return false; } } if (rule.controlBehavior === undefined || rule.controlBehavior < 0) { alert('无效的流控整形方式'); return false; } if (rule.controlBehavior == 1 && notNumberGreaterThanZero(rule.warmUpPeriodSec)) { alert('预热时长必须大于 0'); return false; } if (rule.controlBehavior == 2 && notNumberGreaterThanZero(rule.maxQueueingTimeMs)) { alert('排队超时时间必须大于 0'); return false; } if (rule.clusterMode && (rule.clusterConfig === undefined || rule.clusterConfig.thresholdType === undefined)) { alert('集群限流配置不正确'); return false; } return true; }; }]); ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/scripts/services/identityservice.js ================================================ var app = angular.module('sentinelDashboardApp'); app.service('IdentityService', ['$http', function ($http) { this.fetchIdentityOfMachine = function (ip, port, searchKey) { var param = { ip: ip, port: port, searchKey: searchKey }; return $http({ url: 'resource/machineResource.json', params: param, method: 'GET' }); }; this.fetchClusterNodeOfMachine = function (ip, port, searchKey) { var param = { ip: ip, port: port, type: 'cluster', searchKey: searchKey }; return $http({ url: 'resource/machineResource.json', params: param, method: 'GET' }); }; }]); ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/scripts/services/machineservice.js ================================================ var app = angular.module('sentinelDashboardApp'); app.service('MachineService', ['$http', '$httpParamSerializerJQLike', function ($http, $httpParamSerializerJQLike) { this.getAppMachines = function (app) { return $http({ url: 'app/' + app + '/machines.json', method: 'GET' }); }; this.removeAppMachine = function (app, ip, port) { return $http({ url: 'app/' + app + '/machine/remove.json', method: 'POST', headers: { "Content-type": 'application/x-www-form-urlencoded; charset=UTF-8' }, data: $httpParamSerializerJQLike({ ip: ip, port: port }) }); }; }]); ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/scripts/services/metricservice.js ================================================ var app = angular.module('sentinelDashboardApp'); app.service('MetricService', ['$http', function ($http) { this.queryAppSortedIdentities = function (params) { return $http({ url: '/metric/queryTopResourceMetric.json', params: params, method: 'GET' }); }; this.queryByAppAndIdentity = function (params) { return $http({ url: '/metric/queryByAppAndResource.json', params: params, method: 'GET' }); }; this.queryByMachineAndIdentity = function (ip, port, identity, startTime, endTime) { var param = { ip: ip, port: port, identity: identity, startTime: startTime.getTime(), endTime: endTime.getTime() }; return $http({ url: '/metric/queryByAppAndResource.json', params: param, method: 'GET' }); }; }]); ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/scripts/services/param_flow_service.js ================================================ /** * Parameter flow control service. * * @author Eric Zhao */ angular.module('sentinelDashboardApp').service('ParamFlowService', ['$http', function ($http) { this.queryMachineRules = function(app, ip, port) { var param = { app: app, ip: ip, port: port }; return $http({ url: '/paramFlow/rules', params: param, method: 'GET' }); }; this.addNewRule = function(rule) { return $http({ url: '/paramFlow/rule', data: rule, method: 'POST' }); }; this.saveRule = function (entity) { return $http({ url: '/paramFlow/rule/' + entity.id, data: entity, method: 'PUT' }); }; this.deleteRule = function (entity) { return $http({ url: '/paramFlow/rule/' + entity.id, method: 'DELETE' }); }; function isNumberClass(classType) { return classType === 'int' || classType === 'double' || classType === 'float' || classType === 'long' || classType === 'short'; } function isByteClass(classType) { return classType === 'byte'; } function notNumberAtLeastZero(num) { return num === undefined || num === '' || isNaN(num) || num < 0; } function notGoodNumber(num) { return num === undefined || num === '' || isNaN(num); } function notGoodNumberBetweenExclusive(num, l ,r) { return num === undefined || num === '' || isNaN(num) || num < l || num > r; } function notValidParamItem(curExItem) { if (isNumberClass(curExItem.classType) && notGoodNumber(curExItem.object)) { return true; } if (isByteClass(curExItem.classType) && notGoodNumberBetweenExclusive(curExItem.object, -128, 127)) { return true; } return curExItem.object === undefined || curExItem.classType === undefined || notNumberAtLeastZero(curExItem.count); } this.checkRuleValid = function (rule) { if (!rule.resource || rule.resource === '') { alert('资源名称不能为空'); return false; } if (rule.grade != 1) { alert('未知的限流模式'); return false; } if (rule.count < 0) { alert('限流阈值必须大于等于 0'); return false; } if (rule.paramIdx === undefined || rule.paramIdx === '' || isNaN(rule.paramIdx) || rule.paramIdx < 0) { alert('热点参数索引必须大于等于 0'); return false; } if (rule.paramFlowItemList !== undefined) { for (var i = 0; i < rule.paramFlowItemList.length; i++) { var item = rule.paramFlowItemList[i]; if (notValidParamItem(item)) { alert('热点参数例外项不合法,请检查值和类型是否正确:参数为 ' + item.object + ', 类型为 ' + item.classType + ', 限流阈值为 ' + item.count); return false; } } } return true; }; }]); ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/scripts/services/systemservice.js ================================================ var app = angular.module('sentinelDashboardApp'); app.service('SystemService', ['$http', function ($http) { this.queryMachineRules = function (app, ip, port) { var param = { app: app, ip: ip, port: port }; return $http({ url: 'system/rules.json', params: param, method: 'GET' }); }; this.newRule = function (rule) { var param = { app: rule.app, ip: rule.ip, port: rule.port }; if (rule.grade == 0) {// avgLoad param.avgLoad = rule.avgLoad; } else if (rule.grade == 1) {// avgRt param.avgRt = rule.avgRt; } else if (rule.grade == 2) {// maxThread param.maxThread = rule.maxThread; } else if (rule.grade == 3) {// qps param.qps = rule.qps; } return $http({ url: '/system/new.json', params: param, method: 'GET' }); }; this.saveRule = function (rule) { var param = { id: rule.id, }; if (rule.grade == 0) {// avgLoad param.avgLoad = rule.avgLoad; } else if (rule.grade == 1) {// avgRt param.avgRt = rule.avgRt; } else if (rule.grade == 2) {// maxThread param.maxThread = rule.maxThread; } else if (rule.grade == 3) {// qps param.qps = rule.qps; } return $http({ url: '/system/save.json', params: param, method: 'GET' }); }; this.deleteRule = function (rule) { var param = { id: rule.id, app: rule.app }; return $http({ url: '/system/delete.json', params: param, method: 'GET' }); }; }]); ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/styles/main.css ================================================ .browsehappy { margin: 0.2em 0; background: #ccc; color: #000; padding: 0.2em 0; } body { padding: 0; } /* Everything but the jumbotron gets side spacing for mobile first views */ .header, .marketing, .footer { padding-left: 15px; padding-right: 15px; } /* Custom page header */ .header { border-bottom: 1px solid #e5e5e5; margin-bottom: 10px; } /* Make the masthead heading the same height as the navigation */ .header h3 { margin-top: 0; margin-bottom: 0; line-height: 40px; padding-bottom: 19px; } /* Custom page footer */ .footer { padding-top: 19px; color: #777; border-top: 1px solid #e5e5e5; } .container-narrow > hr { margin: 30px 0; } /* Main marketing message and sign up button */ .jumbotron { text-align: center; border-bottom: 1px solid #e5e5e5; } .jumbotron .btn { font-size: 21px; padding: 14px 24px; } /* Supporting marketing content */ .marketing { margin: 40px 0; } .marketing p + h4 { margin-top: 28px; } /* Responsive: Portrait tablets and up */ @media screen and (min-width: 768px) { .container { width: inherit; margin-left: 60px; margin-right: 5px; } /* Remove the padding we set earlier */ .header, .marketing, .footer { padding-left: 0; padding-right: 0; } /* Space out the masthead */ .header { margin-bottom: 30px; } /* Remove the bottom border on the jumbotron for visual effect */ .jumbotron { border-bottom: 0; } } .navbar-inverse { background-color: #1d9d74; border-color: #1b926c; } .navbar-inverse .navbar-nav > li > a { color: #b0ddce; font-size: 15px; } .navbar-inverse .navbar-nav>.open>a, .navbar-inverse .navbar-nav>.open>a:focus, .navbar-inverse .navbar-nav>.open>a:hover { background-color: #1b926c; } @media (min-width: 900px) { .navbar-left { float: left !important; } .navbar-right { float: right !important; margin-right: 0%; } .navbar-right ~ .navbar-right { margin-right: 0; } } .dropdown-menu { min-width: 100px !important; } .nav-sidebar li.active a { background: #DDD; } .dropdown-menu>li>a:hover, .dropdown-menu>li>a:focus { background: #1d9d74; /*background: #d9d9d9;*/ color: white; } .broadcast-message, .broadcast-message-preview { padding: 10px; text-align: center; background: #555; color: #BBB; margin-top: 50px; } .card { position: relative; border: 1px solid #d9d9d9; border-radius: 3px; color: #666; background-color: #fff; width: 100%; border-radius: 5px; } .card .card-header { padding: 9px 0; height: 40px; background: #555; color: #fff; text-align: center; border-top-left-radius: 4px; border-top-right-radius: 4px; } .card .card-body { padding: 12px 10px; } .card .card-footer { height: 20px; font-size: 10px; color: #777; margin-top: -15px; margin-bottom: 5px; margin-left: 20px; margin-right: 20px; } .card .detail-brand { float: left; width: 30%; line-height: 98px; font-size: 30px; text-align: center; color: white; } .card .default { background: #1d9d74; } .card .info { background: #6EBEE7; } .card .warn { background: #ED7F54; } .card .danger { background: #6583BE; } .card .detail .text-default { color: #1d9d74; } .card .detail .text-info { color: #6EBEE7; } .card .detail .text-warn { color: #ED7F54; } .card .detail .text-danger { color: #6583BE; } .card .detail { float: right; width: 70%; line-height: 98px; text-align: center; } .card .detail .text { font-size: 12px; } .card .detail .number { font-size: 30px; font-weight: 500; } .h100 { height: 100px; } .inline { display: inline; } .separator { height: 1px; background-color: #e5e5e5; margin-top: 10px; } .card > .card-body > table > thead > tr > td, .card > .card-body > table > tbody > tr > td { word-wrap: break-word; word-break: break-all; } .card > .card-body > table > thead > tr > td { font-weight: 500; font-size: 13px; text-align: center; } .card > .card-body > table > thead > tr > td > span { font-weight: 500; font-size: 10px; } .card > .card-body > table > tbody > tr > td { font-size: 12px; text-align: center; } .card > .card-body > table > tbody > tr > td > a { color: #666; } .thumbnails > .card > .card-body > table > thead > tr > td, .thumbnails > .card > .card-body > table > tbody > tr > td { font-size: 12px; color: #777; word-wrap: break-word; word-break: break-all; } .thumbnails > .card > .card-body > table > thead > tr > td:nth-child(n+2) { text-align: center; } .thumbnails > .card > .card-body > table > tbody > tr > td:nth-child(n+2) { font-weight: 700; text-align: center; } .thumbnails > .card > .card-body > table > thead > tr > td:nth-child(1), .thumbnails > .card > .card-body > table > tbody > tr > td:nth-child(1) { text-align: left; } .tools-header { background: whitesmoke; padding: 9px 0; height: 40px; border-top-left-radius: 4px; border-top-right-radius: 4px; } .tools-header .brand { font-size: 13px; margin: 2px 10px; font-weight: 700; float: left; } .tools-header .brand > a { color: #666; } .tools-header > button, .tools-header > select, .tools-header > a { float: right; max-width: 80px; margin: 1px 10px; height: 25px; padding: 0 10px; line-height: 25px; color: #666; } .tools-header .paged { margin-right: 0px; } .btn { height: 32px; } .btn.btn-main { color: #ffffff; background-color: #337ab7; border-color: #337ab7; } .btn:focus, .btn:active { outline: none !important; } .btn-default:hover, .btn-default:focus, .btn-default:active { color: #1d9d74; border-color: #1d9d74; background: white; } .btn.btn-danger-tag { color: #ffffff; background-color: #d9534f; border-color: #d43f3a; line-height: 1px; font-size: 11px; padding: 4px 4px; } .btn.btn-danger { color: #333; background-color: #fff; border-color: #ccc; } .btn.btn-danger:hover, .btn.btn-danger:focus, .btn.btn-danger:active { color: #d9534f; border-color: #d9534f; background: white; } .form-control { height : 32px; } .form-control:focus { border-color: #337ab7; box-shadow: 0px 0px 0px rgba(0, 0, 0, 0.075) inset, 0px 0px 0px rgba(29, 157, 116, 1); } .form-control { border-radius: 8px; } .input-label:before { display: inline-block; content: "*"; color: #f44336; font-family: SimSun; font-size: 12px; -webkit-transform: TranslateX(-10px); -ms-transform: TranslateX(-10px); transform: TranslateX(-10px); } .label.label-main { color: #ffffff; background-color: #1d9d74; border-color: #1d9d74; } .badge-main { color: #ffffff; background-color: #1d9d74; border-color: #1d9d74; } .bootstrap-tagsinput { background-color: #fff; border: 1px solid #ccc; box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); display: inline-block; padding: 4px 6px; color: #555; vertical-align: middle; border-radius: 4px; /* max-width: 100%; */ width: 85%; height: 100px; line-height: 20px; cursor: text; } .bootstrap-tagsinput > .dropdown-menu { min-width: 40px; font-size: 12px; } .bootstrap-tagsinput > .dropdown-menu>.active>a, .bootstrap-tagsinput > .dropdown-menu>.active>a:focus, .bootstrap-tagsinput > .dropdown-menu>.active>a:hover { background-color: #1d9d74; background-image: -webkit-linear-gradient(top, #1d9d74 0, #1d9d74 100%); background-image: -o-linear-gradient(top, #1d9d74 0, #1d9d74 100%); background-image: -webkit-gradient(linear, left top, left bottom, from(#1d9d74), to(#1d9d74)); background-image: linear-gradient(to bottom, #1d9d74 0, #1d9d74 100%); filter: progid: DXImageTransform.Microsoft.gradient(startColorstr='#1d9d74', endColorstr='#1d9d74', GradientType=0); background-repeat: repeat-x; } .bootstrap-tagsinput > .dropdown-menu>.active>a, .bootstrap-tagsinput > .dropdown-menu>.active>a:focus, .bootstrap-tagsinput > .dropdown-menu>.active>a:hover { color: #fff; text-decoration: none; background-color: #1d9d74; outline: 0; } .bootstrap-tagsinput > .dropdown-menu>.active>a, .bootstrap-tagsinput > .dropdown-menu>.active>a:hover, .bootstrap-tagsinput > .dropdown-menu>.active>a:focus { color: white; text-decoration: none; outline: 0; background-color: #1d9d74; } .inputs-header { padding: 9px 0; height: 50px; border-top-left-radius: 4px; border-top-right-radius: 4px; } .inputs-header .brand { font-size: 13px; margin: 2px 10px; font-weight: 700; float: left; } .inputs-header .brand > a { color: #666; } .inputs-header > input { float: right; margin: 1px 10px; height: 30px; padding: 0 10px; color: #666; } .inputs-header > a { float: right; margin: 1px 10px; height: 30px; padding: 5 5px; } .inputs-header > select { float: right; max-width: 80px; margin: 1px 10px; height: 30px; padding: 0 10px; color: #666; height: 25px; font-size: 12px; } .witdh-150 { max-width: 150px; } .witdh-200 { max-width: 200px; } .width-200 { max-width: 200px; } .witdh-300 { max-width: 300px; } .width-300 { max-width: 300px; } .card.highlight { border-color: #d9534f; } .card .pagination-footer { height: 40px; font-size: 10px; color: #777; margin-top: -15px; margin-bottom: 5px; margin-left: 20px; margin-right: 20px; } .card .pagination-footer .tools { font-size: 12px; margin: 11px 0; float: right; display: inline; margin-right: 20px; } .card > .pagination-footer > .tools > span > input { height: 25px; max-width: 50px; display: inline; } .pagination { display: inline-block; padding-left: 0; margin: 8px 0; float: right; border-radius: 4px; } .pagination > a { margin-right: 5px; height: 28px; width: 28px; padding: 5px 0px; } .pagination > .btn.active { color: #ffffff; background-color: #1d9d74; border-color: #1d9d74; } .datepicker > .table > thead > tr > td, .datepicker > .table > tbody > tr > td, .timepicker > .table > thead > tr > td, .timepicker > .table > tbody > tr > td { padding: 5px 3px; } .datepicker > .table > thead > tr > td > .btn, .datepicker > .table > tbody > tr > td > .btn, .timepicker > .table > thead > tr > td > .btn, .timepicker > .table > tbody > tr > td > .btn { border: 1px solid #FFFDFD; } .datepicker > .table > thead > tr > td > .btn-default:hover, .datepicker > .table > thead > tr > td > .btn-default:focus, .datepicker > .table > thead > tr > td > .btn-default:active, .datepicker > .table > tbody > tr > td > .btn-default:hover, .datepicker > .table > tbody > tr > td > .btn-default:focus, .datepicker > .table > tbody > tr > td > .btn-default:active, .timepicker > .table > thead > tr > td > .btn-default:hover, .timepicker > .table > thead > tr > td > .btn-default:focus, .timepicker > .table > thead > tr > td > .btn-default:active, .timepicker > .table > tbody > tr > td > .btn-default:hover, .timepicker > .table > tbody > tr > td > .btn-default:focus, .timepicker > .table > tbody > tr > td > .btn-default:active { color: #1d9d74; border-color: #1d9d74; background: white; } .datepicker > .table > thead > tr > td > a, .datepicker > .table > tbody > tr > td > a, .timepicker > .table > thead > tr > td > a, .timepicker > .table > tbody > tr > td > a { height: 25px; width: 25px; padding: 3px 0px; } .datepicker > .table > tbody > tr:first-child > td > a { padding: 4px 0px; } .datepicker > .table > thead > tr > td > a.btn.active, .datepicker > .table > tbody > tr > td > a.btn.active, .timepicker > .table > thead > tr > td > a.btn.active, .timepicker > .table > tbody > tr > td > a.btn.active { /* color: #ffffff; background-color: #1d9d74; border-color: #1d9d74;*/ color: #1d9d74; border-color: #1d9d74; background: white; box-shadow: inset 0 0px 0px rgba(0,0,0,0.125); } .datepicker > .table > thead > tr > td:not(:first-child):last-child > a, .timepicker > .table > thead > tr > td:not(:first-child):last-child > a { height: 25px; width: 50px; padding: 5px 0px; } .datepicker > .table > tbody > tr > td > a, .timepicker > .table > tbody > tr > td > a { margin-left: 8px; } .selectize-input-200 > .selectize-input { min-width: 250px; } .highlight-border { border-color: #337ab7; box-shadow: 0px 0px 0px rgba(0, 0, 0, 0.075) inset, 0px 0px 0px rgba(29, 157, 116, 1); }.browsehappy { margin: 0.2em 0; background: #ccc; color: #000; padding: 0.2em 0; } body { padding: 0; } /* Everything but the jumbotron gets side spacing for mobile first views */ .header, .marketing, .footer { padding-left: 15px; padding-right: 15px; } /* Custom page header */ .header { border-bottom: 1px solid #e5e5e5; margin-bottom: 10px; } /* Make the masthead heading the same height as the navigation */ .header h3 { margin-top: 0; margin-bottom: 0; line-height: 40px; padding-bottom: 19px; } /* Custom page footer */ .footer { padding-top: 19px; color: #777; border-top: 1px solid #e5e5e5; } .container-narrow > hr { margin: 30px 0; } /* Main marketing message and sign up button */ .jumbotron { text-align: center; border-bottom: 1px solid #e5e5e5; } .jumbotron .btn { font-size: 21px; padding: 14px 24px; } /* Supporting marketing content */ .marketing { margin: 40px 0; } .marketing p + h4 { margin-top: 28px; } /* Responsive: Portrait tablets and up */ @media screen and (min-width: 768px) { .container { width: inherit; margin-left: 60px; margin-right: 5px; } /* Remove the padding we set earlier */ .header, .marketing, .footer { padding-left: 0; padding-right: 0; } /* Space out the masthead */ .header { margin-bottom: 30px; } /* Remove the bottom border on the jumbotron for visual effect */ .jumbotron { border-bottom: 0; } } .navbar-inverse { background-color: #1d9d74; border-color: #1b926c; } .navbar-inverse .navbar-nav > li > a { color: #b0ddce; font-size: 15px; } .navbar-inverse .navbar-nav>.open>a, .navbar-inverse .navbar-nav>.open>a:focus, .navbar-inverse .navbar-nav>.open>a:hover { background-color: #1b926c; } @media (min-width: 900px) { .navbar-left { float: left !important; } .navbar-right { float: right !important; margin-right: 0%; } .navbar-right ~ .navbar-right { margin-right: 0; } } .dropdown-menu { min-width: 100px !important; } .nav-sidebar li.active a { background: #DDD; } .dropdown-menu>li>a:hover, .dropdown-menu>li>a:focus { background: #1d9d74; /*background: #d9d9d9;*/ color: white; } .broadcast-message, .broadcast-message-preview { padding: 10px; text-align: center; background: #555; color: #BBB; margin-top: 50px; } .card { position: relative; border: 1px solid #d9d9d9; border-radius: 3px; color: #666; background-color: #fff; width: 100%; border-radius: 5px; } .card .card-header { padding: 9px 0; height: 40px; background: #555; color: #fff; text-align: center; border-top-left-radius: 4px; border-top-right-radius: 4px; } .card .card-body { padding: 12px 10px; } .card .card-footer { height: 20px; font-size: 10px; color: #777; margin-top: -15px; margin-bottom: 5px; margin-left: 20px; margin-right: 20px; } .card .detail-brand { float: left; width: 30%; line-height: 98px; font-size: 30px; text-align: center; color: white; } .card .default { background: #1d9d74; } .card .info { background: #6EBEE7; } .card .warn { background: #ED7F54; } .card .danger { background: #6583BE; } .card .detail .text-default { color: #1d9d74; } .card .detail .text-info { color: #6EBEE7; } .card .detail .text-warn { color: #ED7F54; } .card .detail .text-danger { color: #6583BE; } .card .detail { float: right; width: 70%; line-height: 98px; text-align: center; } .card .detail .text { font-size: 12px; } .card .detail .number { font-size: 30px; font-weight: 500; } .h100 { height: 100px; } .inline { display: inline; } .separator { height: 1px; background-color: #e5e5e5; margin-top: 10px; } .card > .card-body > table > thead > tr > td, .card > .card-body > table > tbody > tr > td { word-wrap: break-word; word-break: break-all; } .card > .card-body > table > thead > tr > td { font-weight: 500; font-size: 13px; text-align: center; } .card > .card-body > table > thead > tr > td > span { font-weight: 500; font-size: 10px; } .card > .card-body > table > tbody > tr > td { font-size: 12px; text-align: center; } .card > .card-body > table > tbody > tr > td > a { color: #666; } .thumbnails > .card > .card-body > table > thead > tr > td, .thumbnails > .card > .card-body > table > tbody > tr > td { font-size: 12px; color: #777; word-wrap: break-word; word-break: break-all; } .thumbnails > .card > .card-body > table > thead > tr > td:nth-child(n+2) { text-align: center; } .thumbnails > .card > .card-body > table > tbody > tr > td:nth-child(n+2) { font-weight: 700; text-align: center; } .thumbnails > .card > .card-body > table > thead > tr > td:nth-child(1), .thumbnails > .card > .card-body > table > tbody > tr > td:nth-child(1) { text-align: left; } .tools-header { background: whitesmoke; padding: 9px 0; height: 40px; border-top-left-radius: 4px; border-top-right-radius: 4px; } .tools-header .brand { font-size: 13px; margin: 2px 10px; font-weight: 700; float: left; } .tools-header .brand > a { color: #666; } .tools-header > button, .tools-header > select, .tools-header > a { float: right; max-width: 80px; margin: 1px 10px; height: 25px; padding: 0 10px; line-height: 25px; color: #666; } .tools-header .paged { margin-right: 0px; } .btn.btn-main { color: #ffffff; background-color: #1d9d74; border-color: #1d9d74; } .btn:focus, .btn:active { outline: none !important; } .btn-default:hover, .btn-default:focus, .btn-default:active { color: #1d9d74; border-color: #1d9d74; background: white; } .btn-default-inverse { color: #1d9d74; border-color: #1d9d74; background: white; } .btn-default-inverse:hover, .btn-default-inverse:focus, .btn-default:active { color: #1d9d74; border-color: #1d9d74; background: white; } .btn.btn-danger-tag { color: #ffffff; background-color: #d9534f; border-color: #d43f3a; line-height: 1px; font-size: 11px; padding: 4px 4px; } .btn.btn-danger { color: #333; background-color: #fff; border-color: #ccc; } .btn.btn-danger:hover, .btn.btn-danger:focus, .btn.btn-danger:active { color: #d9534f; border-color: #d9534f; background: white; } .form-control { height : 32px; } .form-control:focus { border-color: #1d9d74; box-shadow: 0px 0px 0px rgba(0, 0, 0, 0.075) inset, 0px 0px 0px rgba(29, 157, 116, 1); } .form-control { border-radius: 8px; } .input-label:before { display: inline-block; content: "*"; color: #f44336; font-family: SimSun; font-size: 12px; -webkit-transform: TranslateX(-10px); -ms-transform: TranslateX(-10px); transform: TranslateX(-10px); } .label.label-main { color: #ffffff; background-color: #1d9d74; border-color: #1d9d74; } .badge-main { color: #ffffff; background-color: #1d9d74; border-color: #1d9d74; } .bootstrap-tagsinput { background-color: #fff; border: 1px solid #ccc; box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); display: inline-block; padding: 4px 6px; color: #555; vertical-align: middle; border-radius: 4px; /* max-width: 100%; */ width: 85%; height: 100px; line-height: 20px; cursor: text; } .bootstrap-tagsinput > .dropdown-menu { min-width: 40px; font-size: 12px; } .bootstrap-tagsinput > .dropdown-menu>.active>a, .bootstrap-tagsinput > .dropdown-menu>.active>a:focus, .bootstrap-tagsinput > .dropdown-menu>.active>a:hover { background-color: #1d9d74; background-image: -webkit-linear-gradient(top, #1d9d74 0, #1d9d74 100%); background-image: -o-linear-gradient(top, #1d9d74 0, #1d9d74 100%); background-image: -webkit-gradient(linear, left top, left bottom, from(#1d9d74), to(#1d9d74)); background-image: linear-gradient(to bottom, #1d9d74 0, #1d9d74 100%); filter: progid: DXImageTransform.Microsoft.gradient(startColorstr='#1d9d74', endColorstr='#1d9d74', GradientType=0); background-repeat: repeat-x; } .bootstrap-tagsinput > .dropdown-menu>.active>a, .bootstrap-tagsinput > .dropdown-menu>.active>a:focus, .bootstrap-tagsinput > .dropdown-menu>.active>a:hover { color: #fff; text-decoration: none; background-color: #1d9d74; outline: 0; } .bootstrap-tagsinput > .dropdown-menu>.active>a, .bootstrap-tagsinput > .dropdown-menu>.active>a:hover, .bootstrap-tagsinput > .dropdown-menu>.active>a:focus { color: white; text-decoration: none; outline: 0; background-color: #1d9d74; } .inputs-header { padding: 9px 0; height: 50px; border-top-left-radius: 4px; border-top-right-radius: 4px; } .inputs-header .brand { font-size: 13px; margin: 2px 10px; font-weight: 700; float: left; } .inputs-header .brand > a { color: #666; } .inputs-header > input { float: right; margin: 1px 10px; height: 30px; padding: 0 10px; color: #666; } .inputs-header > a { float: right; margin: 1px 10px; height: 30px; padding: 5 5px; } .inputs-header > select { float: right; max-width: 80px; margin: 1px 10px; height: 30px; padding: 0 10px; color: #666; height: 25px; font-size: 12px; } .witdh-150 { max-width: 150px; } .witdh-200 { max-width: 200px; } .card.highlight { border-color: #d9534f; } .card .pagination-footer { height: 40px; font-size: 10px; color: #777; margin-top: -15px; margin-bottom: 5px; margin-left: 20px; margin-right: 20px; } .card .pagination-footer .tools { font-size: 12px; margin: 11px 0; float: right; display: inline; margin-right: 20px; } .card > .pagination-footer > .tools > span > input { height: 25px; max-width: 50px; display: inline; } .pagination { display: inline-block; padding-left: 0; margin: 8px 0; float: right; border-radius: 4px; } .pagination > a { margin-right: 5px; height: 28px; width: 28px; padding: 5px 0px; } .pagination > .btn.active { color: #ffffff; background-color: #449d44; border-color: #449d44; } .datepicker > .table > thead > tr > td, .datepicker > .table > tbody > tr > td, .timepicker > .table > thead > tr > td, .timepicker > .table > tbody > tr > td { padding: 5px 3px; } .datepicker > .table > thead > tr > td > .btn, .datepicker > .table > tbody > tr > td > .btn, .timepicker > .table > thead > tr > td > .btn, .timepicker > .table > tbody > tr > td > .btn { border: 1px solid #FFFDFD; } .datepicker > .table > thead > tr > td > .btn-default:hover, .datepicker > .table > thead > tr > td > .btn-default:focus, .datepicker > .table > thead > tr > td > .btn-default:active, .datepicker > .table > tbody > tr > td > .btn-default:hover, .datepicker > .table > tbody > tr > td > .btn-default:focus, .datepicker > .table > tbody > tr > td > .btn-default:active, .timepicker > .table > thead > tr > td > .btn-default:hover, .timepicker > .table > thead > tr > td > .btn-default:focus, .timepicker > .table > thead > tr > td > .btn-default:active, .timepicker > .table > tbody > tr > td > .btn-default:hover, .timepicker > .table > tbody > tr > td > .btn-default:focus, .timepicker > .table > tbody > tr > td > .btn-default:active { color: #1d9d74; border-color: #1d9d74; background: white; } .datepicker > .table > thead > tr > td > a, .datepicker > .table > tbody > tr > td > a, .timepicker > .table > thead > tr > td > a, .timepicker > .table > tbody > tr > td > a { height: 25px; width: 25px; padding: 3px 0px; } .datepicker > .table > tbody > tr:first-child > td > a { padding: 4px 0px; } .datepicker > .table > thead > tr > td > a.btn.active, .datepicker > .table > tbody > tr > td > a.btn.active, .timepicker > .table > thead > tr > td > a.btn.active, .timepicker > .table > tbody > tr > td > a.btn.active { /* color: #ffffff; background-color: #1d9d74; border-color: #1d9d74;*/ color: #1d9d74; border-color: #1d9d74; background: white; box-shadow: inset 0 0px 0px rgba(0,0,0,0.125); } .datepicker > .table > thead > tr > td:not(:first-child):last-child > a, .timepicker > .table > thead > tr > td:not(:first-child):last-child > a { height: 25px; width: 50px; padding: 5px 0px; } .datepicker > .table > tbody > tr > td > a, .timepicker > .table > tbody > tr > td > a { margin-left: 8px; } .selectize-input-200 > .selectize-input { min-width: 250px; } .highlight-border { border-color: #1d9d74; box-shadow: 0px 0px 0px rgba(0, 0, 0, 0.075) inset, 0px 0px 0px rgba(29, 157, 116, 1); } .sortorder:after { content: '\25b2'; } .sortorder.reverse:after { content: '\25bc'; } .input-control select { -moz-appearance: none; -webkit-appearance: none; appearance: none; position: relative; border: 1px #d9d9d9 solid; width: 100%; height: 100%; padding: .3125rem; z-index: 0; } .navbar-inverse { background-color: #337ab7; border-color: #337ab7; } .sidebar { z-index: 1; width: 220px; /*position: fixed;*/ top: 0; left: 0; height: 100%; } #page-wrapper { position: inherit; margin: 70px 0 0 220px; padding: 12px 30px; border-left: 0px solid #e7e7e7; } .sidebar .sidebar-nav.navbar-collapse { padding-right: 0; padding-left: 0; background-color: #F5F5F5; position: relative; color: black; width: 100%; padding: 0; margin: 0; list-style: none inside none; } .sidebar a { color: #555; } .sidebar ul li:hover { color:red; } .form-control { border-radius: 8px; } .form-control:focus { border-color: #337ab7; box-shadow: 0px 0px 0px rgba(0, 0, 0, 0.075) inset, 0px 0px 0px rgba(29, 157, 116, 1); } .highlight-border { border-color: #337ab7; box-shadow: 0px 0px 0px rgba(0, 0, 0, 0.075) inset, 0px 0px 0px rgba(29, 157, 116, 1); }.browsehappy { margin: 0.2em 0; background: #ccc; color: #000; padding: 0.2em 0; } .btn.btn-main { color: #ffffff; background-color: #337ab7; border-color: #337ab7; } .btn-default-inverse { color: #337ab7; border-color: #337ab7; background: white; } .btn-default-inverse:hover, .btn-default-inverse:focus, .btn-default:active { color: #337ab7; border-color: #337ab7; background: white; } .btn-danger-inverse { color: #d9534f; border-color: #d9534f; background: white; } .btn-danger-inverse:hover, .btn-danger-inverse:focus, .btn-danger:active { color: #d9534f; border-color: #d9534f; background: white; } .btn-tab-active, .btn-tab-active:hover, .btn-tab-active:focus, .btn-tab-default:hover, .btn-tab-default:focus, .btn-tab-default:active { color: #337ab7; border-color: #337ab7; background: white; font-weight: 600; } .btn-tab-default { color: #777; background: white; font-weight: 600; } .pagination > .btn.active { color: #ffffff; background-color: #337ab7; border-color: #337ab7; } .btn-default:hover, .btn-default:focus, .btn-default:active { color: #337ab7; border-color: #337ab7; background: white; } .bootstrap-switch.bootstrap-switch-on { border-color: #337ab7; } .bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-success, .bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-success { color: #fff; background: #337ab7; } .selectize-input-200 > .selectize-input { min-width: 200px; border-color: #337ab7; } .btn-outline-primary { color: #007bff; background-color: transparent; background-image: none; border-color: #007bff; } .btn-outline-primary:hover { color: #fff; background-color: #007bff; border-color: #007bff; } .btn-outline-primary:focus, .btn-outline-primary.focus { box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.5); } .btn-outline-primary.disabled, .btn-outline-primary:disabled { color: #007bff; background-color: transparent; } .btn-outline-primary:not(:disabled):not(.disabled):active, .btn-outline-primary:not(:disabled):not(.disabled).active, .show > .btn-outline-primary.dropdown-toggle { color: #fff; background-color: #007bff; border-color: #007bff; } .btn-outline-primary:not(:disabled):not(.disabled):active:focus, .btn-outline-primary:not(:disabled):not(.disabled).active:focus, .show > .btn-outline-primary.dropdown-toggle:focus { box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.5); } .btn-outline-secondary { color: #6c757d; background-color: transparent; background-image: none; border-color: #6c757d; } .btn-outline-secondary:hover { color: #fff; background-color: #6c757d; border-color: #6c757d; } .btn-outline-secondary:focus, .btn-outline-secondary.focus { box-shadow: 0 0 0 0.2rem rgba(108, 117, 125, 0.5); } .btn-outline-secondary.disabled, .btn-outline-secondary:disabled { color: #6c757d; background-color: transparent; } .btn-outline-secondary:not(:disabled):not(.disabled):active, .btn-outline-secondary:not(:disabled):not(.disabled).active, .show > .btn-outline-secondary.dropdown-toggle { color: #fff; background-color: #6c757d; border-color: #6c757d; } .btn-outline-secondary:not(:disabled):not(.disabled):active:focus, .btn-outline-secondary:not(:disabled):not(.disabled).active:focus, .show > .btn-outline-secondary.dropdown-toggle:focus { box-shadow: 0 0 0 0.2rem rgba(108, 117, 125, 0.5); } .btn-outline-success { color: #28a745; background-color: transparent; background-image: none; border-color: #28a745; } .btn-outline-success:hover { color: #fff; background-color: #28a745; border-color: #28a745; } .btn-outline-success:focus, .btn-outline-success.focus { box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.5); } .btn-outline-success.disabled, .btn-outline-success:disabled { color: #28a745; background-color: transparent; } .btn-outline-success:not(:disabled):not(.disabled):active, .btn-outline-success:not(:disabled):not(.disabled).active, .show > .btn-outline-success.dropdown-toggle { color: #fff; background-color: #28a745; border-color: #28a745; } .btn-outline-success:not(:disabled):not(.disabled):active:focus, .btn-outline-success:not(:disabled):not(.disabled).active:focus, .show > .btn-outline-success.dropdown-toggle:focus { box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.5); } .btn-outline-info { color: #17a2b8; background-color: transparent; background-image: none; border-color: #17a2b8; } .btn-outline-info:hover { color: #fff; background-color: #17a2b8; border-color: #17a2b8; } .btn-outline-info:focus, .btn-outline-info.focus { box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5); } .btn-outline-info.disabled, .btn-outline-info:disabled { color: #17a2b8; background-color: transparent; } .btn-outline-info:not(:disabled):not(.disabled):active, .btn-outline-info:not(:disabled):not(.disabled).active, .show > .btn-outline-info.dropdown-toggle { color: #fff; background-color: #17a2b8; border-color: #17a2b8; } .btn-outline-info:not(:disabled):not(.disabled):active:focus, .btn-outline-info:not(:disabled):not(.disabled).active:focus, .show > .btn-outline-info.dropdown-toggle:focus { box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5); } .btn-outline-warning { color: #ffc107; background-color: transparent; background-image: none; border-color: #ffc107; } .btn-outline-warning:hover { color: #212529; background-color: #ffc107; border-color: #ffc107; } .btn-outline-warning:focus, .btn-outline-warning.focus { box-shadow: 0 0 0 0.2rem rgba(255, 193, 7, 0.5); } .btn-outline-warning.disabled, .btn-outline-warning:disabled { color: #ffc107; background-color: transparent; } .btn-outline-warning:not(:disabled):not(.disabled):active, .btn-outline-warning:not(:disabled):not(.disabled).active, .show > .btn-outline-warning.dropdown-toggle { color: #212529; background-color: #ffc107; border-color: #ffc107; } .btn-outline-warning:not(:disabled):not(.disabled):active:focus, .btn-outline-warning:not(:disabled):not(.disabled).active:focus, .show > .btn-outline-warning.dropdown-toggle:focus { box-shadow: 0 0 0 0.2rem rgba(255, 193, 7, 0.5); } .btn-outline-danger { color: #dc3545; background-color: transparent; background-image: none; border-color: #dc3545; } .btn-outline-danger:hover { color: #fff; background-color: #dc3545; border-color: #dc3545; } .btn-outline-danger:focus, .btn-outline-danger.focus { box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.5); } .btn-outline-danger.disabled, .btn-outline-danger:disabled { color: #dc3545; background-color: transparent; } .btn-outline-danger:not(:disabled):not(.disabled):active, .btn-outline-danger:not(:disabled):not(.disabled).active, .show > .btn-outline-danger.dropdown-toggle { color: #fff; background-color: #dc3545; border-color: #dc3545; } .btn-outline-danger:not(:disabled):not(.disabled):active:focus, .btn-outline-danger:not(:disabled):not(.disabled).active:focus, .show > .btn-outline-danger.dropdown-toggle:focus { box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.5); } .btn-outline-light { color: #f8f9fa; background-color: transparent; background-image: none; border-color: #f8f9fa; } .btn-outline-light:hover { color: #212529; background-color: #f8f9fa; border-color: #f8f9fa; } .btn-outline-light:focus, .btn-outline-light.focus { box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5); } .btn-outline-light.disabled, .btn-outline-light:disabled { color: #f8f9fa; background-color: transparent; } .btn-outline-light:not(:disabled):not(.disabled):active, .btn-outline-light:not(:disabled):not(.disabled).active, .show > .btn-outline-light.dropdown-toggle { color: #212529; background-color: #f8f9fa; border-color: #f8f9fa; } .btn-outline-light:not(:disabled):not(.disabled):active:focus, .btn-outline-light:not(:disabled):not(.disabled).active:focus, .show > .btn-outline-light.dropdown-toggle:focus { box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5); } .btn-outline-dark { color: #343a40; background-color: transparent; background-image: none; border-color: #343a40; } .btn-outline-dark:hover { color: #fff; background-color: #343a40; border-color: #343a40; } .btn-outline-dark:focus, .btn-outline-dark.focus { box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5); } .btn-outline-dark.disabled, .btn-outline-dark:disabled { color: #343a40; background-color: transparent; } .btn-outline-dark:not(:disabled):not(.disabled):active, .btn-outline-dark:not(:disabled):not(.disabled).active, .show > .btn-outline-dark.dropdown-toggle { color: #fff; background-color: #343a40; border-color: #343a40; } .btn-outline-dark:not(:disabled):not(.disabled):active:focus, .btn-outline-dark:not(:disabled):not(.disabled).active:focus, .show > .btn-outline-dark.dropdown-toggle:focus { box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5); } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/styles/page.css ================================================ /*! * Start Bootstrap - SB Admin 2 Bootstrap Admin Theme (http://startbootstrap.com) * Code licensed under the Apache License v2.0. * For details, see http://www.apache.org/licenses/LICENSE-2.0. */ body { background-color: #f8f8f8; } .example { padding: .625rem 1.825rem .625rem 2.5rem; border: 1px #ccc dashed; position: relative; margin: 0 0 .625rem 0; background-color: #ffffff; } dl dt, dl dd { line-height: 1.25rem; } dl dt { font-style: normal; font-weight: 700; } dl dd { margin-left: .9375rem; } dl.horizontal dt { float: left; width: 10rem; overflow: hidden; clear: left; text-align: right; text-overflow: ellipsis; white-space: nowrap; } dl.horizontal dd { margin-left: 11.25rem; } #wrapper { width: 100%; } #page-wrapper { padding: 0 15px; min-height: 568px; background-color: #fff; } @media(min-width:768px) { #page-wrapper { position: inherit; margin: 0 0 0 250px; padding: 0 30px; border-left: 1px solid #e7e7e7; } } .navbar-top-links { margin-right: 0; } .navbar-top-links li { display: inline-block; } .navbar-top-links li:last-child { margin-right: 15px; } .navbar-top-links li a { padding: 15px; min-height: 50px; } .navbar-top-links .dropdown-menu li { display: block; } .navbar-top-links .dropdown-menu li:last-child { margin-right: 0; } .navbar-top-links .dropdown-menu li a { padding: 3px 20px; min-height: 0; } .navbar-top-links .dropdown-menu li a div { white-space: normal; } .navbar-top-links .dropdown-messages, .navbar-top-links .dropdown-tasks, .navbar-top-links .dropdown-alerts { width: 310px; min-width: 0; } .navbar-top-links .dropdown-messages { margin-left: 5px; } .navbar-top-links .dropdown-tasks { margin-left: -59px; } .navbar-top-links .dropdown-alerts { margin-left: -123px; } .navbar-top-links .dropdown-user { right: 0; left: auto; } .sidebar .sidebar-nav.navbar-collapse { padding-right: 0; padding-left: 0; background-color: #71b1d1; color: #ffffff; position: relative; width: 100%; padding: 0; margin: 0; list-style: none inside none; } .sidebar .sidebar-search { padding: 15px; } .sidebar ul li { border-bottom: 1px solid #e7e7e7; } .sidebar ul li a.active { background-color: #ffffff; color: #ffffff; } .sidebar a{ color: #fff; } .sidebar .arrow { float: right; } .sidebar .fa.arrow:before { content: "\f104"; } .sidebar .active>a>.fa.arrow:before { content: "\f107"; } .sidebar .nav-second-level li, .sidebar .nav-third-level li { border-bottom: 0!important; } .sidebar .nav-second-level li a { padding-left: 37px; } .sidebar .nav-third-level li a { padding-left: 52px; } @media(min-width:768px) { .sidebar { z-index: 1; position: absolute; width: 250px; margin-top: 51px; } .navbar-top-links .dropdown-messages, .navbar-top-links .dropdown-tasks, .navbar-top-links .dropdown-alerts { margin-left: auto; } } .btn-outline { color: inherit; background-color: transparent; transition: all .5s; } .btn-primary.btn-outline { color: #428bca; } .btn-success.btn-outline { color: #5cb85c; } .btn-info.btn-outline { color: #5bc0de; } .btn-warning.btn-outline { color: #f0ad4e; } .btn-danger.btn-outline { color: #d9534f; } .btn-primary.btn-outline:hover, .btn-success.btn-outline:hover, .btn-info.btn-outline:hover, .btn-warning.btn-outline:hover, .btn-danger.btn-outline:hover { color: #fff; } .chat { margin: 0; padding: 0; list-style: none; } .chat li { margin-bottom: 10px; padding-bottom: 5px; border-bottom: 1px dotted #999; } .chat li.left .chat-body { margin-left: 60px; } .chat li.right .chat-body { margin-right: 60px; } .chat li .chat-body p { margin: 0; } .panel .slidedown .glyphicon, .chat .glyphicon { margin-right: 5px; } .chat-panel .panel-body { height: 350px; overflow-y: scroll; } .login-panel { margin-top: 25%; } .flot-chart { display: block; height: 400px; } .flot-chart-content { width: 100%; height: 100%; } .dataTables_wrapper { position: relative; clear: both; } table.dataTable thead .sorting, table.dataTable thead .sorting_asc, table.dataTable thead .sorting_desc, table.dataTable thead .sorting_asc_disabled, table.dataTable thead .sorting_desc_disabled { background: 0 0; } table.dataTable thead .sorting_asc:after { content: "\f0de"; float: right; font-family: fontawesome; } table.dataTable thead .sorting_desc:after { content: "\f0dd"; float: right; font-family: fontawesome; } table.dataTable thead .sorting:after { content: "\f0dc"; float: right; font-family: fontawesome; color: rgba(50,50,50,.5); } .btn-circle { width: 30px; height: 30px; padding: 6px 0; border-radius: 15px; text-align: center; font-size: 12px; line-height: 1.428571429; } .btn-circle.btn-lg { width: 50px; height: 50px; padding: 10px 16px; border-radius: 25px; font-size: 18px; line-height: 1.33; } .btn-circle.btn-xl { width: 70px; height: 70px; padding: 10px 16px; border-radius: 35px; font-size: 24px; line-height: 1.33; } .show-grid [class^=col-] { padding-top: 10px; padding-bottom: 10px; border: 1px solid #ddd; background-color: #eee!important; } .show-grid { margin: 15px 0; } .huge { font-size: 40px; } .panel-green { border-color: #5cb85c; } .panel-green .panel-heading { border-color: #5cb85c; color: #fff; background-color: #5cb85c; } .panel-green a { color: #5cb85c; } .panel-green a:hover { color: #3d8b3d; } .panel-red { border-color: #d9534f; } .panel-red .panel-heading { border-color: #d9534f; color: #fff; background-color: #d9534f; } .panel-red a { color: #d9534f; } .panel-red a:hover { color: #b52b27; } .panel-yellow { border-color: #f0ad4e; } .panel-yellow .panel-heading { border-color: #f0ad4e; color: #fff; background-color: #f0ad4e; } .panel-yellow a { color: #f0ad4e; } .panel-yellow a:hover { color: #df8a13; } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/styles/timeline.css ================================================ .timeline { position: relative; padding: 20px 0 20px; list-style: none; } .timeline:before { content: " "; position: absolute; top: 0; bottom: 0; left: 50%; width: 3px; margin-left: -1.5px; background-color: #eeeeee; } .timeline > li { position: relative; margin-bottom: 20px; } .timeline > li:before, .timeline > li:after { content: " "; display: table; } .timeline > li:after { clear: both; } .timeline > li:before, .timeline > li:after { content: " "; display: table; } .timeline > li:after { clear: both; } .timeline > li > .timeline-panel { float: left; position: relative; width: 46%; padding: 20px; border: 1px solid #d4d4d4; border-radius: 2px; -webkit-box-shadow: 0 1px 6px rgba(0,0,0,0.175); box-shadow: 0 1px 6px rgba(0,0,0,0.175); } .timeline > li > .timeline-panel:before { content: " "; display: inline-block; position: absolute; top: 26px; right: -15px; border-top: 15px solid transparent; border-right: 0 solid #ccc; border-bottom: 15px solid transparent; border-left: 15px solid #ccc; } .timeline > li > .timeline-panel:after { content: " "; display: inline-block; position: absolute; top: 27px; right: -14px; border-top: 14px solid transparent; border-right: 0 solid #fff; border-bottom: 14px solid transparent; border-left: 14px solid #fff; } .timeline > li > .timeline-badge { z-index: 100; position: absolute; top: 16px; left: 50%; width: 50px; height: 50px; margin-left: -25px; border-radius: 50% 50% 50% 50%; text-align: center; font-size: 1.4em; line-height: 50px; color: #fff; background-color: #999999; } .timeline > li.timeline-inverted > .timeline-panel { float: right; } .timeline > li.timeline-inverted > .timeline-panel:before { right: auto; left: -15px; border-right-width: 15px; border-left-width: 0; } .timeline > li.timeline-inverted > .timeline-panel:after { right: auto; left: -14px; border-right-width: 14px; border-left-width: 0; } .timeline-badge.primary { background-color: #2e6da4 !important; } .timeline-badge.success { background-color: #3f903f !important; } .timeline-badge.warning { background-color: #f0ad4e !important; } .timeline-badge.danger { background-color: #d9534f !important; } .timeline-badge.info { background-color: #5bc0de !important; } .timeline-title { margin-top: 0; color: inherit; } .timeline-body > p, .timeline-body > ul { margin-bottom: 0; } .timeline-body > p + p { margin-top: 5px; } @media(max-width:767px) { ul.timeline:before { left: 40px; } ul.timeline > li > .timeline-panel { width: calc(100% - 90px); width: -moz-calc(100% - 90px); width: -webkit-calc(100% - 90px); } ul.timeline > li > .timeline-badge { top: 16px; left: 15px; margin-left: 0; } ul.timeline > li > .timeline-panel { float: right; } ul.timeline > li > .timeline-panel:before { right: auto; left: -15px; border-right-width: 15px; border-left-width: 0; } ul.timeline > li > .timeline-panel:after { right: auto; left: -14px; border-right-width: 14px; border-left-width: 0; } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/views/authority.html ================================================
{{app}}
授权规则
资源名 流控应用 授权类型 操作
{{ruleEntity.rule.resource}} {{ruleEntity.rule.limitApp }} 白名单 黑名单
================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/views/cluster/client.html ================================================

未连接

连接中

已连接

================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/views/cluster/server.html ================================================

独立模式 (Alone)

嵌入模式 (Embedded)

================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/views/cluster_app_assign_manage.html ================================================
{{app}}
集群限流 - 机器分配/管控

{{loadError.message}}

================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/views/cluster_app_client_list.html ================================================
集群限流 - Token Client 列表

{{loadError.message}}

Client ID Server IP Server 端口 连接状态 操作
{{clientVO.id}} {{clientVO.state.clientConfig.serverHost}} {{clientVO.state.clientConfig.serverPort}} 未连接 连接中 已连接
================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/views/cluster_app_server_list.html ================================================
{{app}}
Token Client 列表
集群限流 - Token Server 列表

{{loadError.message}}

Server ID Port 命名空间集合 运行模式 总连接数 QPS 总览 操作
{{serverVO.id}} {{serverVO.id}}(自主指定) {{serverVO.port}} {{serverVO.state.namespaceSetStr}} 未知 未知 嵌入模式 独立模式 {{serverVO.connectedCount}} 未知 {{serverVO.state.requestLimitDataStr}} 未知
================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/views/cluster_app_server_overview.html ================================================
{{app}}
集群限流 - Token Server 总览

{{loadError.message}}

Server ID Port 命名空间集合 总连接数 连接情况 QPS 总览
{{serverVO.id}} {{serverVO.port}} {{serverVO.state.namespaceSetStr}} {{serverVO.connectedCount}}

namespace: {{cg.namespace}}, 连接数: {{cg.connectedCount}}, clients: {{generateConnectionSet(cg.connectionSet)}}

namespace: {{crl.namespace}}, 当前 QPS: {{crl.currentQps}}, 最大允许 QPS: {{crl.maxAllowedQps}}

================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/views/cluster_single_config.html ================================================
{{app}}
集群限流

{{loadError.message}}

Client

Server

未开启

 Client    Server

该机器未引入 Sentinel 集群限流客户端或服务端的相关依赖,请引入相关依赖。

================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/views/dashboard/home.html ================================================

欢迎使用 Sentinel 控制台

================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/views/dashboard/main.html ================================================
================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/views/degrade.html ================================================
{{app}}
降级规则
资源名 降级模式 阈值 时间窗口(s) 操作
{{rule.resource}} RT 异常比例 异常数 {{rule.count}} {{rule.timeWindow}}s
================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/views/dialog/authority-rule-dialog.html ================================================
{{authorityRuleDialog.title}}
 白名单    黑名单
================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/views/dialog/cluster/cluster-client-config-dialog.html ================================================
修改 Token Client 配置

{{ccDialogData.clientId}}

================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/views/dialog/cluster/cluster-server-assign-dialog.html ================================================
{{serverAssignDialogData.title}}

{{serverAssignDialogData.serverData.currentServer}}

 应用内机器    外部指定机器

若指定外部 server,请先在相应页面对外部 server 进行配置,然后在此页面指定。

================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/views/dialog/cluster/cluster-server-connection-detail-dialog.html ================================================
连接详情

{{connectionDetailDialogData.serverData.id}}

命名空间 连接数 连接详情
{{cg.namespace}} {{cg.connectedCount}} {{generateConnectionSet(cg.connectionSet)}}
================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/views/dialog/confirm-dialog.html ================================================
{{confirmDialog.title}}

{{confirmDialog.attentionTitle}}:

{{confirmDialog.attention}}

================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/views/dialog/degrade-rule-dialog.html ================================================
{{degradeRuleDialog.title}}
 RT    异常比例    异常数
================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/views/dialog/flow-rule-dialog.html ================================================
{{flowRuleDialog.title}}
 QPS    线程数
 单机均摊    总体阈值
 如果 Token Server 不可用是否退化到单机限流
 直接    关联    链路  
 快速失败    Warm Up    排队等待
================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/views/dialog/param-flow-rule-dialog.html ================================================
{{paramFlowRuleDialog.title}}

QPS 模式

 单机均摊    总体阈值

参数值 参数类型 限流阈值 操作

{{paramItem.classType}}

================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/views/dialog/system-rule-dialog.html ================================================
{{systemRuleDialog.title}}
 LOAD    RT    线程数    入口 QPS
 LOAD    RT    线程数    入口 QPS
================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/views/flow_v1.html ================================================
{{app}}
流控规则
资源名 来源应用 流控模式 阈值类型 阈值 阈值模式 流控效果 操作
{{rule.resource}} {{rule.limitApp }} 直接 关联 链路 {{rule.grade==0 ? '线程数' : 'QPS'}} {{rule.count}} {{generateThresholdTypeShow(rule)}} 快速失败 Warm Up 排队等待 预热排队
================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/views/flow_v2.html ================================================
{{app}}
回到单机页面
流控规则
资源名 来源应用 流控模式 阈值类型 阈值 阈值模式 流控效果 操作
{{rule.resource}} {{rule.limitApp }} 直接 关联 链路 {{rule.grade == 0 ? '线程数' : 'QPS'}} {{rule.count}} {{generateThresholdTypeShow(rule)}} 快速失败 Warm Up 排队等待 预热排队
================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/views/identity.html ================================================
{{app}}
簇点链路
资源名 通过QPS 拒绝QPS 线程数 平均RT 分钟通过 分钟拒绝 操作
{{resource.resource}} {{resource.passQps}} {{resource.blockQps}} {{resource.threadNum}} {{resource.averageRt}} {{resource.oneMinutePass}} {{resource.oneMinuteBlock}} {{resource.oneMinuteBlock}}
================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/views/login.html ================================================
Sentinel Logo
================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/views/machine.html ================================================
{{app}}
机器列表 实例总数 {{machines.length}}, 健康 {{healthyCount}}, 失联 {{machines.length - healthyCount}}.
机器名 IP 地址 端口号 Sentinel 客户端版本 健康状态 心跳时间 操作
{{entry.hostname}} {{entry.ip}} {{entry.port}} {{entry.version}} 健康 失联 {{entry.lastHeartbeat | date: 'yyyy/MM/dd HH:mm:ss'}}
================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/views/metric.html ================================================
{{app}}
{{metricTypeDesc}} 实时监控
 {{metric.resource}}
时间 通过 QPS 拒绝QPS 响应时间(ms)
{{tableObj.timestamp | date: 'HH:mm:ss'}} {{tableObj.passQps | number : 1}} {{tableObj.blockQps | number : 1}} {{tableObj.rt | number : 1}}
-
  • ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/views/pagination.tpl.html ================================================ ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/views/param_flow.html ================================================
    {{app}}
    热点参数限流规则

    {{loadError.message}}

    资源名 参数索引 流控模式 阈值 是否集群 例外项数目 操作
    {{ruleEntity.rule.resource}} {{ruleEntity.rule.paramIdx}} {{ruleEntity.rule.grade == 1 ? 'QPS' : '未知'}} {{ruleEntity.rule.count}} {{ruleEntity.rule.paramFlowItemList == undefined ? 0 : ruleEntity.rule.paramFlowItemList.length}}
    ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/app/views/system.html ================================================
    {{app}}
    系统规则
    阈值类型 单机阈值 操作
    LOAD RT 线程数 QPS {{rule.avgLoad}} {{rule.avgRt}} {{rule.maxThread}} {{rule.qps}}
    ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/dist/css/app.css ================================================ .chat,.timeline{list-style:none}#loading-bar,#loading-bar-spinner{pointer-events:none;-webkit-pointer-events:none;-webkit-transition:350ms linear all;-moz-transition:350ms linear all;-o-transition:350ms linear all;transition:350ms linear all}#loading-bar-spinner.ng-enter,#loading-bar-spinner.ng-leave.ng-leave-active,#loading-bar.ng-enter,#loading-bar.ng-leave.ng-leave-active{opacity:0}#loading-bar-spinner.ng-enter.ng-enter-active,#loading-bar-spinner.ng-leave,#loading-bar.ng-enter.ng-enter-active,#loading-bar.ng-leave{opacity:1}#loading-bar .bar{-webkit-transition:width 350ms;-moz-transition:width 350ms;-o-transition:width 350ms;transition:width 350ms;background:#29d;position:fixed;z-index:10002;top:0;left:0;width:100%;height:2px;border-bottom-right-radius:1px;border-top-right-radius:1px}#loading-bar .peg{position:absolute;width:70px;right:0;top:0;height:2px;opacity:.45;-moz-box-shadow:#29d 1px 0 6px 1px;-ms-box-shadow:#29d 1px 0 6px 1px;-webkit-box-shadow:#29d 1px 0 6px 1px;box-shadow:#29d 1px 0 6px 1px;-moz-border-radius:100%;-webkit-border-radius:100%;border-radius:100%}#loading-bar-spinner{display:block;position:fixed;z-index:10002;top:10px;left:10px}#loading-bar-spinner .spinner-icon{width:14px;height:14px;border:2px solid transparent;border-top-color:#29d;border-left-color:#29d;border-radius:50%;-webkit-animation:loading-bar-spinner .4s linear infinite;-moz-animation:loading-bar-spinner .4s linear infinite;-ms-animation:loading-bar-spinner .4s linear infinite;-o-animation:loading-bar-spinner .4s linear infinite;animation:loading-bar-spinner .4s linear infinite}@-webkit-keyframes loading-bar-spinner{0%{-webkit-transform:rotate(0);transform:rotate(0)}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@-moz-keyframes loading-bar-spinner{0%{-moz-transform:rotate(0);transform:rotate(0)}100%{-moz-transform:rotate(360deg);transform:rotate(360deg)}}@-o-keyframes loading-bar-spinner{0%{-o-transform:rotate(0);transform:rotate(0)}100%{-o-transform:rotate(360deg);transform:rotate(360deg)}}@-ms-keyframes loading-bar-spinner{0%{-ms-transform:rotate(0);transform:rotate(0)}100%{-ms-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes loading-bar-spinner{0%{transform:rotate(0)}100%{transform:rotate(360deg)}}.bootstrap-switch{display:inline-block;direction:ltr;cursor:pointer;border-radius:4px;border:1px solid #ccc;position:relative;text-align:left;overflow:hidden;line-height:8px;z-index:0;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;vertical-align:middle;-webkit-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;-o-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.bootstrap-switch .bootstrap-switch-container{display:inline-block;top:0;border-radius:4px;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}.bootstrap-switch .bootstrap-switch-handle-off,.bootstrap-switch .bootstrap-switch-handle-on,.bootstrap-switch .bootstrap-switch-label{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;cursor:pointer;display:table-cell;vertical-align:middle;padding:6px 12px;font-size:14px;line-height:20px}.bootstrap-switch .bootstrap-switch-handle-off,.bootstrap-switch .bootstrap-switch-handle-on{text-align:center;z-index:1}.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-primary,.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-primary{color:#fff;background:#337ab7}.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-info,.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-info{color:#fff;background:#5bc0de}.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-warning,.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-warning{background:#f0ad4e;color:#fff}.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-danger,.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-danger{color:#fff;background:#d9534f}.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-default,.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-default{color:#000;background:#eee}.bootstrap-switch .bootstrap-switch-label{text-align:center;margin-top:-1px;margin-bottom:-1px;z-index:100;color:#333;background:#fff}.bootstrap-switch span::before{content:"\200b"}.bootstrap-switch .bootstrap-switch-handle-on{border-bottom-left-radius:3px;border-top-left-radius:3px}.bootstrap-switch .bootstrap-switch-handle-off{border-bottom-right-radius:3px;border-top-right-radius:3px}.bootstrap-switch input[type=radio],.bootstrap-switch input[type=checkbox]{position:absolute!important;top:0;left:0;margin:0;z-index:-1;opacity:0;filter:alpha(opacity=0);visibility:hidden}.bootstrap-switch.bootstrap-switch-mini .bootstrap-switch-handle-off,.bootstrap-switch.bootstrap-switch-mini .bootstrap-switch-handle-on,.bootstrap-switch.bootstrap-switch-mini .bootstrap-switch-label{padding:1px 5px;font-size:12px;line-height:1.5}.bootstrap-switch.bootstrap-switch-small .bootstrap-switch-handle-off,.bootstrap-switch.bootstrap-switch-small .bootstrap-switch-handle-on,.bootstrap-switch.bootstrap-switch-small .bootstrap-switch-label{padding:5px 10px;font-size:12px;line-height:1.5}.bootstrap-switch.bootstrap-switch-large .bootstrap-switch-handle-off,.bootstrap-switch.bootstrap-switch-large .bootstrap-switch-handle-on,.bootstrap-switch.bootstrap-switch-large .bootstrap-switch-label{padding:6px 16px;font-size:18px;line-height:1.3333333}.bootstrap-switch.bootstrap-switch-disabled,.bootstrap-switch.bootstrap-switch-indeterminate,.bootstrap-switch.bootstrap-switch-readonly{cursor:default!important}.bootstrap-switch.bootstrap-switch-disabled .bootstrap-switch-handle-off,.bootstrap-switch.bootstrap-switch-disabled .bootstrap-switch-handle-on,.bootstrap-switch.bootstrap-switch-disabled .bootstrap-switch-label,.bootstrap-switch.bootstrap-switch-indeterminate .bootstrap-switch-handle-off,.bootstrap-switch.bootstrap-switch-indeterminate .bootstrap-switch-handle-on,.bootstrap-switch.bootstrap-switch-indeterminate .bootstrap-switch-label,.bootstrap-switch.bootstrap-switch-readonly .bootstrap-switch-handle-off,.bootstrap-switch.bootstrap-switch-readonly .bootstrap-switch-handle-on,.bootstrap-switch.bootstrap-switch-readonly .bootstrap-switch-label{opacity:.5;filter:alpha(opacity=50);cursor:default!important}.bootstrap-switch.bootstrap-switch-animate .bootstrap-switch-container{-webkit-transition:margin-left .5s;-o-transition:margin-left .5s;transition:margin-left .5s}.bootstrap-switch.bootstrap-switch-inverse .bootstrap-switch-handle-on{border-radius:0 3px 3px 0}.bootstrap-switch.bootstrap-switch-inverse .bootstrap-switch-handle-off{border-radius:3px 0 0 3px}.bootstrap-switch.bootstrap-switch-focused{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)}.bootstrap-switch.bootstrap-switch-inverse.bootstrap-switch-off .bootstrap-switch-label,.bootstrap-switch.bootstrap-switch-on .bootstrap-switch-label{border-bottom-right-radius:3px;border-top-right-radius:3px}.bootstrap-switch.bootstrap-switch-inverse.bootstrap-switch-on .bootstrap-switch-label,.bootstrap-switch.bootstrap-switch-off .bootstrap-switch-label{border-bottom-left-radius:3px;border-top-left-radius:3px}.ngdialog,.ngdialog-overlay{position:fixed;top:0;right:0;bottom:0;left:0}@-webkit-keyframes ngdialog-fadeout{0%{opacity:1}100%{opacity:0}}@keyframes ngdialog-fadeout{0%{opacity:1}100%{opacity:0}}@-webkit-keyframes ngdialog-fadein{0%{opacity:0}100%{opacity:1}}@keyframes ngdialog-fadein{0%{opacity:0}100%{opacity:1}}.ngdialog{box-sizing:border-box;overflow:auto;-webkit-overflow-scrolling:touch;z-index:10000}.ngdialog *,.ngdialog :after,.ngdialog :before{box-sizing:inherit}.ngdialog.ngdialog-disabled-animation,.ngdialog.ngdialog-disabled-animation .ngdialog-content,.ngdialog.ngdialog-disabled-animation .ngdialog-overlay{-webkit-animation:none!important;animation:none!important}.ngdialog-overlay{background:rgba(0,0,0,.4);-webkit-backface-visibility:hidden;-webkit-animation:ngdialog-fadein .5s;animation:ngdialog-fadein .5s}.ngdialog-no-overlay{pointer-events:none}.ngdialog.ngdialog-closing .ngdialog-overlay{-webkit-backface-visibility:hidden;-webkit-animation:ngdialog-fadeout .5s;animation:ngdialog-fadeout .5s}.ngdialog-content{background:#fff;-webkit-backface-visibility:hidden;-webkit-animation:ngdialog-fadein .5s;animation:ngdialog-fadein .5s;pointer-events:all}.ngdialog.ngdialog-closing .ngdialog-content{-webkit-backface-visibility:hidden;-webkit-animation:ngdialog-fadeout .5s;animation:ngdialog-fadeout .5s}.ngdialog-close:before{font-family:Helvetica,Arial,sans-serif;content:'\00D7';cursor:pointer}body.ngdialog-open,html.ngdialog-open{overflow:hidden}@-webkit-keyframes ngdialog-flyin{0%{opacity:0;-webkit-transform:translateY(-40px);transform:translateY(-40px)}100%{opacity:1;-webkit-transform:translateY(0);transform:translateY(0)}}@keyframes ngdialog-flyin{0%{opacity:0;-webkit-transform:translateY(-40px);transform:translateY(-40px)}100%{opacity:1;-webkit-transform:translateY(0);transform:translateY(0)}}@-webkit-keyframes ngdialog-flyout{0%{opacity:1;-webkit-transform:translateY(0);transform:translateY(0)}100%{opacity:0;-webkit-transform:translateY(-40px);transform:translateY(-40px)}}@keyframes ngdialog-flyout{0%{opacity:1;-webkit-transform:translateY(0);transform:translateY(0)}100%{opacity:0;-webkit-transform:translateY(-40px);transform:translateY(-40px)}}.ngdialog.ngdialog-theme-default{padding-bottom:160px;padding-top:160px}.ngdialog.ngdialog-theme-default.ngdialog-closing .ngdialog-content{-webkit-animation:ngdialog-flyout .5s;animation:ngdialog-flyout .5s}.ngdialog.ngdialog-theme-default .ngdialog-content{-webkit-animation:ngdialog-flyin .5s;animation:ngdialog-flyin .5s;background:#f0f0f0;border-radius:5px;color:#444;font-family:Helvetica,sans-serif;font-size:1.1em;line-height:1.5em;margin:0 auto;max-width:100%;padding:1em;position:relative;width:450px}.ngdialog.ngdialog-theme-default .ngdialog-close{border-radius:5px;cursor:pointer;position:absolute;right:0;top:0}.ngdialog.ngdialog-theme-default .ngdialog-close:before{background:0 0;border-radius:3px;color:#bbb;content:'\00D7';font-size:26px;font-weight:400;height:30px;line-height:26px;position:absolute;right:3px;text-align:center;top:3px;width:30px}.ngdialog.ngdialog-theme-default .ngdialog-close:active:before,.ngdialog.ngdialog-theme-default .ngdialog-close:hover:before{color:#777}.ngdialog.ngdialog-theme-default .ngdialog-message{margin-bottom:.5em}.ngdialog.ngdialog-theme-default .ngdialog-input{margin-bottom:1em}.ngdialog.ngdialog-theme-default .ngdialog-input input[type=text],.ngdialog.ngdialog-theme-default .ngdialog-input input[type=password],.ngdialog.ngdialog-theme-default .ngdialog-input input[type=email],.ngdialog.ngdialog-theme-default .ngdialog-input input[type=url],.ngdialog.ngdialog-theme-default .ngdialog-input textarea{background:#fff;border:0;border-radius:3px;font-family:inherit;font-size:inherit;font-weight:inherit;margin:0 0 .25em;min-height:2.5em;padding:.25em .67em;width:100%}.ngdialog.ngdialog-theme-default .ngdialog-input input[type=text]:focus,.ngdialog.ngdialog-theme-default .ngdialog-input input[type=password]:focus,.ngdialog.ngdialog-theme-default .ngdialog-input input[type=email]:focus,.ngdialog.ngdialog-theme-default .ngdialog-input input[type=url]:focus,.ngdialog.ngdialog-theme-default .ngdialog-input textarea:focus{box-shadow:inset 0 0 0 2px #8dbdf1;outline:0}.ngdialog.ngdialog-theme-default .ngdialog-buttons:after{content:'';display:table;clear:both}.ngdialog.ngdialog-theme-default .ngdialog-button{border:0;border-radius:3px;cursor:pointer;float:right;font-family:inherit;font-size:.8em;letter-spacing:.1em;line-height:1em;margin:0 0 0 .5em;padding:.75em 2em;text-transform:uppercase}.ngdialog.ngdialog-theme-default .ngdialog-button:focus{-webkit-animation:ngdialog-pulse 1.1s infinite;animation:ngdialog-pulse 1.1s infinite;outline:0}.btn:active,.btn:focus,.selectize-input>input:focus{outline:0!important}@media (max-width:568px){.ngdialog.ngdialog-theme-default .ngdialog-button:focus{-webkit-animation:none;animation:none}}.ngdialog.ngdialog-theme-default .ngdialog-button.ngdialog-button-primary{background:#3288e6;color:#fff}.ngdialog.ngdialog-theme-default .ngdialog-button.ngdialog-button-secondary{background:#e0e0e0;color:#777}.datetimepicker{border-radius:4px;direction:ltr;display:block;margin-top:1px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;width:320px}.datetimepicker>div{display:none}.datetimepicker .hour,.datetimepicker .minute{height:34px;line-height:34px;margin:0;width:25%}.datetimepicker .table{margin:0}.datetimepicker .table td,.datetimepicker .table th{border:0;border-radius:4px;height:20px;text-align:center}.datetimepicker .day:hover,.datetimepicker .hour:hover,.datetimepicker .left:hover,.datetimepicker .minute:hover,.datetimepicker .right:hover,.datetimepicker .switch:hover{background:#eee;cursor:pointer}.datetimepicker .disabled,.datetimepicker .disabled:hover{background:0 0;color:#ebebeb;cursor:default}.datetimepicker .active,.datetimepicker .active.disabled,.datetimepicker .active.disabled:hover,.datetimepicker .active:hover{background-color:#04c;background-image:linear-gradient(to bottom,#08c,#04c);background-repeat:repeat-x;border-color:#04c #04c #002a80;color:#fff;filter:progid:dximagetransform.microsoft.gradient(startColorstr='#08c', endColorstr='#04c', GradientType=0);text-shadow:0 -1px 0 rgba(0,0,0,.25)}.datetimepicker .current,.datetimepicker .current.disabled,.datetimepicker .current.disabled:hover,.datetimepicker .current:hover{background-color:#e5e5e5}.datetimepicker .active.active,.datetimepicker .active.disabled,.datetimepicker .active.disabled.active,.datetimepicker .active.disabled.disabled,.datetimepicker .active.disabled:active,.datetimepicker .active.disabled:hover,.datetimepicker .active.disabled:hover.active,.datetimepicker .active.disabled:hover.disabled,.datetimepicker .active.disabled:hover:active,.datetimepicker .active.disabled:hover:hover,.datetimepicker .active:active,.datetimepicker .active:hover,.datetimepicker .active:hover.active,.datetimepicker .active:hover.disabled,.datetimepicker .active:hover:active,.datetimepicker .active:hover:hover,.datetimepicker span.active.disabled:hover[disabled],.datetimepicker span.active.disabled[disabled],.datetimepicker span.active:hover[disabled],.datetimepicker span.active[disabled],.datetimepicker td.active.disabled:hover[disabled],.datetimepicker td.active.disabled[disabled],.datetimepicker td.active:hover[disabled],.datetimepicker td.active[disabled]{background-color:#04c}.datetimepicker span{border-radius:4px;cursor:pointer;display:block;float:left;height:54px;line-height:54px;margin:1%;width:23%}.datetimepicker span:hover{background:#eee}.datetimepicker .future,.datetimepicker .past{color:#999}.ui-notification{position:fixed;z-index:9999;width:300px;-webkit-transition:all ease .5s;-o-transition:all ease .5s;transition:all ease .5s;color:#fff;border-radius:0;background:#337ab7;box-shadow:5px 5px 10px rgba(0,0,0,.3)}.ui-notification.clickable{cursor:pointer}.ui-notification.clickable:hover{opacity:.7}.ui-notification.killed{-webkit-transition:opacity ease 1s;-o-transition:opacity ease 1s;transition:opacity ease 1s;opacity:0}.ui-notification>h3{font-size:14px;font-weight:700;display:block;margin:10px 10px 0;padding:0 0 5px;text-align:left;border-bottom:1px solid rgba(255,255,255,.3)}.ui-notification a{color:#fff}.ui-notification a:hover{text-decoration:underline}.ui-notification>.message{margin:10px}.ui-notification.warning{color:#fff;background:#f0ad4e}.ui-notification.error{color:#fff;background:#d9534f}.ui-notification.success{color:#fff;background:#5cb85c}.ui-notification.info{color:#fff;background:#5bc0de}table.rz-table{table-layout:fixed;border-collapse:collapse}table.rz-table th{position:relative;min-width:25px}table.rz-table th .rz-handle{width:10px;height:100%;position:absolute;top:0;right:0;cursor:ew-resize!important}table.rz-table th .rz-handle.rz-handle-active{border-right:1px dotted #000}.selectize-control.plugin-drag_drop.multi>.selectize-input>div.ui-sortable-placeholder{visibility:visible!important;background:#f2f2f2!important;background:rgba(0,0,0,.06)!important;border:0!important;-webkit-box-shadow:inset 0 0 12px 4px #fff;box-shadow:inset 0 0 12px 4px #fff}.selectize-control.plugin-drag_drop .ui-sortable-placeholder::after{content:'!';visibility:hidden}.selectize-control.plugin-drag_drop .ui-sortable-helper{-webkit-box-shadow:0 2px 5px rgba(0,0,0,.2);box-shadow:0 2px 5px rgba(0,0,0,.2)}.selectize-dropdown-header{position:relative;padding:5px 8px;border-bottom:1px solid #d0d0d0;background:#f8f8f8;-webkit-border-radius:3px 3px 0 0;-moz-border-radius:3px 3px 0 0;border-radius:3px 3px 0 0}.selectize-dropdown-header-close{position:absolute;right:8px;top:50%;color:#303030;opacity:.4;margin-top:-12px;line-height:20px;font-size:20px!important}.selectize-dropdown-header-close:hover{color:#000}.selectize-dropdown.plugin-optgroup_columns .optgroup{border-right:1px solid #f2f2f2;border-top:0 none;float:left;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.selectize-control.plugin-remove_button [data-value] .remove,.selectize-input{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;display:inline-block}.selectize-dropdown.plugin-optgroup_columns .optgroup:last-child{border-right:0 none}.selectize-dropdown.plugin-optgroup_columns .optgroup:before{display:none}.selectize-dropdown.plugin-optgroup_columns .optgroup-header{border-top:0 none}.selectize-control.plugin-remove_button [data-value]{position:relative;padding-right:24px!important}.selectize-control.plugin-remove_button [data-value] .remove{z-index:1;position:absolute;top:0;right:0;bottom:0;width:17px;text-align:center;font-weight:700;font-size:12px;color:inherit;text-decoration:none;vertical-align:middle;padding:2px 0 0;border-left:1px solid #d0d0d0;-webkit-border-radius:0 2px 2px 0;-moz-border-radius:0 2px 2px 0;border-radius:0 2px 2px 0;box-sizing:border-box}.selectize-control.plugin-remove_button [data-value] .remove:hover{background:rgba(0,0,0,.05)}.selectize-control.plugin-remove_button [data-value].active .remove{border-left-color:#cacaca}.selectize-control.plugin-remove_button .disabled [data-value] .remove:hover{background:0 0}.selectize-control.plugin-remove_button .disabled [data-value] .remove{border-left-color:#fff}.selectize-control.plugin-remove_button .remove-single{position:absolute;right:0;top:0;font-size:23px}.selectize-control,.selectize-input{position:relative}.selectize-dropdown,.selectize-input,.selectize-input input{color:#303030;font-family:inherit;font-size:13px;line-height:18px;-webkit-font-smoothing:inherit}.selectize-control.single .selectize-input.input-active,.selectize-input{background:#fff;cursor:text;display:inline-block}.selectize-input{border:1px solid #d0d0d0;padding:8px;width:100%;overflow:hidden;z-index:1;box-sizing:border-box;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.1);box-shadow:inset 0 1px 1px rgba(0,0,0,.1);-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.selectize-control.multi .selectize-input.has-items{padding:6px 8px 3px}.selectize-input.full{background-color:#fff}.selectize-input.disabled,.selectize-input.disabled *{cursor:default!important}.selectize-input.focus{-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.15);box-shadow:inset 0 1px 2px rgba(0,0,0,.15)}.selectize-input.dropdown-active{-webkit-border-radius:3px 3px 0 0;-moz-border-radius:3px 3px 0 0;border-radius:3px 3px 0 0}.selectize-input>*{vertical-align:baseline;display:-moz-inline-stack;display:inline-block;zoom:1}.selectize-control.multi .selectize-input>div{cursor:pointer;margin:0 3px 3px 0;padding:2px 6px;background:#f2f2f2;color:#303030;border:0 solid #d0d0d0}.selectize-control.multi .selectize-input>div.active{background:#e8e8e8;color:#303030;border:0 solid #cacaca}.selectize-control.multi .selectize-input.disabled>div,.selectize-control.multi .selectize-input.disabled>div.active{color:#7d7d7d;background:#fff;border:0 solid #fff}.selectize-input>input{display:inline-block!important;padding:0!important;min-height:0!important;max-height:none!important;max-width:100%!important;margin:0 2px 0 0!important;text-indent:0!important;border:0!important;background:0 0!important;line-height:inherit!important;-webkit-user-select:auto!important;-webkit-box-shadow:none!important;box-shadow:none!important}.selectize-input>input::-ms-clear{display:none}.selectize-input::after{content:' ';display:block;clear:left}.selectize-input.dropdown-active::before{content:' ';display:block;position:absolute;background:#f0f0f0;height:1px;bottom:0;left:0;right:0}.selectize-dropdown{position:absolute;z-index:10;border:1px solid #d0d0d0;background:#fff;margin:-1px 0 0;border-top:0 none;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;-webkit-box-shadow:0 1px 3px rgba(0,0,0,.1);box-shadow:0 1px 3px rgba(0,0,0,.1);-webkit-border-radius:0 0 3px 3px;-moz-border-radius:0 0 3px 3px;border-radius:0 0 3px 3px}.selectize-dropdown [data-selectable]{cursor:pointer;overflow:hidden}.selectize-dropdown [data-selectable] .highlight{background:rgba(125,168,208,.2);-webkit-border-radius:1px;-moz-border-radius:1px;border-radius:1px}.selectize-dropdown .optgroup-header,.selectize-dropdown .option{padding:5px 8px}.selectize-dropdown .option,.selectize-dropdown [data-disabled],.selectize-dropdown [data-disabled] [data-selectable].option{cursor:inherit;opacity:.5}.selectize-dropdown [data-selectable].option{opacity:1}.selectize-dropdown .optgroup:first-child .optgroup-header{border-top:0 none}.selectize-dropdown .optgroup-header{color:#303030;background:#fff;cursor:default}.selectize-dropdown .active{background-color:#f5fafd;color:#495c68}.selectize-dropdown .active.create{color:#495c68}.selectize-dropdown .create{color:rgba(48,48,48,.5)}.selectize-dropdown-content{overflow-y:auto;overflow-x:hidden;max-height:200px;-webkit-overflow-scrolling:touch}.selectize-control.single .selectize-input,.selectize-control.single .selectize-input input{cursor:pointer}.selectize-control.single .selectize-input.input-active,.selectize-control.single .selectize-input.input-active input{cursor:text}.selectize-control.single .selectize-input:after{content:' ';display:block;position:absolute;top:50%;right:15px;margin-top:-3px;width:0;height:0;border-style:solid;border-width:5px 5px 0;border-color:grey transparent transparent}.selectize-control.single .selectize-input.dropdown-active:after{margin-top:-4px;border-width:0 5px 5px;border-color:transparent transparent grey}.selectize-control.rtl.single .selectize-input:after{left:15px;right:auto}.selectize-control.rtl .selectize-input>input{margin:0 4px 0 -2px!important}.selectize-control .selectize-input.disabled{opacity:.5;background-color:#fafafa}/*! * Start Bootstrap - SB Admin 2 Bootstrap Admin Theme (http://startbootstrap.com) * Code licensed under the Apache License v2.0. * For details, see http://www.apache.org/licenses/LICENSE-2.0. */body{background-color:#f8f8f8}.example{padding:.625rem 1.825rem .625rem 2.5rem;border:1px dashed #ccc;position:relative;margin:0 0 .625rem;background-color:#fff}dl dd,dl dt{line-height:1.25rem}dl dt{font-style:normal;font-weight:700}dl dd{margin-left:.9375rem}dl.horizontal dt{float:left;width:10rem;overflow:hidden;clear:left;text-align:right;text-overflow:ellipsis;white-space:nowrap}dl.horizontal dd{margin-left:11.25rem}#wrapper{width:100%}#page-wrapper{min-height:568px;background-color:#fff}@media(min-width:768px){#page-wrapper{position:inherit;margin:0 0 0 250px;padding:0 30px;border-left:1px solid #e7e7e7}}.navbar-top-links{margin-right:0}.navbar-top-links li{display:inline-block}.flot-chart,.navbar-top-links .dropdown-menu li{display:block}.navbar-top-links li:last-child{margin-right:15px}.navbar-top-links li a{padding:15px;min-height:50px}.navbar-top-links .dropdown-menu li:last-child{margin-right:0}.navbar-top-links .dropdown-menu li a{padding:3px 20px;min-height:0}.navbar-top-links .dropdown-menu li a div{white-space:normal}.navbar-top-links .dropdown-alerts,.navbar-top-links .dropdown-messages,.navbar-top-links .dropdown-tasks{width:310px;min-width:0}.navbar-top-links .dropdown-messages{margin-left:5px}.navbar-top-links .dropdown-tasks{margin-left:-59px}.navbar-top-links .dropdown-alerts{margin-left:-123px}.navbar-top-links .dropdown-user{right:0;left:auto}.sidebar .sidebar-search{padding:15px}.sidebar ul li{border-bottom:1px solid #e7e7e7}.sidebar ul li a.active{background-color:#fff;color:#fff}.sidebar .arrow{float:right}.sidebar .fa.arrow:before{content:"\f104"}.sidebar .active>a>.fa.arrow:before{content:"\f107"}.sidebar .nav-second-level li,.sidebar .nav-third-level li{border-bottom:0!important}.sidebar .nav-second-level li a{padding-left:37px}.sidebar .nav-third-level li a{padding-left:52px}@media(min-width:768px){.sidebar{z-index:1;position:absolute;width:250px;margin-top:51px}.navbar-top-links .dropdown-alerts,.navbar-top-links .dropdown-messages,.navbar-top-links .dropdown-tasks{margin-left:auto}}.btn-outline{color:inherit;background-color:transparent;transition:all .5s}.btn-primary.btn-outline{color:#428bca}.btn-success.btn-outline{color:#5cb85c}.btn-info.btn-outline{color:#5bc0de}.btn-warning.btn-outline{color:#f0ad4e}.btn-danger.btn-outline{color:#d9534f}.btn-danger.btn-outline:hover,.btn-info.btn-outline:hover,.btn-primary.btn-outline:hover,.btn-success.btn-outline:hover,.btn-warning.btn-outline:hover{color:#fff}.chat{margin:0;padding:0}.chat li{margin-bottom:10px;padding-bottom:5px;border-bottom:1px dotted #999}.chat li.left .chat-body{margin-left:60px}.chat li.right .chat-body{margin-right:60px}.chat li .chat-body p{margin:0}.chat .glyphicon,.panel .slidedown .glyphicon{margin-right:5px}.chat-panel .panel-body{height:350px;overflow-y:scroll}.login-panel{margin-top:25%}.flot-chart{height:400px}.flot-chart-content{width:100%;height:100%}.dataTables_wrapper{position:relative;clear:both}table.dataTable thead .sorting,table.dataTable thead .sorting_asc,table.dataTable thead .sorting_asc_disabled,table.dataTable thead .sorting_desc,table.dataTable thead .sorting_desc_disabled{background:0 0}table.dataTable thead .sorting_asc:after{content:"\f0de";float:right;font-family:fontawesome}table.dataTable thead .sorting_desc:after{content:"\f0dd";float:right;font-family:fontawesome}table.dataTable thead .sorting:after{content:"\f0dc";float:right;font-family:fontawesome;color:rgba(50,50,50,.5)}.btn-circle{width:30px;height:30px;padding:6px 0;border-radius:15px;text-align:center;font-size:12px;line-height:1.428571429}.btn-circle.btn-lg{width:50px;height:50px;padding:10px 16px;border-radius:25px;font-size:18px;line-height:1.33}.btn-circle.btn-xl{width:70px;height:70px;padding:10px 16px;border-radius:35px;font-size:24px;line-height:1.33}.show-grid [class^=col-]{padding-top:10px;padding-bottom:10px;border:1px solid #ddd;background-color:#eee!important}.show-grid{margin:15px 0}.huge{font-size:40px}.panel-green{border-color:#5cb85c}.panel-green .panel-heading{border-color:#5cb85c;color:#fff;background-color:#5cb85c}.panel-green a{color:#5cb85c}.panel-green a:hover{color:#3d8b3d}.panel-red{border-color:#d9534f}.panel-red .panel-heading{border-color:#d9534f;color:#fff;background-color:#d9534f}.panel-red a{color:#d9534f}.panel-red a:hover{color:#b52b27}.panel-yellow{border-color:#f0ad4e}.panel-yellow .panel-heading{border-color:#f0ad4e;color:#fff;background-color:#f0ad4e}.panel-yellow a{color:#f0ad4e}.panel-yellow a:hover{color:#df8a13}.timeline{position:relative;padding:20px 0}.timeline:before{content:" ";position:absolute;top:0;bottom:0;left:50%;width:3px;margin-left:-1.5px;background-color:#eee}.timeline>li{position:relative;margin-bottom:20px}.timeline>li:after,.timeline>li:before{content:" ";display:table}.timeline>li:after{clear:both}.timeline>li>.timeline-panel{float:left;position:relative;width:46%;padding:20px;border:1px solid #d4d4d4;border-radius:2px;-webkit-box-shadow:0 1px 6px rgba(0,0,0,.175);box-shadow:0 1px 6px rgba(0,0,0,.175)}.timeline>li>.timeline-panel:before{content:" ";display:inline-block;position:absolute;top:26px;right:-15px;border-top:15px solid transparent;border-right:0 solid #ccc;border-bottom:15px solid transparent;border-left:15px solid #ccc}.timeline>li>.timeline-panel:after{content:" ";display:inline-block;position:absolute;top:27px;right:-14px;border-top:14px solid transparent;border-right:0 solid #fff;border-bottom:14px solid transparent;border-left:14px solid #fff}.timeline>li>.timeline-badge{z-index:100;position:absolute;top:16px;left:50%;width:50px;height:50px;margin-left:-25px;border-radius:50%;text-align:center;font-size:1.4em;line-height:50px;color:#fff;background-color:#999}.timeline>li.timeline-inverted>.timeline-panel{float:right}.timeline>li.timeline-inverted>.timeline-panel:before{right:auto;left:-15px;border-right-width:15px;border-left-width:0}.timeline>li.timeline-inverted>.timeline-panel:after{right:auto;left:-14px;border-right-width:14px;border-left-width:0}.timeline-badge.primary{background-color:#2e6da4!important}.timeline-badge.success{background-color:#3f903f!important}.timeline-badge.warning{background-color:#f0ad4e!important}.timeline-badge.danger{background-color:#d9534f!important}.timeline-badge.info{background-color:#5bc0de!important}.timeline-title{margin-top:0;color:inherit}.timeline-body>p,.timeline-body>ul{margin-bottom:0}.timeline-body>p+p{margin-top:5px}@media(max-width:767px){ul.timeline:before{left:40px}ul.timeline>li>.timeline-panel{width:calc(100% - 90px);width:-moz-calc(100% - 90px);width:-webkit-calc(100% - 90px);float:right}ul.timeline>li>.timeline-badge{top:16px;left:15px;margin-left:0}ul.timeline>li>.timeline-panel:before{right:auto;left:-15px;border-right-width:15px;border-left-width:0}ul.timeline>li>.timeline-panel:after{right:auto;left:-14px;border-right-width:14px;border-left-width:0}}.header,.jumbotron{border-bottom:1px solid #e5e5e5}.btn{height:32px}.width-200{max-width:200px}.width-300,.witdh-300{max-width:300px}body{padding:0}.footer,.header,.marketing{padding-left:15px;padding-right:15px}.header{margin-bottom:10px}.header h3{margin-top:0;margin-bottom:0;line-height:40px;padding-bottom:19px}.card .detail,.card .detail-brand{line-height:98px;text-align:center}.footer{padding-top:19px;color:#777;border-top:1px solid #e5e5e5}.container-narrow>hr{margin:30px 0}.jumbotron{text-align:center}.jumbotron .btn{font-size:21px;padding:14px 24px}.marketing{margin:40px 0}.marketing p+h4{margin-top:28px}@media screen and (min-width:768px){.container{width:inherit;margin-left:60px;margin-right:5px}.footer,.header,.marketing{padding-left:0;padding-right:0}.header{margin-bottom:30px}.jumbotron{border-bottom:0}}.navbar-inverse .navbar-nav>li>a{color:#b0ddce;font-size:15px}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:focus,.navbar-inverse .navbar-nav>.open>a:hover{background-color:#1b926c}@media (min-width:900px){.navbar-right,.navbar-right~.navbar-right{margin-right:0}.navbar-left{float:left!important}.navbar-right{float:right!important}}.dropdown-menu{min-width:100px!important}.nav-sidebar li.active a{background:#DDD}.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{background:#1d9d74;color:#fff}.broadcast-message,.broadcast-message-preview{padding:10px;text-align:center;background:#555;color:#BBB;margin-top:50px}.card{position:relative;border:1px solid #d9d9d9;color:#666;background-color:#fff;width:100%;border-radius:5px}.card .card-header,.tools-header{border-top-left-radius:4px;border-top-right-radius:4px}.card .card-header{padding:9px 0;height:40px;background:#555;color:#fff;text-align:center}.card .card-body{padding:12px 10px}.card .card-footer{height:20px;font-size:10px;color:#777;margin:-15px 20px 5px}.card .detail-brand{float:left;width:30%;font-size:30px;color:#fff}.card .default{background:#1d9d74}.card .info{background:#6EBEE7}.card .warn{background:#ED7F54}.card .danger{background:#6583BE}.card .detail .text-default{color:#1d9d74}.card .detail .text-info{color:#6EBEE7}.card .detail .text-warn{color:#ED7F54}.card .detail .text-danger{color:#6583BE}.card .detail{float:right;width:70%}.card .detail .text{font-size:12px}.card .detail .number{font-size:30px;font-weight:500}.h100{height:100px}.inline{display:inline}.separator{height:1px;background-color:#e5e5e5;margin-top:10px}.card>.card-body>table>tbody>tr>td,.card>.card-body>table>thead>tr>td{word-wrap:break-word;word-break:break-all}.card>.card-body>table>thead>tr>td{font-weight:500;font-size:13px;text-align:center}.card>.card-body>table>thead>tr>td>span{font-weight:500;font-size:10px}.card>.card-body>table>tbody>tr>td{font-size:12px;text-align:center}.card>.card-body>table>tbody>tr>td>a{color:#666}.thumbnails>.card>.card-body>table>tbody>tr>td,.thumbnails>.card>.card-body>table>thead>tr>td{font-size:12px;color:#777;word-wrap:break-word;word-break:break-all}.thumbnails>.card>.card-body>table>thead>tr>td:nth-child(n+2){text-align:center}.thumbnails>.card>.card-body>table>tbody>tr>td:nth-child(n+2){font-weight:700;text-align:center}.thumbnails>.card>.card-body>table>tbody>tr>td:nth-child(1),.thumbnails>.card>.card-body>table>thead>tr>td:nth-child(1){text-align:left}.tools-header{background:#f5f5f5;padding:9px 0;height:40px}.tools-header .brand{font-size:13px;margin:2px 10px;font-weight:700;float:left}.tools-header .brand>a{color:#666}.tools-header>a,.tools-header>button,.tools-header>select{float:right;max-width:80px;margin:1px 10px;height:25px;padding:0 10px;line-height:25px;color:#666}.tools-header .paged{margin-right:0}.btn.btn-danger-tag{color:#fff;background-color:#d9534f;border-color:#d43f3a;line-height:1px;font-size:11px;padding:4px}.btn.btn-danger{color:#333;background-color:#fff;border-color:#ccc}.btn.btn-danger:active,.btn.btn-danger:focus,.btn.btn-danger:hover{color:#d9534f;border-color:#d9534f;background:#fff}.form-control{height:32px}.input-label:before{display:inline-block;content:"*";color:#f44336;font-family:SimSun;font-size:12px;-webkit-transform:TranslateX(-10px);-ms-transform:TranslateX(-10px);transform:TranslateX(-10px)}.badge-main,.label.label-main{color:#fff;background-color:#1d9d74;border-color:#1d9d74}.bootstrap-tagsinput{background-color:#fff;border:1px solid #ccc;box-shadow:inset 0 1px 1px rgba(0,0,0,.075);display:inline-block;padding:4px 6px;color:#555;vertical-align:middle;border-radius:4px;width:85%;height:100px;line-height:20px;cursor:text}.bootstrap-tagsinput>.dropdown-menu{min-width:40px;font-size:12px}.bootstrap-tagsinput>.dropdown-menu>.active>a,.bootstrap-tagsinput>.dropdown-menu>.active>a:focus,.bootstrap-tagsinput>.dropdown-menu>.active>a:hover{background-image:-webkit-linear-gradient(top,#1d9d74 0,#1d9d74 100%);background-image:-o-linear-gradient(top,#1d9d74 0,#1d9d74 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#1d9d74),to(#1d9d74));background-image:linear-gradient(to bottom,#1d9d74 0,#1d9d74 100%);filter:progid: DXImageTransform.Microsoft.gradient(startColorstr='#1d9d74', endColorstr='#1d9d74', GradientType=0);background-repeat:repeat-x;color:#fff;text-decoration:none;outline:0;background-color:#1d9d74}.inputs-header{padding:9px 0;height:50px;border-top-left-radius:4px;border-top-right-radius:4px}.inputs-header .brand{font-size:13px;margin:2px 10px;font-weight:700;float:left}.inputs-header .brand>a{color:#666}.inputs-header>input{float:right;margin:1px 10px;height:30px;padding:0 10px;color:#666}.inputs-header>a{float:right;margin:1px 10px;height:30px;padding:5 5px}.inputs-header>select{float:right;max-width:80px;margin:1px 10px;padding:0 10px;color:#666;height:25px;font-size:12px}.witdh-150{max-width:150px}.witdh-200{max-width:200px}.card.highlight{border-color:#d9534f}.card .pagination-footer{height:40px;font-size:10px;color:#777;margin:-15px 20px 5px}.card .pagination-footer .tools{font-size:12px;margin:11px 20px 11px 0;float:right;display:inline}.card>.pagination-footer>.tools>span>input{height:25px;max-width:50px;display:inline}.pagination{display:inline-block;padding-left:0;margin:8px 0;float:right;border-radius:4px}.pagination>a{margin-right:5px;height:28px;width:28px;padding:5px 0}.datepicker>.table>tbody>tr>td,.datepicker>.table>thead>tr>td,.timepicker>.table>tbody>tr>td,.timepicker>.table>thead>tr>td{padding:5px 3px}.datepicker>.table>tbody>tr>td>.btn,.datepicker>.table>thead>tr>td>.btn,.timepicker>.table>tbody>tr>td>.btn,.timepicker>.table>thead>tr>td>.btn{border:1px solid #FFFDFD}.datepicker>.table>tbody>tr>td>.btn-default:active,.datepicker>.table>tbody>tr>td>.btn-default:focus,.datepicker>.table>tbody>tr>td>.btn-default:hover,.datepicker>.table>thead>tr>td>.btn-default:active,.datepicker>.table>thead>tr>td>.btn-default:focus,.datepicker>.table>thead>tr>td>.btn-default:hover,.timepicker>.table>tbody>tr>td>.btn-default:active,.timepicker>.table>tbody>tr>td>.btn-default:focus,.timepicker>.table>tbody>tr>td>.btn-default:hover,.timepicker>.table>thead>tr>td>.btn-default:active,.timepicker>.table>thead>tr>td>.btn-default:focus,.timepicker>.table>thead>tr>td>.btn-default:hover{color:#1d9d74;border-color:#1d9d74;background:#fff}.datepicker>.table>tbody>tr>td>a,.datepicker>.table>thead>tr>td>a,.timepicker>.table>tbody>tr>td>a,.timepicker>.table>thead>tr>td>a{height:25px;width:25px;padding:3px 0}.datepicker>.table>tbody>tr:first-child>td>a{padding:4px 0}.datepicker>.table>tbody>tr>td>a.btn.active,.datepicker>.table>thead>tr>td>a.btn.active,.timepicker>.table>tbody>tr>td>a.btn.active,.timepicker>.table>thead>tr>td>a.btn.active{color:#1d9d74;border-color:#1d9d74;background:#fff;box-shadow:inset 0 0 0 rgba(0,0,0,.125)}.datepicker>.table>thead>tr>td:not(:first-child):last-child>a,.timepicker>.table>thead>tr>td:not(:first-child):last-child>a{height:25px;width:50px;padding:5px 0}.datepicker>.table>tbody>tr>td>a,.timepicker>.table>tbody>tr>td>a{margin-left:8px}.sortorder:after{content:'\25b2'}.sortorder.reverse:after{content:'\25bc'}.input-control select{-moz-appearance:none;-webkit-appearance:none;appearance:none;position:relative;border:1px solid #d9d9d9;width:100%;height:100%;padding:.3125rem;z-index:0}.navbar-inverse{background-color:#337ab7;border-color:#337ab7}.sidebar{z-index:1;width:220px;top:0;left:0;height:100%}#page-wrapper{position:inherit;margin:70px 0 0 220px;padding:12px 30px;border-left:0 solid #e7e7e7}.sidebar .sidebar-nav.navbar-collapse{background-color:#F5F5F5;position:relative;color:#000;width:100%;padding:0;margin:0;list-style:none inside}.sidebar a{color:#555}.sidebar ul li:hover{color:red}.form-control{border-radius:8px}.form-control:focus,.highlight-border{border-color:#337ab7;box-shadow:0 0 0 rgba(0,0,0,.075) inset,0 0 0 rgba(29,157,116,1)}.btn-outline-primary.focus,.btn-outline-primary:focus,.btn-outline-primary:not(:disabled):not(.disabled).active:focus,.btn-outline-primary:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-primary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.browsehappy{margin:.2em 0;background:#ccc;color:#000;padding:.2em 0}.btn.btn-main{color:#fff;background-color:#337ab7;border-color:#337ab7}.btn-default-inverse,.btn-default-inverse:focus,.btn-default-inverse:hover,.btn-default:active{color:#337ab7;border-color:#337ab7;background:#fff}.btn-danger-inverse,.btn-danger-inverse:focus,.btn-danger-inverse:hover,.btn-danger:active{color:#d9534f;border-color:#d9534f;background:#fff}.btn-tab-active,.btn-tab-active:focus,.btn-tab-active:hover,.btn-tab-default:active,.btn-tab-default:focus,.btn-tab-default:hover{color:#337ab7;border-color:#337ab7;background:#fff;font-weight:600}.btn-tab-default{color:#777;background:#fff;font-weight:600}.pagination>.btn.active{color:#fff;background-color:#337ab7;border-color:#337ab7}.btn-default:active,.btn-default:focus,.btn-default:hover{color:#337ab7;border-color:#337ab7;background:#fff}.bootstrap-switch.bootstrap-switch-on{border-color:#337ab7}.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-success,.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-success{color:#fff;background:#337ab7}.selectize-input-200>.selectize-input{min-width:200px;border-color:#337ab7}.btn-outline-primary{color:#007bff;background-color:transparent;background-image:none;border-color:#007bff}.btn-outline-primary:hover{color:#fff;background-color:#007bff;border-color:#007bff}.btn-outline-primary.disabled,.btn-outline-primary:disabled{color:#007bff;background-color:transparent}.btn-outline-primary:not(:disabled):not(.disabled).active,.btn-outline-primary:not(:disabled):not(.disabled):active,.show>.btn-outline-primary.dropdown-toggle{color:#fff;background-color:#007bff;border-color:#007bff}.btn-outline-secondary.focus,.btn-outline-secondary:focus,.btn-outline-secondary:not(:disabled):not(.disabled).active:focus,.btn-outline-secondary:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.btn-outline-secondary{color:#6c757d;background-color:transparent;background-image:none;border-color:#6c757d}.btn-outline-secondary:hover{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-outline-secondary.disabled,.btn-outline-secondary:disabled{color:#6c757d;background-color:transparent}.btn-outline-secondary:not(:disabled):not(.disabled).active,.btn-outline-secondary:not(:disabled):not(.disabled):active,.show>.btn-outline-secondary.dropdown-toggle{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-outline-success.focus,.btn-outline-success:focus,.btn-outline-success:not(:disabled):not(.disabled).active:focus,.btn-outline-success:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-success.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-outline-success{color:#28a745;background-color:transparent;background-image:none;border-color:#28a745}.btn-outline-success:hover{color:#fff;background-color:#28a745;border-color:#28a745}.btn-outline-success.disabled,.btn-outline-success:disabled{color:#28a745;background-color:transparent}.btn-outline-success:not(:disabled):not(.disabled).active,.btn-outline-success:not(:disabled):not(.disabled):active,.show>.btn-outline-success.dropdown-toggle{color:#fff;background-color:#28a745;border-color:#28a745}.btn-outline-info.focus,.btn-outline-info:focus,.btn-outline-info:not(:disabled):not(.disabled).active:focus,.btn-outline-info:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-info.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-outline-info{color:#17a2b8;background-color:transparent;background-image:none;border-color:#17a2b8}.btn-outline-info:hover{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-outline-info.disabled,.btn-outline-info:disabled{color:#17a2b8;background-color:transparent}.btn-outline-info:not(:disabled):not(.disabled).active,.btn-outline-info:not(:disabled):not(.disabled):active,.show>.btn-outline-info.dropdown-toggle{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-outline-warning.focus,.btn-outline-warning:focus,.btn-outline-warning:not(:disabled):not(.disabled).active:focus,.btn-outline-warning:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-warning.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-outline-warning{color:#ffc107;background-color:transparent;background-image:none;border-color:#ffc107}.btn-outline-warning:hover{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-outline-warning.disabled,.btn-outline-warning:disabled{color:#ffc107;background-color:transparent}.btn-outline-warning:not(:disabled):not(.disabled).active,.btn-outline-warning:not(:disabled):not(.disabled):active,.show>.btn-outline-warning.dropdown-toggle{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-outline-danger.focus,.btn-outline-danger:focus,.btn-outline-danger:not(:disabled):not(.disabled).active:focus,.btn-outline-danger:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-danger.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-outline-danger{color:#dc3545;background-color:transparent;background-image:none;border-color:#dc3545}.btn-outline-danger:hover{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-outline-danger.disabled,.btn-outline-danger:disabled{color:#dc3545;background-color:transparent}.btn-outline-danger:not(:disabled):not(.disabled).active,.btn-outline-danger:not(:disabled):not(.disabled):active,.show>.btn-outline-danger.dropdown-toggle{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-outline-light.focus,.btn-outline-light:focus,.btn-outline-light:not(:disabled):not(.disabled).active:focus,.btn-outline-light:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-light.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-outline-light{color:#f8f9fa;background-color:transparent;background-image:none;border-color:#f8f9fa}.btn-outline-light:hover{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light.disabled,.btn-outline-light:disabled{color:#f8f9fa;background-color:transparent}.btn-outline-light:not(:disabled):not(.disabled).active,.btn-outline-light:not(:disabled):not(.disabled):active,.show>.btn-outline-light.dropdown-toggle{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-outline-dark.focus,.btn-outline-dark:focus,.btn-outline-dark:not(:disabled):not(.disabled).active:focus,.btn-outline-dark:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-dark.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-outline-dark{color:#343a40;background-color:transparent;background-image:none;border-color:#343a40}.btn-outline-dark:hover{color:#fff;background-color:#343a40;border-color:#343a40}.btn-outline-dark.disabled,.btn-outline-dark:disabled{color:#343a40;background-color:transparent}.btn-outline-dark:not(:disabled):not(.disabled).active,.btn-outline-dark:not(:disabled):not(.disabled):active,.show>.btn-outline-dark.dropdown-toggle{color:#fff;background-color:#343a40;border-color:#343a40} ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/dist/js/app.js ================================================ "use strict";var app;angular.module("sentinelDashboardApp",["oc.lazyLoad","ui.router","ui.bootstrap","angular-loading-bar","ngDialog","ui.bootstrap.datetimepicker","ui-notification","rzTable","angular-clipboard","selectize","angularUtils.directives.dirPagination"]).factory("AuthInterceptor",["$window","$state",function(r,t){return{responseError:function(e){return 401==e.status&&(r.localStorage.removeItem("session_sentinel_admin"),t.go("login")),e},response:function(e){return e},request:function(e){return e},requestError:function(e){return e}}}]).config(["$stateProvider","$urlRouterProvider","$ocLazyLoadProvider","$httpProvider",function(e,r,t,a){a.interceptors.push("AuthInterceptor"),t.config({debug:!1,events:!0}),r.otherwise("/dashboard/home"),e.state("login",{url:"/login",templateUrl:"app/views/login.html",controller:"LoginCtl",resolve:{loadMyFiles:["$ocLazyLoad",function(e){return e.load({name:"sentinelDashboardApp",files:["app/scripts/controllers/login.js"]})}]}}).state("dashboard",{url:"/dashboard",templateUrl:"app/views/dashboard/main.html",resolve:{loadMyDirectives:["$ocLazyLoad",function(e){return e.load({name:"sentinelDashboardApp",files:["app/scripts/directives/header/header.js","app/scripts/directives/sidebar/sidebar.js","app/scripts/directives/sidebar/sidebar-search/sidebar-search.js"]})}]}}).state("dashboard.home",{url:"/home",templateUrl:"app/views/dashboard/home.html",resolve:{loadMyFiles:["$ocLazyLoad",function(e){return e.load({name:"sentinelDashboardApp",files:["app/scripts/controllers/main.js"]})}]}}).state("dashboard.flowV1",{templateUrl:"app/views/flow_v1.html",url:"/flow/:app",controller:"FlowControllerV1",resolve:{loadMyFiles:["$ocLazyLoad",function(e){return e.load({name:"sentinelDashboardApp",files:["app/scripts/controllers/flow_v1.js"]})}]}}).state("dashboard.flow",{templateUrl:"app/views/flow_v2.html",url:"/v2/flow/:app",controller:"FlowControllerV2",resolve:{loadMyFiles:["$ocLazyLoad",function(e){return e.load({name:"sentinelDashboardApp",files:["app/scripts/controllers/flow_v2.js"]})}]}}).state("dashboard.paramFlow",{templateUrl:"app/views/param_flow.html",url:"/paramFlow/:app",controller:"ParamFlowController",resolve:{loadMyFiles:["$ocLazyLoad",function(e){return e.load({name:"sentinelDashboardApp",files:["app/scripts/controllers/param_flow.js"]})}]}}).state("dashboard.clusterAppAssignManage",{templateUrl:"app/views/cluster_app_assign_manage.html",url:"/cluster/assign_manage/:app",controller:"SentinelClusterAppAssignManageController",resolve:{loadMyFiles:["$ocLazyLoad",function(e){return e.load({name:"sentinelDashboardApp",files:["app/scripts/controllers/cluster_app_assign_manage.js"]})}]}}).state("dashboard.clusterAppServerList",{templateUrl:"app/views/cluster_app_server_list.html",url:"/cluster/server/:app",controller:"SentinelClusterAppServerListController",resolve:{loadMyFiles:["$ocLazyLoad",function(e){return e.load({name:"sentinelDashboardApp",files:["app/scripts/controllers/cluster_app_server_list.js"]})}]}}).state("dashboard.clusterAppClientList",{templateUrl:"app/views/cluster_app_client_list.html",url:"/cluster/client/:app",controller:"SentinelClusterAppTokenClientListController",resolve:{loadMyFiles:["$ocLazyLoad",function(e){return e.load({name:"sentinelDashboardApp",files:["app/scripts/controllers/cluster_app_token_client_list.js"]})}]}}).state("dashboard.clusterSingle",{templateUrl:"app/views/cluster_single_config.html",url:"/cluster/single/:app",controller:"SentinelClusterSingleController",resolve:{loadMyFiles:["$ocLazyLoad",function(e){return e.load({name:"sentinelDashboardApp",files:["app/scripts/controllers/cluster_single.js"]})}]}}).state("dashboard.authority",{templateUrl:"app/views/authority.html",url:"/authority/:app",controller:"AuthorityRuleController",resolve:{loadMyFiles:["$ocLazyLoad",function(e){return e.load({name:"sentinelDashboardApp",files:["app/scripts/controllers/authority.js"]})}]}}).state("dashboard.degrade",{templateUrl:"app/views/degrade.html",url:"/degrade/:app",controller:"DegradeCtl",resolve:{loadMyFiles:["$ocLazyLoad",function(e){return e.load({name:"sentinelDashboardApp",files:["app/scripts/controllers/degrade.js"]})}]}}).state("dashboard.system",{templateUrl:"app/views/system.html",url:"/system/:app",controller:"SystemCtl",resolve:{loadMyFiles:["$ocLazyLoad",function(e){return e.load({name:"sentinelDashboardApp",files:["app/scripts/controllers/system.js"]})}]}}).state("dashboard.machine",{templateUrl:"app/views/machine.html",url:"/app/:app",controller:"MachineCtl",resolve:{loadMyFiles:["$ocLazyLoad",function(e){return e.load({name:"sentinelDashboardApp",files:["app/scripts/controllers/machine.js"]})}]}}).state("dashboard.identity",{templateUrl:"app/views/identity.html",url:"/identity/:app",controller:"IdentityCtl",resolve:{loadMyFiles:["$ocLazyLoad",function(e){return e.load({name:"sentinelDashboardApp",files:["app/scripts/controllers/identity.js"]})}]}}).state("dashboard.metric",{templateUrl:"app/views/metric.html",url:"/metric/:app",controller:"MetricCtl",resolve:{loadMyFiles:["$ocLazyLoad",function(e){return e.load({name:"sentinelDashboardApp",files:["app/scripts/controllers/metric.js"]})}]}})}]),(app=angular.module("sentinelDashboardApp")).filter("range",[function(){return function(e,r){if(isNaN(r)||r<=0)return[];e=[];for(var t=1;t<=r;t++)e.push(t);return e}}]),(app=angular.module("sentinelDashboardApp")).service("AuthService",["$http",function(r){this.login=function(e){return r({url:"/auth/login",params:e,method:"POST"})},this.logout=function(){return r({url:"/auth/logout",method:"POST"})}}]),(app=angular.module("sentinelDashboardApp")).service("AppService",["$http",function(e){this.getApps=function(){return e({url:"app/briefinfos.json",method:"GET"})}}]),(app=angular.module("sentinelDashboardApp")).service("FlowServiceV1",["$http",function(a){function r(e){return void 0===e||""===e||isNaN(e)||e<=0}this.queryMachineRules=function(e,r,t){return a({url:"/v1/flow/rules",params:{app:e,ip:r,port:t},method:"GET"})},this.newRule=function(e){e.resource,e.limitApp,e.grade,e.count,e.strategy,e.refResource,e.controlBehavior,e.warmUpPeriodSec,e.maxQueueingTimeMs,e.app,e.ip,e.port;return a({url:"/v1/flow/rule",data:e,method:"POST"})},this.saveRule=function(e){var r={id:e.id,resource:e.resource,limitApp:e.limitApp,grade:e.grade,count:e.count,strategy:e.strategy,refResource:e.refResource,controlBehavior:e.controlBehavior,warmUpPeriodSec:e.warmUpPeriodSec,maxQueueingTimeMs:e.maxQueueingTimeMs};return a({url:"/v1/flow/save.json",params:r,method:"PUT"})},this.deleteRule=function(e){var r={id:e.id,app:e.app};return a({url:"/v1/flow/delete.json",params:r,method:"DELETE"})},this.checkRuleValid=function(e){return void 0===e.resource||""===e.resource?(alert("资源名称不能为空"),!1):void 0===e.count||e.count<0?(alert("限流阈值必须大于等于 0"),!1):void 0===e.strategy||e.strategy<0?(alert("无效的流控模式"),!1):1!=e.strategy&&2!=e.strategy||void 0!==e.refResource&&""!=e.refResource?void 0===e.controlBehavior||e.controlBehavior<0?(alert("无效的流控整形方式"),!1):1==e.controlBehavior&&r(e.warmUpPeriodSec)?(alert("预热时长必须大于 0"),!1):2==e.controlBehavior&&r(e.maxQueueingTimeMs)?(alert("排队超时时间必须大于 0"),!1):!e.clusterMode||void 0!==e.clusterConfig&&void 0!==e.clusterConfig.thresholdType||(alert("集群限流配置不正确"),!1):(alert("请填写关联资源或入口"),!1)}}]),(app=angular.module("sentinelDashboardApp")).service("FlowServiceV2",["$http",function(a){function r(e){return void 0===e||""===e||isNaN(e)||e<=0}this.queryMachineRules=function(e,r,t){return a({url:"/v2/flow/rules",params:{app:e,ip:r,port:t},method:"GET"})},this.newRule=function(e){return a({url:"/v2/flow/rule",data:e,method:"POST"})},this.saveRule=function(e){return a({url:"/v2/flow/rule/"+e.id,data:e,method:"PUT"})},this.deleteRule=function(e){return a({url:"/v2/flow/rule/"+e.id,method:"DELETE"})},this.checkRuleValid=function(e){return void 0===e.resource||""===e.resource?(alert("资源名称不能为空"),!1):void 0===e.count||e.count<0?(alert("限流阈值必须大于等于 0"),!1):void 0===e.strategy||e.strategy<0?(alert("无效的流控模式"),!1):1!=e.strategy&&2!=e.strategy||void 0!==e.refResource&&""!=e.refResource?void 0===e.controlBehavior||e.controlBehavior<0?(alert("无效的流控整形方式"),!1):1==e.controlBehavior&&r(e.warmUpPeriodSec)?(alert("预热时长必须大于 0"),!1):2==e.controlBehavior&&r(e.maxQueueingTimeMs)?(alert("排队超时时间必须大于 0"),!1):!e.clusterMode||void 0!==e.clusterConfig&&void 0!==e.clusterConfig.thresholdType||(alert("集群限流配置不正确"),!1):(alert("请填写关联资源或入口"),!1)}}]),(app=angular.module("sentinelDashboardApp")).service("DegradeService",["$http",function(a){this.queryMachineRules=function(e,r,t){return a({url:"degrade/rules.json",params:{app:e,ip:r,port:t},method:"GET"})},this.newRule=function(e){var r={id:e.id,resource:e.resource,limitApp:e.limitApp,count:e.count,timeWindow:e.timeWindow,grade:e.grade,app:e.app,ip:e.ip,port:e.port};return a({url:"/degrade/new.json",params:r,method:"GET"})},this.saveRule=function(e){var r={id:e.id,resource:e.resource,limitApp:e.limitApp,grade:e.grade,count:e.count,timeWindow:e.timeWindow};return a({url:"/degrade/save.json",params:r,method:"GET"})},this.deleteRule=function(e){var r={id:e.id,app:e.app};return a({url:"/degrade/delete.json",params:r,method:"GET"})},this.checkRuleValid=function(e){return void 0===e.resource||""===e.resource?(alert("资源名称不能为空"),!1):void 0===e.grade||e.grade<0?(alert("未知的降级策略"),!1):void 0===e.count||""===e.count||e.count<0?(alert("降级阈值不能为空或小于 0"),!1):void 0===e.timeWindow||""===e.timeWindow||e.timeWindow<=0?(alert("降级时间窗口必须大于 0"),!1):!(1==e.grade&&1=r?n.apply(null,t):function(){return e(t.concat([].slice.apply(arguments)))}}(e)}function n(){var n=arguments,r=n.length-1;return function(){for(var e=r,t=n[r].apply(this,arguments);e--;)t=n[e].call(this,t);return t}}function u(){for(var e=[],t=0;tthis._limit&&this.evict(),e},e.prototype.evict=function(){var t=this._items.shift();return this._evictListeners.forEach(function(e){return e(t)}),t},e.prototype.dequeue=function(){if(this.size())return this._items.splice(0,1)[0]},e.prototype.clear=function(){var e=this._items;return this._items=[],e},e.prototype.size=function(){return this._items.length},e.prototype.remove=function(e){var t=this._items.indexOf(e);return-1 "+qe(e))},e.prototype.traceTransitionIgnored=function(e){this.enabled(g.Category.TRANSITION)&&console.log(st(e)+": Ignored <> "+qe(e))},e.prototype.traceHookInvocation=function(e,t,n){if(this.enabled(g.Category.HOOK)){var r=C("traceData.hookType")(n)||"internal",i=C("traceData.context.state.name")(n)||C("traceData.context")(n)||"unknown",o=He(e.registeredHook.callback);console.log(st(t)+": Hook -> "+r+" context: "+i+", "+Le(200,o))}},e.prototype.traceHookResult=function(e,t,n){this.enabled(g.Category.HOOK)&&console.log(st(t)+": <- Hook returned: "+Le(200,qe(e)))},e.prototype.traceResolvePath=function(e,t,n){this.enabled(g.Category.RESOLVE)&&console.log(st(n)+": Resolving "+e+" ("+t+")")},e.prototype.traceResolvableResolved=function(e,t){this.enabled(g.Category.RESOLVE)&&console.log(st(t)+": <- Resolved "+e+" to: "+Le(200,qe(e.data)))},e.prototype.traceError=function(e,t){this.enabled(g.Category.TRANSITION)&&console.log(st(t)+": <- Rejected "+qe(t)+", reason: "+e)},e.prototype.traceSuccess=function(e,t){this.enabled(g.Category.TRANSITION)&&console.log(st(t)+": <- Success "+qe(t)+", final state: "+e.name)},e.prototype.traceUIViewEvent=function(e,t,n){void 0===n&&(n=""),this.enabled(g.Category.UIVIEW)&&console.log("ui-view: "+Fe(30,e)+" "+et(t)+n)},e.prototype.traceUIViewConfigUpdated=function(e,t){this.enabled(g.Category.UIVIEW)&&this.traceUIViewEvent("Updating",e," with ViewConfig from context='"+t+"'")},e.prototype.traceUIViewFill=function(e,t){this.enabled(g.Category.UIVIEW)&&this.traceUIViewEvent("Fill",e," with: "+Le(200,t))},e.prototype.traceViewSync=function(e){if(this.enabled(g.Category.VIEWCONFIG)){var a="uiview component fqn",t=e.map(function(e){var t,n=e.uiView,r=e.viewConfig,i=n&&n.fqn,o=r&&r.viewDecl.$context.name+": ("+r.viewDecl.$name+")";return(t={})[a]=i,t["view config state (view name)"]=o,t}).sort(function(e,t){return(e[a]||"").localeCompare(t[a]||"")});it(t)}},e.prototype.traceViewServiceEvent=function(e,t){this.enabled(g.Category.VIEWCONFIG)&&console.log("VIEWCONFIG: "+e+" "+function(e){var t=e.viewDecl,n=t.$context.name||"(root)";return"[View#"+e.$id+" from '"+n+"' state]: target ui-view: '"+t.$uiViewName+"@"+t.$uiViewContextAnchor+"'"}(t))},e.prototype.traceViewServiceUIViewEvent=function(e,t){this.enabled(g.Category.VIEWCONFIG)&&console.log("VIEWCONFIG: "+e+" "+et(t))},e}(),lt=new ut,ct=function(){function e(e){this.pattern=/.*/,this.inherit=!0,Y(this,e)}return e.prototype.is=function(e,t){return!0},e.prototype.encode=function(e,t){return e},e.prototype.decode=function(e,t){return e},e.prototype.equals=function(e,t){return e==t},e.prototype.$subPattern=function(){var e=this.pattern.toString();return e.substr(1,e.length-2)},e.prototype.toString=function(){return"{ParamType:"+this.name+"}"},e.prototype.$normalize=function(e){return this.is(e)?e:this.decode(e)},e.prototype.$asArray=function(e,t){if(!e)return this;if("auto"===e&&!t)throw new Error("'auto' array mode is for query parameters only");return new dt(this,e)},e}();function dt(r,i){var o=this;function a(e){return A(e)?e:k(e)?[e]:[]}function s(n,r){return function(e){if(A(e)&&0===e.length)return e;var t=ce(a(e),n);return!0===r?0===se(t,function(e){return!e}).length:function(e){switch(e.length){case 0:return;case 1:return"auto"===i?e[0]:e;default:return e}}(t)}}function u(o){return function(e,t){var n=a(e),r=a(t);if(n.length!==r.length)return!1;for(var i=0;i=n.invokeLimit&&n.deregister()}}},o.prototype.handleHookResult=function(e){var t=this,n=this.getNotCurrentRejection();return n||(R(e)?e.then(function(e){return t.handleHookResult(e)}):(lt.traceHookResult(e,this.transition,this.options),!1===e?Ve.aborted("Hook aborted transition").toPromise():c($t)(e)?Ve.redirected(e).toPromise():void 0))},o.prototype.getNotCurrentRejection=function(){var e=this.transition.router;return e._disposed?Ve.aborted("UIRouter instance #"+e.$id+" has been stopped (disposed)").toPromise():this.transition._aborted?Ve.aborted().toPromise():this.isSuperseded()?Ve.superseded(this.options.current()).toPromise():void 0},o.prototype.toString=function(){var e=this.options,t=this.registeredHook;return(C("traceData.hookType")(e)||"internal")+" context: "+(C("traceData.context.state.name")(e)||C("traceData.context")(e)||"unknown")+", "+Le(200,Ne(t.callback))},o.HANDLE_RESULT=function(t){return function(e){return t.handleHookResult(e)}},o.LOG_REJECTED_RESULT=function(t){return function(e){R(e)&&e.catch(function(e){return t.logError(Ve.normalize(e))})}},o.LOG_ERROR=function(t){return function(e){return t.logError(e)}},o.REJECT_ERROR=function(e){return function(e){return Pe(e)}},o.THROW_ERROR=function(e){return function(e){throw e}},o}();function Gt(e,t,n){var i=O(t)?[t]:t;return!!(D(i)?i:function(e){for(var t=i,n=0;n "+(this.valid()?"":"(X) ")+"'"+(T(t)?t.name:t)+"'"+qe(n(this.params()))+" )"},t.diToken=t}();function en(e,t){var n=["",""],r=e.replace(/[\\\[\]\^$*+?.()|{}]/g,"\\$&");if(!t)return r;switch(t.squash){case!1:n=["(",")"+(t.isOptional?"?":"")];break;case!0:r=r.replace(/\/$/,""),n=["(?:/(",")|/)?"];break;default:n=["("+t.squash+"|",")?"]}return r+n[0]+t.type.pattern.source+n[1]}var tn=Xe("/"),nn={state:{params:{}},strict:!0,caseInsensitive:!0},rn=function(){function m(o,a,e,t){var s=this;this._cache={path:[this]},this._children=[],this._params=[],this._segments=[],this._compiled=[],this.config=t=te(t,nn),this.pattern=o;for(var n,r,i,u=/([:*])([\w\[\]]+)|\{([\w\[\]]+)(?:\:\s*((?:[^{}\\]+|\\.|\{(?:[^{}\\]+|\\.)*\})+))?\}/g,l=/([:]?)([\w\[\].-]+)|\{([\w\[\].-]+)(?:\:\s*((?:[^{}\\]+|\\.|\{(?:[^{}\\]+|\\.)*\})+))?\}/g,c=[],d=0,p=function(e){if(!m.nameValidator.test(e))throw new Error("Invalid parameter name '"+e+"' in pattern '"+o+"'");if(ue(s._params,y("id",e)))throw new Error("Duplicate parameter name '"+e+"' in pattern '"+o+"'")},h=function(e,t){var n,r=e[2]||e[3],i=t?e[4]:e[4]||("*"===e[1]?"[\\s\\S]*":null);return{id:r,regexp:i,segment:o.substring(d,e.index),type:i?a.type(i)||(n=i,W(a.type(t?"query":"path"),{pattern:new RegExp(n,s.config.caseInsensitive?"i":void 0)})):null}};(n=u.exec(o))&&!(0<=(r=h(n,!1)).segment.indexOf("?"));)p(r.id),this._params.push(e.fromPath(r.id,r.type,t.state)),this._segments.push(r.segment),c.push([r.segment,De(this._params)]),d=u.lastIndex;var f=(i=o.substring(d)).indexOf("?");if(0<=f){var g=i.substring(f);if(i=i.substring(0,f),0 Registering",e),this._viewConfigs.push(e)},u.prototype.sync=function(){var n=this,r=this._uiViews.map(function(e){return[e.fqn,e]}).reduce(ke,{});function i(e){for(var t=e.viewDecl.$context,n=0;++n&&t.parent;)t=t.parent;return n}var o=l(function(e,t,n,r){return t*(e(n)-e(r))}),e=this._uiViews.sort(o(function(e){var t=function(e){return e&&e.parent?t(e.parent)+1:1};return 1e4*e.fqn.split(".").length+t(e.creationContext)},1)).map(function(e){var t=n._viewConfigs.filter(u.matches(r,e));return 1 Registering",t);var e=this._uiViews;return e.filter(function(e){return e.fqn===t.fqn&&e.$type===t.$type}).length&<.traceViewServiceUIViewEvent("!!!! duplicate uiView named:",t),e.push(t),this.sync(),function(){-1!==e.indexOf(t)?(lt.traceViewServiceUIViewEvent("<- Deregistering",t),Q(e)(t)):lt.traceViewServiceUIViewEvent("Tried removing non-registered uiView",t)}},u.prototype.available=function(){return this._uiViews.map(v("fqn"))},u.prototype.active=function(){return this._uiViews.filter(v("$config")).map(v("name"))},u.matches=function(s,u){return function(e){if(u.$type!==e.viewDecl.$type)return!1;var t=e.viewDecl,n=t.$uiViewName.split("."),r=u.fqn.split(".");if(!U(n,r.slice(0-n.length)))return!1;var i=1-n.length||void 0,o=r.slice(0,i).join("."),a=s[o].creationContext;return t.$uiViewContextAnchor===(a&&a.name)}},u}(),hn=function(){function e(){this.params=new wt,this.lastStartedTransitionId=-1,this.transitionHistory=new Re([],1),this.successfulTransitions=new Re([],1)}return e.prototype.dispose=function(){this.transitionHistory.clear(),this.successfulTransitions.clear(),this.transition=null},e}();function fn(e){if(!(D(e)||O(e)||c($t)(e)||$t.isDef(e)))throw new Error("'handler' must be a string, function, TargetState, or have a state: 'newtarget' property");return D(e)?e:p(e)}cn=function(e,t){var n=function(e,t){return(t.priority||0)-(e.priority||0)}(e,t);return 0!==n?n:0!==(n=function(e,t){var n={STATE:4,URLMATCHER:4,REGEXP:3,RAW:2,OTHER:1};return(n[e.type]||0)-(n[t.type]||0)}(e,t))?n:0!==(n=function(e,t){return e.urlMatcher&&t.urlMatcher?rn.compare(e.urlMatcher,t.urlMatcher):0}(e,t))?n:function(e,t){var n={STATE:!0,URLMATCHER:!0};return n[e.type]&&n[t.type]?0:(e.$id||0)-(t.$id||0)}(e,t)};var gn=function(){function e(e){this.router=e,this._sortFn=cn,this._rules=[],this._id=0,this.urlRuleFactory=new un(e)}return e.prototype.dispose=function(e){this._rules=[],delete this._otherwiseFn},e.prototype.initial=function(e){var t=fn(e);this.rule(this.urlRuleFactory.create(function(e,t){return 0===t.globals.transitionHistory.size()&&!!/^\/?$/.exec(e.path)},t))},e.prototype.otherwise=function(e){var t=fn(e);this._otherwiseFn=this.urlRuleFactory.create(p(!0),t),this._sorted=!1},e.prototype.removeRule=function(e){Q(this._rules,e)},e.prototype.rule=function(e){var t=this;if(!un.isUrlRule(e))throw new Error("invalid rule");return e.$id=this._id++,e.priority=e.priority||0,this._rules.push(e),this._sorted=!1,function(){return t.removeRule(e)}},e.prototype.rules=function(){return this.ensureSorted(),this._rules.concat(this._otherwiseFn?[this._otherwiseFn]:[])},e.prototype.sort=function(e){for(var t=this.stableSort(this._rules,this._sortFn=e||this._sortFn),n=0,r=0;rn.weight?s:n}return n},e}(),yn=0,wn=I("LocationServices",["url","path","search","hash","onChange"]),bn=I("LocationConfig",["port","protocol","host","baseHref","html5Mode","hashPrefix"]),$n=function(){function e(e,t){void 0===e&&(e=wn),void 0===t&&(t=bn),this.locationService=e,this.locationConfig=t,this.$id=yn++,this._disposed=!1,this._disposables=[],this.trace=lt,this.viewService=new pn(this),this.globals=new hn,this.transitionService=new Un(this),this.urlMatcherFactory=new sn(this),this.urlRouter=new dn(this),this.urlService=new vn(this),this.stateRegistry=new zt(this),this.stateService=new qn(this),this._plugins={},this.viewService._pluginapi._rootViewContext(this.stateRegistry.root()),this.globals.$current=this.stateRegistry.root(),this.globals.current=this.globals.$current.self,this.disposable(this.globals),this.disposable(this.stateService),this.disposable(this.stateRegistry),this.disposable(this.transitionService),this.disposable(this.urlService),this.disposable(e),this.disposable(t)}return e.prototype.disposable=function(e){this._disposables.push(e)},e.prototype.dispose=function(e){var t=this;e&&D(e.dispose)?e.dispose(this):(this._disposed=!0,this._disposables.slice().forEach(function(e){try{"function"==typeof e.dispose&&e.dispose(t),Q(t._disposables,e)}catch(e){}}))},e.prototype.plugin=function(e,t){void 0===t&&(t={});var n=new e(this,t);if(!n.name)throw new Error("Required property `name` missing on plugin: "+n);return this._disposables.push(n),this._plugins[n.name]=n},e.prototype.getPlugin=function(e){return e?this._plugins[e]:de(this._plugins)},e}();function _n(t){t.addResolvable(kt.fromData($n,t.router),""),t.addResolvable(kt.fromData(Jt,t),""),t.addResolvable(kt.fromData("$transition$",t),""),t.addResolvable(kt.fromData("$stateParams",t.params()),""),t.entering().forEach(function(e){t.addResolvable(kt.fromData("$state$",e),e)})}var Sn=G(["$transition$",Jt]),Cn=function(e){var t=de(e.treeChanges()).reduce(fe,[]).reduce(ve,[]),n=function(e){return Sn(e.token)?kt.fromData(e.token,null):e};t.forEach(function(e){e.resolvables=e.resolvables.map(n)})},kn=function(t){var e=t.to().redirectTo;if(e){var n=t.router.stateService;return D(e)?V.$q.when(e(t)).then(r):r(e)}function r(e){if(e)return e instanceof $t?e:O(e)?n.target(e,t.params(),t.options()):e.state||e.params?n.target(e.state||t.to(),e.params||t.params(),t.options()):void 0}};function Dn(n){return function(e,t){return(0,t.$$state()[n])(e,t)}}var xn=Dn("onExit"),On=Dn("onRetain"),Tn=Dn("onEnter"),An=function(e){return new At(e.treeChanges().to).resolvePath("EAGER",e).then(z)},En=function(e,t){return new At(e.treeChanges().to).subContext(t.$$state()).resolvePath("LAZY",e).then(z)},Pn=function(e){return new At(e.treeChanges().to).resolvePath("LAZY",e).then(z)},Mn=function(e){var t=V.$q,n=e.views("entering");if(n.length)return t.all(n.map(function(e){return t.when(e.load())})).then(z)},Rn=function(e){var t=e.views("entering"),n=e.views("exiting");if(t.length||n.length){var r=e.router.viewService;n.forEach(function(e){return r.deactivateViewConfig(e)}),t.forEach(function(e){return r.activateViewConfig(e)}),r.sync()}},In=function(e){var t=e.router.globals,n=function(){t.transition===e&&(t.transition=null)};e.onSuccess({},function(){t.successfulTransitions.enqueue(e),t.$current=e.$to(),t.current=t.$current.self,xe(e.params(),t.params)},{priority:1e4}),e.promise.then(n,n)},Vn=function(e){var t=e.options(),n=e.router.stateService,r=e.router.urlRouter;if("url"!==t.source&&t.location&&n.$current.navigable){var i={replace:"replace"===t.location};r.push(n.$current.navigable.url,n.params,i)}r.update(!0)},Ln=function(a){var s=a.router;var e=a.entering().filter(function(e){return!!e.$$state().lazyLoad}).map(function(e){return Fn(a,e)});return V.$q.all(e).then(function(){if("url"!==a.originalTransition().options().source){var e=a.targetState();return s.stateService.target(e.identifier(),e.params(),e.options())}var t=s.urlService,n=t.match(t.parts()),r=n&&n.rule;if(r&&"STATE"===r.type){var i=r.state,o=n.match;return s.stateService.target(i,o,a.options())}s.urlService.sync()})};function Fn(t,n){var r=n.$$state().lazyLoad,e=r._promise;if(!e){e=r._promise=V.$q.when(r(t,n)).then(function(e){e&&Array.isArray(e.states)&&e.states.forEach(function(e){return t.router.stateRegistry.register(e)});return e}).then(function(e){return delete n.lazyLoad,delete n.$$state().lazyLoad,delete r._promise,e},function(e){return delete r._promise,V.$q.reject(e)})}return e}var jn=function(e,t,n,r,i,o,a,s){void 0===i&&(i=!1),void 0===o&&(o=Wt.HANDLE_RESULT),void 0===a&&(a=Wt.REJECT_ERROR),void 0===s&&(s=!1),this.name=e,this.hookPhase=t,this.hookOrder=n,this.criteriaMatchPath=r,this.reverseSort=i,this.getResultHandler=o,this.getErrorHandler=a,this.synchronous=s};function Hn(e){var t=e._ignoredReason();if(t){lt.traceTransitionIgnored(e);var n=e.router.globals.transition;return"SameAsCurrent"===t&&n&&n.abort(),Ve.ignored().toPromise()}}function Nn(e){if(!e.valid())throw new Error(e.error().toString())}var Yn={location:!0,relative:null,inherit:!1,notify:!0,reload:!1,custom:{},current:function(){return null},source:"unknown"},Un=function(){function e(e){this._transitionCount=0,this._eventTypes=[],this._registeredHooks={},this._criteriaPaths={},this._router=e,this.$view=e.viewService,this._deregisterHookFns={},this._pluginapi=B(p(this),{},p(this),["_definePathType","_defineEvent","_getPathTypes","_getEvents","getHooks"]),this._defineCorePaths(),this._defineCoreEvents(),this._registerCoreTransitionHooks(),e.globals.successfulTransitions.onEvict(Cn)}return e.prototype.onCreate=function(e,t,n){},e.prototype.onBefore=function(e,t,n){},e.prototype.onStart=function(e,t,n){},e.prototype.onExit=function(e,t,n){},e.prototype.onRetain=function(e,t,n){},e.prototype.onEnter=function(e,t,n){},e.prototype.onFinish=function(e,t,n){},e.prototype.onSuccess=function(e,t,n){},e.prototype.onError=function(e,t,n){},e.prototype.dispose=function(e){de(this._registeredHooks).forEach(function(t){return t.forEach(function(e){e._deregistered=!0,Q(t,e)})})},e.prototype.create=function(e,t){return new Jt(e,t,this._router)},e.prototype._defineCoreEvents=function(){var e=g.TransitionHookPhase,t=Wt,n=this._criteriaPaths;this._defineEvent("onCreate",e.CREATE,0,n.to,!1,t.LOG_REJECTED_RESULT,t.THROW_ERROR,!0),this._defineEvent("onBefore",e.BEFORE,0,n.to),this._defineEvent("onStart",e.RUN,0,n.to),this._defineEvent("onExit",e.RUN,100,n.exiting,!0),this._defineEvent("onRetain",e.RUN,200,n.retained),this._defineEvent("onEnter",e.RUN,300,n.entering),this._defineEvent("onFinish",e.RUN,400,n.to),this._defineEvent("onSuccess",e.SUCCESS,0,n.to,!1,t.LOG_REJECTED_RESULT,t.LOG_ERROR,!0),this._defineEvent("onError",e.ERROR,0,n.to,!1,t.LOG_REJECTED_RESULT,t.LOG_ERROR,!0)},e.prototype._defineCorePaths=function(){var e=g.TransitionHookScope.STATE,t=g.TransitionHookScope.TRANSITION;this._definePathType("to",t),this._definePathType("from",t),this._definePathType("exiting",e),this._definePathType("retained",e),this._definePathType("entering",e)},e.prototype._defineEvent=function(e,t,n,r,i,o,a,s){void 0===i&&(i=!1),void 0===o&&(o=Wt.HANDLE_RESULT),void 0===a&&(a=Wt.REJECT_ERROR),void 0===s&&(s=!1);var u=new jn(e,t,n,r,i,o,a,s);this._eventTypes.push(u),Qt(this,this,u)},e.prototype._getEvents=function(t){return(k(t)?this._eventTypes.filter(function(e){return e.hookPhase===t}):this._eventTypes.slice()).sort(function(e,t){var n=e.hookPhase-t.hookPhase;return 0==n?e.hookOrder-t.hookOrder:n})},e.prototype._definePathType=function(e,t){this._criteriaPaths[e]={name:e,scope:t}},e.prototype._getPathTypes=function(){return this._criteriaPaths},e.prototype.getHooks=function(e){return this._registeredHooks[e]},e.prototype._registerCoreTransitionHooks=function(){var e=this._deregisterHookFns;e.addCoreResolves=function(e){return e.onCreate({},_n)}(this),e.ignored=function(e){return e.onBefore({},Hn,{priority:-9999})}(this),e.invalid=function(e){return e.onBefore({},Nn,{priority:-1e4})}(this),e.redirectTo=function(e){return e.onStart({to:function(e){return!!e.redirectTo}},kn)}(this),e.onExit=function(e){return e.onExit({exiting:function(e){return!!e.onExit}},xn)}(this),e.onRetain=function(e){return e.onRetain({retained:function(e){return!!e.onRetain}},On)}(this),e.onEnter=function(e){return e.onEnter({entering:function(e){return!!e.onEnter}},Tn)}(this),e.eagerResolve=function(e){return e.onStart({},An,{priority:1e3})}(this),e.lazyResolve=function(e){return e.onEnter({entering:p(!0)},En,{priority:1e3})}(this),e.resolveAll=function(e){return e.onFinish({},Pn,{priority:1e3})}(this),e.loadViews=function(e){return e.onFinish({},Mn)}(this),e.activateViews=function(e){return e.onSuccess({},Rn)}(this),e.updateGlobals=function(e){return e.onCreate({},In)}(this),e.updateUrl=function(e){return e.onSuccess({},Vn,{priority:9999})}(this),e.lazyLoad=function(e){return e.onBefore({entering:function(e){return!!e.lazyLoad}},Ln)}(this)},e}(),qn=function(){function n(e){this.router=e,this.invalidCallbacks=[],this._defaultErrorHandler=function(e){e instanceof Error&&e.stack?(console.error(e),console.error(e.stack)):e instanceof Ve?(console.error(e.toString()),e.detail&&e.detail.stack&&console.error(e.detail.stack)):console.error(e)};var t=Object.keys(n.prototype).filter(d(G(["current","$current","params","transition"])));B(p(n.prototype),this,p(this),t)}return Object.defineProperty(n.prototype,"transition",{get:function(){return this.router.globals.transition},enumerable:!0,configurable:!0}),Object.defineProperty(n.prototype,"params",{get:function(){return this.router.globals.params},enumerable:!0,configurable:!0}),Object.defineProperty(n.prototype,"current",{get:function(){return this.router.globals.current},enumerable:!0,configurable:!0}),Object.defineProperty(n.prototype,"$current",{get:function(){return this.router.globals.$current},enumerable:!0,configurable:!0}),n.prototype.dispose=function(){this.defaultErrorHandler(z),this.invalidCallbacks=[]},n.prototype._handleInvalidTargetState=function(e,n){var r=this,i=_t.makeTargetState(this.router.stateRegistry,e),t=this.router.globals,o=function(){return t.transitionHistory.peekTail()},a=o(),s=new Re(this.invalidCallbacks.slice()),u=new At(e).injector(),l=function(e){if(e instanceof $t){var t=e;return(t=r.target(t.identifier(),t.params(),t.options())).valid()?o()!==a?Ve.superseded().toPromise():r.transitionTo(t.identifier(),t.params(),t.options()):Ve.invalid(t.error()).toPromise()}};return function t(){var e=s.dequeue();return void 0===e?Ve.invalid(n.error()).toPromise():V.$q.when(e(n,i,u)).then(l).then(function(e){return e||t()})}()},n.prototype.onInvalid=function(e){return this.invalidCallbacks.push(e),function(){Q(this.invalidCallbacks)(e)}.bind(this)},n.prototype.reload=function(e){return this.transitionTo(this.current,this.params,{reload:!k(e)||e,inherit:!1,notify:!1})},n.prototype.go=function(e,t,n){var r=te(n,{relative:this.$current,inherit:!0},Yn);return this.transitionTo(e,t,r)},n.prototype.target=function(e,t,n){if(void 0===n&&(n={}),T(n.reload)&&!n.reload.name)throw new Error("Invalid reload state object");var r=this.router.stateRegistry;if(n.reloadState=!0===n.reload?r.root():r.matcher.find(n.reload,n.relative),n.reload&&!n.reloadState)throw new Error("No such reload state '"+(O(n.reload)?n.reload:n.reload.name)+"'");return new $t(this.router.stateRegistry,e,t,n)},n.prototype.getCurrentPath=function(){var e=this,t=this.router.globals.successfulTransitions.peekTail();return t?t.treeChanges().to:[new bt(e.router.stateRegistry.root())]},n.prototype.transitionTo=function(e,t,n){var o=this;void 0===t&&(t={}),void 0===n&&(n={});var a=this.router,s=a.globals;n=te(n,Yn);n=Y(n,{current:function(){return s.transition}});var r=this.target(e,t,n),i=this.getCurrentPath();if(!r.exists())return this._handleInvalidTargetState(i,r);if(!r.valid())return Pe(r.error());var u=function(i){return function(e){if(e instanceof Ve){var t=a.globals.lastStartedTransitionId<=i.$id;if(e.type===g.RejectType.IGNORED)return t&&a.urlRouter.update(),V.$q.when(s.current);var n=e.detail;if(e.type===g.RejectType.SUPERSEDED&&e.redirected&&n instanceof $t){var r=i.redirect(n);return r.run().catch(u(r))}if(e.type===g.RejectType.ABORTED)return t&&a.urlRouter.update(),V.$q.reject(e)}return o.defaultErrorHandler()(e),V.$q.reject(e)}},l=this.router.transitionService.create(i,r),c=l.run().catch(u(l));return Ee(c),Y(c,{transition:l})},n.prototype.is=function(e,t,n){n=te(n,{relative:this.$current});var r=this.router.stateRegistry.matcher.find(e,n.relative);if(k(r)){if(this.$current!==r)return!1;if(!t)return!0;var i=r.parameters({inherit:!0,matchingKeys:t});return vt.equals(i,vt.values(i,t),this.params)}},n.prototype.includes=function(e,t,n){n=te(n,{relative:this.$current});var r=O(e)&&Me.fromString(e);if(r){if(!r.matches(this.$current.name))return!1;e=this.$current.name}var i=this.router.stateRegistry.matcher.find(e,n.relative),o=this.$current.includes;if(k(i)){if(!k(o[i.name]))return!1;if(!t)return!0;var a=i.parameters({inherit:!0,matchingKeys:t});return vt.equals(a,vt.values(a,t),this.params)}},n.prototype.href=function(e,t,n){n=te(n,{lossy:!0,inherit:!0,absolute:!1,relative:this.$current}),t=t||{};var r=this.router.stateRegistry.matcher.find(e,n.relative);if(!k(r))return null;n.inherit&&(t=this.params.$inherit(t,this.$current,r));var i=r&&n.lossy?r.navigable:r;return i&&void 0!==i.url&&null!==i.url?this.router.urlRouter.href(i.url,t,{absolute:n.absolute}):null},n.prototype.defaultErrorHandler=function(e){return this._defaultErrorHandler=e||this._defaultErrorHandler},n.prototype.get=function(e,t){var n=this.router.stateRegistry;return 0===arguments.length?n.get():n.get(e,t||this.$current)},n.prototype.lazyLoad=function(e,t){var n=this.get(e);if(!n||!n.lazyLoad)throw new Error("Can not lazy load "+e);var r=this.getCurrentPath(),i=_t.makeTargetState(this.router.stateRegistry,r);return Fn(t=t||this.router.transitionService.create(r,i),n)},n}(),zn={when:function(n){return new Promise(function(e,t){return e(n)})},reject:function(n){return new Promise(function(e,t){t(n)})},defer:function(){var n={};return n.promise=new Promise(function(e,t){n.resolve=e,n.reject=t}),n},all:function(e){if(A(e))return Promise.all(e);if(T(e)){var t=Object.keys(e).map(function(t){return e[t].then(function(e){return{key:t,val:e}})});return zn.all(t).then(function(e){return e.reduce(function(e,t){return e[t.key]=t.val,e},{})})}}},Bn={},Wn=/((\/\/.*$)|(\/\*[\s\S]*?\*\/))/gm,Gn=/([^\s,]+)/g,Kn={get:function(e){return Bn[e]},has:function(e){return null!=Kn.get(e)},invoke:function(e,t,n){var r=Y({},Bn,n||{}),i=Kn.annotate(e),o=be(function(e){return r.hasOwnProperty(e)},function(e){return"DI can't find injectable: '"+e+"'"}),a=i.filter(o).map(function(e){return r[e]});return D(e)?e.apply(t,a):e.slice(-1)[0].apply(t,a)},annotate:function(e){if(!M(e))throw new Error("Not an injectable function: "+e);if(e&&e.$inject)return e.$inject;if(A(e))return e.slice(0,-1);var t=e.toString().replace(Wn,"");return t.slice(t.indexOf("(")+1,t.indexOf(")")).match(Gn)||[]}},Qn=function(e,t){var n=t[0],r=t[1];return e.hasOwnProperty(n)?A(e[n])?e[n].push(r):e[n]=[e[n],r]:e[n]=r,e},Zn=function(e){return e.split("&").filter(q).map(Qe).reduce(Qn,{})};function Xn(e){var t=function(e){return e||""},n=Ge(e).map(t),r=n[0],i=n[1],o=Ke(r).map(t);return{path:o[0],search:o[1],hash:i,url:e}}var Jn=function(e){var t=e.path(),n=e.search(),r=e.hash(),i=Object.keys(n).map(function(t){var e=n[t];return(A(e)?e:[e]).map(function(e){return t+"="+e})}).reduce(fe,[]).join("&");return t+(i?"?"+i:"")+(r?"#"+r:"")};function er(r,i,o,a){return function(e){var t=e.locationService=new o(e),n=e.locationConfig=new a(e,i);return{name:r,service:t,configuration:n,dispose:function(e){e.dispose(t),e.dispose(n)}}}}var tr,nr,rr,ir=function(){function e(e,t){var n=this;this.fireAfterUpdate=t,this._listeners=[],this._listener=function(t){return n._listeners.forEach(function(e){return e(t)})},this.hash=function(){return Xn(n._get()).hash},this.path=function(){return Xn(n._get()).path},this.search=function(){return Zn(Xn(n._get()).search)},this._location=L.location,this._history=L.history}return e.prototype.url=function(t,e){return void 0===e&&(e=!0),k(t)&&t!==this._get()&&(this._set(null,null,t,e),this.fireAfterUpdate&&this._listeners.forEach(function(e){return e({url:t})})),Jn(this)},e.prototype.onChange=function(e){var t=this;return this._listeners.push(e),function(){return Q(t._listeners,e)}},e.prototype.dispose=function(e){ee(this._listeners)},e}(),or=(tr=function(e,t){return(tr=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(e,t)},function(e,t){function n(){this.constructor=e}tr(e,t),e.prototype=null===t?Object.create(t):(n.prototype=t.prototype,new n)}),ar=function(n){function e(e){var t=n.call(this,e,!1)||this;return L.addEventListener("hashchange",t._listener,!1),t}return or(e,n),e.prototype._get=function(){return Ze(this._location.hash)},e.prototype._set=function(e,t,n,r){this._location.hash=n},e.prototype.dispose=function(e){n.prototype.dispose.call(this,e),L.removeEventListener("hashchange",this._listener)},e}(ir),sr=(nr=function(e,t){return(nr=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(e,t)},function(e,t){function n(){this.constructor=e}nr(e,t),e.prototype=null===t?Object.create(t):(n.prototype=t.prototype,new n)}),ur=function(t){function e(e){return t.call(this,e,!0)||this}return sr(e,t),e.prototype._get=function(){return this._url},e.prototype._set=function(e,t,n,r){this._url=n},e}(ir),lr=(rr=function(e,t){return(rr=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(e,t)},function(e,t){function n(){this.constructor=e}rr(e,t),e.prototype=null===t?Object.create(t):(n.prototype=t.prototype,new n)}),cr=function(n){function e(e){var t=n.call(this,e,!0)||this;return t._config=e.urlService.config,L.addEventListener("popstate",t._listener,!1),t}return lr(e,n),e.prototype._getBasePrefix=function(){return We(this._config.baseHref())},e.prototype._get=function(){var e=this._location,t=e.pathname,n=e.hash,r=e.search;r=Ke(r)[1],n=Ge(n)[1];var i=this._getBasePrefix(),o=t===this._config.baseHref(),a=t.substr(0,i.length)===i;return(t=o?"/":a?t.substring(i.length):t)+(r?"?"+r:"")+(n?"#"+n:"")},e.prototype._set=function(e,t,n,r){var i=this._getBasePrefix(),o=n&&"/"!==n[0]?"/":"",a=""===n||"/"===n?this._config.baseHref():i+o+n;r?this._history.replaceState(e,t,a):this._history.pushState(e,t,a)},e.prototype.dispose=function(e){n.prototype.dispose.call(this,e),L.removeEventListener("popstate",this._listener)},e}(ir),dr=function(){var t=this;this.dispose=z,this._baseHref="",this._port=80,this._protocol="http",this._host="localhost",this._hashPrefix="",this.port=function(){return t._port},this.protocol=function(){return t._protocol},this.host=function(){return t._host},this.baseHref=function(){return t._baseHref},this.html5Mode=function(){return!1},this.hashPrefix=function(e){return k(e)?t._hashPrefix=e:t._hashPrefix}},pr=function(){function e(e,t){void 0===t&&(t=!1),this._isHtml5=t,this._baseHref=void 0,this._hashPrefix=""}return e.prototype.port=function(){return location.port?Number(location.port):"https"===this.protocol()?443:80},e.prototype.protocol=function(){return location.protocol.replace(/:/g,"")},e.prototype.host=function(){return location.hostname},e.prototype.html5Mode=function(){return this._isHtml5},e.prototype.hashPrefix=function(e){return k(e)?this._hashPrefix=e:this._hashPrefix},e.prototype.baseHref=function(e){return k(e)&&(this._baseHref=e),b(this._baseHref)&&(this._baseHref=this.getBaseHref()),this._baseHref},e.prototype.getBaseHref=function(){var e=document.getElementsByTagName("base")[0];return e&&e.href?e.href.replace(/^([^/:]*:)?\/\/[^/]*/,""):this._isHtml5?"/":location.pathname||"/"},e.prototype.dispose=function(){},e}();function hr(e){return V.$injector=Kn,{name:"vanilla.services",$q:V.$q=zn,$injector:Kn,dispose:function(){return null}}}var fr=er("vanilla.hashBangLocation",!1,ar,pr),gr=er("vanilla.pushStateLocation",!0,cr,pr),mr=er("vanilla.memoryLocation",!1,ur,dr),vr=function(){function e(){}return e.prototype.dispose=function(e){},e}(),yr=Object.freeze({root:L,fromJson:j,toJson:H,forEach:N,extend:Y,equals:U,identity:q,noop:z,createProxyFunctions:B,inherit:W,inArray:G,_inArray:K,removeFrom:Q,_removeFrom:Z,pushTo:X,_pushTo:J,deregAll:ee,defaults:te,mergeR:ne,ancestors:re,pick:ie,omit:oe,pluck:ae,filter:se,find:ue,mapObj:le,map:ce,values:de,allTrueR:pe,anyTrueR:he,unnestR:fe,flattenR:ge,pushR:me,uniqR:ve,unnest:ye,flatten:we,assertPredicate:be,assertMap:$e,assertFn:_e,pairs:Se,arrayTuples:Ce,applyPairs:ke,tail:De,copy:xe,_extend:Oe,silenceUncaughtInPromise:Ee,silentRejection:Pe,makeStub:I,services:V,Glob:Me,curry:l,compose:n,pipe:u,prop:v,propEq:y,parse:C,not:d,and:r,or:i,all:a,any:s,is:c,eq:o,val:p,invoke:h,pattern:f,isUndefined:b,isDefined:k,isNull:$,isNullOrUndefined:_,isFunction:D,isNumber:x,isString:O,isObject:T,isArray:A,isDate:E,isRegExp:P,isInjectable:M,isPromise:R,Queue:Re,maxLength:Le,padString:Fe,kebobString:je,functionToString:He,fnToString:Ne,stringify:qe,beforeAfterSubstr:ze,hostRegex:Be,stripLastPathElement:We,splitHash:Ge,splitQuery:Ke,splitEqual:Qe,trimHashVal:Ze,splitOnDelim:Xe,joinNeighborsR:Je,get Category(){return g.Category},Trace:ut,trace:lt,get DefType(){return g.DefType},Param:vt,ParamTypes:yt,StateParams:wt,ParamType:ct,PathNode:bt,PathUtils:_t,resolvePolicies:St,defaultResolvePolicy:Ct,Resolvable:kt,NATIVE_INJECTOR_TOKEN:Tt,ResolveContext:At,resolvablesBuilder:Ft,StateBuilder:Nt,StateObject:Yt,StateMatcher:Ut,StateQueueManager:qt,StateRegistry:zt,StateService:qn,TargetState:$t,get TransitionHookPhase(){return g.TransitionHookPhase},get TransitionHookScope(){return g.TransitionHookScope},HookBuilder:Zt,matchState:Gt,RegisteredHook:Kt,makeEvent:Qt,get RejectType(){return g.RejectType},Rejection:Ve,Transition:Jt,TransitionHook:Wt,TransitionEventType:jn,defaultTransOpts:Yn,TransitionService:Un,UrlRules:gn,UrlConfig:mn,UrlMatcher:rn,ParamFactory:an,UrlMatcherFactory:sn,UrlRouter:dn,UrlRuleFactory:un,BaseUrlRule:ln,UrlService:vn,ViewService:pn,UIRouterGlobals:hn,UIRouter:$n,$q:zn,$injector:Kn,BaseLocationServices:ir,HashLocationService:ar,MemoryLocationService:ur,PushStateLocationService:cr,MemoryLocationConfig:dr,BrowserLocationConfig:pr,keyValsToObjectR:Qn,getParams:Zn,parseUrl:Xn,buildUrl:Jn,locationPluginFactory:er,servicesPlugin:hr,hashLocationPlugin:fr,pushStateLocationPlugin:gr,memoryLocationPlugin:mr,UIRouterPluginBase:vr});function wr(){var n=null;return function(e,t){return n=n||V.$injector.get("$templateFactory"),[new Sr(e,t,n)]}}var br=function(e,n){return e.reduce(function(e,t){return e||k(n[t])},!1)};function $r(r){if(!r.parent)return{};var i=["component","bindings","componentProvider"],o=["templateProvider","templateUrl","template","notify","async"].concat(["controller","controllerProvider","controllerAs","resolveAs"]),e=i.concat(o);if(k(r.views)&&br(e,r))throw new Error("State '"+r.name+"' has a 'views' object. It cannot also have \"view properties\" at the state level. Move the following properties into a view (in the 'views' object): "+e.filter(function(e){return k(r[e])}).join(", "));var a={},t=r.views||{$default:ie(r,e)};return N(t,function(e,t){if(t=t||"$default",O(e)&&(e={component:e}),e=Y({},e),br(i,e)&&br(o,e))throw new Error("Cannot combine: "+i.join("|")+" with: "+o.join("|")+" in stateview: '"+t+"@"+r.name+"'");e.resolveAs=e.resolveAs||"$resolve",e.$type="ng1",e.$context=r,e.$name=t;var n=pn.normalizeUIViewTarget(e.$context,e.$name);e.$uiViewName=n.uiViewName,e.$uiViewContextAnchor=n.uiViewContextAnchor,a[t]=e}),a}var _r=0,Sr=function(){function e(e,t,n){var r=this;this.path=e,this.viewDecl=t,this.factory=n,this.$id=_r++,this.loaded=!1,this.getTemplate=function(e,t){return r.component?r.factory.makeComponentTemplate(e,t,r.component,r.viewDecl.bindings):r.template}}return e.prototype.load=function(){var t=this,e=V.$q,n=new At(this.path),r=this.path.reduce(function(e,t){return Y(e,t.paramValues)},{}),i={template:e.when(this.factory.fromConfig(this.viewDecl,r,n)),controller:e.when(this.getController(n))};return e.all(i).then(function(e){return lt.traceViewServiceEvent("Loaded",t),t.controller=e.controller,Y(t,e.template),t})},e.prototype.getController=function(e){var t=this.viewDecl.controllerProvider;if(!M(t))return this.viewDecl.controller;var n=V.$injector.annotate(t),r=A(t)?De(t):t;return new kt("",r,n).get(e)},e}(),Cr=function(){function e(){var r=this;this._useHttp=S.version.minor<3,this.$get=["$http","$templateCache","$injector",function(e,t,n){return r.$templateRequest=n.has&&n.has("$templateRequest")&&n.get("$templateRequest"),r.$http=e,r.$templateCache=t,r}]}return e.prototype.useHttpService=function(e){this._useHttp=e},e.prototype.fromConfig=function(e,t,n){var r=function(e){return V.$q.when(e).then(function(e){return{template:e}})},i=function(e){return V.$q.when(e).then(function(e){return{component:e}})};return k(e.template)?r(this.fromString(e.template,t)):k(e.templateUrl)?r(this.fromUrl(e.templateUrl,t)):k(e.templateProvider)?r(this.fromProvider(e.templateProvider,t,n)):k(e.component)?i(e.component):k(e.componentProvider)?i(this.fromComponentProvider(e.componentProvider,t,n)):r("")},e.prototype.fromString=function(e,t){return D(e)?e(t):e},e.prototype.fromUrl=function(e,t){return D(e)&&(e=e(t)),null==e?null:this._useHttp?this.$http.get(e,{cache:this.$templateCache,headers:{Accept:"text/html"}}).then(function(e){return e.data}):this.$templateRequest(e)},e.prototype.fromProvider=function(e,t,n){var r=V.$injector.annotate(e),i=A(e)?De(e):e;return new kt("",i,r).get(n)},e.prototype.fromComponentProvider=function(e,t,n){var r=V.$injector.annotate(e),i=A(e)?De(e):e;return new kt("",i,r).get(n)},e.prototype.makeComponentTemplate=function(u,l,e,c){c=c||{};var d=3<=S.version.minor?"::":"",p=function(e){var t=je(e);return/^(x|data)-/.exec(t)?"x-"+t:t},t=function(e){var t=V.$injector.get(e+"Directive");if(!t||!t.length)throw new Error("Unable to find component named '"+e+"'");return t.map(kr).reduce(fe,[])}(e).map(function(e){var t=e.name,n=e.type,r=p(t);if(u.attr(r)&&!c[t])return r+"='"+u.attr(r)+"'";var i=c[t]||t;if("@"===n)return r+"='{{"+d+"$resolve."+i+"}}'";if("&"!==n)return r+"='"+d+"$resolve."+i+"'";var o=l.getResolvable(i),a=o&&o.data,s=a&&V.$injector.annotate(a)||[];return r+"='$resolve."+i+(A(a)?"["+(a.length-1)+"]":"")+"("+s.join(",")+")'"}).join(" "),n=p(e);return"<"+n+" "+t+">"},e}();var kr=function(e){return T(e.bindToController)?Dr(e.bindToController):Dr(e.scope)},Dr=function(t){return Object.keys(t||{}).map(function(e){return[e,/^([=<@&])[?]?(.*)/.exec(t[e])]}).filter(function(e){return k(e)&&A(e[1])}).map(function(e){return{name:e[1][2]||e[0],type:e[1][1]}})},xr=function(){function n(e,t){this.stateRegistry=e,this.stateService=t,B(p(n.prototype),this,p(this))}return n.prototype.decorator=function(e,t){return this.stateRegistry.decorator(e,t)||this},n.prototype.state=function(e,t){return T(e)?t=e:t.name=e,this.stateRegistry.register(t),this},n.prototype.onInvalid=function(e){return this.stateService.onInvalid(e)},n}(),Or=function(n){return function(e,t){var i=e[n],o="onExit"===n?"from":"to";return i?function(e,t){var n=new At(e.treeChanges(o)).subContext(t.$$state()),r=Y(zr(n),{$state$:t,$transition$:e});return V.$injector.invoke(i,this,r)}:void 0}},Tr=function(){function e(e){this._urlListeners=[],this.$locationProvider=e;var t=p(e);B(t,this,t,["hashPrefix"])}return e.monkeyPatchPathParameterType=function(e){var t=e.urlMatcherFactory.type("path");t.encode=function(e){return null!=e?e.toString().replace(/(~|\/)/g,function(e){return{"~":"~~","/":"~2F"}[e]}):e},t.decode=function(e){return null!=e?e.toString().replace(/(~~|~2F)/g,function(e){return{"~~":"~","~2F":"/"}[e]}):e}},e.prototype.dispose=function(){},e.prototype.onChange=function(e){var t=this;return this._urlListeners.push(e),function(){return Q(t._urlListeners)(e)}},e.prototype.html5Mode=function(){var e=this.$locationProvider.html5Mode();return(e=T(e)?e.enabled:e)&&this.$sniffer.history},e.prototype.baseHref=function(){return this._baseHref||(this._baseHref=this.$browser.baseHref()||this.$window.location.pathname)},e.prototype.url=function(e,t,n){return void 0===t&&(t=!1),k(e)&&this.$location.url(e),t&&this.$location.replace(),n&&this.$location.state(n),this.$location.url()},e.prototype._runtimeServices=function(e,t,n,r,i){var o=this;this.$location=t,this.$sniffer=n,this.$browser=r,this.$window=i,e.$on("$locationChangeSuccess",function(t){return o._urlListeners.forEach(function(e){return e(t)})});var a=p(t);B(a,this,a,["replace","path","search","hash"]),B(a,this,a,["port","protocol","host"])},e}(),Ar=function(){function n(e){this.router=e}return n.injectableHandler=function(t,n){return function(e){return V.$injector.invoke(n,null,{$match:e,$stateParams:t.globals.params})}},n.prototype.$get=function(){var e=this.router.urlService;return this.router.urlRouter.update(!0),e.interceptDeferred||e.listen(),this.router.urlRouter},n.prototype.rule=function(e){var t=this;if(!D(e))throw new Error("'rule' must be a function");var n=new ln(function(){return e(V.$injector,t.router.locationService)},q);return this.router.urlService.rules.rule(n),this},n.prototype.otherwise=function(e){var t=this,n=this.router.urlService.rules;if(O(e))n.otherwise(e);else{if(!D(e))throw new Error("'rule' must be a string or function");n.otherwise(function(){return e(V.$injector,t.router.locationService)})}return this},n.prototype.when=function(e,t){return(A(t)||D(t))&&(t=n.injectableHandler(this.router,t)),this.router.urlService.rules.when(e,t),this},n.prototype.deferIntercept=function(e){this.router.urlService.deferIntercept(e)},n}();S.module("ui.router.angular1",[]);var Er=S.module("ui.router.init",["ng"]),Pr=S.module("ui.router.util",["ui.router.init"]),Mr=S.module("ui.router.router",["ui.router.util"]),Rr=S.module("ui.router.state",["ui.router.router","ui.router.util","ui.router.angular1"]),Ir=S.module("ui.router",["ui.router.init","ui.router.state","ui.router.angular1"]),Vr=(S.module("ui.router.compat",["ui.router"]),null);function Lr(e){(Vr=this.router=new $n).stateProvider=new xr(Vr.stateRegistry,Vr.stateService),Vr.stateRegistry.decorator("views",$r),Vr.stateRegistry.decorator("onExit",Or("onExit")),Vr.stateRegistry.decorator("onRetain",Or("onRetain")),Vr.stateRegistry.decorator("onEnter",Or("onEnter")),Vr.viewService._pluginapi._viewConfigFactory("ng1",wr());var s=Vr.locationService=Vr.locationConfig=new Tr(e);function t(e,t,n,r,i,o,a){return s._runtimeServices(i,e,r,t,n),delete Vr.router,delete Vr.$get,Vr}return Tr.monkeyPatchPathParameterType(Vr),((Vr.router=Vr).$get=t).$inject=["$location","$browser","$window","$sniffer","$rootScope","$http","$templateCache"],Vr}Lr.$inject=["$locationProvider"];var Fr=function(n){return["$uiRouterProvider",function(e){var t=e.router[n];return t.$get=function(){return t},t}]};function jr(t,e,n){if(V.$injector=t,V.$q=e,!t.hasOwnProperty("strictDi"))try{t.invoke(function(e){})}catch(e){t.strictDi=!!/strict mode/.exec(e&&e.toString())}n.stateRegistry.get().map(function(e){return e.$$state().resolvables}).reduce(fe,[]).filter(function(e){return"deferred"===e.deps}).forEach(function(e){return e.deps=t.annotate(e.resolveFn,t.strictDi)})}jr.$inject=["$injector","$q","$uiRouter"];function Hr(e){e.$watch(function(){lt.approximateDigests++})}Hr.$inject=["$rootScope"],Er.provider("$uiRouter",Lr),Mr.provider("$urlRouter",["$uiRouterProvider",function(e){return e.urlRouterProvider=new Ar(e)}]),Pr.provider("$urlService",Fr("urlService")),Pr.provider("$urlMatcherFactory",["$uiRouterProvider",function(){return Vr.urlMatcherFactory}]),Pr.provider("$templateFactory",function(){return new Cr}),Rr.provider("$stateRegistry",Fr("stateRegistry")),Rr.provider("$uiRouterGlobals",Fr("globals")),Rr.provider("$transitions",Fr("transitionService")),Rr.provider("$state",["$uiRouterProvider",function(){return Y(Vr.stateProvider,{$get:function(){return Vr.stateService}})}]),Rr.factory("$stateParams",["$uiRouter",function(e){return e.globals.params}]),Ir.factory("$view",function(){return Vr.viewService}),Ir.service("$trace",function(){return lt}),Ir.run(Hr),Pr.run(["$urlMatcherFactory",function(e){}]),Rr.run(["$state",function(e){}]),Mr.run(["$urlRouter",function(e){}]),Er.run(jr);var Nr,Yr,Ur,qr,zr=function(n){return n.getTokens().filter(O).map(function(e){var t=n.getResolvable(e);return[e,"NOWAIT"===n.getPolicy(t).async?t.promise:t.data]}).reduce(ke,{})};function Br(e){var t,n=e.match(/^\s*({[^}]*})\s*$/);if(n&&(e="("+n[1]+")"),!(t=e.replace(/\n/g," ").match(/^\s*([^(]*?)\s*(\((.*)\))?\s*$/))||4!==t.length)throw new Error("Invalid state ref '"+e+"'");return{state:t[1]||null,paramExpr:t[3]||null}}function Wr(e){var t=e.parent().inheritedData("$uiView"),n=C("$cfg.path")(t);return n?De(n).state.name:void 0}function Gr(e,t,n){var r=n.uiState||e.current.name,i=Y(function(e,t){return{relative:Wr(e)||t.$current,inherit:!0,source:"sref"}}(t,e),n.uiStateOpts||{}),o=e.href(r,n.uiStateParams,i);return{uiState:r,uiStateParams:n.uiStateParams,uiStateOpts:i,href:o}}function Kr(e){var t="[object SVGAnimatedString]"===Object.prototype.toString.call(e.prop("href")),n="FORM"===e[0].nodeName;return{attr:n?"action":t?"xlink:href":"href",isAnchor:"A"===e.prop("tagName").toUpperCase(),clickable:!n}}function Qr(o,a,s,u,l){return function(e){var t=e.which||e.button,n=l();if(!(1>>0;if(0==i)return-1;var o=+t||0;if(Math.abs(o)===1/0&&(o=0),i<=o)return-1;for(n=Math.max(0<=o?o:i-Math.abs(o),0);n
    ',this.loadingBarTemplate='
    ',this.$get=["$injector","$document","$timeout","$rootScope",function(i,o,a,s){function u(e){if(m){var t=100*e+"%";f.css("width",t),v=e,y&&(a.cancel(c),c=a(function(){n()},250))}}function n(){if(!(1<=r())){var e,t=r();e=0<=t&&t<.25?(3*Math.random()+3)/100:.25<=t&&t<.65?3*Math.random()/100:.65<=t&&t<.9?2*Math.random()/100:.9<=t&&t<.99?.005:0,u(r()+e)}}function r(){return v}function t(){v=0,m=!1}var l,c,d,p=this.parentSelector,h=angular.element(this.loadingBarTemplate),f=h.find("div").eq(0),g=angular.element(this.spinnerTemplate),m=!1,v=0,y=this.autoIncrement,w=this.includeSpinner,b=this.includeBar,$=this.startSize;return{start:function(){if(l||(l=i.get("$animate")),a.cancel(d),!m){var e=o[0],t=e.querySelector?e.querySelector(p):o.find(p)[0];t||(t=e.getElementsByTagName("body")[0]);var n=angular.element(t),r=t.lastChild&&angular.element(t.lastChild);s.$broadcast("cfpLoadingBar:started"),m=!0,b&&l.enter(h,n,r),w&&l.enter(g,n,h),u($)}},set:u,status:r,inc:n,complete:function(){l||(l=i.get("$animate")),s.$broadcast("cfpLoadingBar:completed"),u(1),a.cancel(d),d=a(function(){var e=l.leave(h,t);e&&e.then&&e.then(t),l.leave(g)},500)},autoIncrement:this.autoIncrement,includeSpinner:this.includeSpinner,latencyThreshold:this.latencyThreshold,parentSelector:this.parentSelector,startSize:this.startSize}}]})}(),angular.module("ui.bootstrap",["ui.bootstrap.tpls","ui.bootstrap.transition","ui.bootstrap.collapse","ui.bootstrap.accordion","ui.bootstrap.alert","ui.bootstrap.bindHtml","ui.bootstrap.buttons","ui.bootstrap.carousel","ui.bootstrap.dateparser","ui.bootstrap.position","ui.bootstrap.datepicker","ui.bootstrap.dropdown","ui.bootstrap.modal","ui.bootstrap.pagination","ui.bootstrap.tooltip","ui.bootstrap.popover","ui.bootstrap.progressbar","ui.bootstrap.rating","ui.bootstrap.tabs","ui.bootstrap.timepicker","ui.bootstrap.typeahead"]),angular.module("ui.bootstrap.tpls",["template/accordion/accordion-group.html","template/accordion/accordion.html","template/alert/alert.html","template/carousel/carousel.html","template/carousel/slide.html","template/datepicker/datepicker.html","template/datepicker/day.html","template/datepicker/month.html","template/datepicker/popup.html","template/datepicker/year.html","template/modal/backdrop.html","template/modal/window.html","template/pagination/pager.html","template/pagination/pagination.html","template/tooltip/tooltip-html-unsafe-popup.html","template/tooltip/tooltip-popup.html","template/popover/popover.html","template/progressbar/bar.html","template/progressbar/progress.html","template/progressbar/progressbar.html","template/rating/rating.html","template/tabs/tab.html","template/tabs/tabset.html","template/timepicker/timepicker.html","template/typeahead/typeahead-match.html","template/typeahead/typeahead-popup.html"]),angular.module("ui.bootstrap.transition",[]).factory("$transition",["$q","$timeout","$rootScope",function(a,s,u){function e(e){for(var t in e)if(void 0!==n.style[t])return e[t]}var l=function(e,t,n){n=n||{};var r=a.defer(),i=l[n.animation?"animationEndEventName":"transitionEndEventName"],o=function(){u.$apply(function(){e.unbind(i,o),r.resolve(e)})};return i&&e.bind(i,o),s(function(){angular.isString(t)?e.addClass(t):angular.isFunction(t)?t(e):angular.isObject(t)&&e.css(t),i||r.resolve(e)}),r.promise.cancel=function(){i&&e.unbind(i,o),r.reject("Transition cancelled")},r.promise},n=document.createElement("trans");return l.transitionEndEventName=e({WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd",transition:"transitionend"}),l.animationEndEventName=e({WebkitTransition:"webkitAnimationEnd",MozTransition:"animationend",OTransition:"oAnimationEnd",transition:"animationend"}),l}]),angular.module("ui.bootstrap.collapse",["ui.bootstrap.transition"]).directive("collapse",["$transition",function(u){return{link:function(e,r,t){function n(e){function t(){a===n&&(a=void 0)}var n=u(r,e);return a&&a.cancel(),(a=n).then(t,t),n}function i(){r.removeClass("collapsing"),r.addClass("collapse in"),r.css({height:"auto"})}function o(){r.removeClass("collapsing"),r.addClass("collapse")}var a,s=!0;e.$watch(t.collapse,function(e){e?s?(s=!1,o(),r.css({height:0})):(r.css({height:r[0].scrollHeight+"px"}),r[0].offsetWidth,r.removeClass("collapse in").addClass("collapsing"),n({height:0}).then(o)):s?(s=!1,i()):(r.removeClass("collapse").addClass("collapsing"),n({height:r[0].scrollHeight+"px"}).then(i))})}}}]),angular.module("ui.bootstrap.accordion",["ui.bootstrap.collapse"]).constant("accordionConfig",{closeOthers:!0}).controller("AccordionController",["$scope","$attrs","accordionConfig",function(e,n,r){this.groups=[],this.closeOthers=function(t){(angular.isDefined(n.closeOthers)?e.$eval(n.closeOthers):r.closeOthers)&&angular.forEach(this.groups,function(e){e!==t&&(e.isOpen=!1)})},this.addGroup=function(e){var t=this;this.groups.push(e),e.$on("$destroy",function(){t.removeGroup(e)})},this.removeGroup=function(e){var t=this.groups.indexOf(e);-1!==t&&this.groups.splice(t,1)}}]).directive("accordion",function(){return{restrict:"EA",controller:"AccordionController",transclude:!0,replace:!1,templateUrl:"template/accordion/accordion.html"}}).directive("accordionGroup",function(){return{require:"^accordion",restrict:"EA",transclude:!0,replace:!0,templateUrl:"template/accordion/accordion-group.html",scope:{heading:"@",isOpen:"=?",isDisabled:"=?"},controller:function(){this.setHeading=function(e){this.heading=e}},link:function(t,e,n,r){r.addGroup(t),t.$watch("isOpen",function(e){e&&r.closeOthers(t)}),t.toggleOpen=function(){t.isDisabled||(t.isOpen=!t.isOpen)}}}}).directive("accordionHeading",function(){return{restrict:"EA",transclude:!0,template:"",replace:!0,require:"^accordionGroup",link:function(e,t,n,r,i){r.setHeading(i(e,function(){}))}}}).directive("accordionTransclude",function(){return{require:"^accordionGroup",link:function(e,t,n,r){e.$watch(function(){return r[n.accordionTransclude]},function(e){e&&(t.html(""),t.append(e))})}}}),angular.module("ui.bootstrap.alert",[]).controller("AlertController",["$scope","$attrs",function(e,t){e.closeable="close"in t,this.close=e.close}]).directive("alert",function(){return{restrict:"EA",controller:"AlertController",templateUrl:"template/alert/alert.html",transclude:!0,replace:!0,scope:{type:"@",close:"&"}}}).directive("dismissOnTimeout",["$timeout",function(i){return{require:"alert",link:function(e,t,n,r){i(function(){r.close()},parseInt(n.dismissOnTimeout,10))}}}]),angular.module("ui.bootstrap.bindHtml",[]).directive("bindHtmlUnsafe",function(){return function(e,t,n){t.addClass("ng-binding").data("$binding",n.bindHtmlUnsafe),e.$watch(n.bindHtmlUnsafe,function(e){t.html(e||"")})}}),angular.module("ui.bootstrap.buttons",[]).constant("buttonConfig",{activeClass:"active",toggleEvent:"click"}).controller("ButtonsController",["buttonConfig",function(e){this.activeClass=e.activeClass||"active",this.toggleEvent=e.toggleEvent||"click"}]).directive("btnRadio",function(){return{require:["btnRadio","ngModel"],controller:"ButtonsController",link:function(t,n,r,e){var i=e[0],o=e[1];o.$render=function(){n.toggleClass(i.activeClass,angular.equals(o.$modelValue,t.$eval(r.btnRadio)))},n.bind(i.toggleEvent,function(){var e=n.hasClass(i.activeClass);(!e||angular.isDefined(r.uncheckable))&&t.$apply(function(){o.$setViewValue(e?null:t.$eval(r.btnRadio)),o.$render()})})}}}).directive("btnCheckbox",function(){return{require:["btnCheckbox","ngModel"],controller:"ButtonsController",link:function(r,e,t,n){function i(){return o(t.btnCheckboxTrue,!0)}function o(e,t){var n=r.$eval(e);return angular.isDefined(n)?n:t}var a=n[0],s=n[1];s.$render=function(){e.toggleClass(a.activeClass,angular.equals(s.$modelValue,i()))},e.bind(a.toggleEvent,function(){r.$apply(function(){s.$setViewValue(e.hasClass(a.activeClass)?o(t.btnCheckboxFalse,!1):i()),s.$render()})})}}}),angular.module("ui.bootstrap.carousel",["ui.bootstrap.transition"]).controller("CarouselController",["$scope","$timeout","$interval","$transition",function(a,t,n,s){function u(){r();var e=+a.interval;!isNaN(e)&&0=d.length?d[t-1]:d[t]):t
    ");e.attr({"ng-model":"date","ng-change":"dateSelection()"});var c=angular.element(e.children()[0]);i.datepickerOptions&&angular.forEach(r.$parent.$eval(i.datepickerOptions),function(e,t){c.attr(o(t),e)}),r.watchData={},angular.forEach(["minDate","maxDate","datepickerMode"],function(t){if(i[t]){var e=g(i[t]);if(r.$parent.$watch(e,function(e){r.watchData[t]=e}),c.attr(o(t),"watchData."+t),"datepickerMode"===t){var n=e.assign;r.$watch("watchData."+t,function(e,t){e!==t&&n(r.$parent,e)})}}}),i.dateDisabled&&c.attr("date-disabled","dateDisabled({ date: date, mode: mode })"),n.$parsers.unshift(a),r.dateSelection=function(e){angular.isDefined(e)&&(r.date=e),n.$setViewValue(r.date),n.$render(),u&&(r.isOpen=!1,t[0].focus())},t.bind("input change keyup",function(){r.$apply(function(){r.date=n.$modelValue})}),n.$render=function(){var e=n.$viewValue?y(n.$viewValue,s):"";t.val(e),r.date=a(n.$modelValue)};var d=function(e){r.isOpen&&e.target!==t[0]&&r.$apply(function(){r.isOpen=!1})},p=function(e){r.keydown(e)};t.bind("keydown",p),r.keydown=function(e){27===e.which?(e.preventDefault(),e.stopPropagation(),r.close()):40!==e.which||r.isOpen||(r.isOpen=!0)},r.$watch("isOpen",function(e){e?(r.$broadcast("datepicker.focus"),r.position=l?v.offset(t):v.position(t),r.position.top=r.position.top+t.prop("offsetHeight"),m.bind("click",d)):m.unbind("click",d)}),r.select=function(e){if("today"===e){var t=new Date;angular.isDate(n.$modelValue)?(e=new Date(n.$modelValue)).setFullYear(t.getFullYear(),t.getMonth(),t.getDate()):e=new Date(t.setHours(0,0,0,0))}r.dateSelection(e)},r.close=function(){r.isOpen=!1,t[0].focus()};var h=f(e)(r);e.remove(),l?m.find("body").append(h):t.after(h),r.$on("$destroy",function(){h.remove(),t.unbind("keydown",p),m.unbind("click",d)})}}}]).directive("datepickerPopupWrap",function(){return{restrict:"EA",replace:!0,transclude:!0,templateUrl:"template/datepicker/popup.html",link:function(e,t){t.bind("click",function(e){e.preventDefault(),e.stopPropagation()})}}}),angular.module("ui.bootstrap.dropdown",[]).constant("dropdownConfig",{openClass:"open"}).service("dropdownService",["$document",function(t){var n=null;this.open=function(e){n||(t.bind("click",r),t.bind("keydown",i)),n&&n!==e&&(n.isOpen=!1),n=e},this.close=function(e){n===e&&(n=null,t.unbind("click",r),t.unbind("keydown",i))};var r=function(e){if(n){var t=n.getToggleElement();e&&t&&t[0].contains(e.target)||n.$apply(function(){n.isOpen=!1})}},i=function(e){27===e.which&&(n.focusToggleElement(),r())}}]).controller("DropdownController",["$scope","$attrs","$parse","dropdownConfig","dropdownService","$animate",function(n,t,r,e,i,o){var a,s=this,u=n.$new(),l=e.openClass,c=angular.noop,d=t.onToggle?r(t.onToggle):angular.noop;this.init=function(e){s.$element=e,t.isOpen&&(a=r(t.isOpen),c=a.assign,n.$watch(a,function(e){u.isOpen=!!e}))},this.toggle=function(e){return u.isOpen=arguments.length?!!e:!u.isOpen},this.isOpen=function(){return u.isOpen},u.getToggleElement=function(){return s.toggleElement},u.focusToggleElement=function(){s.toggleElement&&s.toggleElement[0].focus()},u.$watch("isOpen",function(e,t){o[e?"addClass":"removeClass"](s.$element,l),e?(u.focusToggleElement(),i.open(u)):i.close(u),c(n,e),angular.isDefined(e)&&e!==t&&d(n,{open:!!e})}),n.$on("$locationChangeSuccess",function(){u.isOpen=!1}),n.$on("$destroy",function(){u.$destroy()})}]).directive("dropdown",function(){return{controller:"DropdownController",link:function(e,t,n,r){r.init(t)}}}).directive("dropdownToggle",function(){return{require:"?^dropdown",link:function(t,n,r,i){if(i){i.toggleElement=n;var e=function(e){e.preventDefault(),n.hasClass("disabled")||r.disabled||t.$apply(function(){i.toggle()})};n.bind("click",e),n.attr({"aria-haspopup":!0,"aria-expanded":!1}),t.$watch(i.isOpen,function(e){n.attr("aria-expanded",!!e)}),t.$on("$destroy",function(){n.unbind("click",e)})}}}}),angular.module("ui.bootstrap.modal",["ui.bootstrap.transition"]).factory("$$stackedMap",function(){return{createNew:function(){var r=[];return{add:function(e,t){r.push({key:e,value:t})},get:function(e){for(var t=0;t");i.attr("backdrop-class",t.backdropClass),h=c(i)(f),n.append(h)}var o=angular.element("
    ");o.attr({"template-url":t.windowTemplateUrl,"window-class":t.windowClass,size:t.size,index:m.length()-1,animate:"animate"}).html(t.content);var a=c(o)(t.scope);m.top().value.modalDomEl=a,n.append(a),n.addClass(g)},n.close=function(e,t){var n=m.get(e);n&&(n.value.deferred.resolve(t),r(e))},n.dismiss=function(e,t){var n=m.get(e);n&&(n.value.deferred.reject(t),r(e))},n.dismissAll=function(e){for(var t=this.getTop();t;)this.dismiss(t.key,e),t=this.getTop()},n.getTop=function(){return m.top()},n}]).provider("$modal",function(){var p={options:{backdrop:!0,keyboard:!0},$get:["$injector","$rootScope","$q","$http","$templateCache","$controller","$modalStack",function(n,u,r,i,l,c,d){var e={};return e.open=function(o){var a=r.defer(),e=r.defer(),s={result:a.promise,opened:e.promise,close:function(e){d.close(s,e)},dismiss:function(e){d.dismiss(s,e)}};if((o=angular.extend({},p.options,o)).resolve=o.resolve||{},!o.template&&!o.templateUrl)throw new Error("One of template or templateUrl options is required.");var t=r.all([function(e){return e.template?r.when(e.template):i.get(angular.isFunction(e.templateUrl)?e.templateUrl():e.templateUrl,{cache:l}).then(function(e){return e.data})}(o)].concat(function(e){var t=[];return angular.forEach(e,function(e){(angular.isFunction(e)||angular.isArray(e))&&t.push(r.when(n.invoke(e)))}),t}(o.resolve)));return t.then(function(n){var e=(o.scope||u).$new();e.$close=s.close,e.$dismiss=s.dismiss;var t,r={},i=1;o.controller&&(r.$scope=e,r.$modalInstance=s,angular.forEach(o.resolve,function(e,t){r[t]=n[i++]}),t=c(o.controller,r),o.controllerAs&&(e[o.controllerAs]=t)),d.open(s,{scope:e,deferred:a,content:n[0],backdrop:o.backdrop,keyboard:o.keyboard,backdropClass:o.backdropClass,windowClass:o.windowClass,windowTemplateUrl:o.windowTemplateUrl,size:o.size})},function(e){a.reject(e)}),t.then(function(){e.resolve(!0)},function(){e.reject(!1)}),s},e}]};return p}),angular.module("ui.bootstrap.pagination",[]).controller("PaginationController",["$scope","$attrs","$parse",function(n,r,i){var o=this,a={$setViewValue:angular.noop},t=r.numPages?i(r.numPages).assign:angular.noop;this.init=function(e,t){a=e,this.config=t,a.$render=function(){o.render()},r.itemsPerPage?n.$parent.$watch(i(r.itemsPerPage),function(e){o.itemsPerPage=parseInt(e,10),n.totalPages=o.calculateTotalPages()}):this.itemsPerPage=t.itemsPerPage},this.calculateTotalPages=function(){var e=this.itemsPerPage<1?1:Math.ceil(n.totalItems/this.itemsPerPage);return Math.max(e||0,1)},this.render=function(){n.page=parseInt(a.$viewValue,10)||1},n.selectPage=function(e){n.page!==e&&0e?n.selectPage(e):a.$render()})}]).constant("paginationConfig",{itemsPerPage:10,boundaryLinks:!1,directionLinks:!0,firstText:"First",previousText:"Previous",nextText:"Next",lastText:"Last",rotate:!0}).directive("pagination",["$parse","paginationConfig",function(s,u){return{restrict:"EA",scope:{totalItems:"=",firstText:"@",previousText:"@",nextText:"@",lastText:"@"},require:["pagination","?ngModel"],controller:"PaginationController",templateUrl:"template/pagination/pagination.html",replace:!0,link:function(e,t,n,r){function c(e,t,n){return{number:e,text:t,active:n}}var i=r[0],o=r[1];if(o){var d=angular.isDefined(n.maxSize)?e.$parent.$eval(n.maxSize):u.maxSize,p=angular.isDefined(n.rotate)?e.$parent.$eval(n.rotate):u.rotate;e.boundaryLinks=angular.isDefined(n.boundaryLinks)?e.$parent.$eval(n.boundaryLinks):u.boundaryLinks,e.directionLinks=angular.isDefined(n.directionLinks)?e.$parent.$eval(n.directionLinks):u.directionLinks,i.init(o,u),n.maxSize&&e.$parent.$watch(s(n.maxSize),function(e){d=parseInt(e,10),i.render()});var a=i.render;i.render=function(){a(),0';return{restrict:"EA",compile:function(){var _=o(i);return function(e,t,n){function r(){m.isOpen?o():i()}function i(){(!g||e.$eval(n[C+"Enable"]))&&(function(){var e=n[C+"Placement"];m.placement=angular.isDefined(e)?e:D.placement}(),function(){var e=n[C+"PopupDelay"],t=parseInt(e,10);m.popupDelay=isNaN(t)?D.popupDelay:t}(),m.popupDelay?p||(p=x(a,m.popupDelay,!1)).then(function(e){e()}):a()())}function o(){e.$apply(function(){s()})}function a(){return p=null,d&&(x.cancel(d),d=null),m.content?(l&&u(),c=m.$new(),(l=_(c,function(e){h?O.find("body").append(e):t.after(e)})).css({top:0,left:0,display:"block"}),m.$digest(),v(),m.isOpen=!0,m.$digest(),v):angular.noop}function s(){m.isOpen=!1,x.cancel(p),p=null,m.animation?d||(d=x(u,500)):u()}function u(){d=null,l&&(l.remove(),l=null),c&&(c.$destroy(),c=null)}var l,c,d,p,h=!!angular.isDefined(D.appendToBody)&&D.appendToBody,f=k(void 0),g=angular.isDefined(n[C+"Enable"]),m=e.$new(!0),v=function(){var e=T.positionElements(t,l,m.placement,h);e.top+="px",e.left+="px",l.css(e)};m.isOpen=!1,n.$observe(S,function(e){!(m.content=e)&&m.isOpen&&s()}),n.$observe(C+"Title",function(e){m.title=e});var y,w=function(){t.unbind(f.show,i),t.unbind(f.hide,o)};y=n[C+"Trigger"],w(),(f=k(y)).show===f.hide?t.bind(f.show,r):(t.bind(f.show,i),t.bind(f.hide,o));var b=e.$eval(n[C+"Animation"]);m.animation=angular.isDefined(b)?!!b:D.animation;var $=e.$eval(n[C+"AppendToBody"]);(h=angular.isDefined($)?$:h)&&e.$on("$locationChangeSuccess",function(){m.isOpen&&s()}),e.$on("$destroy",function(){x.cancel(d),x.cancel(p),w(),u(),m=null})}}}}}]}).directive("tooltipPopup",function(){return{restrict:"EA",replace:!0,scope:{content:"@",placement:"@",animation:"&",isOpen:"&"},templateUrl:"template/tooltip/tooltip-popup.html"}}).directive("tooltip",["$tooltip",function(e){return e("tooltip","tooltip","mouseenter")}]).directive("tooltipHtmlUnsafePopup",function(){return{restrict:"EA",replace:!0,scope:{content:"@",placement:"@",animation:"&",isOpen:"&"},templateUrl:"template/tooltip/tooltip-html-unsafe-popup.html"}}).directive("tooltipHtmlUnsafe",["$tooltip",function(e){return e("tooltipHtmlUnsafe","tooltip","mouseenter")}]),angular.module("ui.bootstrap.popover",["ui.bootstrap.tooltip"]).directive("popoverPopup",function(){return{restrict:"EA",replace:!0,scope:{title:"@",content:"@",placement:"@",animation:"&",isOpen:"&"},templateUrl:"template/popover/popover.html"}}).directive("popover",["$tooltip",function(e){return e("popover","popover","click")}]),angular.module("ui.bootstrap.progressbar",[]).constant("progressConfig",{animate:!0,max:100}).controller("ProgressController",["$scope","$attrs","progressConfig",function(n,e,t){var r=this,i=angular.isDefined(e.animate)?n.$parent.$eval(e.animate):t.animate;this.bars=[],n.max=angular.isDefined(e.max)?n.$parent.$eval(e.max):t.max,this.addBar=function(t,e){i||e.css({transition:"none"}),this.bars.push(t),t.$watch("value",function(e){t.percent=+(100*e/n.max).toFixed(2)}),t.$on("$destroy",function(){e=null,r.removeBar(t)})},this.removeBar=function(e){this.bars.splice(this.bars.indexOf(e),1)}}]).directive("progress",function(){return{restrict:"EA",replace:!0,transclude:!0,controller:"ProgressController",require:"progress",scope:{},templateUrl:"template/progressbar/progress.html"}}).directive("bar",function(){return{restrict:"EA",replace:!0,transclude:!0,require:"^progress",scope:{value:"=",type:"@"},templateUrl:"template/progressbar/bar.html",link:function(e,t,n,r){r.addBar(e,t)}}}).directive("progressbar",function(){return{restrict:"EA",replace:!0,transclude:!0,controller:"ProgressController",scope:{value:"=",type:"@"},templateUrl:"template/progressbar/progressbar.html",link:function(e,t,n,r){r.addBar(e,angular.element(t.children()[0]))}}}),angular.module("ui.bootstrap.rating",[]).constant("ratingConfig",{max:5,stateOn:null,stateOff:null}).controller("RatingController",["$scope","$attrs","ratingConfig",function(n,r,i){var o={$setViewValue:angular.noop};this.init=function(e){(o=e).$render=this.render,this.stateOn=angular.isDefined(r.stateOn)?n.$parent.$eval(r.stateOn):i.stateOn,this.stateOff=angular.isDefined(r.stateOff)?n.$parent.$eval(r.stateOff):i.stateOff;var t=angular.isDefined(r.ratingStates)?n.$parent.$eval(r.ratingStates):new Array(angular.isDefined(r.max)?n.$parent.$eval(r.max):i.max);n.range=this.buildTemplateObjects(t)},this.buildTemplateObjects=function(e){for(var t=0,n=e.length;t");v.attr({id:m,matches:"matches",active:"activeIdx",select:"select(activeIdx)",query:"query",position:"position"}),angular.isDefined(e.typeaheadTemplateUrl)&&v.attr("template-url",e.typeaheadTemplateUrl);var y=function(){g.matches=[],g.activeIdx=-1,a.attr("aria-expanded",!1)},w=function(e){return m+"-option-"+e};g.$watch("activeIdx",function(e){e<0?a.removeAttr("aria-activedescendant"):a.attr("aria-activedescendant",w(e))});var b=function(r){var i={$viewValue:r};l(o,!0),x.when(f.source(o,i)).then(function(e){var t=r===s.$viewValue;if(t&&u)if(0=t?0$&"):e}}),angular.module("template/accordion/accordion-group.html",[]).run(["$templateCache",function(e){e.put("template/accordion/accordion-group.html",'
    \n
    \n

    \n {{heading}}\n

    \n
    \n
    \n\t
    \n
    \n
    \n')}]),angular.module("template/accordion/accordion.html",[]).run(["$templateCache",function(e){e.put("template/accordion/accordion.html",'
    ')}]),angular.module("template/alert/alert.html",[]).run(["$templateCache",function(e){e.put("template/alert/alert.html",'\n')}]),angular.module("template/carousel/carousel.html",[]).run(["$templateCache",function(e){e.put("template/carousel/carousel.html",'\n')}]),angular.module("template/carousel/slide.html",[]).run(["$templateCache",function(e){e.put("template/carousel/slide.html","
    \n")}]),angular.module("template/datepicker/datepicker.html",[]).run(["$templateCache",function(e){e.put("template/datepicker/datepicker.html",'
    \n \n \n \n
    ')}]),angular.module("template/datepicker/day.html",[]).run(["$templateCache",function(e){e.put("template/datepicker/day.html",'\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
    {{label.abbr}}
    {{ weekNumbers[$index] }}\n \n
    \n')}]),angular.module("template/datepicker/month.html",[]).run(["$templateCache",function(e){e.put("template/datepicker/month.html",'\n \n \n \n \n \n \n \n \n \n \n \n \n
    \n \n
    \n')}]),angular.module("template/datepicker/popup.html",[]).run(["$templateCache",function(e){e.put("template/datepicker/popup.html",'\n')}]),angular.module("template/datepicker/year.html",[]).run(["$templateCache",function(e){e.put("template/datepicker/year.html",'\n \n \n \n \n \n \n \n \n \n \n \n \n
    \n \n
    \n')}]),angular.module("template/modal/backdrop.html",[]).run(["$templateCache",function(e){e.put("template/modal/backdrop.html",'\n')}]),angular.module("template/modal/window.html",[]).run(["$templateCache",function(e){e.put("template/modal/window.html",'')}]),angular.module("template/pagination/pager.html",[]).run(["$templateCache",function(e){e.put("template/pagination/pager.html",'')}]),angular.module("template/pagination/pagination.html",[]).run(["$templateCache",function(e){e.put("template/pagination/pagination.html",'')}]),angular.module("template/tooltip/tooltip-html-unsafe-popup.html",[]).run(["$templateCache",function(e){e.put("template/tooltip/tooltip-html-unsafe-popup.html",'
    \n
    \n
    \n
    \n')}]),angular.module("template/tooltip/tooltip-popup.html",[]).run(["$templateCache",function(e){e.put("template/tooltip/tooltip-popup.html",'
    \n
    \n
    \n
    \n')}]),angular.module("template/popover/popover.html",[]).run(["$templateCache",function(e){e.put("template/popover/popover.html",'
    \n
    \n\n
    \n

    \n
    \n
    \n
    \n')}]),angular.module("template/progressbar/bar.html",[]).run(["$templateCache",function(e){e.put("template/progressbar/bar.html",'
    ')}]),angular.module("template/progressbar/progress.html",[]).run(["$templateCache",function(e){e.put("template/progressbar/progress.html",'
    ')}]),angular.module("template/progressbar/progressbar.html",[]).run(["$templateCache",function(e){e.put("template/progressbar/progressbar.html",'
    \n
    \n
    ')}]),angular.module("template/rating/rating.html",[]).run(["$templateCache",function(e){e.put("template/rating/rating.html",'\n \n ({{ $index < value ? \'*\' : \' \' }})\n \n')}]),angular.module("template/tabs/tab.html",[]).run(["$templateCache",function(e){e.put("template/tabs/tab.html",'
  • \n {{heading}}\n
  • \n')}]),angular.module("template/tabs/tabset.html",[]).run(["$templateCache",function(e){e.put("template/tabs/tabset.html",'
    \n \n
    \n
    \n
    \n
    \n
    \n')}]),angular.module("template/timepicker/timepicker.html",[]).run(["$templateCache",function(e){e.put("template/timepicker/timepicker.html",'\n\t\n\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\n
     
    \n\t\t\t\t\n\t\t\t:\n\t\t\t\t\n\t\t\t
     
    \n')}]),angular.module("template/typeahead/typeahead-match.html",[]).run(["$templateCache",function(e){e.put("template/typeahead/typeahead-match.html",'')}]),angular.module("template/typeahead/typeahead-popup.html",[]).run(["$templateCache",function(e){e.put("template/typeahead/typeahead-popup.html",'\n')}]),function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):e.moment=t()}(this,function(){"use strict";var e,i;function p(){return e.apply(null,arguments)}function a(e){return e instanceof Array||"[object Array]"===Object.prototype.toString.call(e)}function s(e){return null!=e&&"[object Object]"===Object.prototype.toString.call(e)}function o(e){return void 0===e}function u(e){return"number"==typeof e||"[object Number]"===Object.prototype.toString.call(e)}function l(e){return e instanceof Date||"[object Date]"===Object.prototype.toString.call(e)}function r(e,t){var n,r=[];for(n=0;n>>0,r=0;rSe(e)?(o=e+1,s-Se(e)):(o=e,s),{year:o,dayOfYear:a}}function Ye(e,t,n){var r,i,o=He(e.year(),t,n),a=Math.floor((e.dayOfYear()-o-1)/7)+1;return a<1?r=a+Ue(i=e.year()-1,t,n):a>Ue(e.year(),t,n)?(r=a-Ue(e.year(),t,n),i=e.year()+1):(i=e.year(),r=a),{week:r,year:i}}function Ue(e,t,n){var r=He(e,t,n),i=He(e+1,t,n);return(Se(e)-r+i)/7}Y("w",["ww",2],"wo","week"),Y("W",["WW",2],"Wo","isoWeek"),P("week","w"),P("isoWeek","W"),V("week",5),V("isoWeek",5),ue("w",Q),ue("ww",Q,B),ue("W",Q),ue("WW",Q,B),he(["w","ww","W","WW"],function(e,t,n,r){t[r.substr(0,1)]=_(e)});function qe(e,t){return e.slice(t,7).concat(e.slice(0,t))}Y("d",0,"do","day"),Y("dd",0,0,function(e){return this.localeData().weekdaysMin(this,e)}),Y("ddd",0,0,function(e){return this.localeData().weekdaysShort(this,e)}),Y("dddd",0,0,function(e){return this.localeData().weekdays(this,e)}),Y("e",0,0,"weekday"),Y("E",0,0,"isoWeekday"),P("day","d"),P("weekday","e"),P("isoWeekday","E"),V("day",11),V("weekday",11),V("isoWeekday",11),ue("d",Q),ue("e",Q),ue("E",Q),ue("dd",function(e,t){return t.weekdaysMinRegex(e)}),ue("ddd",function(e,t){return t.weekdaysShortRegex(e)}),ue("dddd",function(e,t){return t.weekdaysRegex(e)}),he(["dd","ddd","dddd"],function(e,t,n,r){var i=n._locale.weekdaysParse(e,r,n._strict);null!=i?t.d=i:f(n).invalidWeekday=e}),he(["d","e","E"],function(e,t,n,r){t[r]=_(e)});var ze="Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_");var Be="Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_");var We="Su_Mo_Tu_We_Th_Fr_Sa".split("_");var Ge=ae;var Ke=ae;var Qe=ae;function Ze(){function e(e,t){return t.length-e.length}var t,n,r,i,o,a=[],s=[],u=[],l=[];for(t=0;t<7;t++)n=d([2e3,1]).day(t),r=this.weekdaysMin(n,""),i=this.weekdaysShort(n,""),o=this.weekdays(n,""),a.push(r),s.push(i),u.push(o),l.push(r),l.push(i),l.push(o);for(a.sort(e),s.sort(e),u.sort(e),l.sort(e),t=0;t<7;t++)s[t]=ce(s[t]),u[t]=ce(u[t]),l[t]=ce(l[t]);this._weekdaysRegex=new RegExp("^("+l.join("|")+")","i"),this._weekdaysShortRegex=this._weekdaysRegex,this._weekdaysMinRegex=this._weekdaysRegex,this._weekdaysStrictRegex=new RegExp("^("+u.join("|")+")","i"),this._weekdaysShortStrictRegex=new RegExp("^("+s.join("|")+")","i"),this._weekdaysMinStrictRegex=new RegExp("^("+a.join("|")+")","i")}function Xe(){return this.hours()%12||12}function Je(e,t){Y(e,0,0,function(){return this.localeData().meridiem(this.hours(),this.minutes(),t)})}function et(e,t){return t._meridiemParse}Y("H",["HH",2],0,"hour"),Y("h",["hh",2],0,Xe),Y("k",["kk",2],0,function(){return this.hours()||24}),Y("hmm",0,0,function(){return""+Xe.apply(this)+L(this.minutes(),2)}),Y("hmmss",0,0,function(){return""+Xe.apply(this)+L(this.minutes(),2)+L(this.seconds(),2)}),Y("Hmm",0,0,function(){return""+this.hours()+L(this.minutes(),2)}),Y("Hmmss",0,0,function(){return""+this.hours()+L(this.minutes(),2)+L(this.seconds(),2)}),Je("a",!0),Je("A",!1),P("hour","h"),V("hour",13),ue("a",et),ue("A",et),ue("H",Q),ue("h",Q),ue("k",Q),ue("HH",Q,B),ue("hh",Q,B),ue("kk",Q,B),ue("hmm",Z),ue("hmmss",X),ue("Hmm",Z),ue("Hmmss",X),pe(["H","HH"],ve),pe(["k","kk"],function(e,t,n){var r=_(e);t[ve]=24===r?0:r}),pe(["a","A"],function(e,t,n){n._isPm=n._locale.isPM(e),n._meridiem=e}),pe(["h","hh"],function(e,t,n){t[ve]=_(e),f(n).bigHour=!0}),pe("hmm",function(e,t,n){var r=e.length-2;t[ve]=_(e.substr(0,r)),t[ye]=_(e.substr(r)),f(n).bigHour=!0}),pe("hmmss",function(e,t,n){var r=e.length-4,i=e.length-2;t[ve]=_(e.substr(0,r)),t[ye]=_(e.substr(r,2)),t[we]=_(e.substr(i)),f(n).bigHour=!0}),pe("Hmm",function(e,t,n){var r=e.length-2;t[ve]=_(e.substr(0,r)),t[ye]=_(e.substr(r))}),pe("Hmmss",function(e,t,n){var r=e.length-4,i=e.length-2;t[ve]=_(e.substr(0,r)),t[ye]=_(e.substr(r,2)),t[we]=_(e.substr(i))});var tt,nt=xe("Hours",!0),rt={calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},longDateFormat:{LTS:"h:mm:ss A",LT:"h:mm A",L:"MM/DD/YYYY",LL:"MMMM D, YYYY",LLL:"MMMM D, YYYY h:mm A",LLLL:"dddd, MMMM D, YYYY h:mm A"},invalidDate:"Invalid date",ordinal:"%d",dayOfMonthOrdinalParse:/\d{1,2}/,relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",ss:"%d seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},months:Pe,monthsShort:Me,week:{dow:0,doy:6},weekdays:ze,weekdaysMin:We,weekdaysShort:Be,meridiemParse:/[ap]\.?m?\.?/i},it={},ot={};function at(e){return e?e.toLowerCase().replace("_","-"):e}function st(e){var t=null;if(!it[e]&&"undefined"!=typeof module&&module&&module.exports)try{t=tt._abbr,require("./locale/"+e),ut(t)}catch(e){}return it[e]}function ut(e,t){var n;return e&&((n=o(t)?ct(e):lt(e,t))?tt=n:"undefined"!=typeof console&&console.warn&&console.warn("Locale "+e+" not found. Did you forget to load it?")),tt._abbr}function lt(e,t){if(null===t)return delete it[e],null;var n,r=rt;if(t.abbr=e,null!=it[e])x("defineLocaleOverride","use moment.updateLocale(localeName, config) to change an existing locale. moment.defineLocale(localeName, config) should only be used for creating a new locale See http://momentjs.com/guides/#/warnings/define-locale/ for more info."),r=it[e]._config;else if(null!=t.parentLocale)if(null!=it[t.parentLocale])r=it[t.parentLocale]._config;else{if(null==(n=st(t.parentLocale)))return ot[t.parentLocale]||(ot[t.parentLocale]=[]),ot[t.parentLocale].push({name:e,config:t}),null;r=n._config}return it[e]=new A(T(r,t)),ot[e]&&ot[e].forEach(function(e){lt(e.name,e.config)}),ut(e),it[e]}function ct(e){var t;if(e&&e._locale&&e._locale._abbr&&(e=e._locale._abbr),!e)return tt;if(!a(e)){if(t=st(e))return t;e=[e]}return function(e){for(var t,n,r,i,o=0;o=t&&S(i,n,!0)>=t-1)break;t--}o++}return tt}(e)}function dt(e){var t,n=e._a;return n&&-2===f(e).overflow&&(t=n[ge]<0||11Ae(n[fe],n[ge])?me:n[ve]<0||24Ue(n,o,a)?f(e)._overflowWeeks=!0:null!=u?f(e)._overflowWeekday=!0:(s=Ne(n,r,i,o,a),e._a[fe]=s.year,e._dayOfYear=s.dayOfYear)}(e),null!=e._dayOfYear&&(o=pt(e._a[fe],r[fe]),(e._dayOfYear>Se(o)||0===e._dayOfYear)&&(f(e)._overflowDayOfYear=!0),n=je(o,0,e._dayOfYear),e._a[ge]=n.getUTCMonth(),e._a[me]=n.getUTCDate()),t=0;t<3&&null==e._a[t];++t)e._a[t]=a[t]=r[t];for(;t<7;t++)e._a[t]=a[t]=null==e._a[t]?2===t?1:0:e._a[t];24===e._a[ve]&&0===e._a[ye]&&0===e._a[we]&&0===e._a[be]&&(e._nextDay=!0,e._a[ve]=0),e._d=(e._useUTC?je:function(e,t,n,r,i,o,a){var s;return e<100&&0<=e?(s=new Date(e+400,t,n,r,i,o,a),isFinite(s.getFullYear())&&s.setFullYear(e)):s=new Date(e,t,n,r,i,o,a),s}).apply(null,a),i=e._useUTC?e._d.getUTCDay():e._d.getDay(),null!=e._tzm&&e._d.setUTCMinutes(e._d.getUTCMinutes()-e._tzm),e._nextDay&&(e._a[ve]=24),e._w&&void 0!==e._w.d&&e._w.d!==i&&(f(e).weekdayMismatch=!0)}}var ft=/^\s*((?:[+-]\d{6}|\d{4})-(?:\d\d-\d\d|W\d\d-\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?::\d\d(?::\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/,gt=/^\s*((?:[+-]\d{6}|\d{4})(?:\d\d\d\d|W\d\d\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?:\d\d(?:\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/,mt=/Z|[+-]\d\d(?::?\d\d)?/,vt=[["YYYYYY-MM-DD",/[+-]\d{6}-\d\d-\d\d/],["YYYY-MM-DD",/\d{4}-\d\d-\d\d/],["GGGG-[W]WW-E",/\d{4}-W\d\d-\d/],["GGGG-[W]WW",/\d{4}-W\d\d/,!1],["YYYY-DDD",/\d{4}-\d{3}/],["YYYY-MM",/\d{4}-\d\d/,!1],["YYYYYYMMDD",/[+-]\d{10}/],["YYYYMMDD",/\d{8}/],["GGGG[W]WWE",/\d{4}W\d{3}/],["GGGG[W]WW",/\d{4}W\d{2}/,!1],["YYYYDDD",/\d{7}/]],yt=[["HH:mm:ss.SSSS",/\d\d:\d\d:\d\d\.\d+/],["HH:mm:ss,SSSS",/\d\d:\d\d:\d\d,\d+/],["HH:mm:ss",/\d\d:\d\d:\d\d/],["HH:mm",/\d\d:\d\d/],["HHmmss.SSSS",/\d\d\d\d\d\d\.\d+/],["HHmmss,SSSS",/\d\d\d\d\d\d,\d+/],["HHmmss",/\d\d\d\d\d\d/],["HHmm",/\d\d\d\d/],["HH",/\d\d/]],wt=/^\/?Date\((\-?\d+)/i;function bt(e){var t,n,r,i,o,a,s=e._i,u=ft.exec(s)||gt.exec(s);if(u){for(f(e).iso=!0,t=0,n=vt.length;tn.valueOf():n.valueOf()this.clone().month(0).utcOffset()||this.utcOffset()>this.clone().month(5).utcOffset()},fn.isLocal=function(){return!!this.isValid()&&!this._isUTC},fn.isUtcOffset=function(){return!!this.isValid()&&this._isUTC},fn.isUtc=Nt,fn.isUTC=Nt,fn.zoneAbbr=function(){return this._isUTC?"UTC":""},fn.zoneName=function(){return this._isUTC?"Coordinated Universal Time":""},fn.dates=n("dates accessor is deprecated. Use date instead.",un),fn.months=n("months accessor is deprecated. Use month instead",Ie),fn.years=n("years accessor is deprecated. Use year instead",De),fn.zone=n("moment().zone is deprecated, use moment().utcOffset instead. http://momentjs.com/guides/#/warnings/zone/",function(e,t){return null!=e?("string"!=typeof e&&(e=-e),this.utcOffset(e,t),this):-this.utcOffset()}),fn.isDSTShifted=n("isDSTShifted is deprecated. See http://momentjs.com/guides/#/warnings/dst-shifted/ for more information",function(){if(!o(this._isDSTShifted))return this._isDSTShifted;var e={};if(y(e,this),(e=Dt(e))._a){var t=e._isUTC?d(e._a):Ot(e._a);this._isDSTShifted=this.isValid()&&0-1/0&&t.selectable&&c[e]){var r=c[e](t.utcDateValue),i=[];if(r.weeks)for(var o=0;o9+n,past:l.year()r.indexOf(e.startView))throw new Error("startView must be greater than minView");if(!a.isNumber(e.minuteStep))throw new Error("minuteStep must be numeric");if(e.minuteStep<=0||60<=e.minuteStep)throw new Error("minuteStep must be greater than zero and less than 60");if(null!==e.configureOn&&!a.isString(e.configureOn))throw new Error("configureOn must be a string");if(null!==e.configureOn&&e.configureOn.length<1)throw new Error("configureOn must not be an empty string");if(null!==e.renderOn&&!a.isString(e.renderOn))throw new Error("renderOn must be a string");if(null!==e.renderOn&&e.renderOn.length<1)throw new Error("renderOn must not be an empty string");if(null!==e.modelType&&!a.isString(e.modelType))throw new Error("modelType must be a string");if(null!==e.modelType&&e.modelType.length<1)throw new Error("modelType must not be an empty string");"Date"!==e.modelType&&"moment"!==e.modelType&&"milliseconds"!==e.modelType&&(e.parseFormat=e.modelType);if(null!==e.dropdownSelector&&!a.isString(e.dropdownSelector))throw new Error("dropdownSelector must be a string");null===e.dropdownSelector||"undefined"!=typeof jQuery&&"function"==typeof jQuery().dropdown||(i.error("Please DO NOT specify the dropdownSelector option unless you are using jQuery AND Bootstrap.js. Please include jQuery AND Bootstrap.js, or write code to close the dropdown in the on-set-time callback. \n\nThe dropdownSelector configuration option is being removed because it will not function properly."),delete e.dropdownSelector)}}}a.module("ui.bootstrap.datetimepicker",[]).service("dateTimePickerConfig",function(){var e={bg:{previous:"предишна",next:"следваща"},ca:{previous:"anterior",next:"següent"},da:{previous:"forrige",next:"næste"},de:{previous:"vorige",next:"weiter"},"en-au":{previous:"previous",next:"next"},"en-gb":{previous:"previous",next:"next"},en:{previous:"previous",next:"next"},"es-us":{previous:"atrás",next:"siguiente"},es:{previous:"atrás",next:"siguiente"},fi:{previous:"edellinen",next:"seuraava"},fr:{previous:"précédent",next:"suivant"},hu:{previous:"előző",next:"következő"},it:{previous:"precedente",next:"successivo"},ja:{previous:"前へ",next:"次へ"},ml:{previous:"മുൻപുള്ളത്",next:"അടുത്തത്"},nl:{previous:"vorige",next:"volgende"},pl:{previous:"poprzednia",next:"następna"},"pt-br":{previous:"anteriores",next:"próximos"},pt:{previous:"anterior",next:"próximo"},ro:{previous:"anterior",next:"următor"},ru:{previous:"предыдущая",next:"следующая"},sk:{previous:"predošlá",next:"ďalšia"},sv:{previous:"föregående",next:"nästa"},tr:{previous:"önceki",next:"sonraki"},uk:{previous:"назад",next:"далі"},"zh-cn":{previous:"上一页",next:"下一页"},"zh-tw":{previous:"上一頁",next:"下一頁"}}[b.locale().toLowerCase()];return a.extend({},{configureOn:null,dropdownSelector:null,minuteStep:5,minView:"minute",modelType:"Date",parseFormat:"YYYY-MM-DDTHH:mm:ss.SSSZZ",renderOn:null,startView:"day"},{screenReader:e})}).service("dateTimePickerValidator",t).directive("datetimepicker",e),e.$inject=["dateTimePickerConfig","dateTimePickerValidator"],t.$inject=["$log"]}),angular.module("rzTable",[]),angular.module("rzTable").directive("rzTable",["resizeStorage","$injector","$parse",function(s,i,o){function e(e){}function r(n,r,i){return function(e,t){!0!==i.busy&&void 0!==t&&t!==e&&(a(n),u(n,r,i))}}function a(e){_=!0,v.map(function(e){e.remove()}),v=[]}function u(e,t,n){if(!n.busy){h=$(e).find("th"),l=n.mode,d=!angular.isDefined(n.saveTableSizes)||n.saveTableSizes,p=n.profile;var r=function(t,e){try{var n=e.rzMode?t.mode:"BasicResizer",r=i.get(n);return r}catch(e){return console.error("The resizer "+t.mode+" was not found"),null}}(n,t);r&&(b=new r(e,h,w),d&&(S=s.loadTableSizes(e,n.mode,n.profile)),g=b.handles(h),f=b.ctrlColumns,b.setup(),function(o){o&&($(y).width("auto"),f.each(function(e,t){var n=angular.element(t).scope(),r=n.rzCol||$(t).attr("id"),i=o[r];$(t).css({width:i})}),b.onTableReady())}(S),g.each(function(e,t){!function(e,t,n){var r=$("
    ",{class:e.options.handleClass||"rz-handle"});$(n).prepend(r),v.push(r);var i=b.handleMiddleware(r,n);!function(i,e,o,a){$(o).mousedown(function(e){_&&(b.onFirstDrag(a,o),b.onTableReady(),_=!1),i.options.onResizeStarted&&i.options.onResizeStarted(a);var t={};b.intervene&&(((t=b.intervene.selector(a)).column=t).orgWidth=$(t).width()),e.preventDefault(),$(o).addClass(i.options.handleClassActive||"rz-handle-active");var n=e.clientX,r=$(a).width();m=function(o,a,s,u,l){return function(e){var t=e.clientX,n=t-s,r=b.calculate(u,n);if(!(rt)||(this.fixedColumn.width()<=this.getMinWidth(this.fixedColumn)?(this.bound=t,$(this.fixedColumn).width(this.minWidth),!0):void 0)},e.prototype.onEndDrag=function(){this.bound=!1},e.prototype.calculate=function(e,t){return e-t},e}]),angular.module("rzTable").factory("OverflowResizer",["ResizerModel",function(r){function e(e,t,n){r.call(this,e,t,n)}return(e.prototype=Object.create(r.prototype)).setup=function(){$(this.container).css({overflow:"auto"})},e.prototype.onTableReady=function(){$(this.table).width(1)},e}]),function(e,t){"function"==typeof define&&define.amd?define(["angular"],t):"object"==typeof module&&module.exports?module.exports=t(require("angular")):e.angularClipboard=t(e.angular)}(this,function(i){return i.module("angular-clipboard",[]).factory("clipboard",["$document","$window",function(o,a){return{copyText:function(e,t){var n=a.pageXOffset||o[0].documentElement.scrollLeft,r=a.pageYOffset||o[0].documentElement.scrollTop,i=function(e,t){var n=o[0].createElement("textarea");return n.style.position="absolute",n.style.fontSize="12pt",n.style.border="0",n.style.padding="0",n.style.margin="0",n.style.left="-10000px",n.style.top=(a.pageYOffset||o[0].documentElement.scrollTop)+"px",n.textContent=e,n}(e);o[0].body.appendChild(i),function(e){try{o[0].body.style.webkitUserSelect="initial";var t=o[0].getSelection();t.removeAllRanges();var n=document.createRange();n.selectNodeContents(e),t.addRange(n),e.select(),e.setSelectionRange(0,999999);try{if(!o[0].execCommand("copy"))throw"failure copy"}finally{t.removeAllRanges()}}finally{o[0].body.style.webkitUserSelect=""}}(i),a.scrollTo(n,r),o[0].body.removeChild(i)},supported:"queryCommandSupported"in o[0]&&o[0].queryCommandSupported("copy")}}]).directive("clipboard",["clipboard",function(r){return{restrict:"A",scope:{onCopied:"&",onError:"&",text:"=",supported:"=?"},link:function(t,n){t.supported=r.supported,n.on("click",function(e){try{r.copyText(t.text,n[0]),i.isFunction(t.onCopied)&&t.$evalAsync(t.onCopied())}catch(e){i.isFunction(t.onError)&&t.$evalAsync(t.onError({err:e}))}})}}}])}),function(e,t){"function"==typeof define&&define.amd?define("sifter",t):"object"==typeof exports?module.exports=t():e.Sifter=t()}(this,function(){var e=function(e,t){this.items=e,this.settings=t||{diacritics:!0}};e.prototype.tokenize=function(e){if(!(e=s(String(e||"").toLowerCase()))||!e.length)return[];var t,n,r,i,o=[],a=e.split(/ +/);for(t=0,n=a.length;t/g,">").replace(/"/g,""")},t={before:function(e,t,n){var r=e[t];e[t]=function(){return n.apply(e,arguments),r.apply(e,arguments)}},after:function(t,e,n){var r=t[e];t[e]=function(){var e=r.apply(t,arguments);return n.apply(t,arguments),e}}},n=function(t,n,e){var r,i=t.trigger,o={};for(r in t.trigger=function(){var e=arguments[0];if(-1===n.indexOf(e))return i.apply(t,arguments);o[e]=arguments},e.apply(t,[]),t.trigger=i,o)o.hasOwnProperty(r)&&i.apply(t,o[r])},p=function(e){var t={};if("selectionStart"in e)t.start=e.selectionStart,t.length=e.selectionEnd-t.start;else if(document.selection){e.focus();var n=document.selection.createRange(),r=document.selection.createRange().text.length;n.moveStart("character",-e.value.length),t.start=n.text.length-r,t.length=r}return t},$=function(c){var d=null,e=function(e,t){var n,r,i,o,a,s,u,l;t=t||{},(e=e||window.event||{}).metaKey||e.altKey||(t.force||!1!==c.data("grow"))&&(n=c.val(),e.type&&"keydown"===e.type.toLowerCase()&&(i=48<=(r=e.keyCode)&&r<=57||65<=r&&r<=90||96<=r&&r<=111||186<=r&&r<=222||32===r,46===r||8===r?(l=p(c[0])).length?n=n.substring(0,l.start)+n.substring(l.start+l.length):8===r&&l.start?n=n.substring(0,l.start-1)+n.substring(l.start+1):46===r&&void 0!==l.start&&(n=n.substring(0,l.start)+n.substring(l.start+1)):i&&(s=e.shiftKey,u=String.fromCharCode(e.keyCode),n+=u=s?u.toUpperCase():u.toLowerCase())),o=c.attr("placeholder"),!n&&o&&(n=o),(a=function(e,t){return e?(_.$testInput||(_.$testInput=C("").css({position:"absolute",top:-99999,left:-99999,width:"auto",padding:0,whiteSpace:"pre"}).appendTo("body")),_.$testInput.text(e),function(e,t,n){var r,i,o={};if(n)for(r=0,i=n.length;r").addClass(d.wrapperClass).addClass(s).addClass(a),t=C("
    ").addClass(d.inputClass).addClass("items").appendTo(e),n=C('').appendTo(t).attr("tabindex",g.is(":disabled")?"-1":c.tabIndex),o=C(d.dropdownParent||e),r=C("
    ").addClass(d.dropdownClass).addClass(a).hide().appendTo(o),i=C("
    ").addClass(d.dropdownContentClass).appendTo(r),(l=g.attr("id"))&&(n.attr("id",l+"-selectized"),C("label[for='"+l+"']").attr("for",l+"-selectized")),c.settings.copyClassesToDropdown&&r.addClass(s),e.css({width:g[0].style.width}),c.plugins.names.length&&(u="plugin-"+c.plugins.names.join(" plugin-"),e.addClass(u),r.addClass(u)),(null===d.maxItems||1[data-selectable]",function(e){e.stopImmediatePropagation()}),r.on("mouseenter","[data-selectable]",function(){return c.onOptionHover.apply(c,arguments)}),r.on("mousedown click","[data-selectable]",function(){return c.onOptionSelect.apply(c,arguments)}),function(n,e,t,r){n.on(e,t,function(e){for(var t=e.target;t&&t.parentNode!==n[0];)t=t.parentNode;return e.currentTarget=t,r.apply(this,[e])})}(t,"mousedown","*:not(input)",function(){return c.onItemSelect.apply(c,arguments)}),$(n),t.on({mousedown:function(){return c.onMouseDown.apply(c,arguments)},click:function(){return c.onClick.apply(c,arguments)}}),n.on({mousedown:function(e){e.stopPropagation()},keydown:function(){return c.onKeyDown.apply(c,arguments)},keyup:function(){return c.onKeyUp.apply(c,arguments)},keypress:function(){return c.onKeyPress.apply(c,arguments)},resize:function(){c.positionDropdown.apply(c,[])},blur:function(){return c.onBlur.apply(c,arguments)},focus:function(){return c.ignoreBlur=!1,c.onFocus.apply(c,arguments)},paste:function(){return c.onPaste.apply(c,arguments)}}),f.on("keydown"+p,function(e){c.isCmdDown=e[v?"metaKey":"ctrlKey"],c.isCtrlDown=e[v?"altKey":"ctrlKey"],c.isShiftDown=e.shiftKey}),f.on("keyup"+p,function(e){e.keyCode===w&&(c.isCtrlDown=!1),16===e.keyCode&&(c.isShiftDown=!1),e.keyCode===y&&(c.isCmdDown=!1)}),f.on("mousedown"+p,function(e){if(c.isFocused){if(e.target===c.$dropdown[0]||e.target.parentNode===c.$dropdown[0])return!1;c.$control.has(e.target).length||e.target===c.$control[0]||c.blur(e.target)}}),h.on(["scroll"+p,"resize"+p].join(" "),function(){c.isOpen&&c.positionDropdown.apply(c,arguments)}),h.on("mousemove"+p,function(){c.ignoreHover=!1}),this.revertSettings={$children:g.children().detach(),tabindex:g.attr("tabindex")},g.attr("tabindex",-1).hide().after(c.$wrapper),C.isArray(d.items)&&(c.setValue(d.items),delete d.items),b&&g.on("invalid"+p,function(e){e.preventDefault(),c.isInvalid=!0,c.refreshState()}),c.updateOriginalInput(),c.refreshItems(),c.refreshState(),c.updatePlaceholder(),c.isSetup=!0,g.is(":disabled")&&c.disable(),c.on("change",this.onChange),g.data("selectize",c),g.addClass("selectized"),c.trigger("initialize"),!0===d.preload&&c.onSearchChange("")},setupTemplates:function(){var n=this.settings.labelField,r=this.settings.optgroupLabelField,e={optgroup:function(e){return'
    '+e.html+"
    "},optgroup_header:function(e,t){return'
    '+t(e[r])+"
    "},option:function(e,t){return'
    '+t(e[n])+"
    "},item:function(e,t){return'
    '+t(e[n])+"
    "},option_create:function(e,t){return'
    Add '+t(e.input)+"
    "}};this.settings.render=C.extend({},e,this.settings.render)},setupCallbacks:function(){var e,t,n={initialize:"onInitialize",change:"onChange",item_add:"onItemAdd",item_remove:"onItemRemove",clear:"onClear",option_add:"onOptionAdd",option_remove:"onOptionRemove",option_clear:"onOptionClear",optgroup_add:"onOptionGroupAdd",optgroup_remove:"onOptionGroupRemove",optgroup_clear:"onOptionGroupClear",dropdown_open:"onDropdownOpen",dropdown_close:"onDropdownClose",type:"onType",load:"onLoad",focus:"onFocus",blur:"onBlur"};for(e in n)n.hasOwnProperty(e)&&(t=this.settings[n[e]])&&this.on(e,t)},onClick:function(e){this.isFocused&&this.isOpen||(this.focus(),e.preventDefault())},onMouseDown:function(e){var t=this,n=e.isDefaultPrevented();C(e.target);if(t.isFocused){if(e.target!==t.$control_input[0])return"single"===t.settings.mode?t.isOpen?t.close():t.open():n||t.setActiveItem(null),!1}else n||window.setTimeout(function(){t.focus()},0)},onChange:function(){this.$input.trigger("change")},onPaste:function(e){var i=this;i.isFull()||i.isInputHidden||i.isLocked?e.preventDefault():i.settings.splitOn&&setTimeout(function(){var e=i.$control_input.val();if(e.match(i.settings.splitOn))for(var t=C.trim(e).split(i.settings.splitOn),n=0,r=t.length;n=this.settings.maxItems},updateOriginalInput:function(e){var t,n,r,i,o=this;if(e=e||{},1===o.tagType){for(r=[],t=0,n=o.items.length;t'+s(i)+"");r.length||this.$input.attr("multiple")||r.push(''),o.$input.html(r.join(""))}else o.$input.val(o.getValue()),o.$input.attr("value",o.$input.val());o.isSetup&&(e.silent||o.trigger("change",o.$input.val()))},updatePlaceholder:function(){if(this.settings.placeholder){var e=this.$control_input;this.items.length?e.removeAttr("placeholder"):e.attr("placeholder",this.settings.placeholder),e.triggerHandler("update",{force:!0})}},open:function(){var e=this;e.isLocked||e.isOpen||"multi"===e.settings.mode&&e.isFull()||(e.focus(),e.isOpen=!0,e.refreshState(),e.$dropdown.css({visibility:"hidden",display:"block"}),e.positionDropdown(),e.$dropdown.css({visibility:"visible"}),e.trigger("dropdown_open",e.$dropdown))},close:function(){var e=this,t=e.isOpen;"single"===e.settings.mode&&e.items.length&&(e.hideInput(),e.isBlurring||e.$control_input.blur()),e.isOpen=!1,e.$dropdown.hide(),e.setActiveOption(null),e.refreshState(),t&&e.trigger("dropdown_close",e.$dropdown)},positionDropdown:function(){var e=this.$control,t="body"===this.settings.dropdownParent?e.offset():e.position();t.top+=e.outerHeight(!0),this.$dropdown.css({width:e[0].getBoundingClientRect().width,top:t.top,left:t.left})},clear:function(e){var t=this;t.items.length&&(t.$control.children(":not(input)").remove(),t.items=[],t.lastQuery=null,t.setCaret(0),t.setActiveItem(null),t.updatePlaceholder(),t.updateOriginalInput({silent:e}),t.refreshState(),t.showInput(),t.trigger("clear"))},insertAtCaret:function(e){var t=Math.min(this.caretPos,this.items.length),n=e[0],r=this.buffer||this.$control[0];0===t?r.insertBefore(n,r.firstChild):r.insertBefore(n,r.childNodes[t]),this.setCaret(t+1)},deleteSelection:function(e){var t,n,r,i,o,a,s,u,l,c=this;if(r=e&&8===e.keyCode?-1:1,i=p(c.$control_input[0]),c.$activeOption&&!c.settings.hideSelected&&(s=c.getAdjacentOption(c.$activeOption,-1).attr("data-value")),o=[],c.$activeItems.length){for(l=c.$control.children(".active:"+(0
    '+e.title+'×
    '}},e),n.setup=(t=n.setup,function(){t.apply(n,arguments),n.$dropdown_header=C(e.html(e)),n.$dropdown.prepend(n.$dropdown_header)})}),_.define("optgroup_columns",function(s){var o,u=this;s=C.extend({equalizeWidth:!0,equalizeHeight:!0},s),this.getAdjacentOption=function(e,t){var n=e.closest("[data-group]").find("[data-selectable]"),r=n.index(e)+t;return 0<=r&&r
    ',e=e.firstChild,n.body.appendChild(e),t=l.width=e.offsetWidth-e.clientWidth,n.body.removeChild(e)),t},e=function(){var e,t,n,r,i,o,a;if((t=(a=C("[data-group]",u.$dropdown_content)).length)&&u.$dropdown_content.width()){if(s.equalizeHeight){for(e=n=0;e'+r.label+"",n.setup=(i=o.setup,function(){if(r.append){var t=o.settings.render.item;o.settings.render.item=function(e){return function(e,t){var n=e.search(/(<\/[^>]+>\s*)$/);return e.substring(0,n)+t+e.substring(n)}(t.apply(n,arguments),a)}}i.apply(n,arguments),n.$control.on("click","."+r.className,function(e){if(e.preventDefault(),!o.isLocked){var t=C(e.currentTarget).parent();o.setActiveItem(t),o.deleteSelection()&&o.setCaret(o.items.length)}})})):function(n,r){r.className="remove-single";var i,o=n,a=''+r.label+"";n.setup=(i=o.setup,function(){if(r.append){var e=C(o.$input.context).attr("id"),t=(C("#"+e),o.settings.render.item);o.settings.render.item=function(e){return function(e,t){return C("").append(e).append(t)}(t.apply(n,arguments),a)}}i.apply(n,arguments),n.$control.on("click","."+r.className,function(e){e.preventDefault(),o.isLocked||o.clear()})})}(this,e)}),_.define("restore_on_backspace",function(r){var i,e=this;r.text=r.text||function(e){return e[this.settings.labelField]},this.onKeyDown=(i=e.onKeyDown,function(e){var t,n;return 8===e.keyCode&&""===this.$control_input.val()&&!this.$activeItems.length&&0<=(t=this.caretPos-1)&&t",{class:function(){var e=[];return e.push(i.options.state?"on":"off"),i.options.size&&e.push(i.options.size),i.options.disabled&&e.push("disabled"),i.options.readonly&&e.push("readonly"),i.options.indeterminate&&e.push("indeterminate"),i.options.inverse&&e.push("inverse"),i.$element.attr("id")&&e.push("id-"+i.$element.attr("id")),e.map(i._getClass.bind(i)).concat([i.options.baseClass],i._getClasses(i.options.wrapperClass)).join(" ")}}),this.$container=s("
    ",{class:this._getClass("container")}),this.$on=s("",{html:this.options.onText,class:this._getClass("handle-on")+" "+this._getClass(this.options.onColor)}),this.$off=s("",{html:this.options.offText,class:this._getClass("handle-off")+" "+this._getClass(this.options.offColor)}),this.$label=s("",{html:this.options.labelText,class:this._getClass("label")}),this.$element.on("init.bootstrapSwitch",this.options.onInit.bind(this,r)),this.$element.on("switchChange.bootstrapSwitch",function(){for(var e=arguments.length,t=Array(e),n=0;n-n._handleWidth/2;n._dragEnd=!1,n.state(n.options.inverse?!t:t)}else n.state(!n.options.state);n._dragStart=!1}},"mouseleave.bootstrapSwitch":function(){n.$label.trigger("mouseup.bootstrapSwitch")}})}},{key:"_externalLabelHandler",value:function(){var t=this,n=this.$element.closest("label");n.on("click",function(e){e.preventDefault(),e.stopImmediatePropagation(),e.target===n[0]&&t.toggleState()})}},{key:"_formHandler",value:function(){var e=this.$element.closest("form");e.data("bootstrap-switch")||e.on("reset.bootstrapSwitch",function(){window.setTimeout(function(){e.find("input").filter(function(){return s(this).data("bootstrap-switch")}).each(function(){return s(this).bootstrapSwitch("state",this.checked)})},1)}).data("bootstrap-switch",!0)}},{key:"_getClass",value:function(e){return this.options.baseClass+"-"+e}},{key:"_getClasses",value:function(e){return s.isArray(e)?e.map(this._getClass.bind(this)):[this._getClass(e)]}}]),t}();s.fn.bootstrapSwitch=function(o){for(var e=arguments.length,a=Array(1
    ');var r,i=f.overlay?"":" ngdialog-no-overlay";if((d=T('
    ')).html(f.overlay?'
    '+t+"
    ":'
    '+t+"
    "),d.data("$ngDialogOptions",f),c.ngDialogId=l,f.data&&O.isString(f.data)){var o=f.data.replace(/^\s*/,"")[0];c.ngDialogData="{"===o||"["===o?O.fromJson(f.data):new String(f.data),c.ngDialogData.ngDialogId=l}else f.data&&O.isObject(f.data)&&(c.ngDialogData=f.data,c.ngDialogData.ngDialogId=l);(f.className&&d.addClass(f.className),f.appendClassName&&d.addClass(f.appendClassName),f.width&&(h=d[0].querySelector(".ngdialog-content"),O.isString(f.width)?h.style.width=f.width:h.style.width=f.width+"px"),f.height&&(h=d[0].querySelector(".ngdialog-content"),O.isString(f.height)?h.style.height=f.height:h.style.height=f.height+"px"),f.disableAnimation&&d.addClass("ngdialog-disabled-animation"),p=f.appendTo&&O.isString(f.appendTo)?O.element(document.querySelector(f.appendTo)):b.body,$.applyAriaAttributes(d,f),f.preCloseCallback)&&(O.isFunction(f.preCloseCallback)?r=f.preCloseCallback:O.isString(f.preCloseCallback)&&c&&(O.isFunction(c[f.preCloseCallback])?r=c[f.preCloseCallback]:c.$parent&&O.isFunction(c.$parent[f.preCloseCallback])?r=c.$parent[f.preCloseCallback]:m&&O.isFunction(m[f.preCloseCallback])&&(r=m[f.preCloseCallback])),r&&d.data("$ngDialogPreCloseCallback",r));if(c.closeThisDialog=function(e){$.closeDialog(d,e)},f.controller&&(O.isString(f.controller)||O.isArray(f.controller)||O.isFunction(f.controller))){var a;f.controllerAs&&O.isString(f.controllerAs)&&(a=f.controllerAs);var s=w(f.controller,O.extend(n,{$scope:c,$element:d}),!0,a);f.bindToController&&O.extend(s.instance,{ngDialogId:c.ngDialogId,ngDialogData:c.ngDialogData,closeThisDialog:c.closeThisDialog,confirm:c.confirm}),"function"==typeof s?d.data("$ngDialogControllerController",s()):d.data("$ngDialogControllerController",s)}if(v(function(){var e=document.querySelectorAll(".ngdialog");$.deactivateAll(e),g(d)(c);var t=y.innerWidth-b.body.prop("clientWidth");b.html.addClass(f.bodyClassName),b.body.addClass(f.bodyClassName);var n=t-(y.innerWidth-b.body.prop("clientWidth"));0window.innerHeight&&(u=v,t++,e=0);var l=u?0===e?u:u+w:v,c=n+t*(b+s);o.css(o._positionY,l+"px"),"center"==o._positionX?o.css("left",parseInt(window.innerWidth/2-s/2)+"px"):o.css(o._positionX,c+"px"),r[o._positionY+o._positionX]=l+a,0m.maxCount&&0===i&&o.scope().kill(!0),e++}}},i=c(e)(n);i._positionY=h.positionY,i._positionX=h.positionX,i._priority=h.priority,i.addClass(h.type);var o=function(e){("click"===(e=e.originalEvent||e).type||"opacity"===e.propertyName&&1<=e.elapsedTime)&&(n.onClose&&n.$apply(n.onClose(i)),i.remove(),$.splice($.indexOf(i),1),n.$destroy(),r())};h.closeOnClick&&(i.addClass("clickable"),i.bind("click",o)),i.bind("webkitTransitionEnd oTransitionEnd otransitionend transitionend msTransitionEnd",o),angular.isNumber(h.delay)&&l(function(){i.addClass("killed")},h.delay),t("none"),angular.element(document.querySelector(h.container)).append(i);var a=-(parseInt(i[0].offsetHeight)+50);if(i.css(i._positionY,a+"px"),$.push(i),"center"==h.positionX){var s=parseInt(i[0].offsetWidth);i.css("left",parseInt(window.innerWidth/2-s/2)+"px")}l(function(){t("")}),n._templateElement=i,n.kill=function(e){e?(n.onClose&&n.$apply(n.onClose(n._templateElement)),$.splice($.indexOf(n._templateElement),1),n._templateElement.remove(),n.$destroy(),l(r)):n._templateElement.addClass("killed")},l(r),_||(angular.element(g).bind("resize",function(e){l(r)}),_=!0),u.resolve(n)}var u=a.defer();"object"==typeof h&&null!==h||(h={message:h}),h.scope=h.scope?h.scope:o,h.template=h.templateUrl?h.templateUrl:m.templateUrl,h.delay=angular.isUndefined(h.delay)?s:h.delay,h.type=e||h.type||m.type||"",h.positionY=h.positionY?h.positionY:m.positionY,h.positionX=h.positionX?h.positionX:m.positionX,h.replaceMessage=h.replaceMessage?h.replaceMessage:m.replaceMessage,h.onClose=h.onClose?h.onClose:m.onClose,h.closeOnClick=null!==h.closeOnClick&&void 0!==h.closeOnClick?h.closeOnClick:m.closeOnClick,h.container=h.container?h.container:m.container,h.priority=h.priority?h.priority:m.priority;var n=i.get(h.template);return n?t(n):r.get(h.template,{cache:!0}).then(function(e){t(e.data)}).catch(function(e){throw new Error("Template ("+h.template+") could not be loaded. "+e)}),u.promise};return t.primary=function(e){return this(e,"primary")},t.error=function(e){return this(e,"error")},t.success=function(e){return this(e,"success")},t.info=function(e){return this(e,"info")},t.warning=function(e){return this(e,"warning")},t.clearAll=function(){angular.forEach($,function(e){e.addClass("killed")})},t}]}),angular.module("ui-notification").run(["$templateCache",function(e){e.put("angular-ui-notification.html",'

    ')}]),function(){var g="__default";angular.module("angularUtils.directives.dirPagination",[]).directive("dirPaginate",["$compile","$parse","paginationService",function(l,c,d){return{terminal:!0,multiElement:!0,priority:100,compile:function(e,t){var s=t.dirPaginate,n=s.match(/^\s*([\s\S]+?)\s+in\s+([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+track\s+by\s+([\s\S]+?))?\s*$/),r=/\|\s*itemsPerPage\s*:\s*(.*\(\s*\w*\)|([^\)]*?(?=\s+as\s+))|[^\)]*)/;if(null===n[2].match(r))throw"pagination directive: the 'itemsPerPage' filter must be set.";var i=n[2].replace(r,""),u=c(i);!function(e){angular.forEach(e,function(e){1===e.nodeType&&angular.element(e).attr("dir-paginate-no-compile",!0)})}(e);var o=t.paginationId||g;return d.registerInstance(o),function(e,t,n){var r=c(n.paginationId)(e)||n.paginationId||g;d.registerInstance(r);var i=function(e,t){var n=!!e.match(/(\|\s*itemsPerPage\s*:[^|]*:[^|]*)/);return t===g||n?e:e.replace(/(\|\s*itemsPerPage\s*:\s*[^|\s]*)/,"$1 : '"+t+"'")}(s,r);!function(e,t,n){e[0].hasAttribute("dir-paginate-start")||e[0].hasAttribute("data-dir-paginate-start")?(t.$set("ngRepeatStart",n),e.eq(e.length-1).attr("ng-repeat-end",!0)):t.$set("ngRepeat",n)}(t,n,i),function(e){angular.forEach(e,function(e){1===e.nodeType&&angular.element(e).removeAttr("dir-paginate-no-compile")}),e.eq(0).removeAttr("dir-paginate-start").removeAttr("dir-paginate").removeAttr("data-dir-paginate-start").removeAttr("data-dir-paginate"),e.eq(e.length-1).removeAttr("dir-paginate-end").removeAttr("data-dir-paginate-end")}(t);var o=l(t),a=function(e,t,n){var r;if(t.currentPage)r=c(t.currentPage);else{var i=(n+"__currentPage").replace(/\W/g,"_");e[i]=1,r=c(i)}return r}(e,n,r);d.setCurrentPageParser(r,a,e),void 0!==n.totalItems?(d.setAsyncModeTrue(r),e.$watch(function(){return c(n.totalItems)(e)},function(e){0<=e&&d.setCollectionLength(r,e)})):(d.setAsyncModeFalse(r),e.$watchCollection(function(){return u(e)},function(e){if(e){var t=e instanceof Array?e.length:Object.keys(e).length;d.setCollectionLength(r,t)}})),o(e)}}}}]).directive("dirPaginateNoCompile",function(){return{priority:5e3,terminal:!0}}).directive("dirPaginationControls",["paginationService","paginationTemplate",function(d,n){var p=/^\d+$/,e={restrict:"AE",scope:{maxSize:"=?",onPageChange:"&?",paginationId:"=?",autoHide:"=?"},link:function(r,e,t){var n=t.paginationId||g,i=r.paginationId||t.paginationId||g;if(!d.isRegistered(i)&&!d.isRegistered(n)){var o=i!==g?" (id: "+i+") ":" ";window.console&&console.warn("Pagination directive: the pagination controls"+o+"cannot be used without the corresponding pagination directive, which was not found at link time.")}r.maxSize||(r.maxSize=9);r.autoHide=void 0===r.autoHide||r.autoHide,r.directionLinks=!angular.isDefined(t.directionLinks)||r.$parent.$eval(t.directionLinks),r.boundaryLinks=!!angular.isDefined(t.boundaryLinks)&&r.$parent.$eval(t.boundaryLinks);var a=Math.max(r.maxSize,5);function s(e){if(d.isRegistered(i)&&c(e)){var t=r.pagination.current;r.pages=h(e,d.getCollectionLength(i),d.getItemsPerPage(i),a),r.pagination.current=e,l(),r.onPageChange&&r.onPageChange({newPageNumber:e,oldPageNumber:t})}}function u(){if(d.isRegistered(i)){var e=parseInt(d.getCurrentPage(i))||1;r.pages=h(e,d.getCollectionLength(i),d.getItemsPerPage(i),a),r.pagination.current=e,r.pagination.last=r.pages[r.pages.length-1],r.pagination.last
  • «
  • {{ pageNumber }}
  • »
  • ')}])}();var com_github_culmat_jsTreeTable=function(){function u(e,r,i){return i=i||"children",$.each(e,function(e,t){!function n(e){e[i]&&$.each(e[i],function(e,t){n(t)}),r(e)}(t)}),e}function t(e,n,o,a){var t=e;n=n||"id",o=o||"parent",a=a||"children";var s=[];$.each(t,function(e,t){s[t[n]]=t});var u=[];return $.each(t,function(e,r){var t=r[o];if($.isArray(t)||(t=[t]),0==t.length)u.push(r);else{var i=!1;$.each(t,function(e,t){var n=s[t];n&&(n[a]||(n[a]=[]),$.inArray(r,n[a])<0&&n[a].push(r),i=!0)}),i||u.push(r)}}),u}function l(e,a,s,u,l,t){a=a||"children",s=s||"id",t=t||{};var n=0,r=$("");$.each(t,function(e,t){"class"==e&&"jsTT"!=t?r.addClass(t):r.attr(e,t)});var i=$(""),o=$(""),c=$("");return r.append(i),i.append(o),r.append(c),u?$.each(u,function(e,t){$(o).append($(""))}):($(o).append($("")),$.each(e[0],function(e,t){e!=a&&e!=s&&$(o).append($(""))})),function r(e,i,o,t){n=Math.max(n,o),$.each(e,function(e,n){n["data-tt-level"]=o,function(n,e){var r=$("");$(r).attr("data-tt-id",n[s]),$(r).attr("data-tt-level",n["data-tt-level"]),n[a]&&0!=n[a].length?$(r).attr("data-tt-isnode",!0):$(r).attr("data-tt-isleaf",!0),e&&$(r).attr("data-tt-parent-id",e[s]),l?l($(r),n):u?$.each(u,function(e,t){$(r).append($(""))}):($(r).append($("")),$.each(n,function(e,t){e!=a&&e!=s&&"data-tt-level"!=e&&$(r).append($(""))})),c.append(r)}(n,t),n[i]&&$.each(n[i],function(e,t){r([t],i,o+1,n)})})}(e,a,1),e[0]&&(e[0].maxLevel=n),r}function n(e,t){return $.each(e,function(e,n){$.each(t,function(e,t){n[t]=$(n).attr(t)})}),e}function c(i){i.addClass("jsTT"),i.expandLevel=function(n){$("tr[data-tt-level]",i).each(function(e){var t=parseInt($(this).attr("data-tt-level"));n-1')):r.prepend($('')),r.prepend($('')),t.trExpand=function(e){if(!(this.trChildren.length<1)){e&&(this.trChildrenVisible=!0,$("#state",this).get(0).src=o);var n=e||this.trChildrenVisible;$.each(this.trChildren,function(e,t){n&&$(t).css("display","table-row"),t.trExpand()})}},t.trCollapse=function(e){this.trChildren.length<1||(e&&(this.trChildrenVisible=!1,$("#state",this).get(0).src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAsTAAALEwEAmpwYAAAKT2lDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVNnVFPpFj333vRCS4iAlEtvUhUIIFJCi4AUkSYqIQkQSoghodkVUcERRUUEG8igiAOOjoCMFVEsDIoK2AfkIaKOg6OIisr74Xuja9a89+bN/rXXPues852zzwfACAyWSDNRNYAMqUIeEeCDx8TG4eQuQIEKJHAAEAizZCFz/SMBAPh+PDwrIsAHvgABeNMLCADATZvAMByH/w/qQplcAYCEAcB0kThLCIAUAEB6jkKmAEBGAYCdmCZTAKAEAGDLY2LjAFAtAGAnf+bTAICd+Jl7AQBblCEVAaCRACATZYhEAGg7AKzPVopFAFgwABRmS8Q5ANgtADBJV2ZIALC3AMDOEAuyAAgMADBRiIUpAAR7AGDIIyN4AISZABRG8lc88SuuEOcqAAB4mbI8uSQ5RYFbCC1xB1dXLh4ozkkXKxQ2YQJhmkAuwnmZGTKBNA/g88wAAKCRFRHgg/P9eM4Ors7ONo62Dl8t6r8G/yJiYuP+5c+rcEAAAOF0ftH+LC+zGoA7BoBt/qIl7gRoXgugdfeLZrIPQLUAoOnaV/Nw+H48PEWhkLnZ2eXk5NhKxEJbYcpXff5nwl/AV/1s+X48/Pf14L7iJIEyXYFHBPjgwsz0TKUcz5IJhGLc5o9H/LcL//wd0yLESWK5WCoU41EScY5EmozzMqUiiUKSKcUl0v9k4t8s+wM+3zUAsGo+AXuRLahdYwP2SycQWHTA4vcAAPK7b8HUKAgDgGiD4c93/+8//UegJQCAZkmScQAAXkQkLlTKsz/HCAAARKCBKrBBG/TBGCzABhzBBdzBC/xgNoRCJMTCQhBCCmSAHHJgKayCQiiGzbAdKmAv1EAdNMBRaIaTcA4uwlW4Dj1wD/phCJ7BKLyBCQRByAgTYSHaiAFiilgjjggXmYX4IcFIBBKLJCDJiBRRIkuRNUgxUopUIFVIHfI9cgI5h1xGupE7yAAygvyGvEcxlIGyUT3UDLVDuag3GoRGogvQZHQxmo8WoJvQcrQaPYw2oefQq2gP2o8+Q8cwwOgYBzPEbDAuxsNCsTgsCZNjy7EirAyrxhqwVqwDu4n1Y8+xdwQSgUXACTYEd0IgYR5BSFhMWE7YSKggHCQ0EdoJNwkDhFHCJyKTqEu0JroR+cQYYjIxh1hILCPWEo8TLxB7iEPENyQSiUMyJ7mQAkmxpFTSEtJG0m5SI+ksqZs0SBojk8naZGuyBzmULCAryIXkneTD5DPkG+Qh8lsKnWJAcaT4U+IoUspqShnlEOU05QZlmDJBVaOaUt2ooVQRNY9aQq2htlKvUYeoEzR1mjnNgxZJS6WtopXTGmgXaPdpr+h0uhHdlR5Ol9BX0svpR+iX6AP0dwwNhhWDx4hnKBmbGAcYZxl3GK+YTKYZ04sZx1QwNzHrmOeZD5lvVVgqtip8FZHKCpVKlSaVGyovVKmqpqreqgtV81XLVI+pXlN9rkZVM1PjqQnUlqtVqp1Q61MbU2epO6iHqmeob1Q/pH5Z/YkGWcNMw09DpFGgsV/jvMYgC2MZs3gsIWsNq4Z1gTXEJrHN2Xx2KruY/R27iz2qqaE5QzNKM1ezUvOUZj8H45hx+Jx0TgnnKKeX836K3hTvKeIpG6Y0TLkxZVxrqpaXllirSKtRq0frvTau7aedpr1Fu1n7gQ5Bx0onXCdHZ4/OBZ3nU9lT3acKpxZNPTr1ri6qa6UbobtEd79up+6Ynr5egJ5Mb6feeb3n+hx9L/1U/W36p/VHDFgGswwkBtsMzhg8xTVxbzwdL8fb8VFDXcNAQ6VhlWGX4YSRudE8o9VGjUYPjGnGXOMk423GbcajJgYmISZLTepN7ppSTbmmKaY7TDtMx83MzaLN1pk1mz0x1zLnm+eb15vft2BaeFostqi2uGVJsuRaplnutrxuhVo5WaVYVVpds0atna0l1rutu6cRp7lOk06rntZnw7Dxtsm2qbcZsOXYBtuutm22fWFnYhdnt8Wuw+6TvZN9un2N/T0HDYfZDqsdWh1+c7RyFDpWOt6azpzuP33F9JbpL2dYzxDP2DPjthPLKcRpnVOb00dnF2e5c4PziIuJS4LLLpc+Lpsbxt3IveRKdPVxXeF60vWdm7Obwu2o26/uNu5p7ofcn8w0nymeWTNz0MPIQ+BR5dE/C5+VMGvfrH5PQ0+BZ7XnIy9jL5FXrdewt6V3qvdh7xc+9j5yn+M+4zw33jLeWV/MN8C3yLfLT8Nvnl+F30N/I/9k/3r/0QCngCUBZwOJgUGBWwL7+Hp8Ib+OPzrbZfay2e1BjKC5QRVBj4KtguXBrSFoyOyQrSH355jOkc5pDoVQfujW0Adh5mGLw34MJ4WHhVeGP45wiFga0TGXNXfR3ENz30T6RJZE3ptnMU85ry1KNSo+qi5qPNo3ujS6P8YuZlnM1VidWElsSxw5LiquNm5svt/87fOH4p3iC+N7F5gvyF1weaHOwvSFpxapLhIsOpZATIhOOJTwQRAqqBaMJfITdyWOCnnCHcJnIi/RNtGI2ENcKh5O8kgqTXqS7JG8NXkkxTOlLOW5hCepkLxMDUzdmzqeFpp2IG0yPTq9MYOSkZBxQqohTZO2Z+pn5mZ2y6xlhbL+xW6Lty8elQfJa7OQrAVZLQq2QqboVFoo1yoHsmdlV2a/zYnKOZarnivN7cyzytuQN5zvn//tEsIS4ZK2pYZLVy0dWOa9rGo5sjxxedsK4xUFK4ZWBqw8uIq2Km3VT6vtV5eufr0mek1rgV7ByoLBtQFr6wtVCuWFfevc1+1dT1gvWd+1YfqGnRs+FYmKrhTbF5cVf9go3HjlG4dvyr+Z3JS0qavEuWTPZtJm6ebeLZ5bDpaql+aXDm4N2dq0Dd9WtO319kXbL5fNKNu7g7ZDuaO/PLi8ZafJzs07P1SkVPRU+lQ27tLdtWHX+G7R7ht7vPY07NXbW7z3/T7JvttVAVVN1WbVZftJ+7P3P66Jqun4lvttXa1ObXHtxwPSA/0HIw6217nU1R3SPVRSj9Yr60cOxx++/p3vdy0NNg1VjZzG4iNwRHnk6fcJ3/ceDTradox7rOEH0x92HWcdL2pCmvKaRptTmvtbYlu6T8w+0dbq3nr8R9sfD5w0PFl5SvNUyWna6YLTk2fyz4ydlZ19fi753GDborZ752PO32oPb++6EHTh0kX/i+c7vDvOXPK4dPKy2+UTV7hXmq86X23qdOo8/pPTT8e7nLuarrlca7nuer21e2b36RueN87d9L158Rb/1tWeOT3dvfN6b/fF9/XfFt1+cif9zsu72Xcn7q28T7xf9EDtQdlD3YfVP1v+3Njv3H9qwHeg89HcR/cGhYPP/pH1jw9DBY+Zj8uGDYbrnjg+OTniP3L96fynQ89kzyaeF/6i/suuFxYvfvjV69fO0ZjRoZfyl5O/bXyl/erA6xmv28bCxh6+yXgzMV70VvvtwXfcdx3vo98PT+R8IH8o/2j5sfVT0Kf7kxmTk/8EA5jz/GMzLdsAAAAgY0hSTQAAeiUAAICDAAD5/wAAgOkAAHUwAADqYAAAOpgAABdvkl/FRgAAAHlJREFUeNrcU1sNgDAQ6wgmcAM2MICGGlg1gJnNzWQcvwQGy1j4oUl/7tH0mpwzM7SgQyO+EZAUWh2MkkzSWhJwuRAlHYsJwEwyvs1gABDuzqoJcTw5qxaIJN0bgQRgIjnlmn1heSO5PE6Y2YXe+5Cr5+h++gs12AcAS6FS+7YOsj4AAAAASUVORK5CYII="),$.each(this.trChildren,function(e,t){$(t).css("display","none"),t.trCollapse()}))},$(t).click(function(){this.trChildrenVisible?this.trCollapse(!0):this.trExpand(!0)})}),i}return{depthFirst:u,makeTree:t,renderTree:l,attr2attr:n,treeTable:c,appendTreetable:function(e,t){(t=t||{}).idAttr=t.idAttr||"id",t.childrenAttr=t.childrenAttr||"children";var n=t.controls||[];t.mountPoint||(t.mountPoint=$("body")),t.depthFirst&&u(e,t.depthFirst,t.childrenAttr);var r=l(e,t.childrenAttr,t.idAttr,t.renderedAttr,t.renderer,t.tableAttributes);c(r),t.replaceContent&&t.mountPoint.html("");var i,o,a=t.initialExpandLevel?parseInt(t.initialExpandLevel):-1;if(a=Math.min(a,e[0].maxLevel),r.expandLevel(a),t.slider){var s=$('
    ');s.width("200px"),s.slider({min:1,max:e[0].maxLevel,range:"min",value:a,slide:function(e,t){r.expandLevel(t.value)}}),n=[s].concat(t.controls)}return 0"),$.each(i,function(e,t){o.append($('
    "+t+""+s+""+e+"
    "+n[e]+""+n[s]+""+t+"').append(t))}),$('').append(o))),t.mountPoint.append(r),r},jsTreeTable:"1.0",register:function(n){$.each(this,function(e,t){"register"!=e&&(n[e]=t)})}}}(); ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/gulpfile.js ================================================ const gulp = require('gulp'); const plugins = require('gulp-load-plugins')(); const open = require('open'); const app = { srcPath: 'app/', // 源代码 devPath: 'tmp/', // 开发打包 prdPath: 'dist/' // 生产打包 }; const JS_LIBS = [ 'node_modules/angular-ui-router/release/angular-ui-router.js', 'node_modules/oclazyload/dist/ocLazyLoad.min.js', 'node_modules/angular-loading-bar/build/loading-bar.min.js', 'node_modules/angular-bootstrap/ui-bootstrap-tpls.min.js', 'node_modules/moment/moment.js', 'node_modules/angular-date-time-input/src/dateTimeInput.js', 'node_modules/angularjs-bootstrap-datetimepicker/src/js/datetimepicker.js', 'node_modules/angular-table-resize/dist/angular-table-resize.min.js', 'node_modules/angular-clipboard/angular-clipboard.js', 'node_modules/selectize/dist/js/standalone/selectize.js', 'node_modules/angular-selectize2/dist/selectize.js', 'node_modules/bootstrap-switch/dist/js/bootstrap-switch.min.js', 'node_modules/ng-dialog/js/ngDialog.js', 'node_modules/angular-ui-notification/dist/angular-ui-notification.min.js', 'node_modules/angular-utils-pagination/dirPagination.js', 'app/scripts/libs/treeTable.js', ]; const CSS_APP = [ 'node_modules/angular-loading-bar/build/loading-bar.min.css', 'node_modules/bootstrap-switch/dist/css/bootstrap3/bootstrap-switch.css', 'node_modules/ng-dialog/css/ngDialog.min.css', 'node_modules/ng-dialog/css/ngDialog-theme-default.css', 'node_modules/angularjs-bootstrap-datetimepicker/src/css/datetimepicker.css', 'node_modules/angular-ui-notification/dist/angular-ui-notification.min.css', 'node_modules/angular-table-resize/dist/angular-table-resize.css', 'node_modules/selectize/dist/css/selectize.css', 'app/styles/page.css', 'app/styles/timeline.css', 'app/styles/main.css' ]; const JS_APP = [ 'app/scripts/app.js', 'app/scripts/filters/filters.js', 'app/scripts/services/auth_service.js', 'app/scripts/services/appservice.js', 'app/scripts/services/flow_service_v1.js', 'app/scripts/services/flow_service_v2.js', 'app/scripts/services/degradeservice.js', 'app/scripts/services/systemservice.js', 'app/scripts/services/machineservice.js', 'app/scripts/services/identityservice.js', 'app/scripts/services/metricservice.js', 'app/scripts/services/param_flow_service.js', 'app/scripts/services/authority_service.js', 'app/scripts/services/cluster_state_service.js', ]; gulp.task('lib', function () { gulp.src(JS_LIBS) .pipe(plugins.concat('app.vendor.js')) .pipe(gulp.dest(app.devPath + 'js')) .pipe(plugins.uglify()) .pipe(gulp.dest(app.prdPath + 'js')) .pipe(plugins.connect.reload()); }); /* * css任务 * 在src下创建style文件夹,里面存放less文件。 */ gulp.task('css', function () { gulp.src(CSS_APP) .pipe(plugins.concat('app.css')) .pipe(gulp.dest(app.devPath + 'css')) .pipe(plugins.cssmin()) .pipe(gulp.dest(app.prdPath + 'css')) .pipe(plugins.connect.reload()); }); /* * js任务 * 在src目录下创建script文件夹,里面存放所有的js文件 */ gulp.task('js', function () { gulp.src(JS_APP) .pipe(plugins.concat('app.js')) .pipe(gulp.dest(app.devPath + 'js')) .pipe(plugins.uglify()) .pipe(gulp.dest(app.prdPath + 'js')) .pipe(plugins.connect.reload()); }); /* * js任务 * 在src目录下创建script文件夹,里面存放所有的js文件 */ gulp.task('jshint', function () { gulp.src(JS_APP) .pipe(plugins.jshint()) .pipe(plugins.jshint.reporter()); }); // 每次发布的时候,可能需要把之前目录内的内容清除,避免旧的文件对新的容有所影响。 需要在每次发布前删除dist和build目录 gulp.task('clean', function () { gulp.src([app.devPath, app.prdPath]) .pipe(plugins.clean()); }); // 总任务 gulp.task('build', ['clean', 'jshint', 'lib', 'js', 'css']); // 服务 gulp.task('serve', ['build'], function () { plugins.connect.server({ //启动一个服务器 root: [app.devPath], // 服务器从哪个路径开始读取,默认从开发路径读取 livereload: true, // 自动刷新 port: 1234 }); // 打开浏览器 setTimeout(() => { open('http://localhost:8080/index_dev.htm') }, 200); // 监听 gulp.watch(app.srcPath + '**/*.js', ['js']); gulp.watch(app.srcPath + '**/*.css', ['css']); }); // 定义default任务 gulp.task('default', ['serve']); ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/index.htm ================================================ Sentinel Dashboard
    ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/index_dev.htm ================================================ Sentinel 控制台
    ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/license-stat.csv ================================================ Type,Package,License npm,angular,MIT License npm,angular-animate,MIT License npm,angular-bootstrap,MIT License npm,angular-clipboard,MIT License npm,angular-cookies,MIT License npm,angular-date-time-input,MIT License npm,angular-loading-bar,MIT License npm,angular-mocks,MIT License npm,angular-resource,MIT License npm,angular-route,MIT License npm,angular-selectize2,MIT License npm,angular-table-resize,MIT License npm,angular-touch,MIT License npm,angular-ui-notification,MIT License npm,angular-ui-router,MIT License npm,angular-utils-pagination,MIT License npm,angularjs-bootstrap-datetimepicker,MIT License npm,bootstrap-switch,Apache License 2.0 npm,bootstrap-tagsinput,MIT License npm,moment,MIT License npm,ng-dialog,MIT License npm,ng-tags-input,MIT License npm,oclazyload,MIT License npm,selectize,Apache License 2.0 lib,jsTreeTable,MIT License ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/main/webapp/resources/package.json ================================================ { "name": "sentinel-dashboard", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo no test case", "build": "gulp build", "start": "gulp" }, "author": "x-cold ", "license": "MIT", "dependencies": { "angular": "^1.4.8", "angular-animate": "^1.4.0", "angular-bootstrap": "^0.12.2", "angular-clipboard": "^1.6.2", "angular-cookies": "^1.4.0", "angular-date-time-input": "^1.2.1", "angular-loading-bar": "^0.9.0", "angular-mocks": "^1.4.0", "angular-resource": "^1.4.0", "angular-route": "^1.4.0", "angular-selectize2": "^v1.2.3", "angular-table-resize": "^2.0.1", "angular-touch": "^1.4.0", "angular-ui-notification": "^0.3.6", "angular-ui-router": "^1.0.18", "angular-utils-pagination": "^0.11.1", "angularjs-bootstrap-datetimepicker": "^1.1.4", "bootstrap-switch": "^3.3.4", "bootstrap-tagsinput": "~0.7.1", "moment": "^2.12.0", "ng-dialog": "^0.6.6", "ng-tags-input": "~3.0.0", "oclazyload": "^1.1.0", "selectize": "^0.12.1", "lodash": ">=4.17.11" }, "devDependencies": { "gulp": "^3.9.1", "gulp-clean": "^0.4.0", "gulp-concat": "^2.6.1", "gulp-connect": "^5.7.0", "gulp-csscomb": "^3.0.8", "gulp-cssmin": "^0.2.0", "gulp-jshint": "^2.1.0", "gulp-load-plugins": "^1.5.0", "gulp-serv": "0.0.1", "gulp-uglify": "^3.0.0", "jshint": "^2.10.2", "open": "0.0.5", "source-map": "^0.7.3" } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/test/java/com/alibaba/csp/sentinel/dashboard/config/DashboardConfigTest.java ================================================ /* * 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 static org.junit.Assert.assertEquals; import org.junit.Rule; import org.junit.Test; import org.junit.contrib.java.lang.system.EnvironmentVariables; public class DashboardConfigTest { @Rule public final EnvironmentVariables environmentVariables = new EnvironmentVariables(); @Test public void testGetConfigStr() { // clear cache DashboardConfig.clearCache(); // if not set, return null assertEquals(null, DashboardConfig.getConfigStr("a")); // test property System.setProperty("a", "111"); assertEquals("111", DashboardConfig.getConfigStr("a")); // test env environmentVariables.set("a", "222"); // return value in cache assertEquals("111", DashboardConfig.getConfigStr("a")); // clear cache and then test DashboardConfig.clearCache(); assertEquals("222", DashboardConfig.getConfigStr("a")); } @Test public void testGetConfigInt() { // clear cache DashboardConfig.clearCache(); // default value assertEquals(0, DashboardConfig.getConfigInt("t", 0, 10)); DashboardConfig.clearCache(); assertEquals(1, DashboardConfig.getConfigInt("t", 1, 10)); // property, wrong format System.setProperty("t", "asdf"); DashboardConfig.clearCache(); assertEquals(0, DashboardConfig.getConfigInt("t", 0, 10)); System.setProperty("t", ""); DashboardConfig.clearCache(); assertEquals(0, DashboardConfig.getConfigInt("t", 0, 10)); // min value System.setProperty("t", "2"); DashboardConfig.clearCache(); assertEquals(2, DashboardConfig.getConfigInt("t", 0, 1)); DashboardConfig.clearCache(); assertEquals(10, DashboardConfig.getConfigInt("t", 0, 10)); DashboardConfig.clearCache(); assertEquals(2, DashboardConfig.getConfigInt("t", 0, -1)); // env environmentVariables.set("t", "20"); DashboardConfig.clearCache(); assertEquals(20, DashboardConfig.getConfigInt("t", 0, 10)); // wrong format env var, but it will override property environmentVariables.set("t", "20dddd"); DashboardConfig.clearCache(); assertEquals(0, DashboardConfig.getConfigInt("t", 0, 10)); // clear env, it will take property environmentVariables.set("t", ""); DashboardConfig.clearCache(); assertEquals(10, DashboardConfig.getConfigInt("t", 0, 10)); DashboardConfig.clearCache(); assertEquals(2, DashboardConfig.getConfigInt("t", 0, 1)); // enable cache System.setProperty("t", "666"); DashboardConfig.clearCache(); assertEquals(666, DashboardConfig.getConfigInt("t", 0, 1)); System.setProperty("t", "777"); assertEquals(666, DashboardConfig.getConfigInt("t", 0, 1)); System.setProperty("t", "555"); assertEquals(666, DashboardConfig.getConfigInt("t", 0, 1)); } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/test/java/com/alibaba/csp/sentinel/dashboard/datasource/entity/SentinelVersionTest.java ================================================ package com.alibaba.csp.sentinel.dashboard.datasource.entity; import static org.junit.Assert.*; import org.junit.Test; public class SentinelVersionTest { @Test public void testEqual() { assertEquals(new SentinelVersion(1, 0, 0), new SentinelVersion(1, 0, 0)); assertNotEquals(new SentinelVersion(1, 0, 0), new SentinelVersion(1, 2, 3)); assertNotEquals(new SentinelVersion(1, 0, 0), new SentinelVersion(1, 0, 0, "")); assertEquals(new SentinelVersion(1, 0, 0, ""), new SentinelVersion(1, 0, 0, "")); assertNotEquals(new SentinelVersion(1, 0, 0, ""), new SentinelVersion(1, 0, 0, null)); assertEquals(new SentinelVersion(1, 0, 0, null), new SentinelVersion(1, 0, 0, null)); } @Test public void testGreater() { assertTrue(new SentinelVersion(2, 0, 0).greaterThan(new SentinelVersion(1, 0, 0))); assertTrue(new SentinelVersion(1, 1, 0).greaterThan(new SentinelVersion(1, 0, 0))); assertTrue(new SentinelVersion(1, 1, 2).greaterThan(new SentinelVersion(1, 1, 0))); assertTrue(new SentinelVersion(1, 1, 4).greaterThan(new SentinelVersion(1, 1, 3))); assertFalse(new SentinelVersion(1, 0, 0).greaterThan(new SentinelVersion(1, 0, 0))); assertFalse(new SentinelVersion(1, 0, 0).greaterThan(new SentinelVersion(1, 1, 0))); assertFalse(new SentinelVersion(1, 1, 3).greaterThan(new SentinelVersion(1, 1, 3))); assertFalse(new SentinelVersion(1, 1, 2).greaterThan(new SentinelVersion(1, 1, 3))); assertFalse(new SentinelVersion(1, 0, 0, "").greaterThan(new SentinelVersion(1, 0, 0))); assertTrue(new SentinelVersion(1, 0, 1).greaterThan(new SentinelVersion(1, 0, 0))); assertTrue(new SentinelVersion(1, 0, 1, "a").greaterThan(new SentinelVersion(1, 0, 0, "b"))); assertFalse(new SentinelVersion(1, 0, 0, "b").greaterThan(new SentinelVersion(1, 0, 0, "a"))); } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/test/java/com/alibaba/csp/sentinel/dashboard/discovery/AppInfoTest.java ================================================ /* * Copyright 1999-2019 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.discovery; import java.util.ConcurrentModificationException; import java.util.Set; import org.junit.Test; import com.alibaba.csp.sentinel.dashboard.config.DashboardConfig; import static org.junit.Assert.*; public class AppInfoTest { @Test public void testConcurrentGetMachines() throws Exception { AppInfo appInfo = new AppInfo("testApp"); appInfo.addMachine(genMachineInfo("hostName1", "10.18.129.91")); appInfo.addMachine(genMachineInfo("hostName2", "10.18.129.92")); Set machines = appInfo.getMachines(); new Thread(() -> { try { for (MachineInfo m : machines) { System.out.println(m); try { Thread.sleep(200); } catch (InterruptedException e) { } } } catch (ConcurrentModificationException e) { e.printStackTrace(); fail(); } }).start(); Thread.sleep(100); try { appInfo.addMachine(genMachineInfo("hostName3", "10.18.129.93")); } catch (ConcurrentModificationException e) { e.printStackTrace(); fail(); } Thread.sleep(1000); } private MachineInfo genMachineInfo(String hostName, String ip) { MachineInfo machine = new MachineInfo(); machine.setApp("testApp"); machine.setHostname(hostName); machine.setIp(ip); machine.setPort(8719); machine.setVersion(String.valueOf(System.currentTimeMillis())); return machine; } @Test public void addRemoveMachineTest() { AppInfo appInfo = new AppInfo("default"); assertEquals("default", appInfo.getApp()); assertEquals(0, appInfo.getMachines().size()); //add one { MachineInfo machineInfo = new MachineInfo(); machineInfo.setApp("default"); machineInfo.setHostname("bogon"); machineInfo.setIp("127.0.0.1"); machineInfo.setPort(3389); machineInfo.setLastHeartbeat(System.currentTimeMillis()); machineInfo.setHeartbeatVersion(1); machineInfo.setVersion("0.4.1"); appInfo.addMachine(machineInfo); } assertEquals(1, appInfo.getMachines().size()); //add duplicated one { MachineInfo machineInfo = new MachineInfo(); machineInfo.setApp("default"); machineInfo.setHostname("bogon"); machineInfo.setIp("127.0.0.1"); machineInfo.setPort(3389); machineInfo.setLastHeartbeat(System.currentTimeMillis()); machineInfo.setHeartbeatVersion(1); machineInfo.setVersion("0.4.2"); appInfo.addMachine(machineInfo); } assertEquals(1, appInfo.getMachines().size()); //add different one { MachineInfo machineInfo = new MachineInfo(); machineInfo.setApp("default"); machineInfo.setHostname("bogon"); machineInfo.setIp("127.0.0.1"); machineInfo.setPort(3390); machineInfo.setLastHeartbeat(System.currentTimeMillis()); machineInfo.setHeartbeatVersion(1); machineInfo.setVersion("0.4.3"); appInfo.addMachine(machineInfo); } assertEquals(2, appInfo.getMachines().size()); appInfo.removeMachine("127.0.0.1", 3389); assertEquals(1, appInfo.getMachines().size()); appInfo.removeMachine("127.0.0.1", 3390); assertEquals(0, appInfo.getMachines().size()); } @Test public void testHealthyAndDead() { System.setProperty(DashboardConfig.CONFIG_HIDE_APP_NO_MACHINE_MILLIS, "60000"); System.setProperty(DashboardConfig.CONFIG_REMOVE_APP_NO_MACHINE_MILLIS, "600000"); DashboardConfig.clearCache(); String appName = "default"; AppInfo appInfo = new AppInfo(); appInfo.setApp(appName); { MachineInfo machineInfo = MachineInfo.of(appName, "127.0.0.1", 8801); machineInfo.setHeartbeatVersion(1); machineInfo.setLastHeartbeat(System.currentTimeMillis()); appInfo.addMachine(machineInfo); } assertTrue(appInfo.isShown()); assertFalse(appInfo.isDead()); { MachineInfo machineInfo = MachineInfo.of(appName, "127.0.0.1", 8801); machineInfo.setHeartbeatVersion(1); machineInfo.setLastHeartbeat(System.currentTimeMillis() - 70000); appInfo.addMachine(machineInfo); } assertFalse(appInfo.isShown()); assertFalse(appInfo.isDead()); { MachineInfo machineInfo = MachineInfo.of(appName, "127.0.0.1", 8801); machineInfo.setHeartbeatVersion(1); machineInfo.setLastHeartbeat(System.currentTimeMillis() - 700000); appInfo.addMachine(machineInfo); } assertFalse(appInfo.isShown()); assertTrue(appInfo.isDead()); } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/test/java/com/alibaba/csp/sentinel/dashboard/discovery/MachineInfoTest.java ================================================ /* * 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.discovery; import static org.junit.Assert.*; import org.junit.Test; import com.alibaba.csp.sentinel.dashboard.config.DashboardConfig; /** * @author Jason Joo */ public class MachineInfoTest { @Test public void testHealthyAndDead() { System.setProperty(DashboardConfig.CONFIG_UNHEALTHY_MACHINE_MILLIS, "60000"); System.setProperty(DashboardConfig.CONFIG_AUTO_REMOVE_MACHINE_MILLIS, "600000"); DashboardConfig.clearCache(); MachineInfo machineInfo = new MachineInfo(); machineInfo.setHeartbeatVersion(1); machineInfo.setLastHeartbeat(System.currentTimeMillis() - 10000); assertTrue(machineInfo.isHealthy()); assertFalse(machineInfo.isDead()); machineInfo.setLastHeartbeat(System.currentTimeMillis() - 100000); assertFalse(machineInfo.isHealthy()); assertFalse(machineInfo.isDead()); machineInfo.setLastHeartbeat(System.currentTimeMillis() - 1000000); assertFalse(machineInfo.isHealthy()); assertTrue(machineInfo.isDead()); } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/test/java/com/alibaba/csp/sentinel/dashboard/repository/metric/InMemoryMetricsRepositoryTest.java ================================================ /* * Copyright 1999-2019 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.repository.metric; import com.alibaba.csp.sentinel.dashboard.datasource.entity.MetricEntity; import org.assertj.core.util.Lists; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.springframework.util.CollectionUtils; import java.util.ConcurrentModificationException; import java.util.Date; import java.util.List; import java.util.concurrent.*; import static org.junit.Assert.*; /** * Test cases for {@link InMemoryMetricsRepository}. * * @author Nick Tan */ public class InMemoryMetricsRepositoryTest { private static final String DEFAULT_APP = "default"; private static final String DEFAULT_EXPIRE_APP = "default_expire_app"; private static final String DEFAULT_RESOURCE = "test"; private static final long EXPIRE_TIME = 1000 * 60 * 5L; private InMemoryMetricsRepository inMemoryMetricsRepository; private ExecutorService executorService; @Before public void setUp() throws Exception { inMemoryMetricsRepository = new InMemoryMetricsRepository(); executorService = Executors.newFixedThreadPool(8); } @After public void tearDown() { executorService.shutdownNow(); } private void testSave() { for (int i = 0; i < 1000000; i++) { MetricEntity entry = new MetricEntity(); entry.setApp(DEFAULT_APP); entry.setResource(DEFAULT_RESOURCE); entry.setTimestamp(new Date(System.currentTimeMillis())); entry.setPassQps(1L); entry.setExceptionQps(1L); entry.setBlockQps(0L); entry.setSuccessQps(1L); inMemoryMetricsRepository.save(entry); } } @Test public void testExpireMetric() { long now = System.currentTimeMillis(); MetricEntity expireEntry = new MetricEntity(); expireEntry.setApp(DEFAULT_EXPIRE_APP); expireEntry.setResource(DEFAULT_RESOURCE); expireEntry.setTimestamp(new Date(now - EXPIRE_TIME - 10L)); expireEntry.setPassQps(1L); expireEntry.setExceptionQps(1L); expireEntry.setBlockQps(0L); expireEntry.setSuccessQps(1L); inMemoryMetricsRepository.save(expireEntry); MetricEntity entry = new MetricEntity(); entry.setApp(DEFAULT_EXPIRE_APP); entry.setResource(DEFAULT_RESOURCE); entry.setTimestamp(new Date(now)); entry.setPassQps(1L); entry.setExceptionQps(1L); entry.setBlockQps(0L); entry.setSuccessQps(1L); inMemoryMetricsRepository.save(entry); List list = inMemoryMetricsRepository.queryByAppAndResourceBetween( DEFAULT_EXPIRE_APP, DEFAULT_RESOURCE, now - 2 * EXPIRE_TIME, now + EXPIRE_TIME); assertFalse(CollectionUtils.isEmpty(list)); assertEquals(1, list.size()); } @Test public void testListResourcesOfApp() { // prepare basic test data testSave(); System.out.println( "[" + System.currentTimeMillis() + "] Basic test data ready in testListResourcesOfApp"); List futures = Lists.newArrayList(); // concurrent query resources of app final CyclicBarrier cyclicBarrier = new CyclicBarrier(8); for (int j = 0; j < 10000; j++) { futures.add( CompletableFuture.runAsync(() -> { try { cyclicBarrier.await(); inMemoryMetricsRepository.listResourcesOfApp(DEFAULT_APP); } catch (InterruptedException | BrokenBarrierException e) { e.printStackTrace(); } }, executorService) ); } // batch add metric entity for (int i = 0; i < 10000; i++) { MetricEntity entry = new MetricEntity(); entry.setApp(DEFAULT_APP); entry.setResource(DEFAULT_RESOURCE); entry.setTimestamp(new Date(System.currentTimeMillis() - EXPIRE_TIME - 1000L)); entry.setPassQps(1L); entry.setExceptionQps(1L); entry.setBlockQps(0L); entry.setSuccessQps(1L); inMemoryMetricsRepository.save(entry); } CompletableFuture all = CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])); try { all.get(10, TimeUnit.SECONDS); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.getCause().printStackTrace(); if (e.getCause() instanceof ConcurrentModificationException) { fail("concurrent error occurred"); } else { fail("unexpected exception"); } } catch (TimeoutException e) { fail("allOf future timeout"); } } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/test/java/com/alibaba/csp/sentinel/dashboard/rule/apollo/ApolloConfig.java ================================================ /* * 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.rule.apollo; import java.util.List; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity; import com.alibaba.csp.sentinel.datasource.Converter; import com.alibaba.fastjson.JSON; import com.ctrip.framework.apollo.openapi.client.ApolloOpenApiClient; /** * @author hantianwei@gmail.com * @since 1.5.0 */ @Configuration public class ApolloConfig { @Bean public Converter, String> flowRuleEntityEncoder() { return JSON::toJSONString; } @Bean public Converter> flowRuleEntityDecoder() { return s -> JSON.parseArray(s, FlowRuleEntity.class); } @Bean public ApolloOpenApiClient apolloOpenApiClient() { ApolloOpenApiClient client = ApolloOpenApiClient.newBuilder() .withPortalUrl("http://localhost:10034") .withToken("token") .build(); return client; } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/test/java/com/alibaba/csp/sentinel/dashboard/rule/apollo/ApolloConfigUtil.java ================================================ /* * 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.rule.apollo; /** * @author hantianwei@gmail.com * @since 1.5.0 */ public final class ApolloConfigUtil { public static final String FLOW_DATA_ID_POSTFIX = "-flow-rules"; private ApolloConfigUtil() { } public static String getFlowDataId(String appName) { return String.format("%s%s", appName, FLOW_DATA_ID_POSTFIX); } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/test/java/com/alibaba/csp/sentinel/dashboard/rule/apollo/FlowRuleApolloProvider.java ================================================ /* * 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.rule.apollo; import java.util.ArrayList; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity; import com.alibaba.csp.sentinel.dashboard.rule.DynamicRuleProvider; import com.alibaba.csp.sentinel.datasource.Converter; import com.alibaba.csp.sentinel.util.StringUtil; import com.ctrip.framework.apollo.openapi.client.ApolloOpenApiClient; import com.ctrip.framework.apollo.openapi.dto.OpenItemDTO; import com.ctrip.framework.apollo.openapi.dto.OpenNamespaceDTO; /** * @author hantianwei@gmail.com * @since 1.5.0 */ @Component("flowRuleApolloProvider") public class FlowRuleApolloProvider implements DynamicRuleProvider> { @Autowired private ApolloOpenApiClient apolloOpenApiClient; @Autowired private Converter> converter; @Override public List getRules(String appName) throws Exception { String appId = "appId"; String flowDataId = ApolloConfigUtil.getFlowDataId(appName); OpenNamespaceDTO openNamespaceDTO = apolloOpenApiClient.getNamespace(appId, "DEV", "default", "application"); String rules = openNamespaceDTO .getItems() .stream() .filter(p -> p.getKey().equals(flowDataId)) .map(OpenItemDTO::getValue) .findFirst() .orElse(""); if (StringUtil.isEmpty(rules)) { return new ArrayList<>(); } return converter.convert(rules); } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/test/java/com/alibaba/csp/sentinel/dashboard/rule/apollo/FlowRuleApolloPublisher.java ================================================ /* * 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.rule.apollo; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity; import com.alibaba.csp.sentinel.dashboard.rule.DynamicRulePublisher; import com.alibaba.csp.sentinel.datasource.Converter; import com.alibaba.csp.sentinel.util.AssertUtil; import com.ctrip.framework.apollo.openapi.client.ApolloOpenApiClient; import com.ctrip.framework.apollo.openapi.dto.NamespaceReleaseDTO; import com.ctrip.framework.apollo.openapi.dto.OpenItemDTO; /** * @author hantianwei@gmail.com * @since 1.5.0 */ @Component("flowRuleApolloPublisher") public class FlowRuleApolloPublisher implements DynamicRulePublisher> { @Autowired private ApolloOpenApiClient apolloOpenApiClient; @Autowired private Converter, String> converter; @Override public void publish(String app, List rules) throws Exception { AssertUtil.notEmpty(app, "app name cannot be empty"); if (rules == null) { return; } // Increase the configuration String appId = "appId"; String flowDataId = ApolloConfigUtil.getFlowDataId(app); OpenItemDTO openItemDTO = new OpenItemDTO(); openItemDTO.setKey(flowDataId); openItemDTO.setValue(converter.convert(rules)); openItemDTO.setComment("Program auto-join"); openItemDTO.setDataChangeCreatedBy("some-operator"); apolloOpenApiClient.createOrUpdateItem(appId, "DEV", "default", "application", openItemDTO); // Release configuration NamespaceReleaseDTO namespaceReleaseDTO = new NamespaceReleaseDTO(); namespaceReleaseDTO.setEmergencyPublish(true); namespaceReleaseDTO.setReleaseComment("Modify or add configurations"); namespaceReleaseDTO.setReleasedBy("some-operator"); namespaceReleaseDTO.setReleaseTitle("Modify or add configurations"); apolloOpenApiClient.publishNamespace(appId, "DEV", "default", "application", namespaceReleaseDTO); } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/test/java/com/alibaba/csp/sentinel/dashboard/rule/nacos/FlowRuleNacosProvider.java ================================================ /* * 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.rule.nacos; import java.util.ArrayList; import java.util.List; import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity; import com.alibaba.csp.sentinel.dashboard.rule.DynamicRuleProvider; import com.alibaba.csp.sentinel.datasource.Converter; import com.alibaba.csp.sentinel.util.StringUtil; import com.alibaba.nacos.api.config.ConfigService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; /** * @author Eric Zhao * @since 1.4.0 */ @Component("flowRuleNacosProvider") public class FlowRuleNacosProvider implements DynamicRuleProvider> { @Autowired private ConfigService configService; @Autowired private Converter> converter; @Override public List getRules(String appName) throws Exception { String rules = configService.getConfig(appName + NacosConfigUtil.FLOW_DATA_ID_POSTFIX, NacosConfigUtil.GROUP_ID, 3000); if (StringUtil.isEmpty(rules)) { return new ArrayList<>(); } return converter.convert(rules); } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/test/java/com/alibaba/csp/sentinel/dashboard/rule/nacos/FlowRuleNacosPublisher.java ================================================ /* * 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.rule.nacos; import java.util.List; import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity; import com.alibaba.csp.sentinel.dashboard.rule.DynamicRulePublisher; import com.alibaba.csp.sentinel.datasource.Converter; import com.alibaba.csp.sentinel.util.AssertUtil; import com.alibaba.nacos.api.config.ConfigService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; /** * @author Eric Zhao * @since 1.4.0 */ @Component("flowRuleNacosPublisher") public class FlowRuleNacosPublisher implements DynamicRulePublisher> { @Autowired private ConfigService configService; @Autowired private Converter, String> converter; @Override public void publish(String app, List rules) throws Exception { AssertUtil.notEmpty(app, "app name cannot be empty"); if (rules == null) { return; } configService.publishConfig(app + NacosConfigUtil.FLOW_DATA_ID_POSTFIX, NacosConfigUtil.GROUP_ID, converter.convert(rules)); } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/test/java/com/alibaba/csp/sentinel/dashboard/rule/nacos/NacosConfig.java ================================================ /* * 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.rule.nacos; import java.util.List; import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity; import com.alibaba.csp.sentinel.datasource.Converter; import com.alibaba.fastjson.JSON; import com.alibaba.nacos.api.config.ConfigFactory; import com.alibaba.nacos.api.config.ConfigService; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * @author Eric Zhao * @since 1.4.0 */ @Configuration public class NacosConfig { @Bean public Converter, String> flowRuleEntityEncoder() { return JSON::toJSONString; } @Bean public Converter> flowRuleEntityDecoder() { return s -> JSON.parseArray(s, FlowRuleEntity.class); } @Bean public ConfigService nacosConfigService() throws Exception { return ConfigFactory.createConfigService("localhost"); } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/test/java/com/alibaba/csp/sentinel/dashboard/rule/nacos/NacosConfigUtil.java ================================================ /* * 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.rule.nacos; /** * @author Eric Zhao * @since 1.4.0 */ public final class NacosConfigUtil { public static final String GROUP_ID = "SENTINEL_GROUP"; public static final String FLOW_DATA_ID_POSTFIX = "-flow-rules"; public static final String PARAM_FLOW_DATA_ID_POSTFIX = "-param-rules"; public static final String CLUSTER_MAP_DATA_ID_POSTFIX = "-cluster-map"; /** * cc for `cluster-client` */ public static final String CLIENT_CONFIG_DATA_ID_POSTFIX = "-cc-config"; /** * cs for `cluster-server` */ public static final String SERVER_TRANSPORT_CONFIG_DATA_ID_POSTFIX = "-cs-transport-config"; public static final String SERVER_FLOW_CONFIG_DATA_ID_POSTFIX = "-cs-flow-config"; public static final String SERVER_NAMESPACE_SET_DATA_ID_POSTFIX = "-cs-namespace-set"; private NacosConfigUtil() {} } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-apollo/src/test/java/com/alibaba/csp/sentinel/dashboard/util/VersionUtilsTest.java ================================================ package com.alibaba.csp.sentinel.dashboard.util; import static org.junit.Assert.*; import java.util.Optional; import org.junit.Test; import com.alibaba.csp.sentinel.dashboard.datasource.entity.SentinelVersion; public class VersionUtilsTest { @Test public void test() { Optional version = VersionUtils.parseVersion("1.2.3"); assertTrue(version.isPresent()); assertEquals(1, version.get().getMajorVersion()); assertEquals(2, version.get().getMinorVersion()); assertEquals(3, version.get().getFixVersion()); assertNull(version.get().getPostfix()); version = VersionUtils.parseVersion("1.2"); assertTrue(version.isPresent()); assertEquals(1, version.get().getMajorVersion()); assertEquals(2, version.get().getMinorVersion()); assertEquals(0, version.get().getFixVersion()); assertNull(version.get().getPostfix()); version = VersionUtils.parseVersion("1."); assertTrue(version.isPresent()); assertEquals(1, version.get().getMajorVersion()); assertEquals(0, version.get().getMinorVersion()); assertEquals(0, version.get().getFixVersion()); assertNull(version.get().getPostfix()); version = VersionUtils.parseVersion("1.2."); assertTrue(version.isPresent()); assertEquals(1, version.get().getMajorVersion()); assertEquals(2, version.get().getMinorVersion()); assertEquals(0, version.get().getFixVersion()); assertNull(version.get().getPostfix()); version = VersionUtils.parseVersion("1.2.3."); assertTrue(version.isPresent()); assertEquals(1, version.get().getMajorVersion()); assertEquals(2, version.get().getMinorVersion()); assertEquals(3, version.get().getFixVersion()); assertNull(version.get().getPostfix()); version = VersionUtils.parseVersion("1.2.3.4"); assertTrue(version.isPresent()); assertEquals(1, version.get().getMajorVersion()); assertEquals(2, version.get().getMinorVersion()); assertEquals(3, version.get().getFixVersion()); assertNull(version.get().getPostfix()); version = VersionUtils.parseVersion("1"); assertTrue(version.isPresent()); assertEquals(1, version.get().getMajorVersion()); assertEquals(0, version.get().getMinorVersion()); assertEquals(0, version.get().getFixVersion()); assertNull(version.get().getPostfix()); version = VersionUtils.parseVersion("1.2.3-"); assertTrue(version.isPresent()); assertEquals(1, version.get().getMajorVersion()); assertEquals(2, version.get().getMinorVersion()); assertEquals(3, version.get().getFixVersion()); assertNull(version.get().getPostfix()); version = VersionUtils.parseVersion("-"); assertFalse(version.isPresent()); version = VersionUtils.parseVersion("-t"); assertFalse(version.isPresent()); version = VersionUtils.parseVersion(""); assertFalse(version.isPresent()); version = VersionUtils.parseVersion(null); assertFalse(version.isPresent()); version = VersionUtils.parseVersion("1.2.3-SNAPSHOTS"); assertTrue(version.isPresent()); assertEquals(1, version.get().getMajorVersion()); assertEquals(2, version.get().getMinorVersion()); assertEquals(3, version.get().getFixVersion()); assertEquals("SNAPSHOTS", version.get().getPostfix()); } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/pom.xml ================================================ 4.0.0 com.alibaba.csp sentinel-parent 1.6.0 alibaba-sentinel-dashboard-nacos jar 1.8 1.8 2.0.5.RELEASE com.alibaba.csp sentinel-core com.alibaba.csp sentinel-web-servlet ${project.version} com.alibaba.csp sentinel-transport-simple-http com.alibaba.csp sentinel-parameter-flow-control ${project.version} org.springframework.boot spring-boot-starter-web ${spring.boot.version} org.springframework.boot spring-boot-starter-logging ${spring.boot.version} org.springframework.boot spring-boot-starter-test ${spring.boot.version} test log4j log4j 1.2.14 commons-lang commons-lang 2.6 org.apache.httpcomponents httpclient 4.5.3 org.apache.httpcomponents httpcore 4.4.5 org.apache.httpcomponents httpasyncclient 4.1.3 org.apache.httpcomponents httpcore-nio 4.4.6 com.alibaba fastjson com.alibaba.csp sentinel-datasource-nacos com.ctrip.framework.apollo apollo-openapi 1.2.0 test junit junit test org.mockito mockito-core test com.github.stefanbirkner system-rules 1.16.1 test sentinel-dashboard org.springframework.boot spring-boot-maven-plugin true com.alibaba.csp.sentinel.dashboard.DashboardApplication repackage org.apache.maven.plugins maven-compiler-plugin ${maven.compiler.source} ${maven.compiler.target} org.apache.maven.plugins maven-deploy-plugin ${maven.deploy.version} true src/main/resources src/main/webapp/ resources/node_modules/** ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/DashboardApplication.java ================================================ /* * 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; import com.alibaba.csp.sentinel.init.InitExecutor; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; /** * Sentinel dashboard application. * * @author Carpenter Lee */ @SpringBootApplication public class DashboardApplication { public static void main(String[] args) { triggerSentinelInit(); SpringApplication.run(DashboardApplication.class, args); } private static void triggerSentinelInit() { new Thread(() -> InitExecutor.doInit()).start(); } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/auth/AuthService.java ================================================ /* * 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; /** * Interface for authentication and authorization. * * @author Carpenter Lee * @since 1.5.0 */ public interface AuthService { /** * Get the authentication user. * * @param request the request contains the user information * @return the auth user represent the current user, when the user is illegal, a null value will return. */ AuthUser getAuthUser(R request); /** * Privilege type. */ enum PrivilegeType { /** * Read rule */ READ_RULE, /** * Create or modify rule */ WRITE_RULE, /** * Delete rule */ DELETE_RULE, /** * Read metrics */ READ_METRIC, /** * Add machine */ ADD_MACHINE, /** * All privileges above are granted. */ ALL } /** * Represents the current user. */ interface AuthUser { /** * Query whether current user has the specific privilege to the target, the target * may be an app name or an ip address, or other destination. *

    * This method will use return value to represent whether user has the specific * privileges to the target, but to throw a RuntimeException to represent no auth * is also a good way. *

    * * @param target the target to check * @param privilegeType the privilege type to check * @return if current user has the specific privileges to the target, return true, * otherwise return false. */ boolean authTarget(String target, PrivilegeType privilegeType); /** * Check whether current user is a super-user. * * @return if current user is super user return true, else return false. */ boolean isSuperUser(); /** * Get current user's nick name. * * @return current user's nick name. */ String getNickName(); /** * Get current user's login name. * * @return current user's login name. */ String getLoginName(); /** * Get current user's ID. * * @return ID of current user */ String getId(); } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/auth/FakeAuthServiceImpl.java ================================================ /* * 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 javax.servlet.http.HttpServletRequest; import org.springframework.stereotype.Component; /** * A fake AuthService implementation, which will pass all user auth checking. * * @author Carpenter Lee * @since 1.5.0 */ @Component public class FakeAuthServiceImpl implements AuthService { @Override public AuthUser getAuthUser(HttpServletRequest request) { return new AuthUserImpl(); } static final class AuthUserImpl implements AuthUser { @Override public boolean authTarget(String target, PrivilegeType privilegeType) { // fake implementation, always return true return true; } @Override public boolean isSuperUser() { // fake implementation, always return true return true; } @Override public String getNickName() { return "FAKE_NICK_NAME"; } @Override public String getLoginName() { return "FAKE_LOGIN_NAME"; } @Override public String getId() { return "FAKE_EMP_ID"; } } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/auth/SimpleWebAuthServiceImpl.java ================================================ /* * 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.springframework.context.annotation.Primary; import org.springframework.stereotype.Component; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; /** * @author cdfive * @since 1.6.0 */ @Primary @Component public class SimpleWebAuthServiceImpl implements AuthService { public static final String WEB_SESSTION_KEY = "session_sentinel_admin"; @Override public AuthUser getAuthUser(HttpServletRequest request) { HttpSession session = request.getSession(); Object sentinelUserObj = session.getAttribute(SimpleWebAuthServiceImpl.WEB_SESSTION_KEY); if (sentinelUserObj != null && sentinelUserObj instanceof AuthUser) { return (AuthUser) sentinelUserObj; } return null; } public static final class SimpleWebAuthUserImpl implements AuthUser { private String username; public SimpleWebAuthUserImpl(String username) { this.username = username; } @Override public boolean authTarget(String target, PrivilegeType privilegeType) { return true; } @Override public boolean isSuperUser() { return true; } @Override public String getNickName() { return username; } @Override public String getLoginName() { return username; } @Override public String getId() { return username; } } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/client/CommandFailedException.java ================================================ /* * 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.client; /** * @author Eric Zhao */ public class CommandFailedException extends RuntimeException { public CommandFailedException() {} public CommandFailedException(String message) { super(message); } @Override public synchronized Throwable fillInStackTrace() { return this; } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/client/CommandNotFoundException.java ================================================ /* * 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.client; /** * @author Eric Zhao * @since 0.2.1 */ public class CommandNotFoundException extends Exception { public CommandNotFoundException() { } public CommandNotFoundException(String message) { super(message); } @Override public synchronized Throwable fillInStackTrace() { return this; } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/client/SentinelApiClient.java ================================================ /* * 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.client; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Optional; import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.stream.Collectors; import com.alibaba.csp.sentinel.command.CommandConstants; import com.alibaba.csp.sentinel.config.SentinelConfig; import com.alibaba.csp.sentinel.command.vo.NodeVo; import com.alibaba.csp.sentinel.dashboard.util.AsyncUtils; import com.alibaba.csp.sentinel.slots.block.Rule; import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRule; import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule; import com.alibaba.csp.sentinel.slots.block.flow.FlowRule; import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule; import com.alibaba.csp.sentinel.slots.system.SystemRule; import com.alibaba.csp.sentinel.util.AssertUtil; import com.alibaba.csp.sentinel.util.StringUtil; import com.alibaba.fastjson.JSON; import com.alibaba.csp.sentinel.dashboard.datasource.entity.SentinelVersion; import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.AuthorityRuleEntity; import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.DegradeRuleEntity; import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity; import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.ParamFlowRuleEntity; import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.RuleEntity; import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.SystemRuleEntity; import com.alibaba.csp.sentinel.dashboard.discovery.AppManagement; import com.alibaba.csp.sentinel.dashboard.domain.cluster.ClusterClientInfoVO; import com.alibaba.csp.sentinel.dashboard.domain.cluster.state.ClusterServerStateVO; import com.alibaba.csp.sentinel.dashboard.domain.cluster.state.ClusterStateSimpleEntity; import com.alibaba.csp.sentinel.dashboard.domain.cluster.config.ClusterClientConfig; import com.alibaba.csp.sentinel.dashboard.domain.cluster.config.ServerFlowConfig; import com.alibaba.csp.sentinel.dashboard.domain.cluster.config.ServerTransportConfig; import com.alibaba.csp.sentinel.dashboard.util.VersionUtils; import org.apache.http.HttpResponse; import org.apache.http.NameValuePair; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.concurrent.FutureCallback; import org.apache.http.entity.ContentType; import org.apache.http.impl.client.DefaultRedirectStrategy; import org.apache.http.impl.nio.client.CloseableHttpAsyncClient; import org.apache.http.impl.nio.client.HttpAsyncClients; import org.apache.http.impl.nio.reactor.IOReactorConfig; import org.apache.http.message.BasicNameValuePair; import org.apache.http.util.EntityUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.lang.Nullable; import org.springframework.stereotype.Component; /** * Communicate with Sentinel client. * * @author leyou */ @Component public class SentinelApiClient { private static Logger logger = LoggerFactory.getLogger(SentinelApiClient.class); private static final Charset DEFAULT_CHARSET = Charset.forName(SentinelConfig.charset()); private static final String RESOURCE_URL_PATH = "jsonTree"; private static final String CLUSTER_NODE_PATH = "clusterNode"; private static final String GET_RULES_PATH = "getRules"; private static final String SET_RULES_PATH = "setRules"; private static final String GET_PARAM_RULE_PATH = "getParamFlowRules"; private static final String SET_PARAM_RULE_PATH = "setParamFlowRules"; private static final String FETCH_CLUSTER_MODE_PATH = "getClusterMode"; private static final String MODIFY_CLUSTER_MODE_PATH = "setClusterMode"; private static final String FETCH_CLUSTER_CLIENT_CONFIG_PATH = "cluster/client/fetchConfig"; private static final String MODIFY_CLUSTER_CLIENT_CONFIG_PATH = "cluster/client/modifyConfig"; private static final String FETCH_CLUSTER_SERVER_ALL_CONFIG_PATH = "cluster/server/fetchConfig"; private static final String FETCH_CLUSTER_SERVER_BASIC_INFO_PATH = "cluster/server/info"; private static final String MODIFY_CLUSTER_SERVER_TRANSPORT_CONFIG_PATH = "cluster/server/modifyTransportConfig"; private static final String MODIFY_CLUSTER_SERVER_FLOW_CONFIG_PATH = "cluster/server/modifyFlowConfig"; private static final String MODIFY_CLUSTER_SERVER_NAMESPACE_SET_PATH = "cluster/server/modifyNamespaceSet"; private static final String FLOW_RULE_TYPE = "flow"; private static final String DEGRADE_RULE_TYPE = "degrade"; private static final String SYSTEM_RULE_TYPE = "system"; private static final String AUTHORITY_TYPE = "authority"; private CloseableHttpAsyncClient httpClient; private static final SentinelVersion version160 = new SentinelVersion(1, 6, 0); @Autowired private AppManagement appManagement; public SentinelApiClient() { IOReactorConfig ioConfig = IOReactorConfig.custom().setConnectTimeout(3000).setSoTimeout(10000) .setIoThreadCount(Runtime.getRuntime().availableProcessors() * 2).build(); httpClient = HttpAsyncClients.custom().setRedirectStrategy(new DefaultRedirectStrategy() { @Override protected boolean isRedirectable(final String method) { return false; } }).setMaxConnTotal(4000).setMaxConnPerRoute(1000).setDefaultIOReactorConfig(ioConfig).build(); httpClient.start(); } private boolean isSuccess(int statusCode) { return statusCode >= 200 && statusCode < 300; } private boolean isCommandNotFound(int statusCode, String body) { return statusCode == 400 && StringUtil.isNotEmpty(body) && body.contains(CommandConstants.MSG_UNKNOWN_COMMAND_PREFIX); } private StringBuilder queryString(Map params) { StringBuilder queryStringBuilder = new StringBuilder(); for (Entry entry : params.entrySet()) { if (StringUtil.isEmpty(entry.getValue())) { continue; } String name = urlEncode(entry.getKey()); String value = urlEncode(entry.getValue()); if (name != null && value != null) { if (queryStringBuilder.length() > 0) { queryStringBuilder.append('&'); } queryStringBuilder.append(name).append('=').append(value); } } return queryStringBuilder; } private HttpUriRequest postRequest(String url, Map params) { HttpPost httpPost = new HttpPost(url); if (params != null && params.size() > 0) { List list = new ArrayList<>(params.size()); for (Entry entry : params.entrySet()) { list.add(new BasicNameValuePair(entry.getKey(), entry.getValue())); } try { httpPost.setEntity(new UrlEncodedFormEntity(list)); } catch (UnsupportedEncodingException e) { logger.warn("httpPostContent encode entity error: {}", params, e); return null; } } return httpPost; } private String urlEncode(String str) { try { return URLEncoder.encode(str, DEFAULT_CHARSET.name()); } catch (UnsupportedEncodingException e) { logger.info("encode string error: {}", str, e); return null; } } private String getBody(HttpResponse response) throws Exception { Charset charset = null; try { String contentTypeStr = response.getFirstHeader("Content-type").getValue(); if (StringUtil.isNotEmpty(contentTypeStr)) { ContentType contentType = ContentType.parse(contentTypeStr); charset = contentType.getCharset(); } } catch (Exception ignore) { } return EntityUtils.toString(response.getEntity(), charset != null ? charset : DEFAULT_CHARSET); } /** * With no param * * @param ip * @param port * @param api * @return */ private CompletableFuture executeCommand(String ip, int port, String api, boolean useHttpPost) { return executeCommand(ip, port, api, null, useHttpPost); } /** * No app specified, force to GET * * @param ip * @param port * @param api * @param params * @return */ private CompletableFuture executeCommand(String ip, int port, String api, Map params, boolean useHttpPost) { return executeCommand(null, ip, port, api, params, useHttpPost); } /** * Prefer to execute request using POST * * @param app * @param ip * @param port * @param api * @param params * @return */ private CompletableFuture executeCommand(String app, String ip, int port, String api, Map params, boolean useHttpPost) { CompletableFuture future = new CompletableFuture<>(); if (StringUtil.isBlank(ip) || StringUtil.isBlank(api)) { future.completeExceptionally(new IllegalArgumentException("Bad URL or command name")); return future; } StringBuilder urlBuilder = new StringBuilder(); urlBuilder.append("http://"); urlBuilder.append(ip).append(':').append(port).append('/').append(api); if (params == null) { params = Collections.emptyMap(); } boolean supportPost = StringUtil.isNotEmpty(app) && Optional.ofNullable(appManagement.getDetailApp(app)) .flatMap(e -> e.getMachine(ip, port)) .flatMap(m -> VersionUtils.parseVersion(m.getVersion()) .map(v -> v.greaterOrEqual(version160))) .orElse(false); if (!useHttpPost || !supportPost) { // Using GET in older versions, append parameters after url if (!params.isEmpty()) { if (urlBuilder.indexOf("?") == -1) { urlBuilder.append('?'); } else { urlBuilder.append('&'); } urlBuilder.append(queryString(params)); } return executeCommand(new HttpGet(urlBuilder.toString())); } else { // Using POST return executeCommand(postRequest(urlBuilder.toString(), params)); } } private CompletableFuture executeCommand(HttpUriRequest request) { CompletableFuture future = new CompletableFuture<>(); httpClient.execute(request, new FutureCallback() { @Override public void completed(final HttpResponse response) { int statusCode = response.getStatusLine().getStatusCode(); try { String value = getBody(response); if (isSuccess(statusCode)) { future.complete(value); } else { if (isCommandNotFound(statusCode, value)) { future.completeExceptionally(new CommandNotFoundException(request.getURI().getPath())); } else { future.completeExceptionally(new CommandFailedException(value)); } } } catch (Exception ex) { future.completeExceptionally(ex); logger.error("HTTP request failed: {}", request.getURI().toString(), ex); } } @Override public void failed(final Exception ex) { future.completeExceptionally(ex); logger.error("HTTP request failed: {}", request.getURI().toString(), ex); } @Override public void cancelled() { future.complete(null); } }); return future; } public void close() throws Exception { httpClient.close(); } @Nullable private CompletableFuture> fetchItemsAsync(String ip, int port, String api, String type, Class ruleType) { AssertUtil.notEmpty(ip, "Bad machine IP"); AssertUtil.isTrue(port > 0, "Bad machine port"); Map params = null; if (StringUtil.isNotEmpty(type)) { params = new HashMap<>(1); params.put("type", type); } return executeCommand(ip, port, api, params, false) .thenApply(json -> JSON.parseArray(json, ruleType)); } @Nullable private List fetchItems(String ip, int port, String api, String type, Class ruleType) { try { AssertUtil.notEmpty(ip, "Bad machine IP"); AssertUtil.isTrue(port > 0, "Bad machine port"); Map params = null; if (StringUtil.isNotEmpty(type)) { params = new HashMap<>(1); params.put("type", type); } return fetchItemsAsync(ip, port, api, type, ruleType).get(); } catch (InterruptedException | ExecutionException e) { logger.error("Error when fetching items from api: {} -> {}", api, type, e); return null; } catch (Exception e) { logger.error("Error when fetching items: {} -> {}", api, type, e); return null; } } private List fetchRules(String ip, int port, String type, Class ruleType) { return fetchItems(ip, port, GET_RULES_PATH, type, ruleType); } private boolean setRules(String app, String ip, int port, String type, List entities) { if (entities == null) { return true; } try { AssertUtil.notEmpty(app, "Bad app name"); AssertUtil.notEmpty(ip, "Bad machine IP"); AssertUtil.isTrue(port > 0, "Bad machine port"); String data = JSON.toJSONString( entities.stream().map(r -> r.toRule()).collect(Collectors.toList())); Map params = new HashMap<>(2); params.put("type", type); params.put("data", data); String result = executeCommand(app, ip, port, SET_RULES_PATH, params, true).get(); logger.info("setRules: {}", result); return true; } catch (InterruptedException | ExecutionException e) { logger.warn("setRules api failed: {}", type, e); return false; } catch (Exception e) { logger.warn("setRules failed", e); return false; } } public List fetchResourceOfMachine(String ip, int port, String type) { return fetchItems(ip, port, RESOURCE_URL_PATH, type, NodeVo.class); } /** * Fetch cluster node. * * @param ip ip to fetch * @param port port of the ip * @param includeZero whether zero value should in the result list. * @return */ public List fetchClusterNodeOfMachine(String ip, int port, boolean includeZero) { String type = "noZero"; if (includeZero) { type = "zero"; } return fetchItems(ip, port, CLUSTER_NODE_PATH, type, NodeVo.class); } public List fetchFlowRuleOfMachine(String app, String ip, int port) { List rules = fetchRules(ip, port, FLOW_RULE_TYPE, FlowRule.class); if (rules != null) { return rules.stream().map(rule -> FlowRuleEntity.fromFlowRule(app, ip, port, rule)) .collect(Collectors.toList()); } else { return null; } } public List fetchDegradeRuleOfMachine(String app, String ip, int port) { List rules = fetchRules(ip, port, DEGRADE_RULE_TYPE, DegradeRule.class); if (rules != null) { return rules.stream().map(rule -> DegradeRuleEntity.fromDegradeRule(app, ip, port, rule)) .collect(Collectors.toList()); } else { return null; } } public List fetchSystemRuleOfMachine(String app, String ip, int port) { List rules = fetchRules(ip, port, SYSTEM_RULE_TYPE, SystemRule.class); if (rules != null) { return rules.stream().map(rule -> SystemRuleEntity.fromSystemRule(app, ip, port, rule)) .collect(Collectors.toList()); } else { return null; } } /** * Fetch all parameter flow rules from provided machine. * * @param app application name * @param ip machine client IP * @param port machine client port * @return all retrieved parameter flow rules * @since 0.2.1 */ public CompletableFuture> fetchParamFlowRulesOfMachine(String app, String ip, int port) { try { AssertUtil.notEmpty(app, "Bad app name"); 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)) .collect(Collectors.toList()) ); } catch (Exception e) { logger.error("Error when fetching parameter flow rules", e); return AsyncUtils.newFailedFuture(e); } } /** * Fetch all authority rules from provided machine. * * @param app application name * @param ip machine client IP * @param port machine client port * @return all retrieved authority rules * @since 0.2.1 */ public List fetchAuthorityRulesOfMachine(String app, String ip, int port) { AssertUtil.notEmpty(app, "Bad app name"); AssertUtil.notEmpty(ip, "Bad machine IP"); AssertUtil.isTrue(port > 0, "Bad machine port"); Map params = new HashMap<>(1); params.put("type", AUTHORITY_TYPE); List rules = fetchRules(ip, port, AUTHORITY_TYPE, AuthorityRule.class); return Optional.ofNullable(rules).map(r -> r.stream() .map(e -> AuthorityRuleEntity.fromAuthorityRule(app, ip, port, e)) .collect(Collectors.toList()) ).orElse(null); } /** * set rules of the machine. rules == null will return immediately; * rules.isEmpty() means setting the rules to empty. * * @param app * @param ip * @param port * @param rules * @return whether successfully set the rules. */ public boolean setFlowRuleOfMachine(String app, String ip, int port, List rules) { return setRules(app, ip, port, FLOW_RULE_TYPE, rules); } /** * set rules of the machine. rules == null will return immediately; * rules.isEmpty() means setting the rules to empty. * * @param app * @param ip * @param port * @param rules * @return whether successfully set the rules. */ public boolean setDegradeRuleOfMachine(String app, String ip, int port, List rules) { return setRules(app, ip, port, DEGRADE_RULE_TYPE, rules); } /** * set rules of the machine. rules == null will return immediately; * rules.isEmpty() means setting the rules to empty. * * @param app * @param ip * @param port * @param rules * @return whether successfully set the rules. */ public boolean setSystemRuleOfMachine(String app, String ip, int port, List rules) { return setRules(app, ip, port, SYSTEM_RULE_TYPE, rules); } public boolean setAuthorityRuleOfMachine(String app, String ip, int port, List rules) { return setRules(app, ip, port, AUTHORITY_TYPE, rules); } public CompletableFuture setParamFlowRuleOfMachine(String app, String ip, int port, List rules) { if (rules == null) { return CompletableFuture.completedFuture(null); } if (StringUtil.isBlank(ip) || port <= 0) { return AsyncUtils.newFailedFuture(new IllegalArgumentException("Invalid parameter")); } try { String data = JSON.toJSONString( rules.stream().map(ParamFlowRuleEntity::getRule).collect(Collectors.toList()) ); Map params = new HashMap<>(1); params.put("data", data); return executeCommand(app, ip, port, SET_PARAM_RULE_PATH, params, true) .thenCompose(e -> { if (CommandConstants.MSG_SUCCESS.equals(e)) { return CompletableFuture.completedFuture(null); } else { logger.warn("Push parameter flow rules to client failed: " + e); return AsyncUtils.newFailedFuture(new RuntimeException(e)); } }); } catch (Exception ex) { logger.warn("Error when setting parameter flow rule", ex); return AsyncUtils.newFailedFuture(ex); } } // Cluster related public CompletableFuture fetchClusterMode(String ip, int port) { if (StringUtil.isBlank(ip) || port <= 0) { return AsyncUtils.newFailedFuture(new IllegalArgumentException("Invalid parameter")); } try { return executeCommand(ip, port, FETCH_CLUSTER_MODE_PATH, false) .thenApply(r -> JSON.parseObject(r, ClusterStateSimpleEntity.class)); } catch (Exception ex) { logger.warn("Error when fetching cluster mode", ex); return AsyncUtils.newFailedFuture(ex); } } public CompletableFuture modifyClusterMode(String ip, int port, int mode) { if (StringUtil.isBlank(ip) || port <= 0) { return AsyncUtils.newFailedFuture(new IllegalArgumentException("Invalid parameter")); } try { Map params = new HashMap<>(1); params.put("mode", String.valueOf(mode)); return executeCommand(ip, port, MODIFY_CLUSTER_MODE_PATH, params, false) .thenCompose(e -> { if (CommandConstants.MSG_SUCCESS.equals(e)) { return CompletableFuture.completedFuture(null); } else { logger.warn("Error when modifying cluster mode: " + e); return AsyncUtils.newFailedFuture(new RuntimeException(e)); } }); } catch (Exception ex) { logger.warn("Error when modifying cluster mode", ex); return AsyncUtils.newFailedFuture(ex); } } public CompletableFuture fetchClusterClientInfoAndConfig(String ip, int port) { if (StringUtil.isBlank(ip) || port <= 0) { return AsyncUtils.newFailedFuture(new IllegalArgumentException("Invalid parameter")); } try { return executeCommand(ip, port, FETCH_CLUSTER_CLIENT_CONFIG_PATH, false) .thenApply(r -> JSON.parseObject(r, ClusterClientInfoVO.class)); } catch (Exception ex) { logger.warn("Error when fetching cluster client config", ex); return AsyncUtils.newFailedFuture(ex); } } public CompletableFuture modifyClusterClientConfig(String app, String ip, int port, ClusterClientConfig config) { if (StringUtil.isBlank(ip) || port <= 0) { return AsyncUtils.newFailedFuture(new IllegalArgumentException("Invalid parameter")); } try { Map params = new HashMap<>(1); params.put("data", JSON.toJSONString(config)); return executeCommand(app, ip, port, MODIFY_CLUSTER_CLIENT_CONFIG_PATH, params, true) .thenCompose(e -> { if (CommandConstants.MSG_SUCCESS.equals(e)) { return CompletableFuture.completedFuture(null); } else { logger.warn("Error when modifying cluster client config: " + e); return AsyncUtils.newFailedFuture(new RuntimeException(e)); } }); } catch (Exception ex) { logger.warn("Error when modifying cluster client config", ex); return AsyncUtils.newFailedFuture(ex); } } public CompletableFuture modifyClusterServerFlowConfig(String app, String ip, int port, ServerFlowConfig config) { if (StringUtil.isBlank(ip) || port <= 0) { return AsyncUtils.newFailedFuture(new IllegalArgumentException("Invalid parameter")); } try { Map params = new HashMap<>(1); params.put("data", JSON.toJSONString(config)); return executeCommand(app, ip, port, MODIFY_CLUSTER_SERVER_FLOW_CONFIG_PATH, params, true) .thenCompose(e -> { if (CommandConstants.MSG_SUCCESS.equals(e)) { return CompletableFuture.completedFuture(null); } else { logger.warn("Error when modifying cluster server flow config: " + e); return AsyncUtils.newFailedFuture(new RuntimeException(e)); } }); } catch (Exception ex) { logger.warn("Error when modifying cluster server flow config", ex); return AsyncUtils.newFailedFuture(ex); } } public CompletableFuture modifyClusterServerTransportConfig(String app, String ip, int port, ServerTransportConfig config) { if (StringUtil.isBlank(ip) || port <= 0) { return AsyncUtils.newFailedFuture(new IllegalArgumentException("Invalid parameter")); } try { Map params = new HashMap<>(2); params.put("port", config.getPort().toString()); params.put("idleSeconds", config.getIdleSeconds().toString()); return executeCommand(app, ip, port, MODIFY_CLUSTER_SERVER_TRANSPORT_CONFIG_PATH, params, false) .thenCompose(e -> { if (CommandConstants.MSG_SUCCESS.equals(e)) { return CompletableFuture.completedFuture(null); } else { logger.warn("Error when modifying cluster server transport config: " + e); return AsyncUtils.newFailedFuture(new RuntimeException(e)); } }); } catch (Exception ex) { logger.warn("Error when modifying cluster server transport config", ex); return AsyncUtils.newFailedFuture(ex); } } public CompletableFuture modifyClusterServerNamespaceSet(String app, String ip, int port, Set set) { if (StringUtil.isBlank(ip) || port <= 0) { return AsyncUtils.newFailedFuture(new IllegalArgumentException("Invalid parameter")); } try { Map params = new HashMap<>(1); params.put("data", JSON.toJSONString(set)); return executeCommand(app, ip, port, MODIFY_CLUSTER_SERVER_NAMESPACE_SET_PATH, params, true) .thenCompose(e -> { if (CommandConstants.MSG_SUCCESS.equals(e)) { return CompletableFuture.completedFuture(null); } else { logger.warn("Error when modifying cluster server NamespaceSet", e); return AsyncUtils.newFailedFuture(new RuntimeException(e)); } }); } catch (Exception ex) { logger.warn("Error when modifying cluster server NamespaceSet", ex); return AsyncUtils.newFailedFuture(ex); } } public CompletableFuture fetchClusterServerBasicInfo(String ip, int port) { if (StringUtil.isBlank(ip) || port <= 0) { return AsyncUtils.newFailedFuture(new IllegalArgumentException("Invalid parameter")); } try { return executeCommand(ip, port, FETCH_CLUSTER_SERVER_BASIC_INFO_PATH, false) .thenApply(r -> JSON.parseObject(r, ClusterServerStateVO.class)); } catch (Exception ex) { logger.warn("Error when fetching cluster sever all config and basic info", ex); return AsyncUtils.newFailedFuture(ex); } } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/config/DashboardConfig.java ================================================ /* * 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 java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.math.NumberUtils; import org.springframework.lang.NonNull; /** *

    Dashboard local config support.

    *

    * Dashboard supports configuration loading by several ways by order:
    * 1. System.properties
    * 2. Env *

    * * @author jason * @since 1.5.0 */ public class DashboardConfig { public static final int DEFAULT_MACHINE_HEALTHY_TIMEOUT_MS = 60_000; /** * Login username */ public static final String CONFIG_AUTH_USERNAME = "sentinel.dashboard.auth.username"; /** * Login password */ public static final String CONFIG_AUTH_PASSWORD = "sentinel.dashboard.auth.password"; /** * Hide application name in sidebar when it has no healthy machines after specific period in millisecond. */ public static final String CONFIG_HIDE_APP_NO_MACHINE_MILLIS = "sentinel.dashboard.app.hideAppNoMachineMillis"; /** * Remove application when it has no healthy machines after specific period in millisecond. */ public static final String CONFIG_REMOVE_APP_NO_MACHINE_MILLIS = "sentinel.dashboard.removeAppNoMachineMillis"; /** * Timeout */ public static final String CONFIG_UNHEALTHY_MACHINE_MILLIS = "sentinel.dashboard.unhealthyMachineMillis"; /** * Auto remove unhealthy machine after specific period in millisecond. */ public static final String CONFIG_AUTO_REMOVE_MACHINE_MILLIS = "sentinel.dashboard.autoRemoveMachineMillis"; private static final ConcurrentMap cacheMap = new ConcurrentHashMap<>(); @NonNull private static String getConfig(String name) { // env String val = System.getenv(name); if (StringUtils.isNotEmpty(val)) { return val; } // properties val = System.getProperty(name); if (StringUtils.isNotEmpty(val)) { return val; } return ""; } protected static String getConfigStr(String name) { if (cacheMap.containsKey(name)) { return (String) cacheMap.get(name); } String val = getConfig(name); if (StringUtils.isBlank(val)) { return null; } cacheMap.put(name, val); return val; } protected static int getConfigInt(String name, int defaultVal, int minVal) { if (cacheMap.containsKey(name)) { return (int)cacheMap.get(name); } int val = NumberUtils.toInt(getConfig(name)); if (val == 0) { val = defaultVal; } else if (val < minVal) { val = minVal; } cacheMap.put(name, val); return val; } public static String getAuthUsername() { return getConfigStr(CONFIG_AUTH_USERNAME); } public static String getAuthPassword() { return getConfigStr(CONFIG_AUTH_PASSWORD); } public static int getHideAppNoMachineMillis() { return getConfigInt(CONFIG_HIDE_APP_NO_MACHINE_MILLIS, 0, 60000); } public static int getRemoveAppNoMachineMillis() { return getConfigInt(CONFIG_REMOVE_APP_NO_MACHINE_MILLIS, 0, 120000); } public static int getAutoRemoveMachineMillis() { return getConfigInt(CONFIG_AUTO_REMOVE_MACHINE_MILLIS, 0, 300000); } public static int getUnhealthyMachineMillis() { return getConfigInt(CONFIG_UNHEALTHY_MACHINE_MILLIS, DEFAULT_MACHINE_HEALTHY_TIMEOUT_MS, 30000); } public static void clearCache() { cacheMap.clear(); } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/config/WebConfig.java ================================================ /* * 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.adapter.servlet.CommonFilter; import com.alibaba.csp.sentinel.dashboard.filter.AuthFilter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import javax.servlet.Filter; /** * @author leyou */ @Configuration public class WebConfig implements WebMvcConfigurer { private final Logger logger = LoggerFactory.getLogger(WebConfig.class); @Autowired private AuthFilter authFilter; @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/**").addResourceLocations("classpath:/resources/"); } @Override public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController("/").setViewName("forward:/index.htm"); } /** * Add {@link CommonFilter} to the server, this is the simplest way to use Sentinel * for Web application. */ @Bean public FilterRegistrationBean sentinelFilterRegistration() { FilterRegistrationBean registration = new FilterRegistrationBean<>(); registration.setFilter(new CommonFilter()); registration.addUrlPatterns("/*"); registration.setName("sentinelFilter"); registration.setOrder(1); logger.info("Sentinel servlet CommonFilter registered"); return registration; } @Bean public FilterRegistrationBean authenticationFilterRegistration() { FilterRegistrationBean registration = new FilterRegistrationBean<>(); registration.setFilter(authFilter); registration.addUrlPatterns("/*"); registration.setName("authenticationFilter"); registration.setOrder(0); return registration; } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/AppController.java ================================================ /* * 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.controller; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; import javax.servlet.http.HttpServletRequest; import com.alibaba.csp.sentinel.dashboard.discovery.AppInfo; import com.alibaba.csp.sentinel.dashboard.discovery.AppManagement; import com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo; import com.alibaba.csp.sentinel.dashboard.domain.Result; import com.alibaba.csp.sentinel.dashboard.domain.vo.MachineInfoVo; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; /** * @author Carpenter Lee */ @RestController @RequestMapping(value = "/app") public class AppController { @Autowired private AppManagement appManagement; @GetMapping("/names.json") public Result> queryApps(HttpServletRequest request) { return Result.ofSuccess(appManagement.getAppNames()); } @GetMapping("/briefinfos.json") public Result> queryAppInfos(HttpServletRequest request) { List list = new ArrayList<>(appManagement.getBriefApps()); Collections.sort(list, Comparator.comparing(AppInfo::getApp)); return Result.ofSuccess(list); } @GetMapping(value = "/{app}/machines.json") public Result> getMachinesByApp(@PathVariable("app") String app) { AppInfo appInfo = appManagement.getDetailApp(app); if (appInfo == null) { return Result.ofSuccess(null); } List list = new ArrayList<>(appInfo.getMachines()); Collections.sort(list, (o1, o2) -> { int t = o1.getApp().compareTo(o2.getApp()); if (t != 0) { return t; } t = o1.getIp().compareTo(o2.getIp()); if (t != 0) { return t; } return o1.getPort().compareTo(o2.getPort()); }); return Result.ofSuccess(MachineInfoVo.fromMachineInfoList(list)); } @RequestMapping(value = "/{app}/machine/remove.json") public Result removeMachineById( @PathVariable("app") String app, @RequestParam(name = "ip") String ip, @RequestParam(name = "port") int port) { AppInfo appInfo = appManagement.getDetailApp(app); if (appInfo == null) { return Result.ofSuccess(null); } if (appManagement.removeMachine(app, ip, port)) { return Result.ofSuccessMsg("success"); } else { return Result.ofFail(1, "remove failed"); } } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/AuthController.java ================================================ /* * 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.controller; import com.alibaba.csp.sentinel.dashboard.auth.AuthService; import com.alibaba.csp.sentinel.dashboard.auth.SimpleWebAuthServiceImpl; import com.alibaba.csp.sentinel.dashboard.config.DashboardConfig; import com.alibaba.csp.sentinel.dashboard.domain.Result; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; import javax.servlet.http.HttpServletRequest; /** * @author cdfive * @since 1.6.0 */ @RestController @RequestMapping("/auth") public class AuthController { private static Logger LOGGER = LoggerFactory.getLogger(AuthController.class); @Value("${auth.username:sentinel}") private String authUsername; @Value("${auth.password:sentinel}") private String authPassword; @PostMapping("/login") public Result login(HttpServletRequest request, String username, String password) { if (StringUtils.isNotBlank(DashboardConfig.getAuthUsername())) { authUsername = DashboardConfig.getAuthUsername(); } if (StringUtils.isNotBlank(DashboardConfig.getAuthPassword())) { authPassword = DashboardConfig.getAuthPassword(); } /* * If auth.username or auth.password is blank(set in application.properties or VM arguments), * auth will pass, as the front side validate the input which can't be blank, * so user can input any username or password(both are not blank) to login in that case. */ if (StringUtils.isNotBlank(authUsername) && !authUsername.equals(username) || StringUtils.isNotBlank(authPassword) && !authPassword.equals(password)) { LOGGER.error("Login failed: Invalid username or password, username=" + username + ", password=" + password); return Result.ofFail(-1, "Invalid username or password"); } AuthService.AuthUser authUser = new SimpleWebAuthServiceImpl.SimpleWebAuthUserImpl(username); request.getSession().setAttribute(SimpleWebAuthServiceImpl.WEB_SESSTION_KEY, authUser); return Result.ofSuccess(authUser); } @RequestMapping(value = "/logout", method = RequestMethod.POST) public Result logout(HttpServletRequest request) { request.getSession().invalidate(); return Result.ofSuccess(null); } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/AuthorityRuleController.java ================================================ /* * 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.controller; import java.util.Date; import java.util.List; import javax.servlet.http.HttpServletRequest; import com.alibaba.csp.sentinel.dashboard.client.SentinelApiClient; import com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo; import com.alibaba.csp.sentinel.dashboard.auth.AuthService; import com.alibaba.csp.sentinel.dashboard.auth.AuthService.AuthUser; import com.alibaba.csp.sentinel.dashboard.auth.AuthService.PrivilegeType; import com.alibaba.csp.sentinel.slots.block.RuleConstant; import com.alibaba.csp.sentinel.util.StringUtil; import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.AuthorityRuleEntity; import com.alibaba.csp.sentinel.dashboard.domain.Result; import com.alibaba.csp.sentinel.dashboard.repository.rule.RuleRepository; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; /** * @author Eric Zhao * @since 0.2.1 */ @RestController @RequestMapping(value = "/authority") public class AuthorityRuleController { private final Logger logger = LoggerFactory.getLogger(AuthorityRuleController.class); @Autowired private SentinelApiClient sentinelApiClient; @Autowired private RuleRepository repository; @Autowired private AuthService authService; @GetMapping("/rules") public Result> apiQueryAllRulesForMachine(HttpServletRequest request, @RequestParam String app, @RequestParam String ip, @RequestParam Integer port) { AuthUser authUser = authService.getAuthUser(request); authUser.authTarget(app, PrivilegeType.READ_RULE); if (StringUtil.isEmpty(app)) { return Result.ofFail(-1, "app cannot be null or empty"); } if (StringUtil.isEmpty(ip)) { return Result.ofFail(-1, "ip cannot be null or empty"); } if (port == null || port <= 0) { return Result.ofFail(-1, "Invalid parameter: port"); } try { List rules = sentinelApiClient.fetchAuthorityRulesOfMachine(app, ip, port); rules = repository.saveAll(rules); return Result.ofSuccess(rules); } catch (Throwable throwable) { logger.error("Error when querying authority rules", throwable); return Result.ofFail(-1, throwable.getMessage()); } } private Result checkEntityInternal(AuthorityRuleEntity entity) { if (entity == null) { return Result.ofFail(-1, "bad rule body"); } if (StringUtil.isBlank(entity.getApp())) { return Result.ofFail(-1, "app can't be null or empty"); } if (StringUtil.isBlank(entity.getIp())) { return Result.ofFail(-1, "ip can't be null or empty"); } if (entity.getPort() == null || entity.getPort() <= 0) { return Result.ofFail(-1, "port can't be null"); } if (entity.getRule() == null) { return Result.ofFail(-1, "rule can't be null"); } if (StringUtil.isBlank(entity.getResource())) { return Result.ofFail(-1, "resource name cannot be null or empty"); } if (StringUtil.isBlank(entity.getLimitApp())) { return Result.ofFail(-1, "limitApp should be valid"); } if (entity.getStrategy() != RuleConstant.AUTHORITY_WHITE && entity.getStrategy() != RuleConstant.AUTHORITY_BLACK) { return Result.ofFail(-1, "Unknown strategy (must be blacklist or whitelist)"); } return null; } @PostMapping("/rule") public Result apiAddAuthorityRule(HttpServletRequest request, @RequestBody AuthorityRuleEntity entity) { AuthUser authUser = authService.getAuthUser(request); authUser.authTarget(entity.getApp(), PrivilegeType.WRITE_RULE); Result checkResult = checkEntityInternal(entity); if (checkResult != null) { return checkResult; } entity.setId(null); Date date = new Date(); entity.setGmtCreate(date); entity.setGmtModified(date); try { entity = repository.save(entity); } catch (Throwable throwable) { logger.error("Failed to add authority rule", throwable); return Result.ofThrowable(-1, throwable); } if (!publishRules(entity.getApp(), entity.getIp(), entity.getPort())) { logger.info("Publish authority rules failed after rule add"); } return Result.ofSuccess(entity); } @PutMapping("/rule/{id}") public Result apiUpdateParamFlowRule(HttpServletRequest request, @PathVariable("id") Long id, @RequestBody AuthorityRuleEntity entity) { AuthUser authUser = authService.getAuthUser(request); authUser.authTarget(entity.getApp(), PrivilegeType.WRITE_RULE); if (id == null || id <= 0) { return Result.ofFail(-1, "Invalid id"); } Result checkResult = checkEntityInternal(entity); if (checkResult != null) { return checkResult; } entity.setId(id); Date date = new Date(); entity.setGmtCreate(null); entity.setGmtModified(date); try { entity = repository.save(entity); if (entity == null) { return Result.ofFail(-1, "Failed to save authority rule"); } } catch (Throwable throwable) { logger.error("Failed to save authority rule", throwable); return Result.ofThrowable(-1, throwable); } if (!publishRules(entity.getApp(), entity.getIp(), entity.getPort())) { logger.info("Publish authority rules failed after rule update"); } return Result.ofSuccess(entity); } @DeleteMapping("/rule/{id}") public Result apiDeleteRule(HttpServletRequest request, @PathVariable("id") Long id) { AuthUser authUser = authService.getAuthUser(request); if (id == null) { return Result.ofFail(-1, "id cannot be null"); } AuthorityRuleEntity oldEntity = repository.findById(id); if (oldEntity == null) { return Result.ofSuccess(null); } authUser.authTarget(oldEntity.getApp(), PrivilegeType.DELETE_RULE); try { repository.delete(id); } catch (Exception e) { return Result.ofFail(-1, e.getMessage()); } if (!publishRules(oldEntity.getApp(), oldEntity.getIp(), oldEntity.getPort())) { logger.error("Publish authority rules failed after rule delete"); } return Result.ofSuccess(id); } private boolean publishRules(String app, String ip, Integer port) { List rules = repository.findAllByMachine(MachineInfo.of(app, ip, port)); return sentinelApiClient.setAuthorityRuleOfMachine(app, ip, port, rules); } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/DegradeController.java ================================================ /* * 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.controller; import java.util.Date; import java.util.List; import javax.servlet.http.HttpServletRequest; import com.alibaba.csp.sentinel.dashboard.client.SentinelApiClient; import com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo; import com.alibaba.csp.sentinel.dashboard.auth.AuthService; import com.alibaba.csp.sentinel.dashboard.auth.AuthService.AuthUser; import com.alibaba.csp.sentinel.dashboard.auth.AuthService.PrivilegeType; import com.alibaba.csp.sentinel.slots.block.RuleConstant; import com.alibaba.csp.sentinel.util.StringUtil; import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.DegradeRuleEntity; import com.alibaba.csp.sentinel.dashboard.domain.Result; import com.alibaba.csp.sentinel.dashboard.repository.rule.InMemDegradeRuleStore; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; /** * @author leyou */ @Controller @RequestMapping(value = "/degrade", produces = MediaType.APPLICATION_JSON_VALUE) public class DegradeController { private final Logger logger = LoggerFactory.getLogger(DegradeController.class); @Autowired private InMemDegradeRuleStore repository; @Autowired private SentinelApiClient sentinelApiClient; @Autowired private AuthService authService; @ResponseBody @RequestMapping("/rules.json") public Result> queryMachineRules(HttpServletRequest request, String app, String ip, Integer port) { AuthUser authUser = authService.getAuthUser(request); authUser.authTarget(app, PrivilegeType.READ_RULE); if (StringUtil.isEmpty(app)) { return Result.ofFail(-1, "app can't be null or empty"); } if (StringUtil.isEmpty(ip)) { return Result.ofFail(-1, "ip can't be null or empty"); } if (port == null) { return Result.ofFail(-1, "port can't be null"); } try { List rules = sentinelApiClient.fetchDegradeRuleOfMachine(app, ip, port); rules = repository.saveAll(rules); return Result.ofSuccess(rules); } catch (Throwable throwable) { logger.error("queryApps error:", throwable); return Result.ofThrowable(-1, throwable); } } @ResponseBody @RequestMapping("/new.json") public Result add(HttpServletRequest request, String app, String ip, Integer port, String limitApp, String resource, Double count, Integer timeWindow, Integer grade) { AuthUser authUser = authService.getAuthUser(request); authUser.authTarget(app, PrivilegeType.WRITE_RULE); if (StringUtil.isBlank(app)) { return Result.ofFail(-1, "app can't be null or empty"); } if (StringUtil.isBlank(ip)) { return Result.ofFail(-1, "ip can't be null or empty"); } if (port == null) { return Result.ofFail(-1, "port can't be null"); } if (StringUtil.isBlank(limitApp)) { return Result.ofFail(-1, "limitApp can't be null or empty"); } if (StringUtil.isBlank(resource)) { return Result.ofFail(-1, "resource can't be null or empty"); } if (count == null) { return Result.ofFail(-1, "count can't be null"); } if (timeWindow == null) { return Result.ofFail(-1, "timeWindow can't be null"); } if (grade == null) { return Result.ofFail(-1, "grade can't be null"); } if (grade < RuleConstant.DEGRADE_GRADE_RT || grade > RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT) { return Result.ofFail(-1, "Invalid grade: " + grade); } DegradeRuleEntity entity = new DegradeRuleEntity(); entity.setApp(app.trim()); entity.setIp(ip.trim()); entity.setPort(port); entity.setLimitApp(limitApp.trim()); entity.setResource(resource.trim()); entity.setCount(count); entity.setTimeWindow(timeWindow); entity.setGrade(grade); Date date = new Date(); entity.setGmtCreate(date); entity.setGmtModified(date); try { entity = repository.save(entity); } catch (Throwable throwable) { logger.error("add error:", throwable); return Result.ofThrowable(-1, throwable); } if (!publishRules(app, ip, port)) { logger.info("publish degrade rules fail after rule add"); } return Result.ofSuccess(entity); } @ResponseBody @RequestMapping("/save.json") public Result updateIfNotNull(HttpServletRequest request, Long id, String app, String limitApp, String resource, Double count, Integer timeWindow, Integer grade) { AuthUser authUser = authService.getAuthUser(request); if (id == null) { return Result.ofFail(-1, "id can't be null"); } if (grade != null) { if (grade < RuleConstant.DEGRADE_GRADE_RT || grade > RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT) { return Result.ofFail(-1, "Invalid grade: " + grade); } } DegradeRuleEntity entity = repository.findById(id); if (entity == null) { return Result.ofFail(-1, "id " + id + " dose not exist"); } authUser.authTarget(entity.getApp(), PrivilegeType.WRITE_RULE); if (StringUtil.isNotBlank(app)) { entity.setApp(app.trim()); } if (StringUtil.isNotBlank(limitApp)) { entity.setLimitApp(limitApp.trim()); } if (StringUtil.isNotBlank(resource)) { entity.setResource(resource.trim()); } if (count != null) { entity.setCount(count); } if (timeWindow != null) { entity.setTimeWindow(timeWindow); } if (grade != null) { entity.setGrade(grade); } Date date = new Date(); entity.setGmtModified(date); try { entity = repository.save(entity); } catch (Throwable throwable) { logger.error("save error:", throwable); return Result.ofThrowable(-1, throwable); } if (!publishRules(entity.getApp(), entity.getIp(), entity.getPort())) { logger.info("publish degrade rules fail after rule update"); } return Result.ofSuccess(entity); } @ResponseBody @RequestMapping("/delete.json") public Result delete(HttpServletRequest request, Long id) { AuthUser authUser = authService.getAuthUser(request); if (id == null) { return Result.ofFail(-1, "id can't be null"); } DegradeRuleEntity oldEntity = repository.findById(id); if (oldEntity == null) { return Result.ofSuccess(null); } authUser.authTarget(oldEntity.getApp(), PrivilegeType.DELETE_RULE); try { repository.delete(id); } catch (Throwable throwable) { logger.error("delete error:", throwable); return Result.ofThrowable(-1, throwable); } if (!publishRules(oldEntity.getApp(), oldEntity.getIp(), oldEntity.getPort())) { logger.info("publish degrade rules fail after rule delete"); } return Result.ofSuccess(id); } private boolean publishRules(String app, String ip, Integer port) { List rules = repository.findAllByMachine(MachineInfo.of(app, ip, port)); return sentinelApiClient.setDegradeRuleOfMachine(app, ip, port, rules); } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/DemoController.java ================================================ /* * 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.controller; import java.util.Random; import java.util.concurrent.TimeUnit; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.MediaType; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import com.alibaba.csp.sentinel.Entry; import com.alibaba.csp.sentinel.EntryType; import com.alibaba.csp.sentinel.SphU; import com.alibaba.csp.sentinel.context.ContextUtil; import com.alibaba.csp.sentinel.slots.block.BlockException; @Controller @RequestMapping(value = "/demo", produces = MediaType.APPLICATION_JSON_VALUE) public class DemoController { Logger logger = LoggerFactory.getLogger(MachineRegistryController.class); @RequestMapping("/greeting") public String greeting() { return "index"; } @RequestMapping("/link") @ResponseBody public String link() throws BlockException { Entry entry = SphU.entry("head1", EntryType.IN); Entry entry1 = SphU.entry("head2", EntryType.IN); Entry entry2 = SphU.entry("head3", EntryType.IN); Entry entry3 = SphU.entry("head4", EntryType.IN); entry3.exit(); entry2.exit(); entry1.exit(); entry.exit(); return "successfully create a call link"; } @RequestMapping("/loop") @ResponseBody public String loop(String name, int time) throws BlockException { for (int i = 0; i < 10; i++) { Thread timer = new Thread(new RunTask(name, time, false)); timer.setName("false"); timer.start(); } return "successfully create a loop thread"; } @RequestMapping("/slow") @ResponseBody public String slow(String name, int time) throws BlockException { for (int i = 0; i < 10; i++) { Thread timer = new Thread(new RunTask(name, time, true)); timer.setName("false"); timer.start(); } return "successfully create a loop thread"; } static class RunTask implements Runnable { int time; boolean stop = false; String name; boolean slow = false; public RunTask(String name, int time, boolean slow) { super(); this.time = time; this.name = name; this.slow = slow; } @Override public void run() { long startTime = System.currentTimeMillis(); ContextUtil.enter(String.valueOf(startTime)); while (!stop) { long now = System.currentTimeMillis(); if (now - startTime > time * 1000) { stop = true; } Entry e1 = null; try { e1 = SphU.entry(name); if (slow == true) { TimeUnit.MILLISECONDS.sleep(3000); } } catch (Exception e) { } finally { if (e1 != null) { e1.exit(); } } Random random2 = new Random(); try { TimeUnit.MILLISECONDS.sleep(random2.nextInt(200)); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } ContextUtil.exit(); } } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/FlowControllerV1.java ================================================ /* * 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.controller; import java.util.Date; import java.util.List; import javax.servlet.http.HttpServletRequest; import com.alibaba.csp.sentinel.dashboard.auth.AuthService; import com.alibaba.csp.sentinel.dashboard.auth.AuthService.AuthUser; import com.alibaba.csp.sentinel.dashboard.auth.AuthService.PrivilegeType; import com.alibaba.csp.sentinel.util.StringUtil; import com.alibaba.csp.sentinel.dashboard.client.SentinelApiClient; import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity; import com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo; import com.alibaba.csp.sentinel.dashboard.domain.Result; import com.alibaba.csp.sentinel.dashboard.repository.rule.InMemoryRuleRepositoryAdapter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; /** * Flow rule controller. * * @author leyou * @author Eric Zhao */ @RestController @RequestMapping(value = "/v1/flow") public class FlowControllerV1 { private final Logger logger = LoggerFactory.getLogger(FlowControllerV1.class); @Autowired private InMemoryRuleRepositoryAdapter repository; @Autowired private AuthService authService; @Autowired private SentinelApiClient sentinelApiClient; @GetMapping("/rules") public Result> apiQueryMachineRules(HttpServletRequest request, @RequestParam String app, @RequestParam String ip, @RequestParam Integer port) { AuthUser authUser = authService.getAuthUser(request); authUser.authTarget(app, PrivilegeType.READ_RULE); if (StringUtil.isEmpty(app)) { return Result.ofFail(-1, "app can't be null or empty"); } if (StringUtil.isEmpty(ip)) { return Result.ofFail(-1, "ip can't be null or empty"); } if (port == null) { return Result.ofFail(-1, "port can't be null"); } try { List rules = sentinelApiClient.fetchFlowRuleOfMachine(app, ip, port); rules = repository.saveAll(rules); return Result.ofSuccess(rules); } catch (Throwable throwable) { logger.error("Error when querying flow rules", throwable); return Result.ofThrowable(-1, throwable); } } private Result checkEntityInternal(FlowRuleEntity entity) { if (StringUtil.isBlank(entity.getApp())) { return Result.ofFail(-1, "app can't be null or empty"); } if (StringUtil.isBlank(entity.getIp())) { return Result.ofFail(-1, "ip can't be null or empty"); } if (entity.getPort() == null) { return Result.ofFail(-1, "port can't be null"); } if (StringUtil.isBlank(entity.getLimitApp())) { return Result.ofFail(-1, "limitApp can't be null or empty"); } if (StringUtil.isBlank(entity.getResource())) { return Result.ofFail(-1, "resource can't be null or empty"); } if (entity.getGrade() == null) { return Result.ofFail(-1, "grade can't be null"); } if (entity.getGrade() != 0 && entity.getGrade() != 1) { return Result.ofFail(-1, "grade must be 0 or 1, but " + entity.getGrade() + " got"); } if (entity.getCount() == null || entity.getCount() < 0) { return Result.ofFail(-1, "count should be at lease zero"); } if (entity.getStrategy() == null) { return Result.ofFail(-1, "strategy can't be null"); } if (entity.getStrategy() != 0 && StringUtil.isBlank(entity.getRefResource())) { return Result.ofFail(-1, "refResource can't be null or empty when strategy!=0"); } if (entity.getControlBehavior() == null) { return Result.ofFail(-1, "controlBehavior can't be null"); } int controlBehavior = entity.getControlBehavior(); if (controlBehavior == 1 && entity.getWarmUpPeriodSec() == null) { return Result.ofFail(-1, "warmUpPeriodSec can't be null when controlBehavior==1"); } if (controlBehavior == 2 && entity.getMaxQueueingTimeMs() == null) { return Result.ofFail(-1, "maxQueueingTimeMs can't be null when controlBehavior==2"); } if (entity.isClusterMode() && entity.getClusterConfig() == null) { return Result.ofFail(-1, "cluster config should be valid"); } return null; } @PostMapping("/rule") public Result apiAddFlowRule(HttpServletRequest request, @RequestBody FlowRuleEntity entity) { AuthUser authUser = authService.getAuthUser(request); authUser.authTarget(entity.getApp(), PrivilegeType.WRITE_RULE); Result checkResult = checkEntityInternal(entity); if (checkResult != null) { return checkResult; } entity.setId(null); Date date = new Date(); entity.setGmtCreate(date); entity.setGmtModified(date); entity.setLimitApp(entity.getLimitApp().trim()); entity.setResource(entity.getResource().trim()); try { entity = repository.save(entity); } catch (Throwable throwable) { logger.error("Failed to add flow rule", throwable); return Result.ofThrowable(-1, throwable); } if (!publishRules(entity.getApp(), entity.getIp(), entity.getPort())) { logger.error("Publish flow rules failed after rule add"); } return Result.ofSuccess(entity); } @PutMapping("/save.json") public Result updateIfNotNull(HttpServletRequest request, Long id, String app, String limitApp, String resource, Integer grade, Double count, Integer strategy, String refResource, Integer controlBehavior, Integer warmUpPeriodSec, Integer maxQueueingTimeMs) { AuthUser authUser = authService.getAuthUser(request); authUser.authTarget(app, PrivilegeType.WRITE_RULE); if (id == null) { return Result.ofFail(-1, "id can't be null"); } FlowRuleEntity entity = repository.findById(id); if (entity == null) { return Result.ofFail(-1, "id " + id + " dose not exist"); } if (StringUtil.isNotBlank(app)) { entity.setApp(app.trim()); } if (StringUtil.isNotBlank(limitApp)) { entity.setLimitApp(limitApp.trim()); } if (StringUtil.isNotBlank(resource)) { entity.setResource(resource.trim()); } if (grade != null) { if (grade != 0 && grade != 1) { return Result.ofFail(-1, "grade must be 0 or 1, but " + grade + " got"); } entity.setGrade(grade); } if (count != null) { entity.setCount(count); } if (strategy != null) { if (strategy != 0 && strategy != 1 && strategy != 2) { return Result.ofFail(-1, "strategy must be in [0, 1, 2], but " + strategy + " got"); } entity.setStrategy(strategy); if (strategy != 0) { if (StringUtil.isBlank(refResource)) { return Result.ofFail(-1, "refResource can't be null or empty when strategy!=0"); } entity.setRefResource(refResource.trim()); } } if (controlBehavior != null) { if (controlBehavior != 0 && controlBehavior != 1 && controlBehavior != 2) { return Result.ofFail(-1, "controlBehavior must be in [0, 1, 2], but " + controlBehavior + " got"); } if (controlBehavior == 1 && warmUpPeriodSec == null) { return Result.ofFail(-1, "warmUpPeriodSec can't be null when controlBehavior==1"); } if (controlBehavior == 2 && maxQueueingTimeMs == null) { return Result.ofFail(-1, "maxQueueingTimeMs can't be null when controlBehavior==2"); } entity.setControlBehavior(controlBehavior); if (warmUpPeriodSec != null) { entity.setWarmUpPeriodSec(warmUpPeriodSec); } if (maxQueueingTimeMs != null) { entity.setMaxQueueingTimeMs(maxQueueingTimeMs); } } Date date = new Date(); entity.setGmtModified(date); try { entity = repository.save(entity); if (entity == null) { return Result.ofFail(-1, "save entity fail"); } } catch (Throwable throwable) { logger.error("save error:", throwable); return Result.ofThrowable(-1, throwable); } if (!publishRules(entity.getApp(), entity.getIp(), entity.getPort())) { logger.info("publish flow rules fail after rule update"); } return Result.ofSuccess(entity); } @DeleteMapping("/delete.json") public Result delete(HttpServletRequest request, Long id) { AuthUser authUser = authService.getAuthUser(request); if (id == null) { return Result.ofFail(-1, "id can't be null"); } FlowRuleEntity oldEntity = repository.findById(id); if (oldEntity == null) { return Result.ofSuccess(null); } authUser.authTarget(oldEntity.getApp(), PrivilegeType.DELETE_RULE); try { repository.delete(id); } catch (Exception e) { return Result.ofFail(-1, e.getMessage()); } if (!publishRules(oldEntity.getApp(), oldEntity.getIp(), oldEntity.getPort())) { logger.info("publish flow rules fail after rule delete"); } return Result.ofSuccess(id); } private boolean publishRules(String app, String ip, Integer port) { List rules = repository.findAllByMachine(MachineInfo.of(app, ip, port)); return sentinelApiClient.setFlowRuleOfMachine(app, ip, port, rules); } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/MachineRegistryController.java ================================================ /* * 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.controller; import com.alibaba.csp.sentinel.dashboard.discovery.AppManagement; import com.alibaba.csp.sentinel.util.StringUtil; 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 org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; @Controller @RequestMapping(value = "/registry", produces = MediaType.APPLICATION_JSON_VALUE) public class MachineRegistryController { private final Logger logger = LoggerFactory.getLogger(MachineRegistryController.class); @Autowired private AppManagement appManagement; @ResponseBody @RequestMapping("/machine") public Result receiveHeartBeat(String app, Long version, String v, String hostname, String ip, Integer port) { if (app == null) { app = MachineDiscovery.UNKNOWN_APP_NAME; } if (ip == null) { return Result.ofFail(-1, "ip can't be null"); } if (port == null) { return Result.ofFail(-1, "port can't be null"); } if (port == -1) { logger.info("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; version = version == null ? System.currentTimeMillis() : version; try { MachineInfo machineInfo = new MachineInfo(); machineInfo.setApp(app); machineInfo.setHostname(hostname); machineInfo.setIp(ip); machineInfo.setPort(port); machineInfo.setHeartbeatVersion(version); machineInfo.setLastHeartbeat(System.currentTimeMillis()); machineInfo.setVersion(sentinelVersion); appManagement.addMachine(machineInfo); return Result.ofSuccessMsg("success"); } catch (Exception e) { logger.error("Receive heartbeat error", e); return Result.ofFail(-1, e.getMessage()); } } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/MetricController.java ================================================ /* * 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.controller; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.TreeMap; import java.util.concurrent.ConcurrentHashMap; import com.alibaba.csp.sentinel.dashboard.domain.Result; import com.alibaba.csp.sentinel.dashboard.repository.metric.MetricsRepository; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import com.alibaba.csp.sentinel.util.StringUtil; import com.alibaba.csp.sentinel.dashboard.datasource.entity.MetricEntity; import com.alibaba.csp.sentinel.dashboard.domain.vo.MetricVo; /** * @author leyou */ @Controller @RequestMapping(value = "/metric", produces = MediaType.APPLICATION_JSON_VALUE) public class MetricController { private static Logger logger = LoggerFactory.getLogger(MetricController.class); private static final long maxQueryIntervalMs = 1000 * 60 * 60; @Autowired private MetricsRepository metricStore; @ResponseBody @RequestMapping("/queryTopResourceMetric.json") public Result queryTopResourceMetric(final String app, Integer pageIndex, Integer pageSize, Boolean desc, Long startTime, Long endTime, String searchKey) { if (StringUtil.isEmpty(app)) { return Result.ofFail(-1, "app can't be null or empty"); } if (pageIndex == null || pageIndex <= 0) { pageIndex = 1; } if (pageSize == null) { pageSize = 6; } if (pageSize >= 20) { pageSize = 20; } if (desc == null) { desc = true; } if (endTime == null) { endTime = System.currentTimeMillis(); } if (startTime == null) { startTime = endTime - 1000 * 60 * 5; } if (endTime - startTime > maxQueryIntervalMs) { return Result.ofFail(-1, "time intervalMs is too big, must <= 1h"); } List resources = metricStore.listResourcesOfApp(app); logger.debug("queryTopResourceMetric(), resources.size()={}", resources.size()); if (resources == null || resources.isEmpty()) { return Result.ofSuccess(null); } if (!desc) { Collections.reverse(resources); } if (StringUtil.isNotEmpty(searchKey)) { List searched = new ArrayList<>(); for (String resource : resources) { if (resource.contains(searchKey)) { searched.add(resource); } } resources = searched; } int totalPage = (resources.size() + pageSize - 1) / pageSize; List topResource = new ArrayList<>(); if (pageIndex <= totalPage) { topResource = resources.subList((pageIndex - 1) * pageSize, Math.min(pageIndex * pageSize, resources.size())); } final Map> map = new ConcurrentHashMap<>(); logger.debug("topResource={}", topResource); long time = System.currentTimeMillis(); for (final String resource : topResource) { List entities = metricStore.queryByAppAndResourceBetween( app, resource, startTime, endTime); logger.debug("resource={}, entities.size()={}", resource, entities == null ? "null" : entities.size()); List vos = MetricVo.fromMetricEntities(entities, resource); Iterable vosSorted = sortMetricVoAndDistinct(vos); map.put(resource, vosSorted); } logger.debug("queryTopResourceMetric() total query time={} ms", System.currentTimeMillis() - time); Map resultMap = new HashMap<>(16); resultMap.put("totalCount", resources.size()); resultMap.put("totalPage", totalPage); resultMap.put("pageIndex", pageIndex); resultMap.put("pageSize", pageSize); Map> map2 = new LinkedHashMap<>(); // order matters. for (String identity : topResource) { map2.put(identity, map.get(identity)); } resultMap.put("metric", map2); return Result.ofSuccess(resultMap); } @ResponseBody @RequestMapping("/queryByAppAndResource.json") public Result queryByAppAndResource(String app, String identity, Long startTime, Long endTime) { if (StringUtil.isEmpty(app)) { return Result.ofFail(-1, "app can't be null or empty"); } if (StringUtil.isEmpty(identity)) { return Result.ofFail(-1, "identity can't be null or empty"); } if (endTime == null) { endTime = System.currentTimeMillis(); } if (startTime == null) { startTime = endTime - 1000 * 60; } if (endTime - startTime > maxQueryIntervalMs) { return Result.ofFail(-1, "time intervalMs is too big, must <= 1h"); } List entities = metricStore.queryByAppAndResourceBetween( app, identity, startTime, endTime); List vos = MetricVo.fromMetricEntities(entities, identity); return Result.ofSuccess(sortMetricVoAndDistinct(vos)); } private Iterable sortMetricVoAndDistinct(List vos) { if (vos == null) { return null; } Map map = new TreeMap<>(); for (MetricVo vo : vos) { MetricVo oldVo = map.get(vo.getTimestamp()); if (oldVo == null || vo.getGmtCreate() > oldVo.getGmtCreate()) { map.put(vo.getTimestamp(), vo); } } return map.values(); } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/ParamFlowRuleController.java ================================================ /* * 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.controller; import java.util.Date; import java.util.List; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import javax.servlet.http.HttpServletRequest; import com.alibaba.csp.sentinel.dashboard.client.CommandNotFoundException; import com.alibaba.csp.sentinel.dashboard.client.SentinelApiClient; import com.alibaba.csp.sentinel.dashboard.discovery.AppManagement; import com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo; import com.alibaba.csp.sentinel.dashboard.auth.AuthService; import com.alibaba.csp.sentinel.dashboard.auth.AuthService.AuthUser; import com.alibaba.csp.sentinel.dashboard.auth.AuthService.PrivilegeType; import com.alibaba.csp.sentinel.slots.block.RuleConstant; import com.alibaba.csp.sentinel.util.StringUtil; import com.alibaba.csp.sentinel.dashboard.datasource.entity.SentinelVersion; import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.ParamFlowRuleEntity; import com.alibaba.csp.sentinel.dashboard.domain.Result; import com.alibaba.csp.sentinel.dashboard.repository.rule.RuleRepository; import com.alibaba.csp.sentinel.dashboard.util.VersionUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; /** * @author Eric Zhao * @since 0.2.1 */ @RestController @RequestMapping(value = "/paramFlow") public class ParamFlowRuleController { private final Logger logger = LoggerFactory.getLogger(ParamFlowRuleController.class); @Autowired private SentinelApiClient sentinelApiClient; @Autowired private AppManagement appManagement; @Autowired private RuleRepository repository; @Autowired private AuthService authService; private boolean checkIfSupported(String app, String ip, int port) { try { return Optional.ofNullable(appManagement.getDetailApp(app)) .flatMap(e -> e.getMachine(ip, port)) .flatMap(m -> VersionUtils.parseVersion(m.getVersion()) .map(v -> v.greaterOrEqual(version020))) .orElse(true); // If error occurred or cannot retrieve machine info, return true. } catch (Exception ex) { return true; } } @GetMapping("/rules") public Result> apiQueryAllRulesForMachine(HttpServletRequest request, @RequestParam String app, @RequestParam String ip, @RequestParam Integer port) { AuthUser authUser = authService.getAuthUser(request); authUser.authTarget(app, PrivilegeType.READ_RULE); if (StringUtil.isEmpty(app)) { return Result.ofFail(-1, "app cannot be null or empty"); } if (StringUtil.isEmpty(ip)) { return Result.ofFail(-1, "ip cannot be null or empty"); } if (port == null || port <= 0) { return Result.ofFail(-1, "Invalid parameter: port"); } if (!checkIfSupported(app, ip, port)) { return unsupportedVersion(); } try { return sentinelApiClient.fetchParamFlowRulesOfMachine(app, ip, port) .thenApply(repository::saveAll) .thenApply(Result::ofSuccess) .get(); } catch (ExecutionException ex) { logger.error("Error when querying parameter flow rules", ex.getCause()); if (isNotSupported(ex.getCause())) { return unsupportedVersion(); } else { return Result.ofThrowable(-1, ex.getCause()); } } catch (Throwable throwable) { logger.error("Error when querying parameter flow rules", throwable); return Result.ofFail(-1, throwable.getMessage()); } } private boolean isNotSupported(Throwable ex) { return ex instanceof CommandNotFoundException; } @PostMapping("/rule") public Result apiAddParamFlowRule(HttpServletRequest request, @RequestBody ParamFlowRuleEntity entity) { AuthUser authUser = authService.getAuthUser(request); authUser.authTarget(entity.getApp(), PrivilegeType.WRITE_RULE); Result checkResult = checkEntityInternal(entity); if (checkResult != null) { return checkResult; } if (!checkIfSupported(entity.getApp(), entity.getIp(), entity.getPort())) { return unsupportedVersion(); } entity.setId(null); entity.getRule().setResource(entity.getResource().trim()); Date date = new Date(); entity.setGmtCreate(date); entity.setGmtModified(date); try { entity = repository.save(entity); publishRules(entity.getApp(), entity.getIp(), entity.getPort()).get(); return Result.ofSuccess(entity); } catch (ExecutionException ex) { logger.error("Error when adding new parameter flow rules", ex.getCause()); if (isNotSupported(ex.getCause())) { return unsupportedVersion(); } else { return Result.ofThrowable(-1, ex.getCause()); } } catch (Throwable throwable) { logger.error("Error when adding new parameter flow rules", throwable); return Result.ofFail(-1, throwable.getMessage()); } } private Result checkEntityInternal(ParamFlowRuleEntity entity) { if (entity == null) { return Result.ofFail(-1, "bad rule body"); } if (StringUtil.isBlank(entity.getApp())) { return Result.ofFail(-1, "app can't be null or empty"); } if (StringUtil.isBlank(entity.getIp())) { return Result.ofFail(-1, "ip can't be null or empty"); } if (entity.getPort() == null || entity.getPort() <= 0) { return Result.ofFail(-1, "port can't be null"); } if (entity.getRule() == null) { return Result.ofFail(-1, "rule can't be null"); } if (StringUtil.isBlank(entity.getResource())) { return Result.ofFail(-1, "resource name cannot be null or empty"); } if (entity.getCount() < 0) { return Result.ofFail(-1, "count should be valid"); } if (entity.getGrade() != RuleConstant.FLOW_GRADE_QPS) { return Result.ofFail(-1, "Unknown mode (blockGrade) for parameter flow control"); } if (entity.getParamIdx() == null || entity.getParamIdx() < 0) { return Result.ofFail(-1, "paramIdx should be valid"); } return null; } @PutMapping("/rule/{id}") public Result apiUpdateParamFlowRule(HttpServletRequest request, @PathVariable("id") Long id, @RequestBody ParamFlowRuleEntity entity) { AuthUser authUser = authService.getAuthUser(request); if (id == null || id <= 0) { return Result.ofFail(-1, "Invalid id"); } ParamFlowRuleEntity oldEntity = repository.findById(id); if (oldEntity == null) { return Result.ofFail(-1, "id " + id + " does not exist"); } authUser.authTarget(oldEntity.getApp(), PrivilegeType.WRITE_RULE); Result checkResult = checkEntityInternal(entity); if (checkResult != null) { return checkResult; } if (!checkIfSupported(entity.getApp(), entity.getIp(), entity.getPort())) { return unsupportedVersion(); } entity.setId(id); Date date = new Date(); entity.setGmtCreate(oldEntity.getGmtCreate()); entity.setGmtModified(date); try { entity = repository.save(entity); publishRules(entity.getApp(), entity.getIp(), entity.getPort()).get(); return Result.ofSuccess(entity); } catch (ExecutionException ex) { logger.error("Error when updating parameter flow rules, id=" + id, ex.getCause()); if (isNotSupported(ex.getCause())) { return unsupportedVersion(); } else { return Result.ofThrowable(-1, ex.getCause()); } } catch (Throwable throwable) { logger.error("Error when updating parameter flow rules, id=" + id, throwable); return Result.ofFail(-1, throwable.getMessage()); } } @DeleteMapping("/rule/{id}") public Result apiDeleteRule(HttpServletRequest request, @PathVariable("id") Long id) { AuthUser authUser = authService.getAuthUser(request); if (id == null) { return Result.ofFail(-1, "id cannot be null"); } ParamFlowRuleEntity oldEntity = repository.findById(id); if (oldEntity == null) { return Result.ofSuccess(null); } authUser.authTarget(oldEntity.getApp(), PrivilegeType.DELETE_RULE); try { repository.delete(id); publishRules(oldEntity.getApp(), oldEntity.getIp(), oldEntity.getPort()).get(); return Result.ofSuccess(id); } catch (ExecutionException ex) { logger.error("Error when deleting parameter flow rules", ex.getCause()); if (isNotSupported(ex.getCause())) { return unsupportedVersion(); } else { return Result.ofThrowable(-1, ex.getCause()); } } catch (Throwable throwable) { logger.error("Error when deleting parameter flow rules", throwable); return Result.ofFail(-1, throwable.getMessage()); } } private CompletableFuture publishRules(String app, String ip, Integer port) { List rules = repository.findAllByMachine(MachineInfo.of(app, ip, port)); return sentinelApiClient.setParamFlowRuleOfMachine(app, ip, port, rules); } private Result unsupportedVersion() { return Result.ofFail(4041, "Sentinel client not supported for parameter flow control (unsupported version or dependency absent)"); } private final SentinelVersion version020 = new SentinelVersion().setMinorVersion(2); } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/ResourceController.java ================================================ /* * 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.controller; import java.util.List; import java.util.stream.Collectors; import com.alibaba.csp.sentinel.util.StringUtil; import com.alibaba.csp.sentinel.command.vo.NodeVo; import com.alibaba.csp.sentinel.dashboard.domain.ResourceTreeNode; import com.alibaba.csp.sentinel.dashboard.client.SentinelApiClient; import com.alibaba.csp.sentinel.dashboard.domain.Result; import com.alibaba.csp.sentinel.dashboard.domain.vo.ResourceVo; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * @author Carpenter Lee */ @RestController @RequestMapping(value = "/resource") public class ResourceController { private static Logger logger = LoggerFactory.getLogger(ResourceController.class); @Autowired private SentinelApiClient httpFetcher; /** * Fetch real time statistics info of the machine. * * @param ip ip to fetch * @param port port of the ip * @param type one of [root, default, cluster], 'root' means fetching from tree root node, 'default' means * fetching from tree default node, 'cluster' means fetching from cluster node. * @param searchKey key to search * @return node statistics info. */ @GetMapping("/machineResource.json") public Result> fetchResourceChainListOfMachine(String ip, Integer port, String type, String searchKey) { if (StringUtil.isEmpty(ip) || port == null) { return Result.ofFail(-1, "invalid param, give ip, port"); } final String ROOT = "root"; final String DEFAULT = "default"; if (StringUtil.isEmpty(type)) { type = ROOT; } if (ROOT.equalsIgnoreCase(type) || DEFAULT.equalsIgnoreCase(type)) { List nodeVos = httpFetcher.fetchResourceOfMachine(ip, port, type); if (nodeVos == null) { return Result.ofSuccess(null); } ResourceTreeNode treeNode = ResourceTreeNode.fromNodeVoList(nodeVos); treeNode.searchIgnoreCase(searchKey); return Result.ofSuccess(ResourceVo.fromResourceTreeNode(treeNode)); } else { // Normal (cluster node). List nodeVos = httpFetcher.fetchClusterNodeOfMachine(ip, port, true); if (nodeVos == null) { return Result.ofSuccess(null); } if (StringUtil.isNotEmpty(searchKey)) { nodeVos = nodeVos.stream().filter(node -> node.getResource() .toLowerCase().contains(searchKey.toLowerCase())) .collect(Collectors.toList()); } return Result.ofSuccess(ResourceVo.fromNodeVoList(nodeVos)); } } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/SystemController.java ================================================ /* * 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.controller; import java.util.Date; import java.util.List; import javax.servlet.http.HttpServletRequest; import com.alibaba.csp.sentinel.dashboard.auth.AuthService; import com.alibaba.csp.sentinel.dashboard.auth.AuthService.AuthUser; import com.alibaba.csp.sentinel.dashboard.auth.AuthService.PrivilegeType; import com.alibaba.csp.sentinel.util.StringUtil; import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.SystemRuleEntity; import com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo; import com.alibaba.csp.sentinel.dashboard.client.SentinelApiClient; import com.alibaba.csp.sentinel.dashboard.domain.Result; import com.alibaba.csp.sentinel.dashboard.repository.rule.InMemSystemRuleStore; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; /** * @author leyou(lihao) */ @Controller @RequestMapping(value = "/system", produces = MediaType.APPLICATION_JSON_VALUE) public class SystemController { private static Logger logger = LoggerFactory.getLogger(SystemController.class); @Autowired private InMemSystemRuleStore repository; @Autowired private SentinelApiClient sentinelApiClient; @Autowired private AuthService authService; @ResponseBody @RequestMapping("/rules.json") Result> queryMachineRules(HttpServletRequest request, String app, String ip, Integer port) { AuthUser authUser = authService.getAuthUser(request); authUser.authTarget(app, PrivilegeType.READ_RULE); if (StringUtil.isEmpty(app)) { return Result.ofFail(-1, "app can't be null or empty"); } if (StringUtil.isEmpty(ip)) { return Result.ofFail(-1, "ip can't be null or empty"); } if (port == null) { return Result.ofFail(-1, "port can't be null"); } try { List rules = sentinelApiClient.fetchSystemRuleOfMachine(app, ip, port); rules = repository.saveAll(rules); return Result.ofSuccess(rules); } catch (Throwable throwable) { logger.error("queryApps error:", throwable); return Result.ofThrowable(-1, throwable); } } private int countNotNullAndNotNegative(Number... values) { int notNullCount = 0; for (int i = 0; i < values.length; i++) { if (values[i] != null && values[i].doubleValue() >= 0) { notNullCount++; } } return notNullCount; } @ResponseBody @RequestMapping("/new.json") Result add(HttpServletRequest request, String app, String ip, Integer port, Double avgLoad, Long avgRt, Long maxThread, Double qps) { AuthUser authUser = authService.getAuthUser(request); authUser.authTarget(app, PrivilegeType.WRITE_RULE); if (StringUtil.isBlank(app)) { return Result.ofFail(-1, "app can't be null or empty"); } if (StringUtil.isBlank(ip)) { return Result.ofFail(-1, "ip can't be null or empty"); } if (port == null) { return Result.ofFail(-1, "port can't be null"); } int notNullCount = countNotNullAndNotNegative(avgLoad, avgRt, maxThread, qps); if (notNullCount != 1) { return Result.ofFail(-1, "only one of [avgLoad, avgRt, maxThread, qps] " + "value must be set >= 0, but " + notNullCount + " values get"); } SystemRuleEntity entity = new SystemRuleEntity(); entity.setApp(app.trim()); entity.setIp(ip.trim()); entity.setPort(port); // -1 is a fake value if (avgLoad != null) { entity.setAvgLoad(avgLoad); } else { entity.setAvgLoad(-1D); } if (avgRt != null) { entity.setAvgRt(avgRt); } else { entity.setAvgRt(-1L); } if (maxThread != null) { entity.setMaxThread(maxThread); } else { entity.setMaxThread(-1L); } if (qps != null) { entity.setQps(qps); } else { entity.setQps(-1D); } Date date = new Date(); entity.setGmtCreate(date); entity.setGmtModified(date); try { entity = repository.save(entity); } catch (Throwable throwable) { logger.error("add error:", throwable); return Result.ofThrowable(-1, throwable); } if (!publishRules(app, ip, port)) { logger.info("publish system rules fail after rule add"); } return Result.ofSuccess(entity); } @ResponseBody @RequestMapping("/save.json") Result updateIfNotNull(HttpServletRequest request, Long id, String app, Double avgLoad, Long avgRt, Long maxThread, Double qps) { AuthUser authUser = authService.getAuthUser(request); if (id == null) { return Result.ofFail(-1, "id can't be null"); } SystemRuleEntity entity = repository.findById(id); if (entity == null) { return Result.ofFail(-1, "id " + id + " dose not exist"); } authUser.authTarget(entity.getApp(), PrivilegeType.WRITE_RULE); if (StringUtil.isNotBlank(app)) { entity.setApp(app.trim()); } if (avgLoad != null) { if (avgLoad < 0) { return Result.ofFail(-1, "avgLoad must >= 0"); } entity.setAvgLoad(avgLoad); } if (avgRt != null) { if (avgRt < 0) { return Result.ofFail(-1, "avgRt must >= 0"); } entity.setAvgRt(avgRt); } if (maxThread != null) { if (maxThread < 0) { return Result.ofFail(-1, "maxThread must >= 0"); } entity.setMaxThread(maxThread); } if (qps != null) { if (qps < 0) { return Result.ofFail(-1, "qps must >= 0"); } entity.setQps(qps); } Date date = new Date(); entity.setGmtModified(date); try { entity = repository.save(entity); } catch (Throwable throwable) { logger.error("save error:", throwable); return Result.ofThrowable(-1, throwable); } if (!publishRules(entity.getApp(), entity.getIp(), entity.getPort())) { logger.info("publish system rules fail after rule update"); } return Result.ofSuccess(entity); } @ResponseBody @RequestMapping("/delete.json") Result delete(HttpServletRequest request, Long id) { AuthUser authUser = authService.getAuthUser(request); if (id == null) { return Result.ofFail(-1, "id can't be null"); } SystemRuleEntity oldEntity = repository.findById(id); if (oldEntity == null) { return Result.ofSuccess(null); } authUser.authTarget(oldEntity.getApp(), PrivilegeType.DELETE_RULE); try { repository.delete(id); } catch (Throwable throwable) { logger.error("delete error:", throwable); return Result.ofThrowable(-1, throwable); } if (!publishRules(oldEntity.getApp(), oldEntity.getIp(), oldEntity.getPort())) { logger.info("publish system rules fail after rule delete"); } return Result.ofSuccess(id); } private boolean publishRules(String app, String ip, Integer port) { List rules = repository.findAllByMachine(MachineInfo.of(app, ip, port)); return sentinelApiClient.setSystemRuleOfMachine(app, ip, port, rules); } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/cluster/ClusterAssignController.java ================================================ /* * 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.controller.cluster; import java.util.Collections; import java.util.Set; import com.alibaba.csp.sentinel.util.StringUtil; import com.alibaba.csp.sentinel.dashboard.domain.cluster.ClusterAppFullAssignRequest; import com.alibaba.csp.sentinel.dashboard.domain.cluster.ClusterAppAssignResultVO; import com.alibaba.csp.sentinel.dashboard.domain.cluster.ClusterAppSingleServerAssignRequest; import com.alibaba.csp.sentinel.dashboard.service.ClusterAssignService; import com.alibaba.csp.sentinel.dashboard.domain.Result; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * @author Eric Zhao * @since 1.4.1 */ @RestController @RequestMapping("/cluster/assign") public class ClusterAssignController { private final Logger logger = LoggerFactory.getLogger(ClusterAssignController.class); @Autowired private ClusterAssignService clusterAssignService; @PostMapping("/all_server/{app}") public Result apiAssignAllClusterServersOfApp(@PathVariable String app, @RequestBody ClusterAppFullAssignRequest assignRequest) { if (StringUtil.isEmpty(app)) { return Result.ofFail(-1, "app cannot be null or empty"); } if (assignRequest == null || assignRequest.getClusterMap() == null || assignRequest.getRemainingList() == null) { return Result.ofFail(-1, "bad request body"); } try { return Result.ofSuccess(clusterAssignService.applyAssignToApp(app, assignRequest.getClusterMap(), assignRequest.getRemainingList())); } catch (Throwable throwable) { logger.error("Error when assigning full cluster servers for app: " + app, throwable); return Result.ofFail(-1, throwable.getMessage()); } } @PostMapping("/single_server/{app}") public Result apiAssignSingleClusterServersOfApp(@PathVariable String app, @RequestBody ClusterAppSingleServerAssignRequest assignRequest) { if (StringUtil.isEmpty(app)) { return Result.ofFail(-1, "app cannot be null or empty"); } if (assignRequest == null || assignRequest.getClusterMap() == null) { return Result.ofFail(-1, "bad request body"); } try { return Result.ofSuccess(clusterAssignService.applyAssignToApp(app, Collections.singletonList(assignRequest.getClusterMap()), assignRequest.getRemainingList())); } catch (Throwable throwable) { logger.error("Error when assigning single cluster servers for app: " + app, throwable); return Result.ofFail(-1, throwable.getMessage()); } } @PostMapping("/unbind_server/{app}") public Result apiUnbindClusterServersOfApp(@PathVariable String app, @RequestBody Set machineIds) { if (StringUtil.isEmpty(app)) { return Result.ofFail(-1, "app cannot be null or empty"); } if (machineIds == null || machineIds.isEmpty()) { return Result.ofFail(-1, "bad request body"); } try { return Result.ofSuccess(clusterAssignService.unbindClusterServers(app, machineIds)); } catch (Throwable throwable) { logger.error("Error when unbinding cluster server {} for app <{}>", machineIds, app, throwable); return Result.ofFail(-1, throwable.getMessage()); } } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/cluster/ClusterConfigController.java ================================================ /* * 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.controller.cluster; import java.util.List; import java.util.Optional; import java.util.concurrent.ExecutionException; import com.alibaba.csp.sentinel.cluster.ClusterStateManager; import com.alibaba.csp.sentinel.dashboard.client.CommandNotFoundException; import com.alibaba.csp.sentinel.dashboard.discovery.AppManagement; import com.alibaba.csp.sentinel.util.StringUtil; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.alibaba.csp.sentinel.dashboard.datasource.entity.SentinelVersion; import com.alibaba.csp.sentinel.dashboard.domain.cluster.request.ClusterClientModifyRequest; import com.alibaba.csp.sentinel.dashboard.domain.cluster.request.ClusterModifyRequest; import com.alibaba.csp.sentinel.dashboard.domain.cluster.request.ClusterServerModifyRequest; import com.alibaba.csp.sentinel.dashboard.domain.cluster.state.AppClusterClientStateWrapVO; import com.alibaba.csp.sentinel.dashboard.domain.cluster.state.AppClusterServerStateWrapVO; import com.alibaba.csp.sentinel.dashboard.domain.cluster.state.ClusterUniversalStatePairVO; import com.alibaba.csp.sentinel.dashboard.domain.cluster.state.ClusterUniversalStateVO; import com.alibaba.csp.sentinel.dashboard.service.ClusterConfigService; import com.alibaba.csp.sentinel.dashboard.util.ClusterEntityUtils; import com.alibaba.csp.sentinel.dashboard.util.VersionUtils; import com.alibaba.csp.sentinel.dashboard.domain.Result; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; /** * @author Eric Zhao * @since 1.4.0 */ @RestController @RequestMapping(value = "/cluster") public class ClusterConfigController { private final Logger logger = LoggerFactory.getLogger(ClusterConfigController.class); private final SentinelVersion version140 = new SentinelVersion().setMajorVersion(1).setMinorVersion(4); @Autowired private AppManagement appManagement; @Autowired private ClusterConfigService clusterConfigService; @PostMapping("/config/modify_single") public Result apiModifyClusterConfig(@RequestBody String payload) { if (StringUtil.isBlank(payload)) { return Result.ofFail(-1, "empty request body"); } try { JSONObject body = JSON.parseObject(payload); if (body.containsKey(KEY_MODE)) { int mode = body.getInteger(KEY_MODE); switch (mode) { case ClusterStateManager.CLUSTER_CLIENT: ClusterClientModifyRequest data = JSON.parseObject(payload, ClusterClientModifyRequest.class); Result res = checkValidRequest(data); if (res != null) { return res; } clusterConfigService.modifyClusterClientConfig(data).get(); return Result.ofSuccess(true); case ClusterStateManager.CLUSTER_SERVER: ClusterServerModifyRequest d = JSON.parseObject(payload, ClusterServerModifyRequest.class); Result r = checkValidRequest(d); if (r != null) { return r; } // TODO: bad design here, should refactor! clusterConfigService.modifyClusterServerConfig(d).get(); return Result.ofSuccess(true); default: return Result.ofFail(-1, "invalid mode"); } } return Result.ofFail(-1, "invalid parameter"); } catch (ExecutionException ex) { logger.error("Error when modifying cluster config", ex.getCause()); return errorResponse(ex); } catch (Throwable ex) { logger.error("Error when modifying cluster config", ex); return Result.ofFail(-1, ex.getMessage()); } } private Result errorResponse(ExecutionException ex) { if (isNotSupported(ex.getCause())) { return unsupportedVersion(); } else { return Result.ofThrowable(-1, ex.getCause()); } } @GetMapping("/state_single") public Result apiGetClusterState(@RequestParam String app, @RequestParam String ip, @RequestParam Integer port) { if (StringUtil.isEmpty(app)) { return Result.ofFail(-1, "app cannot be null or empty"); } if (StringUtil.isEmpty(ip)) { return Result.ofFail(-1, "ip cannot be null or empty"); } if (port == null || port <= 0) { return Result.ofFail(-1, "Invalid parameter: port"); } if (!checkIfSupported(app, ip, port)) { return unsupportedVersion(); } try { return clusterConfigService.getClusterUniversalState(app, ip, port) .thenApply(Result::ofSuccess) .get(); } catch (ExecutionException ex) { logger.error("Error when fetching cluster state", ex.getCause()); return errorResponse(ex); } catch (Throwable throwable) { logger.error("Error when fetching cluster state", throwable); return Result.ofFail(-1, throwable.getMessage()); } } @GetMapping("/server_state/{app}") public Result> apiGetClusterServerStateOfApp(@PathVariable String app) { if (StringUtil.isEmpty(app)) { return Result.ofFail(-1, "app cannot be null or empty"); } try { return clusterConfigService.getClusterUniversalState(app) .thenApply(ClusterEntityUtils::wrapToAppClusterServerState) .thenApply(Result::ofSuccess) .get(); } catch (ExecutionException ex) { logger.error("Error when fetching cluster server state of app: " + app, ex.getCause()); return errorResponse(ex); } catch (Throwable throwable) { logger.error("Error when fetching cluster server state of app: " + app, throwable); return Result.ofFail(-1, throwable.getMessage()); } } @GetMapping("/client_state/{app}") public Result> apiGetClusterClientStateOfApp(@PathVariable String app) { if (StringUtil.isEmpty(app)) { return Result.ofFail(-1, "app cannot be null or empty"); } try { return clusterConfigService.getClusterUniversalState(app) .thenApply(ClusterEntityUtils::wrapToAppClusterClientState) .thenApply(Result::ofSuccess) .get(); } catch (ExecutionException ex) { logger.error("Error when fetching cluster token client state of app: " + app, ex.getCause()); return errorResponse(ex); } catch (Throwable throwable) { logger.error("Error when fetching cluster token client state of app: " + app, throwable); return Result.ofFail(-1, throwable.getMessage()); } } @GetMapping("/state/{app}") public Result> apiGetClusterStateOfApp(@PathVariable String app) { if (StringUtil.isEmpty(app)) { return Result.ofFail(-1, "app cannot be null or empty"); } try { return clusterConfigService.getClusterUniversalState(app) .thenApply(Result::ofSuccess) .get(); } catch (ExecutionException ex) { logger.error("Error when fetching cluster state of app: " + app, ex.getCause()); return errorResponse(ex); } catch (Throwable throwable) { logger.error("Error when fetching cluster state of app: " + app, throwable); return Result.ofFail(-1, throwable.getMessage()); } } private boolean isNotSupported(Throwable ex) { return ex instanceof CommandNotFoundException; } private boolean checkIfSupported(String app, String ip, int port) { try { return Optional.ofNullable(appManagement.getDetailApp(app)) .flatMap(e -> e.getMachine(ip, port)) .flatMap(m -> VersionUtils.parseVersion(m.getVersion()) .map(v -> v.greaterOrEqual(version140))) .orElse(true); // If error occurred or cannot retrieve machine info, return true. } catch (Exception ex) { return true; } } private Result checkValidRequest(ClusterModifyRequest request) { if (StringUtil.isEmpty(request.getApp())) { return Result.ofFail(-1, "app cannot be empty"); } if (StringUtil.isEmpty(request.getIp())) { return Result.ofFail(-1, "ip cannot be empty"); } if (request.getPort() == null || request.getPort() < 0) { return Result.ofFail(-1, "invalid port"); } if (request.getMode() == null || request.getMode() < 0) { return Result.ofFail(-1, "invalid mode"); } if (!checkIfSupported(request.getApp(), request.getIp(), request.getPort())) { return unsupportedVersion(); } return null; } private Result unsupportedVersion() { return Result.ofFail(4041, "Sentinel client not supported for cluster flow control (unsupported version or dependency absent)"); } private static final String KEY_MODE = "mode"; } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/v2/FlowControllerV2.java ================================================ /* * 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.controller.v2; import java.util.Date; import java.util.List; import javax.servlet.http.HttpServletRequest; import com.alibaba.csp.sentinel.dashboard.auth.AuthService; import com.alibaba.csp.sentinel.dashboard.auth.AuthService.AuthUser; import com.alibaba.csp.sentinel.dashboard.auth.AuthService.PrivilegeType; import com.alibaba.csp.sentinel.util.StringUtil; import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity; import com.alibaba.csp.sentinel.dashboard.repository.rule.InMemoryRuleRepositoryAdapter; import com.alibaba.csp.sentinel.dashboard.rule.DynamicRuleProvider; import com.alibaba.csp.sentinel.dashboard.rule.DynamicRulePublisher; import com.alibaba.csp.sentinel.dashboard.domain.Result; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; /** * Flow rule controller (v2). * * @author Eric Zhao * @since 1.4.0 */ @RestController @RequestMapping(value = "/v2/flow") public class FlowControllerV2 { private final Logger logger = LoggerFactory.getLogger(FlowControllerV2.class); @Autowired private InMemoryRuleRepositoryAdapter repository; @Autowired @Qualifier("flowRuleNacosProvider") private DynamicRuleProvider> ruleProvider; @Autowired @Qualifier("flowRuleNacosPublisher") private DynamicRulePublisher> rulePublisher; @Autowired private AuthService authService; @GetMapping("/rules") public Result> apiQueryMachineRules(HttpServletRequest request, @RequestParam String app) { AuthUser authUser = authService.getAuthUser(request); authUser.authTarget(app, PrivilegeType.READ_RULE); if (StringUtil.isEmpty(app)) { return Result.ofFail(-1, "app can't be null or empty"); } try { List rules = ruleProvider.getRules(app); if (rules != null && !rules.isEmpty()) { for (FlowRuleEntity entity : rules) { entity.setApp(app); if (entity.getClusterConfig() != null && entity.getClusterConfig().getFlowId() != null) { entity.setId(entity.getClusterConfig().getFlowId()); } } } rules = repository.saveAll(rules); return Result.ofSuccess(rules); } catch (Throwable throwable) { logger.error("Error when querying flow rules", throwable); return Result.ofThrowable(-1, throwable); } } private Result checkEntityInternal(FlowRuleEntity entity) { if (entity == null) { return Result.ofFail(-1, "invalid body"); } if (StringUtil.isBlank(entity.getApp())) { return Result.ofFail(-1, "app can't be null or empty"); } if (StringUtil.isBlank(entity.getLimitApp())) { return Result.ofFail(-1, "limitApp can't be null or empty"); } if (StringUtil.isBlank(entity.getResource())) { return Result.ofFail(-1, "resource can't be null or empty"); } if (entity.getGrade() == null) { return Result.ofFail(-1, "grade can't be null"); } if (entity.getGrade() != 0 && entity.getGrade() != 1) { return Result.ofFail(-1, "grade must be 0 or 1, but " + entity.getGrade() + " got"); } if (entity.getCount() == null || entity.getCount() < 0) { return Result.ofFail(-1, "count should be at lease zero"); } if (entity.getStrategy() == null) { return Result.ofFail(-1, "strategy can't be null"); } if (entity.getStrategy() != 0 && StringUtil.isBlank(entity.getRefResource())) { return Result.ofFail(-1, "refResource can't be null or empty when strategy!=0"); } if (entity.getControlBehavior() == null) { return Result.ofFail(-1, "controlBehavior can't be null"); } int controlBehavior = entity.getControlBehavior(); if (controlBehavior == 1 && entity.getWarmUpPeriodSec() == null) { return Result.ofFail(-1, "warmUpPeriodSec can't be null when controlBehavior==1"); } if (controlBehavior == 2 && entity.getMaxQueueingTimeMs() == null) { return Result.ofFail(-1, "maxQueueingTimeMs can't be null when controlBehavior==2"); } if (entity.isClusterMode() && entity.getClusterConfig() == null) { return Result.ofFail(-1, "cluster config should be valid"); } return null; } @PostMapping("/rule") public Result apiAddFlowRule(HttpServletRequest request, @RequestBody FlowRuleEntity entity) { AuthUser authUser = authService.getAuthUser(request); authUser.authTarget(entity.getApp(), PrivilegeType.WRITE_RULE); Result checkResult = checkEntityInternal(entity); if (checkResult != null) { return checkResult; } entity.setId(null); Date date = new Date(); entity.setGmtCreate(date); entity.setGmtModified(date); entity.setLimitApp(entity.getLimitApp().trim()); entity.setResource(entity.getResource().trim()); try { entity = repository.save(entity); publishRules(entity.getApp()); } catch (Throwable throwable) { logger.error("Failed to add flow rule", throwable); return Result.ofThrowable(-1, throwable); } return Result.ofSuccess(entity); } @PutMapping("/rule/{id}") public Result apiUpdateFlowRule(HttpServletRequest request, @PathVariable("id") Long id, @RequestBody FlowRuleEntity entity) { AuthUser authUser = authService.getAuthUser(request); if (id == null || id <= 0) { return Result.ofFail(-1, "Invalid id"); } FlowRuleEntity oldEntity = repository.findById(id); if (oldEntity == null) { return Result.ofFail(-1, "id " + id + " does not exist"); } if (entity == null) { return Result.ofFail(-1, "invalid body"); } authUser.authTarget(oldEntity.getApp(), PrivilegeType.WRITE_RULE); entity.setApp(oldEntity.getApp()); entity.setIp(oldEntity.getIp()); entity.setPort(oldEntity.getPort()); Result checkResult = checkEntityInternal(entity); if (checkResult != null) { return checkResult; } entity.setId(id); Date date = new Date(); entity.setGmtCreate(oldEntity.getGmtCreate()); entity.setGmtModified(date); try { entity = repository.save(entity); if (entity == null) { return Result.ofFail(-1, "save entity fail"); } publishRules(oldEntity.getApp()); } catch (Throwable throwable) { logger.error("Failed to update flow rule", throwable); return Result.ofThrowable(-1, throwable); } return Result.ofSuccess(entity); } @DeleteMapping("/rule/{id}") public Result apiDeleteRule(HttpServletRequest request, @PathVariable("id") Long id) { AuthUser authUser = authService.getAuthUser(request); if (id == null || id <= 0) { return Result.ofFail(-1, "Invalid id"); } FlowRuleEntity oldEntity = repository.findById(id); if (oldEntity == null) { return Result.ofSuccess(null); } authUser.authTarget(oldEntity.getApp(), PrivilegeType.DELETE_RULE); try { repository.delete(id); publishRules(oldEntity.getApp()); } catch (Exception e) { return Result.ofFail(-1, e.getMessage()); } return Result.ofSuccess(id); } private void publishRules(/*@NonNull*/ String app) throws Exception { List rules = repository.findAllByApp(app); rulePublisher.publish(app, rules); } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/datasource/entity/ApplicationEntity.java ================================================ /* * 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.datasource.entity; import java.util.Date; import com.alibaba.csp.sentinel.dashboard.discovery.AppInfo; /** * @author leyou */ public class ApplicationEntity { private Long id; private Date gmtCreate; private Date gmtModified; private String app; private String activeConsole; private Date lastFetch; public long getId() { return id; } public void setId(long id) { this.id = id; } public Date getGmtCreate() { return gmtCreate; } public void setGmtCreate(Date gmtCreate) { this.gmtCreate = gmtCreate; } public Date getGmtModified() { return gmtModified; } public void setGmtModified(Date gmtModified) { this.gmtModified = gmtModified; } public String getApp() { return app; } public void setApp(String app) { this.app = app; } public String getActiveConsole() { return activeConsole; } public Date getLastFetch() { return lastFetch; } public void setLastFetch(Date lastFetch) { this.lastFetch = lastFetch; } public void setActiveConsole(String activeConsole) { this.activeConsole = activeConsole; } public AppInfo toAppInfo() { return new AppInfo(app); } @Override public String toString() { return "ApplicationEntity{" + "id=" + id + ", gmtCreate=" + gmtCreate + ", gmtModified=" + gmtModified + ", app='" + app + '\'' + ", activeConsole='" + activeConsole + '\'' + ", lastFetch=" + lastFetch + '}'; } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/datasource/entity/MachineEntity.java ================================================ /* * 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.datasource.entity; import java.util.Date; import com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo; /** * @author leyou */ public class MachineEntity { private Long id; private Date gmtCreate; private Date gmtModified; private String app; private String ip; private String hostname; private Date timestamp; private Integer port; public long getId() { return id; } public void setId(long id) { this.id = id; } public Date getGmtCreate() { return gmtCreate; } public void setGmtCreate(Date gmtCreate) { this.gmtCreate = gmtCreate; } public Date getGmtModified() { return gmtModified; } public void setGmtModified(Date gmtModified) { this.gmtModified = gmtModified; } public String getApp() { return app; } public void setApp(String app) { this.app = app; } public String getIp() { return ip; } public void setIp(String ip) { this.ip = ip; } public String getHostname() { return hostname; } public void setHostname(String hostname) { this.hostname = hostname; } public Date getTimestamp() { return timestamp; } public void setTimestamp(Date timestamp) { this.timestamp = timestamp; } public Integer getPort() { return port; } public void setPort(Integer port) { this.port = port; } public MachineInfo toMachineInfo() { MachineInfo machineInfo = new MachineInfo(); machineInfo.setApp(app); machineInfo.setHostname(hostname); machineInfo.setIp(ip); machineInfo.setPort(port); machineInfo.setLastHeartbeat(timestamp.getTime()); machineInfo.setHeartbeatVersion(timestamp.getTime()); return machineInfo; } @Override public String toString() { return "MachineEntity{" + "id=" + id + ", gmtCreate=" + gmtCreate + ", gmtModified=" + gmtModified + ", app='" + app + '\'' + ", ip='" + ip + '\'' + ", hostname='" + hostname + '\'' + ", timestamp=" + timestamp + ", port=" + port + '}'; } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/datasource/entity/MetricEntity.java ================================================ /* * 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.datasource.entity; import java.util.Date; /** * @author leyou */ public class MetricEntity { private Long id; private Date gmtCreate; private Date gmtModified; private String app; /** * 监控信息的时间戳 */ private Date timestamp; private String resource; private Long passQps; private Long successQps; private Long blockQps; private Long exceptionQps; /** * summary rt of all success exit qps. */ private double rt; /** * 本次聚合的总条数 */ private int count; private int resourceCode; public static MetricEntity copyOf(MetricEntity oldEntity) { MetricEntity entity = new MetricEntity(); entity.setId(oldEntity.getId()); entity.setGmtCreate(oldEntity.getGmtCreate()); entity.setGmtModified(oldEntity.getGmtModified()); entity.setApp(oldEntity.getApp()); entity.setTimestamp(oldEntity.getTimestamp()); entity.setResource(oldEntity.getResource()); entity.setPassQps(oldEntity.getPassQps()); entity.setBlockQps(oldEntity.getBlockQps()); entity.setSuccessQps(oldEntity.getSuccessQps()); entity.setExceptionQps(oldEntity.getExceptionQps()); entity.setRt(oldEntity.getRt()); entity.setCount(oldEntity.getCount()); entity.setResource(oldEntity.getResource()); return entity; } public synchronized void addPassQps(Long passQps) { this.passQps += passQps; } public synchronized void addBlockQps(Long blockQps) { this.blockQps += blockQps; } public synchronized void addExceptionQps(Long exceptionQps) { this.exceptionQps += exceptionQps; } public synchronized void addCount(int count) { this.count += count; } public synchronized void addRtAndSuccessQps(double avgRt, Long successQps) { this.rt += avgRt * successQps; this.successQps += successQps; } /** * {@link #rt} = {@code avgRt * successQps} * * @param avgRt average rt of {@code successQps} * @param successQps */ public synchronized void setRtAndSuccessQps(double avgRt, Long successQps) { this.rt = avgRt * successQps; this.successQps = successQps; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public Date getGmtCreate() { return gmtCreate; } public void setGmtCreate(Date gmtCreate) { this.gmtCreate = gmtCreate; } public Date getGmtModified() { return gmtModified; } public void setGmtModified(Date gmtModified) { this.gmtModified = gmtModified; } public String getApp() { return app; } public void setApp(String app) { this.app = app; } public Date getTimestamp() { return timestamp; } public void setTimestamp(Date timestamp) { this.timestamp = timestamp; } public String getResource() { return resource; } public void setResource(String resource) { this.resource = resource; this.resourceCode = resource.hashCode(); } public Long getPassQps() { return passQps; } public void setPassQps(Long passQps) { this.passQps = passQps; } public Long getBlockQps() { return blockQps; } public void setBlockQps(Long blockQps) { this.blockQps = blockQps; } public Long getExceptionQps() { return exceptionQps; } public void setExceptionQps(Long exceptionQps) { this.exceptionQps = exceptionQps; } public double getRt() { return rt; } public void setRt(double rt) { this.rt = rt; } public int getCount() { return count; } public void setCount(int count) { this.count = count; } public int getResourceCode() { return resourceCode; } public Long getSuccessQps() { return successQps; } public void setSuccessQps(Long successQps) { this.successQps = successQps; } @Override public String toString() { return "MetricEntity{" + "id=" + id + ", gmtCreate=" + gmtCreate + ", gmtModified=" + gmtModified + ", app='" + app + '\'' + ", timestamp=" + timestamp + ", resource='" + resource + '\'' + ", passQps=" + passQps + ", blockQps=" + blockQps + ", successQps=" + successQps + ", exceptionQps=" + exceptionQps + ", rt=" + rt + ", count=" + count + ", resourceCode=" + resourceCode + '}'; } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/datasource/entity/MetricPositionEntity.java ================================================ /* * 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.datasource.entity; import java.util.Date; /** * @author leyou */ public class MetricPositionEntity { private long id; private Date gmtCreate; private Date gmtModified; private String app; private String ip; /** * Sentinel在该应用上使用的端口 */ private int port; /** * 机器名,冗余字段 */ private String hostname; /** * 上一次拉取的最晚时间戳 */ private Date lastFetch; public long getId() { return id; } public void setId(long id) { this.id = id; } public Date getGmtCreate() { return gmtCreate; } public void setGmtCreate(Date gmtCreate) { this.gmtCreate = gmtCreate; } public Date getGmtModified() { return gmtModified; } public void setGmtModified(Date gmtModified) { this.gmtModified = gmtModified; } public String getApp() { return app; } public void setApp(String app) { this.app = app; } public String getIp() { return ip; } public void setIp(String ip) { this.ip = ip; } public int getPort() { return port; } public void setPort(int port) { this.port = port; } public String getHostname() { return hostname; } public void setHostname(String hostname) { this.hostname = hostname; } public Date getLastFetch() { return lastFetch; } public void setLastFetch(Date lastFetch) { this.lastFetch = lastFetch; } @Override public String toString() { return "MetricPositionEntity{" + "id=" + id + ", gmtCreate=" + gmtCreate + ", gmtModified=" + gmtModified + ", app='" + app + '\'' + ", ip='" + ip + '\'' + ", port=" + port + ", hostname='" + hostname + '\'' + ", lastFetch=" + lastFetch + '}'; } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/datasource/entity/SentinelVersion.java ================================================ /* * 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.datasource.entity; /** * @author Eric Zhao * @since 0.2.1 */ public class SentinelVersion { private int majorVersion; private int minorVersion; private int fixVersion; private String postfix; public SentinelVersion() { this(0, 0, 0); } public SentinelVersion(int major, int minor, int fix) { this(major, minor, fix, null); } public SentinelVersion(int major, int minor, int fix, String postfix) { this.majorVersion = major; this.minorVersion = minor; this.fixVersion = fix; this.postfix = postfix; } /** * 000, 000, 000 */ public int getFullVersion() { return majorVersion * 1000000 + minorVersion * 1000 + fixVersion; } public int getMajorVersion() { return majorVersion; } public SentinelVersion setMajorVersion(int majorVersion) { this.majorVersion = majorVersion; return this; } public int getMinorVersion() { return minorVersion; } public SentinelVersion setMinorVersion(int minorVersion) { this.minorVersion = minorVersion; return this; } public int getFixVersion() { return fixVersion; } public SentinelVersion setFixVersion(int fixVersion) { this.fixVersion = fixVersion; return this; } public String getPostfix() { return postfix; } public SentinelVersion setPostfix(String postfix) { this.postfix = postfix; return this; } public boolean greaterThan(SentinelVersion version) { if (version == null) { return true; } return getFullVersion() > version.getFullVersion(); } public boolean greaterOrEqual(SentinelVersion version) { if (version == null) { return true; } return getFullVersion() >= version.getFullVersion(); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } SentinelVersion that = (SentinelVersion)o; if (getFullVersion() != that.getFullVersion()) { return false; } return postfix != null ? postfix.equals(that.postfix) : that.postfix == null; } @Override public int hashCode() { int result = majorVersion; result = 31 * result + minorVersion; result = 31 * result + fixVersion; result = 31 * result + (postfix != null ? postfix.hashCode() : 0); return result; } @Override public String toString() { return "SentinelVersion{" + "majorVersion=" + majorVersion + ", minorVersion=" + minorVersion + ", fixVersion=" + fixVersion + ", postfix='" + postfix + '\'' + '}'; } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/datasource/entity/rule/AbstractRuleEntity.java ================================================ /* * 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.datasource.entity.rule; import java.util.Date; import com.alibaba.csp.sentinel.slots.block.AbstractRule; /** * @author Eric Zhao * @since 0.2.1 */ public abstract class AbstractRuleEntity implements RuleEntity { protected Long id; protected String app; protected String ip; protected Integer port; protected T rule; private Date gmtCreate; private Date gmtModified; @Override public Long getId() { return id; } @Override public void setId(Long id) { this.id = id; } @Override public String getApp() { return app; } public AbstractRuleEntity setApp(String app) { this.app = app; return this; } @Override public String getIp() { return ip; } public AbstractRuleEntity setIp(String ip) { this.ip = ip; return this; } @Override public Integer getPort() { return port; } public AbstractRuleEntity setPort(Integer port) { this.port = port; return this; } public T getRule() { return rule; } public AbstractRuleEntity setRule(T rule) { this.rule = rule; return this; } @Override public Date getGmtCreate() { return gmtCreate; } public AbstractRuleEntity setGmtCreate(Date gmtCreate) { this.gmtCreate = gmtCreate; return this; } public Date getGmtModified() { return gmtModified; } public AbstractRuleEntity setGmtModified(Date gmtModified) { this.gmtModified = gmtModified; return this; } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/datasource/entity/rule/AuthorityRuleEntity.java ================================================ /* * 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.datasource.entity.rule; import com.alibaba.csp.sentinel.slots.block.Rule; import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRule; import com.alibaba.csp.sentinel.util.AssertUtil; import com.fasterxml.jackson.annotation.JsonIgnore; /** * @author Eric Zhao * @since 0.2.1 */ public class AuthorityRuleEntity extends AbstractRuleEntity { public AuthorityRuleEntity() {} public AuthorityRuleEntity(AuthorityRule authorityRule) { AssertUtil.notNull(authorityRule, "Authority rule should not be null"); this.rule = authorityRule; } public static AuthorityRuleEntity fromAuthorityRule(String app, String ip, Integer port, AuthorityRule rule) { AuthorityRuleEntity entity = new AuthorityRuleEntity(rule); entity.setApp(app); entity.setIp(ip); entity.setPort(port); return entity; } @JsonIgnore public String getLimitApp() { return rule.getLimitApp(); } @JsonIgnore public String getResource() { return rule.getResource(); } @JsonIgnore public int getStrategy() { return rule.getStrategy(); } @Override public Rule toRule() { return rule; } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/datasource/entity/rule/DegradeRuleEntity.java ================================================ /* * 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.datasource.entity.rule; import java.util.Date; import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule; /** * @author leyou */ public class DegradeRuleEntity implements RuleEntity { private Long id; private String app; private String ip; private Integer port; private String resource; private String limitApp; private Double count; private Integer timeWindow; /** * 0 rt 限流; 1为异常; */ private Integer grade; private Date gmtCreate; private Date gmtModified; public static DegradeRuleEntity fromDegradeRule(String app, String ip, Integer port, DegradeRule rule) { DegradeRuleEntity entity = new DegradeRuleEntity(); entity.setApp(app); entity.setIp(ip); entity.setPort(port); entity.setResource(rule.getResource()); entity.setLimitApp(rule.getLimitApp()); entity.setCount(rule.getCount()); entity.setTimeWindow(rule.getTimeWindow()); entity.setGrade(rule.getGrade()); return entity; } @Override public String getIp() { return ip; } public void setIp(String ip) { this.ip = ip; } @Override public Integer getPort() { return port; } public void setPort(Integer port) { this.port = port; } @Override public Long getId() { return id; } @Override public void setId(Long id) { this.id = id; } @Override public String getApp() { return app; } public void setApp(String app) { this.app = app; } public String getResource() { return resource; } public void setResource(String resource) { this.resource = resource; } public String getLimitApp() { return limitApp; } public void setLimitApp(String limitApp) { this.limitApp = limitApp; } public Double getCount() { return count; } public void setCount(Double count) { this.count = count; } public Integer getTimeWindow() { return timeWindow; } public void setTimeWindow(Integer timeWindow) { this.timeWindow = timeWindow; } public Integer getGrade() { return grade; } public void setGrade(Integer grade) { this.grade = grade; } @Override public Date getGmtCreate() { return gmtCreate; } public void setGmtCreate(Date gmtCreate) { this.gmtCreate = gmtCreate; } public Date getGmtModified() { return gmtModified; } public void setGmtModified(Date gmtModified) { this.gmtModified = gmtModified; } @Override public DegradeRule toRule() { DegradeRule rule = new DegradeRule(); rule.setResource(resource); rule.setLimitApp(limitApp); rule.setCount(count); rule.setTimeWindow(timeWindow); rule.setGrade(grade); return rule; } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/datasource/entity/rule/FlowRuleEntity.java ================================================ /* * 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.datasource.entity.rule; import java.util.Date; import com.alibaba.csp.sentinel.slots.block.flow.ClusterFlowConfig; import com.alibaba.csp.sentinel.slots.block.flow.FlowRule; /** * @author leyou */ public class FlowRuleEntity implements RuleEntity { private Long id; private String app; private String ip; private Integer port; private String limitApp; private String resource; /** * 0为线程数;1为qps */ private Integer grade; private Double count; /** * 0为直接限流;1为关联限流;2为链路限流 ***/ private Integer strategy; private String refResource; /** * 0. default, 1. warm up, 2. rate limiter */ private Integer controlBehavior; private Integer warmUpPeriodSec; /** * max queueing time in rate limiter behavior */ private Integer maxQueueingTimeMs; private boolean clusterMode; /** * Flow rule config for cluster mode. */ private ClusterFlowConfig clusterConfig; private Date gmtCreate; private Date gmtModified; public static FlowRuleEntity fromFlowRule(String app, String ip, Integer port, FlowRule rule) { FlowRuleEntity entity = new FlowRuleEntity(); entity.setApp(app); entity.setIp(ip); entity.setPort(port); entity.setLimitApp(rule.getLimitApp()); entity.setResource(rule.getResource()); entity.setGrade(rule.getGrade()); entity.setCount(rule.getCount()); entity.setStrategy(rule.getStrategy()); entity.setRefResource(rule.getRefResource()); entity.setControlBehavior(rule.getControlBehavior()); entity.setWarmUpPeriodSec(rule.getWarmUpPeriodSec()); entity.setMaxQueueingTimeMs(rule.getMaxQueueingTimeMs()); entity.setClusterMode(rule.isClusterMode()); entity.setClusterConfig(rule.getClusterConfig()); return entity; } @Override public String getIp() { return ip; } public void setIp(String ip) { this.ip = ip; } @Override public Integer getPort() { return port; } public void setPort(Integer port) { this.port = port; } @Override public String getApp() { return app; } public void setApp(String app) { this.app = app; } @Override public Long getId() { return id; } @Override public void setId(Long id) { this.id = id; } public String getLimitApp() { return limitApp; } public void setLimitApp(String limitApp) { this.limitApp = limitApp; } public String getResource() { return resource; } public void setResource(String resource) { this.resource = resource; } public Integer getGrade() { return grade; } public void setGrade(Integer grade) { this.grade = grade; } public Double getCount() { return count; } public void setCount(Double count) { this.count = count; } public Integer getStrategy() { return strategy; } public void setStrategy(Integer strategy) { this.strategy = strategy; } public String getRefResource() { return refResource; } public void setRefResource(String refResource) { this.refResource = refResource; } public Integer getControlBehavior() { return controlBehavior; } public void setControlBehavior(Integer controlBehavior) { this.controlBehavior = controlBehavior; } public Integer getWarmUpPeriodSec() { return warmUpPeriodSec; } public void setWarmUpPeriodSec(Integer warmUpPeriodSec) { this.warmUpPeriodSec = warmUpPeriodSec; } public Integer getMaxQueueingTimeMs() { return maxQueueingTimeMs; } public void setMaxQueueingTimeMs(Integer maxQueueingTimeMs) { this.maxQueueingTimeMs = maxQueueingTimeMs; } public boolean isClusterMode() { return clusterMode; } public FlowRuleEntity setClusterMode(boolean clusterMode) { this.clusterMode = clusterMode; return this; } public ClusterFlowConfig getClusterConfig() { return clusterConfig; } public FlowRuleEntity setClusterConfig(ClusterFlowConfig clusterConfig) { this.clusterConfig = clusterConfig; return this; } @Override public Date getGmtCreate() { return gmtCreate; } public void setGmtCreate(Date gmtCreate) { this.gmtCreate = gmtCreate; } public Date getGmtModified() { return gmtModified; } public void setGmtModified(Date gmtModified) { this.gmtModified = gmtModified; } @Override public FlowRule toRule() { FlowRule flowRule = new FlowRule(); flowRule.setCount(this.count); flowRule.setGrade(this.grade); flowRule.setResource(this.resource); flowRule.setLimitApp(this.limitApp); flowRule.setRefResource(this.refResource); flowRule.setStrategy(this.strategy); if (this.controlBehavior != null) { flowRule.setControlBehavior(controlBehavior); } if (this.warmUpPeriodSec != null) { flowRule.setWarmUpPeriodSec(warmUpPeriodSec); } if (this.maxQueueingTimeMs != null) { flowRule.setMaxQueueingTimeMs(maxQueueingTimeMs); } flowRule.setClusterMode(clusterMode); flowRule.setClusterConfig(clusterConfig); return flowRule; } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/datasource/entity/rule/ParamFlowRuleEntity.java ================================================ /* * 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.datasource.entity.rule; import java.util.List; import com.alibaba.csp.sentinel.slots.block.Rule; import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowClusterConfig; import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowItem; import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule; import com.alibaba.csp.sentinel.util.AssertUtil; import com.fasterxml.jackson.annotation.JsonIgnore; /** * @author Eric Zhao * @since 0.2.1 */ public class ParamFlowRuleEntity extends AbstractRuleEntity { public ParamFlowRuleEntity() {} public ParamFlowRuleEntity(ParamFlowRule rule) { AssertUtil.notNull(rule, "Authority rule should not be null"); this.rule = rule; } public static ParamFlowRuleEntity fromAuthorityRule(String app, String ip, Integer port, ParamFlowRule rule) { ParamFlowRuleEntity entity = new ParamFlowRuleEntity(rule); entity.setApp(app); entity.setIp(ip); entity.setPort(port); return entity; } @JsonIgnore public String getLimitApp() { return rule.getLimitApp(); } @JsonIgnore public String getResource() { return rule.getResource(); } @JsonIgnore public int getGrade() { return rule.getGrade(); } @JsonIgnore public Integer getParamIdx() { return rule.getParamIdx(); } @JsonIgnore public double getCount() { return rule.getCount(); } @JsonIgnore public List getParamFlowItemList() { return rule.getParamFlowItemList(); } @JsonIgnore public boolean isClusterMode() { return rule.isClusterMode(); } @JsonIgnore public ParamFlowClusterConfig getClusterConfig() { return rule.getClusterConfig(); } @Override public Rule toRule() { return rule; } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/datasource/entity/rule/RuleEntity.java ================================================ /* * 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.datasource.entity.rule; import java.util.Date; import com.alibaba.csp.sentinel.slots.block.Rule; /** * @author leyou */ public interface RuleEntity { Long getId(); void setId(Long id); String getApp(); String getIp(); Integer getPort(); Date getGmtCreate(); Rule toRule(); } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/datasource/entity/rule/SystemRuleEntity.java ================================================ /* * 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.datasource.entity.rule; import java.util.Date; import com.alibaba.csp.sentinel.slots.system.SystemRule; /** * @author leyou */ public class SystemRuleEntity implements RuleEntity { private Long id; private String app; private String ip; private Integer port; private Double avgLoad; private Long avgRt; private Long maxThread; private Double qps; private Date gmtCreate; private Date gmtModified; public static SystemRuleEntity fromSystemRule(String app, String ip, Integer port, SystemRule rule) { SystemRuleEntity entity = new SystemRuleEntity(); entity.setApp(app); entity.setIp(ip); entity.setPort(port); entity.setAvgLoad(rule.getHighestSystemLoad()); entity.setAvgRt(rule.getAvgRt()); entity.setMaxThread(rule.getMaxThread()); entity.setQps(rule.getQps()); return entity; } @Override public String getIp() { return ip; } public void setIp(String ip) { this.ip = ip; } @Override public Integer getPort() { return port; } public void setPort(Integer port) { this.port = port; } @Override public Long getId() { return id; } @Override public void setId(Long id) { this.id = id; } @Override public String getApp() { return app; } public void setApp(String app) { this.app = app; } public Double getAvgLoad() { return avgLoad; } public void setAvgLoad(Double avgLoad) { this.avgLoad = avgLoad; } public Long getAvgRt() { return avgRt; } public void setAvgRt(Long avgRt) { this.avgRt = avgRt; } public Long getMaxThread() { return maxThread; } public void setMaxThread(Long maxThread) { this.maxThread = maxThread; } public Double getQps() { return qps; } public void setQps(Double qps) { this.qps = qps; } @Override public Date getGmtCreate() { return gmtCreate; } public void setGmtCreate(Date gmtCreate) { this.gmtCreate = gmtCreate; } public Date getGmtModified() { return gmtModified; } public void setGmtModified(Date gmtModified) { this.gmtModified = gmtModified; } @Override public SystemRule toRule() { SystemRule rule = new SystemRule(); rule.setHighestSystemLoad(avgLoad); rule.setAvgRt(avgRt); rule.setMaxThread(maxThread); rule.setQps(qps); return rule; } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/discovery/AppInfo.java ================================================ /* * 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.discovery; import java.util.Comparator; import java.util.HashSet; import java.util.Optional; import java.util.Iterator; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import com.alibaba.csp.sentinel.dashboard.config.DashboardConfig; public class AppInfo { private String app = ""; private Set machines = ConcurrentHashMap.newKeySet(); public AppInfo() {} public AppInfo(String app) { this.app = app; } public String getApp() { return app; } public void setApp(String app) { this.app = app; } /** * Get the current machines. * * @return a new copy of the current machines. */ public Set getMachines() { return new HashSet<>(machines); } @Override public String toString() { return "AppInfo{" + "app='" + app + ", machines=" + machines + '}'; } public boolean addMachine(MachineInfo machineInfo) { machines.remove(machineInfo); return machines.add(machineInfo); } public synchronized boolean removeMachine(String ip, int port) { Iterator it = machines.iterator(); while (it.hasNext()) { MachineInfo machine = it.next(); if (machine.getIp().equals(ip) && machine.getPort() == port) { it.remove(); return true; } } return false; } public Optional getMachine(String ip, int port) { return machines.stream() .filter(e -> e.getIp().equals(ip) && e.getPort().equals(port)) .findFirst(); } private boolean heartbeatJudge(final int threshold) { if (machines.size() == 0) { return false; } if (threshold > 0) { long healthyCount = machines.stream() .filter(MachineInfo::isHealthy) .count(); if (healthyCount == 0) { // No healthy machines. return machines.stream() .max(Comparator.comparingLong(MachineInfo::getLastHeartbeat)) .map(e -> System.currentTimeMillis() - e.getLastHeartbeat() < threshold) .orElse(false); } } return true; } /** * Check whether current application has no healthy machines and should not be displayed. * * @return true if the application should be displayed in the sidebar, otherwise false */ public boolean isShown() { return heartbeatJudge(DashboardConfig.getHideAppNoMachineMillis()); } /** * Check whether current application has no healthy machines and should be removed. * * @return true if the application is dead and should be removed, otherwise false */ public boolean isDead() { return !heartbeatJudge(DashboardConfig.getRemoveAppNoMachineMillis()); } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/discovery/AppManagement.java ================================================ /* * 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.discovery; import java.util.List; import java.util.Set; import javax.annotation.PostConstruct; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.stereotype.Component; @Component public class AppManagement implements MachineDiscovery { @Autowired private ApplicationContext context; private MachineDiscovery machineDiscovery; @PostConstruct public void init() { machineDiscovery = context.getBean(SimpleMachineDiscovery.class); } @Override public Set getBriefApps() { return machineDiscovery.getBriefApps(); } @Override public long addMachine(MachineInfo machineInfo) { return machineDiscovery.addMachine(machineInfo); } @Override public boolean removeMachine(String app, String ip, int port) { return machineDiscovery.removeMachine(app, ip, port); } @Override public List getAppNames() { return machineDiscovery.getAppNames(); } @Override public AppInfo getDetailApp(String app) { return machineDiscovery.getDetailApp(app); } @Override public void removeApp(String app) { machineDiscovery.removeApp(app); } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/discovery/MachineDiscovery.java ================================================ /* * 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.discovery; import java.util.List; import java.util.Set; public interface MachineDiscovery { String UNKNOWN_APP_NAME = "CLUSTER_NOT_STARTED"; List getAppNames(); Set getBriefApps(); AppInfo getDetailApp(String app); /** * Remove the given app from the application registry. * * @param app application name * @since 1.5.0 */ void removeApp(String app); long addMachine(MachineInfo machineInfo); /** * Remove the given machine instance from the application registry. * * @param app the application name of the machine * @param ip machine IP * @param port machine port * @return true if removed, otherwise false * @since 1.5.0 */ boolean removeMachine(String app, String ip, int port); } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/discovery/MachineInfo.java ================================================ /* * 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.discovery; import java.util.Objects; import com.alibaba.csp.sentinel.dashboard.config.DashboardConfig; import com.alibaba.csp.sentinel.util.StringUtil; public class MachineInfo implements Comparable { private String app = ""; private String hostname = ""; private String ip = ""; private Integer port = -1; private long lastHeartbeat; private long heartbeatVersion; /** * Indicates the version of Sentinel client (since 0.2.0). */ private String version; public static MachineInfo of(String app, String ip, Integer port) { MachineInfo machineInfo = new MachineInfo(); machineInfo.setApp(app); machineInfo.setIp(ip); machineInfo.setPort(port); return machineInfo; } public String toHostPort() { return ip + ":" + port; } public Integer getPort() { return port; } public void setPort(Integer port) { this.port = port; } public String getApp() { return app; } public void setApp(String app) { this.app = app; } public String getHostname() { return hostname; } public void setHostname(String hostname) { this.hostname = hostname; } public String getIp() { return ip; } public void setIp(String ip) { this.ip = ip; } public long getHeartbeatVersion() { return heartbeatVersion; } public void setHeartbeatVersion(long heartbeatVersion) { this.heartbeatVersion = heartbeatVersion; } public String getVersion() { return version; } public MachineInfo setVersion(String version) { this.version = version; return this; } public boolean isHealthy() { long delta = System.currentTimeMillis() - lastHeartbeat; return delta < DashboardConfig.getUnhealthyMachineMillis(); } /** * whether dead should be removed * * @return */ public boolean isDead() { if (DashboardConfig.getAutoRemoveMachineMillis() > 0) { long delta = System.currentTimeMillis() - lastHeartbeat; return delta > DashboardConfig.getAutoRemoveMachineMillis(); } return false; } public long getLastHeartbeat() { return lastHeartbeat; } public void setLastHeartbeat(long lastHeartbeat) { this.lastHeartbeat = lastHeartbeat; } @Override public int compareTo(MachineInfo o) { if (this == o) { return 0; } if (!port.equals(o.getPort())) { return port.compareTo(o.getPort()); } if (!StringUtil.equals(app, o.getApp())) { return app.compareToIgnoreCase(o.getApp()); } return ip.compareToIgnoreCase(o.getIp()); } @Override public String toString() { return new StringBuilder("MachineInfo {") .append("app='").append(app).append('\'') .append(", hostname='").append(hostname).append('\'') .append(", ip='").append(ip).append('\'') .append(", port=").append(port) .append(", heartbeatVersion=").append(heartbeatVersion) .append(", lastHeartbeat=").append(lastHeartbeat) .append(", version='").append(version).append('\'') .append(", healthy=").append(isHealthy()) .append('}').toString(); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof MachineInfo)) { return false; } MachineInfo that = (MachineInfo)o; return Objects.equals(app, that.app) && Objects.equals(ip, that.ip) && Objects.equals(port, that.port); } @Override public int hashCode() { return Objects.hash(app, ip, port); } /** * Information for log * * @return */ public String toLogString() { return app + "|" + ip + "|" + port + "|" + version; } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/discovery/SimpleMachineDiscovery.java ================================================ /* * 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.discovery; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import com.alibaba.csp.sentinel.util.AssertUtil; import org.springframework.stereotype.Component; /** * @author leyou */ @Component public class SimpleMachineDiscovery implements MachineDiscovery { private final ConcurrentMap apps = new ConcurrentHashMap<>(); @Override public long addMachine(MachineInfo machineInfo) { AssertUtil.notNull(machineInfo, "machineInfo cannot be null"); AppInfo appInfo = apps.computeIfAbsent(machineInfo.getApp(), AppInfo::new); appInfo.addMachine(machineInfo); return 1; } @Override public boolean removeMachine(String app, String ip, int port) { AssertUtil.assertNotBlank(app, "app name cannot be blank"); AppInfo appInfo = apps.get(app); if (appInfo != null) { return appInfo.removeMachine(ip, port); } return false; } @Override public List getAppNames() { return new ArrayList<>(apps.keySet()); } @Override public AppInfo getDetailApp(String app) { AssertUtil.assertNotBlank(app, "app name cannot be blank"); return apps.get(app); } @Override public Set getBriefApps() { return new HashSet<>(apps.values()); } @Override public void removeApp(String app) { AssertUtil.assertNotBlank(app, "app name cannot be blank"); apps.remove(app); } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/ResourceTreeNode.java ================================================ /* * 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.domain; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import com.alibaba.csp.sentinel.command.vo.NodeVo; /** * @author leyou */ public class ResourceTreeNode { private String id; private String parentId; private String resource; private Integer threadNum; private Long passQps; private Long blockQps; private Long totalQps; private Long averageRt; private Long successQps; private Long exceptionQps; private Long oneMinutePass; private Long oneMinuteBlock; private Long oneMinuteException; private Long oneMinuteTotal; private boolean visible = true; private List children = new ArrayList<>(); public static ResourceTreeNode fromNodeVoList(List nodeVos) { if (nodeVos == null || nodeVos.isEmpty()) { return null; } ResourceTreeNode root = null; Map map = new HashMap<>(); for (NodeVo vo : nodeVos) { ResourceTreeNode node = fromNodeVo(vo); map.put(node.id, node); // real root if (node.parentId == null) { root = node; } else if (map.containsKey(node.parentId)) { map.get(node.parentId).children.add(node); } else { // impossible } } return root; } public static ResourceTreeNode fromNodeVo(NodeVo vo) { ResourceTreeNode node = new ResourceTreeNode(); node.id = vo.getId(); node.parentId = vo.getParentId(); node.resource = vo.getResource(); node.threadNum = vo.getThreadNum(); node.passQps = vo.getPassQps(); node.blockQps = vo.getBlockQps(); node.totalQps = vo.getTotalQps(); node.averageRt = vo.getAverageRt(); node.successQps = vo.getSuccessQps(); node.exceptionQps = vo.getExceptionQps(); node.oneMinutePass = vo.getOneMinutePass(); node.oneMinuteBlock = vo.getOneMinuteBlock(); node.oneMinuteException = vo.getOneMinuteException(); node.oneMinuteTotal = vo.getOneMinuteTotal(); return node; } public void searchIgnoreCase(String searchKey) { search(this, searchKey); } /** * This node is visible only when searchKey matches this.resource or at least * one of this's children is visible */ private boolean search(ResourceTreeNode node, String searchKey) { // empty matches all if (searchKey == null || searchKey.isEmpty() || node.resource.toLowerCase().contains(searchKey.toLowerCase())) { node.visible = true; } else { node.visible = false; } boolean found = false; for (ResourceTreeNode c : node.children) { found |= search(c, searchKey); } node.visible |= found; return node.visible; } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getParentId() { return parentId; } public void setParentId(String parentId) { this.parentId = parentId; } public String getResource() { return resource; } public void setResource(String resource) { this.resource = resource; } public Integer getThreadNum() { return threadNum; } public void setThreadNum(Integer threadNum) { this.threadNum = threadNum; } public Long getPassQps() { return passQps; } public void setPassQps(Long passQps) { this.passQps = passQps; } public Long getBlockQps() { return blockQps; } public void setBlockQps(Long blockQps) { this.blockQps = blockQps; } public Long getTotalQps() { return totalQps; } public void setTotalQps(Long totalQps) { this.totalQps = totalQps; } public Long getAverageRt() { return averageRt; } public void setAverageRt(Long averageRt) { this.averageRt = averageRt; } public Long getSuccessQps() { return successQps; } public void setSuccessQps(Long successQps) { this.successQps = successQps; } public Long getExceptionQps() { return exceptionQps; } public void setExceptionQps(Long exceptionQps) { this.exceptionQps = exceptionQps; } public Long getOneMinutePass() { return oneMinutePass; } public void setOneMinutePass(Long oneMinutePass) { this.oneMinutePass = oneMinutePass; } public Long getOneMinuteBlock() { return oneMinuteBlock; } public void setOneMinuteBlock(Long oneMinuteBlock) { this.oneMinuteBlock = oneMinuteBlock; } public Long getOneMinuteException() { return oneMinuteException; } public void setOneMinuteException(Long oneMinuteException) { this.oneMinuteException = oneMinuteException; } public Long getOneMinuteTotal() { return oneMinuteTotal; } public void setOneMinuteTotal(Long oneMinuteTotal) { this.oneMinuteTotal = oneMinuteTotal; } public boolean isVisible() { return visible; } public void setVisible(boolean visible) { this.visible = visible; } public List getChildren() { return children; } public void setChildren(List children) { this.children = children; } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/Result.java ================================================ /* * 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.domain; /** * @author leyou * @author Eric Zhao */ public class Result { private boolean success; private int code; private String msg; private R data; public static Result ofSuccess(R data) { return new Result() .setSuccess(true) .setMsg("success") .setData(data); } public static Result ofSuccessMsg(String msg) { return new Result() .setSuccess(true) .setMsg(msg); } public static Result ofFail(int code, String msg) { Result result = new Result<>(); result.setSuccess(false); result.setCode(code); result.setMsg(msg); return result; } public static Result ofThrowable(int code, Throwable throwable) { Result result = new Result<>(); result.setSuccess(false); result.setCode(code); result.setMsg(throwable.getClass().getName() + ", " + throwable.getMessage()); return result; } public boolean isSuccess() { return success; } public Result setSuccess(boolean success) { this.success = success; return this; } public int getCode() { return code; } public Result setCode(int code) { this.code = code; return this; } public String getMsg() { return msg; } public Result setMsg(String msg) { this.msg = msg; return this; } public R getData() { return data; } public Result setData(R data) { this.data = data; return this; } @Override public String toString() { return "Result{" + "success=" + success + ", code=" + code + ", msg='" + msg + '\'' + ", data=" + data + '}'; } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/cluster/ClusterAppAssignResultVO.java ================================================ /* * 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.domain.cluster; import java.util.Set; /** * @author Eric Zhao * @since 1.4.1 */ public class ClusterAppAssignResultVO { private Set failedServerSet; private Set failedClientSet; private Integer totalCount; public Set getFailedServerSet() { return failedServerSet; } public ClusterAppAssignResultVO setFailedServerSet(Set failedServerSet) { this.failedServerSet = failedServerSet; return this; } public Set getFailedClientSet() { return failedClientSet; } public ClusterAppAssignResultVO setFailedClientSet(Set failedClientSet) { this.failedClientSet = failedClientSet; return this; } public Integer getTotalCount() { return totalCount; } public ClusterAppAssignResultVO setTotalCount(Integer totalCount) { this.totalCount = totalCount; return this; } @Override public String toString() { return "ClusterAppAssignResultVO{" + "failedServerSet=" + failedServerSet + ", failedClientSet=" + failedClientSet + ", totalCount=" + totalCount + '}'; } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/cluster/ClusterAppFullAssignRequest.java ================================================ /* * 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.domain.cluster; import java.util.List; import java.util.Set; import com.alibaba.csp.sentinel.dashboard.domain.cluster.request.ClusterAppAssignMap; /** * @author Eric Zhao * @since 1.4.1 */ public class ClusterAppFullAssignRequest { private List clusterMap; private Set remainingList; public List getClusterMap() { return clusterMap; } public ClusterAppFullAssignRequest setClusterMap( List clusterMap) { this.clusterMap = clusterMap; return this; } public Set getRemainingList() { return remainingList; } public ClusterAppFullAssignRequest setRemainingList(Set remainingList) { this.remainingList = remainingList; return this; } @Override public String toString() { return "ClusterAppFullAssignRequest{" + "clusterMap=" + clusterMap + ", remainingList=" + remainingList + '}'; } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/cluster/ClusterAppSingleServerAssignRequest.java ================================================ /* * 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.domain.cluster; import java.util.Set; import com.alibaba.csp.sentinel.dashboard.domain.cluster.request.ClusterAppAssignMap; /** * @author Eric Zhao * @since 1.4.1 */ public class ClusterAppSingleServerAssignRequest { private ClusterAppAssignMap clusterMap; private Set remainingList; public ClusterAppAssignMap getClusterMap() { return clusterMap; } public ClusterAppSingleServerAssignRequest setClusterMap(ClusterAppAssignMap clusterMap) { this.clusterMap = clusterMap; return this; } public Set getRemainingList() { return remainingList; } public ClusterAppSingleServerAssignRequest setRemainingList(Set remainingList) { this.remainingList = remainingList; return this; } @Override public String toString() { return "ClusterAppSingleServerAssignRequest{" + "clusterMap=" + clusterMap + ", remainingList=" + remainingList + '}'; } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/cluster/ClusterClientInfoVO.java ================================================ /* * 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.domain.cluster; /** * @author Eric Zhao * @since 1.4.1 */ public class ClusterClientInfoVO { private String serverHost; private Integer serverPort; private Integer clientState; private Integer requestTimeout; public String getServerHost() { return serverHost; } public ClusterClientInfoVO setServerHost(String serverHost) { this.serverHost = serverHost; return this; } public Integer getServerPort() { return serverPort; } public ClusterClientInfoVO setServerPort(Integer serverPort) { this.serverPort = serverPort; return this; } public Integer getClientState() { return clientState; } public ClusterClientInfoVO setClientState(Integer clientState) { this.clientState = clientState; return this; } public Integer getRequestTimeout() { return requestTimeout; } public ClusterClientInfoVO setRequestTimeout(Integer requestTimeout) { this.requestTimeout = requestTimeout; return this; } @Override public String toString() { return "ClusterClientInfoVO{" + "serverHost='" + serverHost + '\'' + ", serverPort=" + serverPort + ", clientState=" + clientState + ", requestTimeout=" + requestTimeout + '}'; } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/cluster/ClusterGroupEntity.java ================================================ /* * 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.domain.cluster; import java.util.HashSet; import java.util.Set; /** * @author Eric Zhao * @since 1.4.1 */ public class ClusterGroupEntity { private String machineId; private String ip; private Integer port; private Set clientSet = new HashSet<>(); private Boolean belongToApp; public String getMachineId() { return machineId; } public ClusterGroupEntity setMachineId(String machineId) { this.machineId = machineId; return this; } public String getIp() { return ip; } public ClusterGroupEntity setIp(String ip) { this.ip = ip; return this; } public Integer getPort() { return port; } public ClusterGroupEntity setPort(Integer port) { this.port = port; return this; } public Set getClientSet() { return clientSet; } public ClusterGroupEntity setClientSet(Set clientSet) { this.clientSet = clientSet; return this; } public Boolean getBelongToApp() { return belongToApp; } public ClusterGroupEntity setBelongToApp(Boolean belongToApp) { this.belongToApp = belongToApp; return this; } @Override public String toString() { return "ClusterGroupEntity{" + "machineId='" + machineId + '\'' + ", ip='" + ip + '\'' + ", port=" + port + ", clientSet=" + clientSet + ", belongToApp=" + belongToApp + '}'; } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/cluster/ClusterStateSingleVO.java ================================================ /* * 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.domain.cluster; /** * @author Eric Zhao * @since 1.4.1 */ public class ClusterStateSingleVO { private String address; private Integer mode; private String target; public String getAddress() { return address; } public ClusterStateSingleVO setAddress(String address) { this.address = address; return this; } public Integer getMode() { return mode; } public ClusterStateSingleVO setMode(Integer mode) { this.mode = mode; return this; } public String getTarget() { return target; } public ClusterStateSingleVO setTarget(String target) { this.target = target; return this; } @Override public String toString() { return "ClusterStateSingleVO{" + "address='" + address + '\'' + ", mode=" + mode + ", target='" + target + '\'' + '}'; } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/cluster/ConnectionDescriptorVO.java ================================================ /* * 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.domain.cluster; /** * @author Eric Zhao * @since 1.4.0 */ public class ConnectionDescriptorVO { private String address; private String host; public String getAddress() { return address; } public ConnectionDescriptorVO setAddress(String address) { this.address = address; return this; } public String getHost() { return host; } public ConnectionDescriptorVO setHost(String host) { this.host = host; return this; } @Override public String toString() { return "ConnectionDescriptorVO{" + "address='" + address + '\'' + ", host='" + host + '\'' + '}'; } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/cluster/ConnectionGroupVO.java ================================================ /* * 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.domain.cluster; import java.util.List; /** * @author Eric Zhao * @since 1.4.0 */ public class ConnectionGroupVO { private String namespace; private List connectionSet; private Integer connectedCount; public String getNamespace() { return namespace; } public ConnectionGroupVO setNamespace(String namespace) { this.namespace = namespace; return this; } public List getConnectionSet() { return connectionSet; } public ConnectionGroupVO setConnectionSet( List connectionSet) { this.connectionSet = connectionSet; return this; } public Integer getConnectedCount() { return connectedCount; } public ConnectionGroupVO setConnectedCount(Integer connectedCount) { this.connectedCount = connectedCount; return this; } @Override public String toString() { return "ConnectionGroupVO{" + "namespace='" + namespace + '\'' + ", connectionSet=" + connectionSet + ", connectedCount=" + connectedCount + '}'; } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/cluster/config/ClusterClientConfig.java ================================================ /* * 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.domain.cluster.config; /** * @author Eric Zhao * @since 1.4.0 */ public class ClusterClientConfig { private String serverHost; private Integer serverPort; private Integer requestTimeout; private Integer connectTimeout; public String getServerHost() { return serverHost; } public ClusterClientConfig setServerHost(String serverHost) { this.serverHost = serverHost; return this; } public Integer getServerPort() { return serverPort; } public ClusterClientConfig setServerPort(Integer serverPort) { this.serverPort = serverPort; return this; } public Integer getRequestTimeout() { return requestTimeout; } public ClusterClientConfig setRequestTimeout(Integer requestTimeout) { this.requestTimeout = requestTimeout; return this; } public Integer getConnectTimeout() { return connectTimeout; } public ClusterClientConfig setConnectTimeout(Integer connectTimeout) { this.connectTimeout = connectTimeout; return this; } @Override public String toString() { return "ClusterClientConfig{" + "serverHost='" + serverHost + '\'' + ", serverPort=" + serverPort + ", requestTimeout=" + requestTimeout + ", connectTimeout=" + connectTimeout + '}'; } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/cluster/config/ServerFlowConfig.java ================================================ /* * 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.domain.cluster.config; /** * @author Eric Zhao * @since 1.4.0 */ public class ServerFlowConfig { public static final double DEFAULT_EXCEED_COUNT = 1.0d; public static final double DEFAULT_MAX_OCCUPY_RATIO = 1.0d; public static final int DEFAULT_INTERVAL_MS = 1000; public static final int DEFAULT_SAMPLE_COUNT= 10; public static final double DEFAULT_MAX_ALLOWED_QPS= 30000; private final String namespace; private Double exceedCount = DEFAULT_EXCEED_COUNT; private Double maxOccupyRatio = DEFAULT_MAX_OCCUPY_RATIO; private Integer intervalMs = DEFAULT_INTERVAL_MS; private Integer sampleCount = DEFAULT_SAMPLE_COUNT; private Double maxAllowedQps = DEFAULT_MAX_ALLOWED_QPS; public ServerFlowConfig() { this("default"); } public ServerFlowConfig(String namespace) { this.namespace = namespace; } public String getNamespace() { return namespace; } public Double getExceedCount() { return exceedCount; } public ServerFlowConfig setExceedCount(Double exceedCount) { this.exceedCount = exceedCount; return this; } public Double getMaxOccupyRatio() { return maxOccupyRatio; } public ServerFlowConfig setMaxOccupyRatio(Double maxOccupyRatio) { this.maxOccupyRatio = maxOccupyRatio; return this; } public Integer getIntervalMs() { return intervalMs; } public ServerFlowConfig setIntervalMs(Integer intervalMs) { this.intervalMs = intervalMs; return this; } public Integer getSampleCount() { return sampleCount; } public ServerFlowConfig setSampleCount(Integer sampleCount) { this.sampleCount = sampleCount; return this; } public Double getMaxAllowedQps() { return maxAllowedQps; } public ServerFlowConfig setMaxAllowedQps(Double maxAllowedQps) { this.maxAllowedQps = maxAllowedQps; return this; } @Override public String toString() { return "ServerFlowConfig{" + "namespace='" + namespace + '\'' + ", exceedCount=" + exceedCount + ", maxOccupyRatio=" + maxOccupyRatio + ", intervalMs=" + intervalMs + ", sampleCount=" + sampleCount + ", maxAllowedQps=" + maxAllowedQps + '}'; } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/cluster/config/ServerTransportConfig.java ================================================ /* * 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.domain.cluster.config; /** * @author Eric Zhao * @since 1.4.0 */ public class ServerTransportConfig { public static final int DEFAULT_PORT = 18730; public static final int DEFAULT_IDLE_SECONDS = 600; private Integer port; private Integer idleSeconds; public ServerTransportConfig() { this(DEFAULT_PORT, DEFAULT_IDLE_SECONDS); } public ServerTransportConfig(Integer port, Integer idleSeconds) { this.port = port; this.idleSeconds = idleSeconds; } public Integer getPort() { return port; } public ServerTransportConfig setPort(Integer port) { this.port = port; return this; } public Integer getIdleSeconds() { return idleSeconds; } public ServerTransportConfig setIdleSeconds(Integer idleSeconds) { this.idleSeconds = idleSeconds; return this; } @Override public String toString() { return "ServerTransportConfig{" + "port=" + port + ", idleSeconds=" + idleSeconds + '}'; } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/cluster/request/ClusterAppAssignMap.java ================================================ /* * 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.domain.cluster.request; import java.util.Set; /** * @author Eric Zhao * @since 1.4.1 */ public class ClusterAppAssignMap { private String machineId; private String ip; private Integer port; private Boolean belongToApp; private Set clientSet; private Set namespaceSet; private Double maxAllowedQps; public String getMachineId() { return machineId; } public ClusterAppAssignMap setMachineId(String machineId) { this.machineId = machineId; return this; } public String getIp() { return ip; } public ClusterAppAssignMap setIp(String ip) { this.ip = ip; return this; } public Integer getPort() { return port; } public ClusterAppAssignMap setPort(Integer port) { this.port = port; return this; } public Set getClientSet() { return clientSet; } public ClusterAppAssignMap setClientSet(Set clientSet) { this.clientSet = clientSet; return this; } public Set getNamespaceSet() { return namespaceSet; } public ClusterAppAssignMap setNamespaceSet(Set namespaceSet) { this.namespaceSet = namespaceSet; return this; } public Boolean getBelongToApp() { return belongToApp; } public ClusterAppAssignMap setBelongToApp(Boolean belongToApp) { this.belongToApp = belongToApp; return this; } public Double getMaxAllowedQps() { return maxAllowedQps; } public ClusterAppAssignMap setMaxAllowedQps(Double maxAllowedQps) { this.maxAllowedQps = maxAllowedQps; return this; } @Override public String toString() { return "ClusterAppAssignMap{" + "machineId='" + machineId + '\'' + ", ip='" + ip + '\'' + ", port=" + port + ", belongToApp=" + belongToApp + ", clientSet=" + clientSet + ", namespaceSet=" + namespaceSet + ", maxAllowedQps=" + maxAllowedQps + '}'; } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/cluster/request/ClusterClientModifyRequest.java ================================================ /* * 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.domain.cluster.request; import com.alibaba.csp.sentinel.dashboard.domain.cluster.config.ClusterClientConfig; /** * @author Eric Zhao * @since 1.4.0 */ public class ClusterClientModifyRequest implements ClusterModifyRequest { private String app; private String ip; private Integer port; private Integer mode; private ClusterClientConfig clientConfig; @Override public String getApp() { return app; } public ClusterClientModifyRequest setApp(String app) { this.app = app; return this; } @Override public String getIp() { return ip; } public ClusterClientModifyRequest setIp(String ip) { this.ip = ip; return this; } @Override public Integer getPort() { return port; } public ClusterClientModifyRequest setPort(Integer port) { this.port = port; return this; } @Override public Integer getMode() { return mode; } public ClusterClientModifyRequest setMode(Integer mode) { this.mode = mode; return this; } public ClusterClientConfig getClientConfig() { return clientConfig; } public ClusterClientModifyRequest setClientConfig( ClusterClientConfig clientConfig) { this.clientConfig = clientConfig; return this; } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/cluster/request/ClusterModifyRequest.java ================================================ /* * 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.domain.cluster.request; /** * @author Eric Zhao * @since 1.4.0 */ public interface ClusterModifyRequest { String getApp(); String getIp(); Integer getPort(); Integer getMode(); } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/cluster/request/ClusterServerModifyRequest.java ================================================ /* * 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.domain.cluster.request; import java.util.Set; import com.alibaba.csp.sentinel.dashboard.domain.cluster.config.ServerFlowConfig; import com.alibaba.csp.sentinel.dashboard.domain.cluster.config.ServerTransportConfig; /** * @author Eric Zhao * @since 1.4.0 */ public class ClusterServerModifyRequest implements ClusterModifyRequest { private String app; private String ip; private Integer port; private Integer mode; private ServerFlowConfig flowConfig; private ServerTransportConfig transportConfig; private Set namespaceSet; @Override public String getApp() { return app; } public ClusterServerModifyRequest setApp(String app) { this.app = app; return this; } @Override public String getIp() { return ip; } public ClusterServerModifyRequest setIp(String ip) { this.ip = ip; return this; } @Override public Integer getPort() { return port; } public ClusterServerModifyRequest setPort(Integer port) { this.port = port; return this; } @Override public Integer getMode() { return mode; } public ClusterServerModifyRequest setMode(Integer mode) { this.mode = mode; return this; } public ServerFlowConfig getFlowConfig() { return flowConfig; } public ClusterServerModifyRequest setFlowConfig( ServerFlowConfig flowConfig) { this.flowConfig = flowConfig; return this; } public ServerTransportConfig getTransportConfig() { return transportConfig; } public ClusterServerModifyRequest setTransportConfig( ServerTransportConfig transportConfig) { this.transportConfig = transportConfig; return this; } public Set getNamespaceSet() { return namespaceSet; } public ClusterServerModifyRequest setNamespaceSet(Set namespaceSet) { this.namespaceSet = namespaceSet; return this; } @Override public String toString() { return "ClusterServerModifyRequest{" + "app='" + app + '\'' + ", ip='" + ip + '\'' + ", port=" + port + ", mode=" + mode + ", flowConfig=" + flowConfig + ", transportConfig=" + transportConfig + ", namespaceSet=" + namespaceSet + '}'; } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/cluster/state/AppClusterClientStateWrapVO.java ================================================ /* * 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.domain.cluster.state; /** * @author Eric Zhao * @since 1.4.1 */ public class AppClusterClientStateWrapVO { /** * {ip}@{transport_command_port}. */ private String id; private Integer commandPort; private String ip; private ClusterClientStateVO state; public String getId() { return id; } public AppClusterClientStateWrapVO setId(String id) { this.id = id; return this; } public String getIp() { return ip; } public AppClusterClientStateWrapVO setIp(String ip) { this.ip = ip; return this; } public ClusterClientStateVO getState() { return state; } public AppClusterClientStateWrapVO setState(ClusterClientStateVO state) { this.state = state; return this; } public Integer getCommandPort() { return commandPort; } public AppClusterClientStateWrapVO setCommandPort(Integer commandPort) { this.commandPort = commandPort; return this; } @Override public String toString() { return "AppClusterClientStateWrapVO{" + "id='" + id + '\'' + ", commandPort=" + commandPort + ", ip='" + ip + '\'' + ", state=" + state + '}'; } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/cluster/state/AppClusterServerStateWrapVO.java ================================================ /* * 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.domain.cluster.state; /** * @author Eric Zhao * @since 1.4.1 */ public class AppClusterServerStateWrapVO { /** * {ip}@{transport_command_port}. */ private String id; private String ip; private Integer port; private Integer connectedCount; private Boolean belongToApp; private ClusterServerStateVO state; public String getId() { return id; } public AppClusterServerStateWrapVO setId(String id) { this.id = id; return this; } public String getIp() { return ip; } public AppClusterServerStateWrapVO setIp(String ip) { this.ip = ip; return this; } public Integer getPort() { return port; } public AppClusterServerStateWrapVO setPort(Integer port) { this.port = port; return this; } public Boolean getBelongToApp() { return belongToApp; } public AppClusterServerStateWrapVO setBelongToApp(Boolean belongToApp) { this.belongToApp = belongToApp; return this; } public Integer getConnectedCount() { return connectedCount; } public AppClusterServerStateWrapVO setConnectedCount(Integer connectedCount) { this.connectedCount = connectedCount; return this; } public ClusterServerStateVO getState() { return state; } public AppClusterServerStateWrapVO setState(ClusterServerStateVO state) { this.state = state; return this; } @Override public String toString() { return "AppClusterServerStateWrapVO{" + "id='" + id + '\'' + ", ip='" + ip + '\'' + ", port='" + port + '\'' + ", belongToApp=" + belongToApp + ", state=" + state + '}'; } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/cluster/state/ClusterClientStateVO.java ================================================ /* * 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.domain.cluster.state; import com.alibaba.csp.sentinel.dashboard.domain.cluster.ClusterClientInfoVO; /** * @author Eric Zhao * @since 1.4.0 */ public class ClusterClientStateVO { /** * Cluster token client state. */ private ClusterClientInfoVO clientConfig; public ClusterClientInfoVO getClientConfig() { return clientConfig; } public ClusterClientStateVO setClientConfig(ClusterClientInfoVO clientConfig) { this.clientConfig = clientConfig; return this; } @Override public String toString() { return "ClusterClientStateVO{" + "clientConfig=" + clientConfig + '}'; } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/cluster/state/ClusterRequestLimitVO.java ================================================ /* * 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.domain.cluster.state; /** * @author Eric Zhao * @since 1.4.1 */ public class ClusterRequestLimitVO { private String namespace; private Double currentQps; private Double maxAllowedQps; public String getNamespace() { return namespace; } public ClusterRequestLimitVO setNamespace(String namespace) { this.namespace = namespace; return this; } public Double getCurrentQps() { return currentQps; } public ClusterRequestLimitVO setCurrentQps(Double currentQps) { this.currentQps = currentQps; return this; } public Double getMaxAllowedQps() { return maxAllowedQps; } public ClusterRequestLimitVO setMaxAllowedQps(Double maxAllowedQps) { this.maxAllowedQps = maxAllowedQps; return this; } @Override public String toString() { return "ClusterRequestLimitVO{" + "namespace='" + namespace + '\'' + ", currentQps=" + currentQps + ", maxAllowedQps=" + maxAllowedQps + '}'; } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/cluster/state/ClusterServerStateVO.java ================================================ /* * 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.domain.cluster.state; import java.util.List; import java.util.Set; import com.alibaba.csp.sentinel.dashboard.domain.cluster.ConnectionGroupVO; import com.alibaba.csp.sentinel.dashboard.domain.cluster.config.ServerFlowConfig; import com.alibaba.csp.sentinel.dashboard.domain.cluster.config.ServerTransportConfig; /** * @author Eric Zhao * @since 1.4.0 */ public class ClusterServerStateVO { private String appName; private ServerTransportConfig transport; private ServerFlowConfig flow; private Set namespaceSet; private Integer port; private List connection; private List requestLimitData; private Boolean embedded; public String getAppName() { return appName; } public ClusterServerStateVO setAppName(String appName) { this.appName = appName; return this; } public ServerTransportConfig getTransport() { return transport; } public ClusterServerStateVO setTransport(ServerTransportConfig transport) { this.transport = transport; return this; } public ServerFlowConfig getFlow() { return flow; } public ClusterServerStateVO setFlow(ServerFlowConfig flow) { this.flow = flow; return this; } public Set getNamespaceSet() { return namespaceSet; } public ClusterServerStateVO setNamespaceSet(Set namespaceSet) { this.namespaceSet = namespaceSet; return this; } public Integer getPort() { return port; } public ClusterServerStateVO setPort(Integer port) { this.port = port; return this; } public List getConnection() { return connection; } public ClusterServerStateVO setConnection(List connection) { this.connection = connection; return this; } public List getRequestLimitData() { return requestLimitData; } public ClusterServerStateVO setRequestLimitData(List requestLimitData) { this.requestLimitData = requestLimitData; return this; } public Boolean getEmbedded() { return embedded; } public ClusterServerStateVO setEmbedded(Boolean embedded) { this.embedded = embedded; return this; } @Override public String toString() { return "ClusterServerStateVO{" + "appName='" + appName + '\'' + ", transport=" + transport + ", flow=" + flow + ", namespaceSet=" + namespaceSet + ", port=" + port + ", connection=" + connection + ", requestLimitData=" + requestLimitData + ", embedded=" + embedded + '}'; } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/cluster/state/ClusterStateSimpleEntity.java ================================================ /* * 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.domain.cluster.state; /** * @author Eric Zhao * @since 1.4.0 */ public class ClusterStateSimpleEntity { private Integer mode; private Long lastModified; private Boolean clientAvailable; private Boolean serverAvailable; public Integer getMode() { return mode; } public ClusterStateSimpleEntity setMode(Integer mode) { this.mode = mode; return this; } public Long getLastModified() { return lastModified; } public ClusterStateSimpleEntity setLastModified(Long lastModified) { this.lastModified = lastModified; return this; } public Boolean getClientAvailable() { return clientAvailable; } public ClusterStateSimpleEntity setClientAvailable(Boolean clientAvailable) { this.clientAvailable = clientAvailable; return this; } public Boolean getServerAvailable() { return serverAvailable; } public ClusterStateSimpleEntity setServerAvailable(Boolean serverAvailable) { this.serverAvailable = serverAvailable; return this; } @Override public String toString() { return "ClusterStateSimpleEntity{" + "mode=" + mode + ", lastModified=" + lastModified + ", clientAvailable=" + clientAvailable + ", serverAvailable=" + serverAvailable + '}'; } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/cluster/state/ClusterUniversalStatePairVO.java ================================================ /* * 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.domain.cluster.state; /** * @author Eric Zhao * @since 1.4.1 */ public class ClusterUniversalStatePairVO { private String ip; private Integer commandPort; private ClusterUniversalStateVO state; public ClusterUniversalStatePairVO() {} public ClusterUniversalStatePairVO(String ip, Integer commandPort, ClusterUniversalStateVO state) { this.ip = ip; this.commandPort = commandPort; this.state = state; } public String getIp() { return ip; } public ClusterUniversalStatePairVO setIp(String ip) { this.ip = ip; return this; } public Integer getCommandPort() { return commandPort; } public ClusterUniversalStatePairVO setCommandPort(Integer commandPort) { this.commandPort = commandPort; return this; } public ClusterUniversalStateVO getState() { return state; } public ClusterUniversalStatePairVO setState(ClusterUniversalStateVO state) { this.state = state; return this; } @Override public String toString() { return "ClusterUniversalStatePairVO{" + "ip='" + ip + '\'' + ", commandPort=" + commandPort + ", state=" + state + '}'; } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/cluster/state/ClusterUniversalStateVO.java ================================================ /* * 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.domain.cluster.state; /** * @author Eric Zhao * @since 1.4.0 */ public class ClusterUniversalStateVO { private ClusterStateSimpleEntity stateInfo; private ClusterClientStateVO client; private ClusterServerStateVO server; public ClusterClientStateVO getClient() { return client; } public ClusterUniversalStateVO setClient(ClusterClientStateVO client) { this.client = client; return this; } public ClusterServerStateVO getServer() { return server; } public ClusterUniversalStateVO setServer(ClusterServerStateVO server) { this.server = server; return this; } public ClusterStateSimpleEntity getStateInfo() { return stateInfo; } public ClusterUniversalStateVO setStateInfo( ClusterStateSimpleEntity stateInfo) { this.stateInfo = stateInfo; return this; } @Override public String toString() { return "ClusterUniversalStateVO{" + "stateInfo=" + stateInfo + ", client=" + client + ", server=" + server + '}'; } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/vo/MachineInfoVo.java ================================================ /* * 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.domain.vo; import java.util.ArrayList; import java.util.List; import com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo; /** * @author leyou */ public class MachineInfoVo { private String app; private String hostname; private String ip; private int port; private long heartbeatVersion; private long lastHeartbeat; private boolean healthy; private String version; public static List fromMachineInfoList(List machines) { List list = new ArrayList<>(); for (MachineInfo machine : machines) { list.add(fromMachineInfo(machine)); } return list; } public static MachineInfoVo fromMachineInfo(MachineInfo machine) { MachineInfoVo vo = new MachineInfoVo(); vo.setApp(machine.getApp()); vo.setHostname(machine.getHostname()); vo.setIp(machine.getIp()); vo.setPort(machine.getPort()); vo.setLastHeartbeat(machine.getLastHeartbeat()); vo.setHeartbeatVersion(machine.getHeartbeatVersion()); vo.setVersion(machine.getVersion()); vo.setHealthy(machine.isHealthy()); return vo; } public String getApp() { return app; } public void setApp(String app) { this.app = app; } public String getHostname() { return hostname; } public void setHostname(String hostname) { this.hostname = hostname; } public String getIp() { return ip; } public void setIp(String ip) { this.ip = ip; } public int getPort() { return port; } public void setPort(int port) { this.port = port; } public long getLastHeartbeat() { return lastHeartbeat; } public void setLastHeartbeat(long lastHeartbeat) { this.lastHeartbeat = lastHeartbeat; } public void setHeartbeatVersion(long heartbeatVersion) { this.heartbeatVersion = heartbeatVersion; } public long getHeartbeatVersion() { return heartbeatVersion; } public String getVersion() { return version; } public MachineInfoVo setVersion(String version) { this.version = version; return this; } public boolean isHealthy() { return healthy; } public void setHealthy(boolean healthy) { this.healthy = healthy; } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/vo/MetricVo.java ================================================ /* * 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.domain.vo; import java.util.ArrayList; import java.util.Collection; import java.util.List; import com.alibaba.csp.sentinel.dashboard.datasource.entity.MetricEntity; /** * @author leyou */ public class MetricVo implements Comparable { private Long id; private String app; private Long timestamp; private Long gmtCreate = System.currentTimeMillis(); private String resource; private Long passQps; private Long blockQps; private Long successQps; private Long exceptionQps; /** * average rt */ private Double rt; private Integer count; public MetricVo() { } public static List fromMetricEntities(Collection entities) { List list = new ArrayList<>(); if (entities != null) { for (MetricEntity entity : entities) { list.add(fromMetricEntity(entity)); } } return list; } /** * 保留资源名为identity的结果。 * * @param entities 通过hashCode查找到的MetricEntities * @param identity 真正需要查找的资源名 * @return */ public static List fromMetricEntities(Collection entities, String identity) { List list = new ArrayList<>(); if (entities != null) { for (MetricEntity entity : entities) { if (entity.getResource().equals(identity)) { list.add(fromMetricEntity(entity)); } } } return list; } public static MetricVo fromMetricEntity(MetricEntity entity) { MetricVo vo = new MetricVo(); vo.id = entity.getId(); vo.app = entity.getApp(); vo.timestamp = entity.getTimestamp().getTime(); vo.gmtCreate = entity.getGmtCreate().getTime(); vo.resource = entity.getResource(); vo.passQps = entity.getPassQps(); vo.blockQps = entity.getBlockQps(); vo.successQps = entity.getSuccessQps(); vo.exceptionQps = entity.getExceptionQps(); if (entity.getSuccessQps() != 0) { vo.rt = entity.getRt() / entity.getSuccessQps(); } else { vo.rt = 0D; } vo.count = entity.getCount(); return vo; } public static MetricVo parse(String line) { String[] strs = line.split("\\|"); long timestamp = Long.parseLong(strs[0]); String identity = strs[1]; long passQps = Long.parseLong(strs[2]); long blockQps = Long.parseLong(strs[3]); long exception = Long.parseLong(strs[4]); double rt = Double.parseDouble(strs[5]); long successQps = Long.parseLong(strs[6]); MetricVo vo = new MetricVo(); vo.timestamp = timestamp; vo.resource = identity; vo.passQps = passQps; vo.blockQps = blockQps; vo.successQps = successQps; vo.exceptionQps = exception; vo.rt = rt; vo.count = 1; return vo; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getApp() { return app; } public void setApp(String app) { this.app = app; } public Long getTimestamp() { return timestamp; } public void setTimestamp(Long timestamp) { this.timestamp = timestamp; } public Long getGmtCreate() { return gmtCreate; } public void setGmtCreate(Long gmtCreate) { this.gmtCreate = gmtCreate; } public String getResource() { return resource; } public void setResource(String resource) { this.resource = resource; } public Long getPassQps() { return passQps; } public void setPassQps(Long passQps) { this.passQps = passQps; } public Long getBlockQps() { return blockQps; } public void setBlockQps(Long blockQps) { this.blockQps = blockQps; } public Long getSuccessQps() { return successQps; } public void setSuccessQps(Long successQps) { this.successQps = successQps; } public Long getExceptionQps() { return exceptionQps; } public void setExceptionQps(Long exceptionQps) { this.exceptionQps = exceptionQps; } public Double getRt() { return rt; } public void setRt(Double rt) { this.rt = rt; } public Integer getCount() { return count; } public void setCount(Integer count) { this.count = count; } @Override public int compareTo(MetricVo o) { return Long.compare(this.timestamp, o.timestamp); } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/domain/vo/ResourceVo.java ================================================ /* * 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.domain.vo; import java.util.ArrayList; import java.util.List; import com.alibaba.csp.sentinel.command.vo.NodeVo; import com.alibaba.csp.sentinel.dashboard.domain.ResourceTreeNode; /** * @author leyou */ public class ResourceVo { private String parentTtId; private String ttId; private String resource; private Integer threadNum; private Long passQps; private Long blockQps; private Long totalQps; private Long averageRt; private Long passRequestQps; private Long exceptionQps; private Long oneMinutePass; private Long oneMinuteBlock; private Long oneMinuteException; private Long oneMinuteTotal; private boolean visible = true; public ResourceVo() { } public static List fromNodeVoList(List nodeVos) { if (nodeVos == null) { return null; } List list = new ArrayList<>(); for (NodeVo nodeVo : nodeVos) { ResourceVo vo = new ResourceVo(); vo.parentTtId = nodeVo.getParentId(); vo.ttId = nodeVo.getId(); vo.resource = nodeVo.getResource(); vo.threadNum = nodeVo.getThreadNum(); vo.passQps = nodeVo.getPassQps(); vo.blockQps = nodeVo.getBlockQps(); vo.totalQps = nodeVo.getTotalQps(); vo.averageRt = nodeVo.getAverageRt(); vo.exceptionQps = nodeVo.getExceptionQps(); vo.oneMinutePass = nodeVo.getOneMinutePass(); vo.oneMinuteBlock = nodeVo.getOneMinuteBlock(); vo.oneMinuteException = nodeVo.getOneMinuteException(); vo.oneMinuteTotal = nodeVo.getOneMinuteTotal(); list.add(vo); } return list; } public static List fromResourceTreeNode(ResourceTreeNode root) { if (root == null) { return null; } List list = new ArrayList<>(); visit(root, list, false, true); //if(!list.isEmpty()){ // list.remove(0); //} return list; } /** * This node is visible when this.visible==true or one of this's parents is visible, * root node is always invisible. */ private static void visit(ResourceTreeNode node, List list, boolean parentVisible, boolean isRoot) { boolean visible = !isRoot && (node.isVisible() || parentVisible); //boolean visible = node.isVisible(); if (visible) { ResourceVo vo = new ResourceVo(); vo.parentTtId = node.getParentId(); vo.ttId = node.getId(); vo.resource = node.getResource(); vo.threadNum = node.getThreadNum(); vo.passQps = node.getPassQps(); vo.blockQps = node.getBlockQps(); vo.totalQps = node.getTotalQps(); vo.averageRt = node.getAverageRt(); vo.exceptionQps = node.getExceptionQps(); vo.oneMinutePass = node.getOneMinutePass(); vo.oneMinuteBlock = node.getOneMinuteBlock(); vo.oneMinuteException = node.getOneMinuteException(); vo.oneMinuteTotal = node.getOneMinuteTotal(); vo.visible = node.isVisible(); list.add(vo); } for (ResourceTreeNode c : node.getChildren()) { visit(c, list, visible, false); } } public String getParentTtId() { return parentTtId; } public void setParentTtId(String parentTtId) { this.parentTtId = parentTtId; } public String getTtId() { return ttId; } public void setTtId(String ttId) { this.ttId = ttId; } public String getResource() { return resource; } public void setResource(String resource) { this.resource = resource; } public Integer getThreadNum() { return threadNum; } public void setThreadNum(Integer threadNum) { this.threadNum = threadNum; } public Long getPassQps() { return passQps; } public void setPassQps(Long passQps) { this.passQps = passQps; } public Long getBlockQps() { return blockQps; } public void setBlockQps(Long blockQps) { this.blockQps = blockQps; } public Long getTotalQps() { return totalQps; } public void setTotalQps(Long totalQps) { this.totalQps = totalQps; } public Long getAverageRt() { return averageRt; } public void setAverageRt(Long averageRt) { this.averageRt = averageRt; } public Long getPassRequestQps() { return passRequestQps; } public void setPassRequestQps(Long passRequestQps) { this.passRequestQps = passRequestQps; } public Long getExceptionQps() { return exceptionQps; } public void setExceptionQps(Long exceptionQps) { this.exceptionQps = exceptionQps; } public Long getOneMinuteException() { return oneMinuteException; } public void setOneMinuteException(Long oneMinuteException) { this.oneMinuteException = oneMinuteException; } public Long getOneMinutePass() { return oneMinutePass; } public void setOneMinutePass(Long oneMinutePass) { this.oneMinutePass = oneMinutePass; } public Long getOneMinuteBlock() { return oneMinuteBlock; } public void setOneMinuteBlock(Long oneMinuteBlock) { this.oneMinuteBlock = oneMinuteBlock; } public Long getOneMinuteTotal() { return oneMinuteTotal; } public void setOneMinuteTotal(Long oneMinuteTotal) { this.oneMinuteTotal = oneMinuteTotal; } public boolean isVisible() { return visible; } public void setVisible(boolean visible) { this.visible = visible; } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/filter/AuthFilter.java ================================================ /* * 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.filter; import com.alibaba.csp.sentinel.dashboard.auth.AuthService; 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 javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.List; /** * Servlet Filter that authenticate requests. * * Note: * Some urls are excluded as they needn't auth, such as: * * Index url: / * Authentication request url: /login,logout * Used for client: /registry/machine * Static resources: htm,html,js and so on. * * The excluded urls and urlSuffixes are configured in application.properties * * @author cdfive * @since 1.6.0 */ @Component public class AuthFilter implements Filter { private static final String URL_SUFFIX_DOT = "."; /**Some urls which needn't auth, such as /auth/login,/registry/machine and so on*/ @Value("#{'${auth.filter.exclude-urls}'.split(',')}") private List 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 authFilterExcludeUrlSuffixes; /**Authentication using AuthService interface*/ @Autowired private 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 requestURI = httpRequest.getRequestURI(); // Exclude the urls which needn't auth if (authFilterExcludeUrls.contains(requestURI)) { 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 (requestURI.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() { } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/metric/MetricFetcher.java ================================================ /* * 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.metric; import java.net.ConnectException; import java.net.SocketTimeoutException; import java.nio.charset.Charset; import java.util.Date; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.RejectedExecutionHandler; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.ThreadPoolExecutor.DiscardPolicy; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; import com.alibaba.csp.sentinel.concurrent.NamedThreadFactory; import com.alibaba.csp.sentinel.config.SentinelConfig; import com.alibaba.csp.sentinel.dashboard.datasource.entity.MetricEntity; import com.alibaba.csp.sentinel.dashboard.discovery.AppInfo; import com.alibaba.csp.sentinel.dashboard.discovery.AppManagement; import com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo; import com.alibaba.csp.sentinel.node.metric.MetricNode; import com.alibaba.csp.sentinel.util.StringUtil; import com.alibaba.csp.sentinel.dashboard.repository.metric.MetricsRepository; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.concurrent.FutureCallback; import org.apache.http.entity.ContentType; import org.apache.http.impl.client.DefaultRedirectStrategy; import org.apache.http.impl.nio.client.CloseableHttpAsyncClient; import org.apache.http.impl.nio.client.HttpAsyncClients; import org.apache.http.impl.nio.reactor.IOReactorConfig; import org.apache.http.protocol.HTTP; import org.apache.http.util.EntityUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; /** * Fetch metric of machines. * * @author leyou */ @Component public class MetricFetcher { public static final String NO_METRICS = "No metrics"; private static final int HTTP_OK = 200; private static final long MAX_LAST_FETCH_INTERVAL_MS = 1000 * 15; private static final long FETCH_INTERVAL_SECOND = 6; private static final Charset DEFAULT_CHARSET = Charset.forName(SentinelConfig.charset()); private final static String METRIC_URL_PATH = "metric"; private static Logger logger = LoggerFactory.getLogger(MetricFetcher.class); private final long intervalSecond = 1; private Map appLastFetchTime = new ConcurrentHashMap<>(); @Autowired private MetricsRepository metricStore; @Autowired private AppManagement appManagement; private CloseableHttpAsyncClient httpclient; @SuppressWarnings("PMD.ThreadPoolCreationRule") private ScheduledExecutorService fetchScheduleService = Executors.newScheduledThreadPool(1, new NamedThreadFactory("sentinel-dashboard-metrics-fetch-task")); private ExecutorService fetchService; private ExecutorService fetchWorker; public MetricFetcher() { int cores = Runtime.getRuntime().availableProcessors() * 2; long keepAliveTime = 0; 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); fetchWorker = new ThreadPoolExecutor(cores, cores, keepAliveTime, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(queueSize), new NamedThreadFactory("sentinel-dashboard-metrics-fetchWorker"), handler); IOReactorConfig ioConfig = IOReactorConfig.custom() .setConnectTimeout(3000) .setSoTimeout(3000) .setIoThreadCount(Runtime.getRuntime().availableProcessors() * 2) .build(); httpclient = HttpAsyncClients.custom() .setRedirectStrategy(new DefaultRedirectStrategy() { @Override protected boolean isRedirectable(final String method) { return false; } }).setMaxConnTotal(4000) .setMaxConnPerRoute(1000) .setDefaultIOReactorConfig(ioConfig) .build(); httpclient.start(); start(); } private void start() { fetchScheduleService.scheduleAtFixedRate(() -> { try { fetchAllApp(); } catch (Exception e) { logger.info("fetchAllApp error:", e); } }, 10, intervalSecond, TimeUnit.SECONDS); } private void writeMetric(Map map) { if (map.isEmpty()) { return; } Date date = new Date(); for (MetricEntity entity : map.values()) { entity.setGmtCreate(date); entity.setGmtModified(date); } metricStore.saveAll(map.values()); } /** * Traverse each APP, and then pull the metric of all machines for that APP. */ private void fetchAllApp() { List apps = appManagement.getAppNames(); if (apps == null) { return; } for (final String app : apps) { fetchService.submit(() -> { try { doFetchAppMetric(app); } catch (Exception e) { logger.error("fetchAppMetric error", e); } }); } } /** * fetch metric between [startTime, endTime], both side inclusive */ private void fetchOnce(String app, long startTime, long endTime, int maxWaitSeconds) { if (maxWaitSeconds <= 0) { throw new IllegalArgumentException("maxWaitSeconds must > 0, but " + maxWaitSeconds); } AppInfo appInfo = appManagement.getDetailApp(app); // auto remove for app if (appInfo.isDead()) { logger.info("Dead app removed: {}", app); appManagement.removeApp(app); return; } Set machines = appInfo.getMachines(); logger.debug("enter fetchOnce(" + app + "), machines.size()=" + machines.size() + ", time intervalMs [" + startTime + ", " + endTime + "]"); if (machines.isEmpty()) { return; } final String msg = "fetch"; AtomicLong unhealthy = new AtomicLong(); final AtomicLong success = new AtomicLong(); final AtomicLong fail = new AtomicLong(); long start = System.currentTimeMillis(); /** app_resource_timeSecond -> metric */ final Map metricMap = new ConcurrentHashMap<>(16); final CountDownLatch latch = new CountDownLatch(machines.size()); for (final MachineInfo machine : machines) { // auto remove if (machine.isDead()) { latch.countDown(); appManagement.getDetailApp(app).removeMachine(machine.getIp(), machine.getPort()); logger.info("Dead machine removed: {}:{} of {}", machine.getIp(), machine.getPort(), app); continue; } if (!machine.isHealthy()) { latch.countDown(); unhealthy.incrementAndGet(); continue; } final String url = "http://" + machine.getIp() + ":" + machine.getPort() + "/" + METRIC_URL_PATH + "?startTime=" + startTime + "&endTime=" + endTime + "&refetch=" + false; final HttpGet httpGet = new HttpGet(url); httpGet.setHeader(HTTP.CONN_DIRECTIVE, HTTP.CONN_CLOSE); httpclient.execute(httpGet, new FutureCallback() { @Override public void completed(final HttpResponse response) { try { handleResponse(response, machine, metricMap); success.incrementAndGet(); } catch (Exception e) { logger.error(msg + " metric " + url + " error:", e); } finally { latch.countDown(); } } @Override public void failed(final Exception ex) { latch.countDown(); fail.incrementAndGet(); httpGet.abort(); if (ex instanceof SocketTimeoutException) { logger.error("Failed to fetch metric from <{}>: socket timeout", url); } else if (ex instanceof ConnectException) { logger.error("Failed to fetch metric from <{}> (ConnectionException: {})", url, ex.getMessage()); } else { logger.error(msg + " metric " + url + " error", ex); } } @Override public void cancelled() { latch.countDown(); fail.incrementAndGet(); httpGet.abort(); } }); } try { latch.await(maxWaitSeconds, TimeUnit.SECONDS); } catch (Exception e) { logger.info(msg + " metric, wait http client error:", e); } long cost = System.currentTimeMillis() - start; //logger.info("finished " + msg + " metric for " + app + ", time intervalMs [" + startTime + ", " + endTime // + "], total machines=" + machines.size() + ", dead=" + dead + ", fetch success=" // + success + ", fetch fail=" + fail + ", time cost=" + cost + " ms"); writeMetric(metricMap); } private void doFetchAppMetric(final String app) { long now = System.currentTimeMillis(); long lastFetchMs = now - MAX_LAST_FETCH_INTERVAL_MS; if (appLastFetchTime.containsKey(app)) { lastFetchMs = Math.max(lastFetchMs, appLastFetchTime.get(app).get() + 1000); } // trim milliseconds lastFetchMs = lastFetchMs / 1000 * 1000; long endTime = lastFetchMs + FETCH_INTERVAL_SECOND * 1000; if (endTime > now - 1000 * 2) { // to near return; } // update last_fetch in advance. appLastFetchTime.computeIfAbsent(app, a -> new AtomicLong()).set(endTime); final long finalLastFetchMs = lastFetchMs; final long finalEndTime = endTime; try { // do real fetch async fetchWorker.submit(() -> { try { fetchOnce(app, finalLastFetchMs, finalEndTime, 5); } catch (Exception e) { logger.info("fetchOnce(" + app + ") error", e); } }); } catch (Exception e) { logger.info("submit fetchOnce(" + app + ") fail, intervalMs [" + lastFetchMs + ", " + endTime + "]", e); } } private void handleResponse(final HttpResponse response, MachineInfo machine, Map metricMap) throws Exception { int code = response.getStatusLine().getStatusCode(); if (code != HTTP_OK) { return; } Charset charset = null; try { String contentTypeStr = response.getFirstHeader("Content-type").getValue(); if (StringUtil.isNotEmpty(contentTypeStr)) { ContentType contentType = ContentType.parse(contentTypeStr); charset = contentType.getCharset(); } } catch (Exception ignore) { } String body = EntityUtils.toString(response.getEntity(), charset != null ? charset : DEFAULT_CHARSET); if (StringUtil.isEmpty(body) || body.startsWith(NO_METRICS)) { //logger.info(machine.getApp() + ":" + machine.getIp() + ":" + machine.getPort() + ", bodyStr is empty"); return; } String[] lines = body.split("\n"); //logger.info(machine.getApp() + ":" + machine.getIp() + ":" + machine.getPort() + // ", bodyStr.length()=" + body.length() + ", lines=" + lines.length); handleBody(lines, machine, metricMap); } private void handleBody(String[] lines, MachineInfo machine, Map map) { //logger.info("handleBody() lines=" + lines.length + ", machine=" + machine); if (lines.length < 1) { return; } for (String line : lines) { try { MetricNode node = MetricNode.fromThinString(line); /** * 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); } } catch (Exception e) { logger.warn("handleBody line exception, machine: {}, line: {}", machine.toLogString(), line); } } } private String buildMetricKey(String app, String resource, long timestamp) { return app + "__" + resource + "__" + (timestamp / 1000); } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/repository/metric/InMemoryMetricsRepository.java ================================================ /* * 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.repository.metric; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; import com.alibaba.csp.sentinel.dashboard.datasource.entity.MetricEntity; import com.alibaba.csp.sentinel.util.StringUtil; import com.googlecode.concurrentlinkedhashmap.ConcurrentLinkedHashMap; import org.springframework.stereotype.Component; /** * Caches metrics data in a period of time in memory. * * @author Carpenter Lee * @author Eric Zhao */ @Component public class InMemoryMetricsRepository implements MetricsRepository { private static final long MAX_METRIC_LIVE_TIME_MS = 1000 * 60 * 5; /** * {@code app -> resource -> timestamp -> metric} */ private Map>> allMetrics = new ConcurrentHashMap<>(); @Override public synchronized void save(MetricEntity entity) { if (entity == null || StringUtil.isBlank(entity.getApp())) { return; } allMetrics.computeIfAbsent(entity.getApp(), e -> new ConcurrentHashMap<>(16)) .computeIfAbsent(entity.getResource(), e -> new ConcurrentLinkedHashMap.Builder() .maximumWeightedCapacity(MAX_METRIC_LIVE_TIME_MS).weigher((key, value) -> { // Metric older than {@link #MAX_METRIC_LIVE_TIME_MS} will be removed. int weight = (int)(System.currentTimeMillis() - key); // weight must be a number greater than or equal to one return Math.max(weight, 1); }).build()).put(entity.getTimestamp().getTime(), entity); } @Override public synchronized void saveAll(Iterable metrics) { if (metrics == null) { return; } metrics.forEach(this::save); } @Override public synchronized List queryByAppAndResourceBetween(String app, String resource, long startTime, long endTime) { List results = new ArrayList<>(); if (StringUtil.isBlank(app)) { return results; } Map> resourceMap = allMetrics.get(app); if (resourceMap == null) { return results; } ConcurrentLinkedHashMap metricsMap = resourceMap.get(resource); if (metricsMap == null) { return results; } for (Entry entry : metricsMap.entrySet()) { if (entry.getKey() >= startTime && entry.getKey() <= endTime) { results.add(entry.getValue()); } } return results; } @Override public List listResourcesOfApp(String app) { List results = new ArrayList<>(); if (StringUtil.isBlank(app)) { return results; } // resource -> timestamp -> metric Map> resourceMap = allMetrics.get(app); if (resourceMap == null) { return results; } final long minTimeMs = System.currentTimeMillis() - 1000 * 60; Map resourceCount = new ConcurrentHashMap<>(32); for (Entry> resourceMetrics : resourceMap.entrySet()) { for (Entry metrics : resourceMetrics.getValue().entrySet()) { if (metrics.getKey() < minTimeMs) { continue; } MetricEntity newEntity = metrics.getValue(); if (resourceCount.containsKey(resourceMetrics.getKey())) { MetricEntity oldEntity = resourceCount.get(resourceMetrics.getKey()); oldEntity.addPassQps(newEntity.getPassQps()); oldEntity.addRtAndSuccessQps(newEntity.getRt(), newEntity.getSuccessQps()); oldEntity.addBlockQps(newEntity.getBlockQps()); oldEntity.addExceptionQps(newEntity.getExceptionQps()); oldEntity.addCount(1); } else { resourceCount.put(resourceMetrics.getKey(), MetricEntity.copyOf(newEntity)); } } } // Order by last minute b_qps DESC. return resourceCount.entrySet() .stream() .sorted((o1, o2) -> { MetricEntity e1 = o1.getValue(); MetricEntity e2 = o2.getValue(); int t = e2.getBlockQps().compareTo(e1.getBlockQps()); if (t != 0) { return t; } return e2.getPassQps().compareTo(e1.getPassQps()); }) .map(Entry::getKey) .collect(Collectors.toList()); } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/repository/metric/MetricsRepository.java ================================================ /* * 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.repository.metric; import java.util.List; /** * Repository interface for aggregated metrics data. * * @param type of metrics * @author Eric Zhao */ public interface MetricsRepository { /** * Save the metric to the storage repository. * * @param metric metric data to save */ void save(T metric); /** * Save all metrics to the storage repository. * * @param metrics metrics to save */ void saveAll(Iterable metrics); /** * Get all metrics by {@code appName} and {@code resourceName} between a period of time. * * @param app application name for Sentinel * @param resource resource name * @param startTime start timestamp * @param endTime end timestamp * @return all metrics in query conditions */ List queryByAppAndResourceBetween(String app, String resource, long startTime, long endTime); /** * List resource name of provided application name. * * @param app application name * @return list of resources */ List listResourcesOfApp(String app); } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/repository/rule/InMemAuthorityRuleStore.java ================================================ /* * 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.repository.rule; import java.util.concurrent.atomic.AtomicLong; import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.AuthorityRuleEntity; import org.springframework.stereotype.Component; /** * In-memory storage for authority rules. * * @author Eric Zhao * @since 0.2.1 */ @Component public class InMemAuthorityRuleStore extends InMemoryRuleRepositoryAdapter { private static AtomicLong ids = new AtomicLong(0); @Override protected long nextId() { return ids.incrementAndGet(); } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/repository/rule/InMemDegradeRuleStore.java ================================================ /* * 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.repository.rule; import java.util.concurrent.atomic.AtomicLong; import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.DegradeRuleEntity; import org.springframework.stereotype.Component; /** * @author leyou */ @Component public class InMemDegradeRuleStore extends InMemoryRuleRepositoryAdapter { private static AtomicLong ids = new AtomicLong(0); @Override protected long nextId() { return ids.incrementAndGet(); } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/repository/rule/InMemFlowRuleStore.java ================================================ /* * 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.repository.rule; import java.util.concurrent.atomic.AtomicLong; import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity; import com.alibaba.csp.sentinel.slots.block.flow.ClusterFlowConfig; import org.springframework.stereotype.Component; /** * Store {@link FlowRuleEntity} in memory. * * @author leyou */ @Component public class InMemFlowRuleStore extends InMemoryRuleRepositoryAdapter { private static AtomicLong ids = new AtomicLong(0); @Override protected long nextId() { return ids.incrementAndGet(); } @Override protected FlowRuleEntity preProcess(FlowRuleEntity entity) { if (entity != null && entity.isClusterMode()) { ClusterFlowConfig config = entity.getClusterConfig(); if (config == null) { config = new ClusterFlowConfig(); entity.setClusterConfig(config); } // Set cluster rule id. config.setFlowId(entity.getId()); } return entity; } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/repository/rule/InMemParamFlowRuleStore.java ================================================ /* * 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.repository.rule; import java.util.concurrent.atomic.AtomicLong; import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.ParamFlowRuleEntity; import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowClusterConfig; import org.springframework.stereotype.Component; /** * @author Eric Zhao * @since 0.2.1 */ @Component public class InMemParamFlowRuleStore extends InMemoryRuleRepositoryAdapter { private static AtomicLong ids = new AtomicLong(0); @Override protected long nextId() { return ids.incrementAndGet(); } @Override protected ParamFlowRuleEntity preProcess(ParamFlowRuleEntity entity) { if (entity != null && entity.isClusterMode()) { ParamFlowClusterConfig config = entity.getClusterConfig(); if (config == null) { config = new ParamFlowClusterConfig(); } // Set cluster rule id. config.setFlowId(entity.getId()); } return entity; } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/repository/rule/InMemSystemRuleStore.java ================================================ /* * 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.repository.rule; import java.util.concurrent.atomic.AtomicLong; import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.SystemRuleEntity; import org.springframework.stereotype.Component; /** * @author leyou */ @Component public class InMemSystemRuleStore extends InMemoryRuleRepositoryAdapter { private static AtomicLong ids = new AtomicLong(0); @Override protected long nextId() { return ids.incrementAndGet(); } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/repository/rule/InMemoryRuleRepositoryAdapter.java ================================================ /* * 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.repository.rule; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.RuleEntity; import com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo; import com.alibaba.csp.sentinel.util.AssertUtil; /** * @author leyou */ public abstract class InMemoryRuleRepositoryAdapter implements RuleRepository { /** * {@code >} */ private Map> machineRules = new ConcurrentHashMap<>(16); private Map allRules = new ConcurrentHashMap<>(16); private Map> appRules = new ConcurrentHashMap<>(16); private static final int MAX_RULES_SIZE = 10000; @Override public T save(T entity) { if (entity.getId() == null) { entity.setId(nextId()); } T processedEntity = preProcess(entity); if (processedEntity != null) { allRules.put(processedEntity.getId(), processedEntity); machineRules.computeIfAbsent(MachineInfo.of(processedEntity.getApp(), processedEntity.getIp(), processedEntity.getPort()), e -> new ConcurrentHashMap<>(32)) .put(processedEntity.getId(), processedEntity); appRules.computeIfAbsent(processedEntity.getApp(), v -> new ConcurrentHashMap<>(32)) .put(processedEntity.getId(), processedEntity); } return processedEntity; } @Override public List saveAll(List rules) { // TODO: check here. allRules.clear(); machineRules.clear(); appRules.clear(); if (rules == null) { return null; } List savedRules = new ArrayList<>(rules.size()); for (T rule : rules) { savedRules.add(save(rule)); } return savedRules; } @Override public T delete(Long id) { T entity = allRules.remove(id); if (entity != null) { if (appRules.get(entity.getApp()) != null) { appRules.get(entity.getApp()).remove(id); } machineRules.get(MachineInfo.of(entity.getApp(), entity.getIp(), entity.getPort())).remove(id); } return entity; } @Override public T findById(Long id) { return allRules.get(id); } @Override public List findAllByMachine(MachineInfo machineInfo) { Map entities = machineRules.get(machineInfo); if (entities == null) { return new ArrayList<>(); } return new ArrayList<>(entities.values()); } @Override public List findAllByApp(String appName) { AssertUtil.notEmpty(appName, "appName cannot be empty"); Map entities = appRules.get(appName); if (entities == null) { return new ArrayList<>(); } return new ArrayList<>(entities.values()); } protected T preProcess(T entity) { return entity; } /** * Get next unused id. * * @return next unused id */ abstract protected long nextId(); } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/repository/rule/RuleRepository.java ================================================ /* * 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.repository.rule; import java.util.List; import com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo; /** * Interface to store and find rules. * * @author leyou */ public interface RuleRepository { /** * Save one. * * @param entity * @return */ T save(T entity); /** * Save all. * * @param rules * @return rules saved. */ List saveAll(List rules); /** * Delete by id * * @param id * @return entity deleted */ T delete(ID id); /** * Find by id. * * @param id * @return */ T findById(ID id); /** * Find all by machine. * * @param machineInfo * @return */ List findAllByMachine(MachineInfo machineInfo); /** * Find all by application. * * @param appName valid app name * @return all rules of the application * @since 1.4.0 */ List findAllByApp(String appName); ///** // * Find all by app and enable switch. // * @param app // * @param enable // * @return // */ //List findAllByAppAndEnable(String app, boolean enable); } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/rule/DynamicRuleProvider.java ================================================ /* * 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.rule; /** * @author Eric Zhao * @since 1.4.0 */ public interface DynamicRuleProvider { T getRules(String appName) throws Exception; } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/rule/DynamicRulePublisher.java ================================================ /* * 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.rule; /** * @author Eric Zhao * @since 1.4.0 */ public interface DynamicRulePublisher { /** * Publish rules to remote rule configuration center for given application name. * * @param app app name * @param rules list of rules to push * @throws Exception if some error occurs */ void publish(String app, T rules) throws Exception; } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/rule/FlowRuleApiProvider.java ================================================ /* * 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.rule; import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; import com.alibaba.csp.sentinel.dashboard.client.SentinelApiClient; import com.alibaba.csp.sentinel.dashboard.discovery.AppManagement; import com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo; import com.alibaba.csp.sentinel.util.StringUtil; import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; /** * @author Eric Zhao */ @Component("flowRuleDefaultProvider") public class FlowRuleApiProvider implements DynamicRuleProvider> { @Autowired private SentinelApiClient sentinelApiClient; @Autowired private AppManagement appManagement; @Override public List getRules(String appName) throws Exception { if (StringUtil.isBlank(appName)) { return new ArrayList<>(); } List list = appManagement.getDetailApp(appName).getMachines() .stream() .filter(MachineInfo::isHealthy) .sorted((e1, e2) -> Long.compare(e2.getLastHeartbeat(), e1.getLastHeartbeat())).collect(Collectors.toList()); if (list.isEmpty()) { return new ArrayList<>(); } else { MachineInfo machine = list.get(0); return sentinelApiClient.fetchFlowRuleOfMachine(machine.getApp(), machine.getIp(), machine.getPort()); } } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/rule/FlowRuleApiPublisher.java ================================================ /* * 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.rule; import java.util.List; import java.util.Set; import com.alibaba.csp.sentinel.dashboard.client.SentinelApiClient; import com.alibaba.csp.sentinel.dashboard.discovery.AppManagement; import com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo; import com.alibaba.csp.sentinel.util.StringUtil; import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; /** * @author Eric Zhao * @since 1.4.0 */ @Component("flowRuleDefaultPublisher") public class FlowRuleApiPublisher implements DynamicRulePublisher> { @Autowired private SentinelApiClient sentinelApiClient; @Autowired private AppManagement appManagement; @Override public void publish(String app, List rules) throws Exception { if (StringUtil.isBlank(app)) { return; } if (rules == null) { return; } Set set = appManagement.getDetailApp(app).getMachines(); for (MachineInfo machine : set) { if (!machine.isHealthy()) { continue; } // TODO: parse the results sentinelApiClient.setFlowRuleOfMachine(app, machine.getIp(), machine.getPort(), rules); } } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/rule/nacos/FlowRuleNacosProvider.java ================================================ /* * 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.rule.nacos; import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity; import com.alibaba.csp.sentinel.dashboard.rule.DynamicRuleProvider; import com.alibaba.csp.sentinel.datasource.Converter; import com.alibaba.csp.sentinel.util.StringUtil; import com.alibaba.nacos.api.config.ConfigService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.util.ArrayList; import java.util.List; /** * @author Eric Zhao * @since 1.4.0 */ @Component("flowRuleNacosProvider") public class FlowRuleNacosProvider implements DynamicRuleProvider> { @Autowired private ConfigService configService; @Autowired private Converter> converter; public static final String FLOW_DATA_ID_POSTFIX = "-sentinel"; public static final String GROUP_ID = "DEFAULT_GROUP"; @Override public List getRules(String appName) throws Exception { String rules = configService.getConfig(appName + FLOW_DATA_ID_POSTFIX, GROUP_ID, 3000); if (StringUtil.isEmpty(rules)) { return new ArrayList<>(); } return converter.convert(rules); } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/rule/nacos/FlowRuleNacosPublisher.java ================================================ /* * 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.rule.nacos; import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity; import com.alibaba.csp.sentinel.dashboard.rule.DynamicRulePublisher; import com.alibaba.csp.sentinel.datasource.Converter; import com.alibaba.csp.sentinel.util.AssertUtil; import com.alibaba.nacos.api.config.ConfigService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.util.List; /** * @author Eric Zhao * @since 1.4.0 */ @Component("flowRuleNacosPublisher") public class FlowRuleNacosPublisher implements DynamicRulePublisher> { @Autowired private ConfigService configService; @Autowired private Converter, String> converter; public static final String FLOW_DATA_ID_POSTFIX = "-sentinel"; public static final String GROUP_ID = "DEFAULT_GROUP"; @Override public void publish(String app, List rules) throws Exception { AssertUtil.notEmpty(app, "app name cannot be empty"); if (rules == null) { return; } configService.publishConfig(app + FLOW_DATA_ID_POSTFIX, GROUP_ID, converter.convert(rules)); } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/rule/nacos/NacosConfig.java ================================================ /* * 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.rule.nacos; import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity; import com.alibaba.csp.sentinel.datasource.Converter; import com.alibaba.fastjson.JSON; import com.alibaba.nacos.api.PropertyKeyConst; import com.alibaba.nacos.api.config.ConfigFactory; import com.alibaba.nacos.api.config.ConfigService; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.List; import java.util.Properties; /** * @author Eric Zhao * @since 1.4.0 */ @Configuration public class NacosConfig { @Bean public Converter, String> flowRuleEntityEncoder() { return JSON::toJSONString; } @Bean public Converter> flowRuleEntityDecoder() { return s -> JSON.parseArray(s, FlowRuleEntity.class); } @Bean public ConfigService nacosConfigService() throws Exception { Properties properties = new Properties(); properties.put(PropertyKeyConst.SERVER_ADDR, "localhost"); // properties.put(PropertyKeyConst.NAMESPACE, "130e71fa-97fe-467d-ad77-967456f2c16d"); return ConfigFactory.createConfigService(properties); } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/service/ClusterAssignService.java ================================================ /* * 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.service; import java.util.List; import java.util.Set; import com.alibaba.csp.sentinel.dashboard.domain.cluster.ClusterAppAssignResultVO; import com.alibaba.csp.sentinel.dashboard.domain.cluster.request.ClusterAppAssignMap; /** * @author Eric Zhao * @since 1.4.1 */ public interface ClusterAssignService { /** * Unbind a specific cluster server and its clients. * * @param app app name * @param machineId valid machine ID ({@code host@commandPort}) * @return assign result */ ClusterAppAssignResultVO unbindClusterServer(String app, String machineId); /** * Unbind a set of cluster servers and its clients. * * @param app app name * @param machineIdSet set of valid machine ID ({@code host@commandPort}) * @return assign result */ ClusterAppAssignResultVO unbindClusterServers(String app, Set machineIdSet); /** * Apply cluster server and client assignment for provided app. * * @param app app name * @param clusterMap cluster assign map (server -> clients) * @param remainingSet unassigned set of machine ID * @return assign result */ ClusterAppAssignResultVO applyAssignToApp(String app, List clusterMap, Set remainingSet); } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/service/ClusterAssignServiceImpl.java ================================================ /* * 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.service; import java.util.HashSet; import java.util.List; import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import com.alibaba.csp.sentinel.cluster.ClusterStateManager; import com.alibaba.csp.sentinel.dashboard.domain.cluster.state.ClusterUniversalStatePairVO; import com.alibaba.csp.sentinel.util.AssertUtil; import com.alibaba.csp.sentinel.util.function.Tuple2; import com.alibaba.csp.sentinel.dashboard.client.SentinelApiClient; import com.alibaba.csp.sentinel.dashboard.domain.cluster.ClusterAppAssignResultVO; import com.alibaba.csp.sentinel.dashboard.domain.cluster.ClusterGroupEntity; import com.alibaba.csp.sentinel.dashboard.domain.cluster.config.ClusterClientConfig; import com.alibaba.csp.sentinel.dashboard.domain.cluster.config.ServerFlowConfig; import com.alibaba.csp.sentinel.dashboard.domain.cluster.config.ServerTransportConfig; import com.alibaba.csp.sentinel.dashboard.domain.cluster.request.ClusterAppAssignMap; import com.alibaba.csp.sentinel.dashboard.util.MachineUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; /** * @author Eric Zhao * @since 1.4.1 */ @Service public class ClusterAssignServiceImpl implements ClusterAssignService { private final Logger LOGGER = LoggerFactory.getLogger(ClusterAssignServiceImpl.class); @Autowired private SentinelApiClient sentinelApiClient; @Autowired private ClusterConfigService clusterConfigService; private boolean isMachineInApp(/*@NonEmpty*/ String machineId) { return machineId.contains(":"); } private ClusterAppAssignResultVO handleUnbindClusterServerNotInApp(String app, String machineId) { Set failedSet = new HashSet<>(); try { List list = clusterConfigService.getClusterUniversalState(app) .get(10, TimeUnit.SECONDS); Set toModifySet = list.stream() .filter(e -> e.getState().getStateInfo().getMode() == ClusterStateManager.CLUSTER_CLIENT) .filter(e -> machineId.equals(e.getState().getClient().getClientConfig().getServerHost() + ':' + e.getState().getClient().getClientConfig().getServerPort())) .map(e -> e.getIp() + '@' + e.getCommandPort()) .collect(Collectors.toSet()); // Modify mode to NOT-STARTED for all associated token clients. modifyToNonStarted(toModifySet, failedSet); } catch (Exception ex) { Throwable e = ex instanceof ExecutionException ? ex.getCause() : ex; LOGGER.error("Failed to unbind machine <{}>", machineId, e); failedSet.add(machineId); } return new ClusterAppAssignResultVO() .setFailedClientSet(failedSet) .setFailedServerSet(new HashSet<>()); } private void modifyToNonStarted(Set toModifySet, Set failedSet) { toModifySet.parallelStream() .map(MachineUtils::parseCommandIpAndPort) .filter(Optional::isPresent) .map(Optional::get) .map(e -> { CompletableFuture f = modifyMode(e.r1, e.r2, ClusterStateManager.CLUSTER_NOT_STARTED); return Tuple2.of(e.r1 + '@' + e.r2, f); }) .forEach(f -> handleFutureSync(f, failedSet)); } @Override public ClusterAppAssignResultVO unbindClusterServer(String app, String machineId) { AssertUtil.assertNotBlank(app, "app cannot be blank"); AssertUtil.assertNotBlank(machineId, "machineId cannot be blank"); if (isMachineInApp(machineId)) { return handleUnbindClusterServerNotInApp(app, machineId); } Set failedSet = new HashSet<>(); try { ClusterGroupEntity entity = clusterConfigService.getClusterUniversalStateForAppMachine(app, machineId) .get(10, TimeUnit.SECONDS); Set toModifySet = new HashSet<>(); toModifySet.add(machineId); if (entity.getClientSet() != null) { toModifySet.addAll(entity.getClientSet()); } // Modify mode to NOT-STARTED for all chosen token servers and associated token clients. modifyToNonStarted(toModifySet, failedSet); } catch (Exception ex) { Throwable e = ex instanceof ExecutionException ? ex.getCause() : ex; LOGGER.error("Failed to unbind machine <{}>", machineId, e); failedSet.add(machineId); } return new ClusterAppAssignResultVO() .setFailedClientSet(failedSet) .setFailedServerSet(new HashSet<>()); } @Override public ClusterAppAssignResultVO unbindClusterServers(String app, Set machineIdSet) { AssertUtil.assertNotBlank(app, "app cannot be blank"); AssertUtil.isTrue(machineIdSet != null && !machineIdSet.isEmpty(), "machineIdSet cannot be empty"); ClusterAppAssignResultVO result = new ClusterAppAssignResultVO() .setFailedClientSet(new HashSet<>()) .setFailedServerSet(new HashSet<>()); for (String machineId : machineIdSet) { ClusterAppAssignResultVO resultVO = unbindClusterServer(app, machineId); result.getFailedClientSet().addAll(resultVO.getFailedClientSet()); result.getFailedServerSet().addAll(resultVO.getFailedServerSet()); } return result; } @Override public ClusterAppAssignResultVO applyAssignToApp(String app, List clusterMap, Set remainingSet) { AssertUtil.assertNotBlank(app, "app cannot be blank"); AssertUtil.notNull(clusterMap, "clusterMap cannot be null"); Set failedServerSet = new HashSet<>(); Set failedClientSet = new HashSet<>(); // Assign server and apply config. clusterMap.stream() .filter(Objects::nonNull) .filter(ClusterAppAssignMap::getBelongToApp) .map(e -> { String ip = e.getIp(); int commandPort = parsePort(e); CompletableFuture f = modifyMode(ip, commandPort, ClusterStateManager.CLUSTER_SERVER) .thenCompose(v -> applyServerConfigChange(app, ip, commandPort, e)); return Tuple2.of(e.getMachineId(), f); }) .forEach(t -> handleFutureSync(t, failedServerSet)); // Assign client of servers and apply config. clusterMap.parallelStream() .filter(Objects::nonNull) .forEach(e -> applyAllClientConfigChange(app, e, failedClientSet)); // Unbind remaining (unassigned) machines. applyAllRemainingMachineSet(app, remainingSet, failedClientSet); return new ClusterAppAssignResultVO() .setFailedClientSet(failedClientSet) .setFailedServerSet(failedServerSet); } private void applyAllRemainingMachineSet(String app, Set remainingSet, Set failedSet) { if (remainingSet == null || remainingSet.isEmpty()) { return; } remainingSet.parallelStream() .filter(Objects::nonNull) .map(MachineUtils::parseCommandIpAndPort) .filter(Optional::isPresent) .map(Optional::get) .map(ipPort -> { String ip = ipPort.r1; int commandPort = ipPort.r2; CompletableFuture f = modifyMode(ip, commandPort, ClusterStateManager.CLUSTER_NOT_STARTED); return Tuple2.of(ip + '@' + commandPort, f); }) .forEach(t -> handleFutureSync(t, failedSet)); } private void applyAllClientConfigChange(String app, ClusterAppAssignMap assignMap, Set failedSet) { Set clientSet = assignMap.getClientSet(); if (clientSet == null || clientSet.isEmpty()) { return; } final String serverIp = assignMap.getIp(); final int serverPort = assignMap.getPort(); clientSet.stream() .map(MachineUtils::parseCommandIpAndPort) .filter(Optional::isPresent) .map(Optional::get) .map(ipPort -> { CompletableFuture f = sentinelApiClient .modifyClusterMode(ipPort.r1, ipPort.r2, ClusterStateManager.CLUSTER_CLIENT) .thenCompose(v -> sentinelApiClient.modifyClusterClientConfig(app, ipPort.r1, ipPort.r2, new ClusterClientConfig().setRequestTimeout(20) .setServerHost(serverIp) .setServerPort(serverPort) )); return Tuple2.of(ipPort.r1 + '@' + ipPort.r2, f); }) .forEach(t -> handleFutureSync(t, failedSet)); } private void handleFutureSync(Tuple2> t, Set failedSet) { try { t.r2.get(10, TimeUnit.SECONDS); } catch (Exception ex) { if (ex instanceof ExecutionException) { LOGGER.error("Request for <{}> failed", t.r1, ex.getCause()); } else { LOGGER.error("Request for <{}> failed", t.r1, ex); } failedSet.add(t.r1); } } private CompletableFuture applyServerConfigChange(String app, String ip, int commandPort, ClusterAppAssignMap assignMap) { ServerTransportConfig transportConfig = new ServerTransportConfig() .setPort(assignMap.getPort()) .setIdleSeconds(600); return sentinelApiClient.modifyClusterServerTransportConfig(app, ip, commandPort, transportConfig) .thenCompose(v -> applyServerFlowConfigChange(app, ip, commandPort, assignMap)) .thenCompose(v -> applyServerNamespaceSetConfig(app, ip, commandPort, assignMap)); } private CompletableFuture applyServerFlowConfigChange(String app, String ip, int commandPort, ClusterAppAssignMap assignMap) { Double maxAllowedQps = assignMap.getMaxAllowedQps(); if (maxAllowedQps == null || maxAllowedQps <= 0 || maxAllowedQps > 20_0000) { return CompletableFuture.completedFuture(null); } return sentinelApiClient.modifyClusterServerFlowConfig(app, ip, commandPort, new ServerFlowConfig().setMaxAllowedQps(maxAllowedQps)); } private CompletableFuture applyServerNamespaceSetConfig(String app, String ip, int commandPort, ClusterAppAssignMap assignMap) { Set namespaceSet = assignMap.getNamespaceSet(); if (namespaceSet == null || namespaceSet.isEmpty()) { return CompletableFuture.completedFuture(null); } return sentinelApiClient.modifyClusterServerNamespaceSet(app, ip, commandPort, namespaceSet); } private CompletableFuture modifyMode(String ip, int port, int mode) { return sentinelApiClient.modifyClusterMode(ip, port, mode); } private int parsePort(ClusterAppAssignMap assignMap) { return MachineUtils.parseCommandPort(assignMap.getMachineId()) .orElse(ServerTransportConfig.DEFAULT_PORT); } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/service/ClusterConfigService.java ================================================ /* * 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.service; import java.util.ArrayList; import java.util.List; import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.stream.Collectors; import com.alibaba.csp.sentinel.cluster.ClusterStateManager; import com.alibaba.csp.sentinel.dashboard.client.SentinelApiClient; import com.alibaba.csp.sentinel.dashboard.discovery.AppInfo; import com.alibaba.csp.sentinel.dashboard.discovery.AppManagement; import com.alibaba.csp.sentinel.dashboard.domain.cluster.request.ClusterServerModifyRequest; import com.alibaba.csp.sentinel.dashboard.util.AsyncUtils; import com.alibaba.csp.sentinel.dashboard.util.ClusterEntityUtils; import com.alibaba.csp.sentinel.util.StringUtil; import com.alibaba.csp.sentinel.dashboard.domain.cluster.ClusterGroupEntity; import com.alibaba.csp.sentinel.dashboard.domain.cluster.request.ClusterClientModifyRequest; import com.alibaba.csp.sentinel.dashboard.domain.cluster.state.ClusterClientStateVO; import com.alibaba.csp.sentinel.dashboard.domain.cluster.state.ClusterUniversalStatePairVO; import com.alibaba.csp.sentinel.dashboard.domain.cluster.state.ClusterUniversalStateVO; import com.alibaba.csp.sentinel.dashboard.domain.cluster.config.ClusterClientConfig; import com.alibaba.csp.sentinel.dashboard.domain.cluster.config.ServerFlowConfig; import com.alibaba.csp.sentinel.dashboard.domain.cluster.config.ServerTransportConfig; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; /** * @author Eric Zhao * @since 1.4.0 */ @Service public class ClusterConfigService { @Autowired private SentinelApiClient sentinelApiClient; @Autowired private AppManagement appManagement; public CompletableFuture modifyClusterClientConfig(ClusterClientModifyRequest request) { if (notClientRequestValid(request)) { throw new IllegalArgumentException("Invalid request"); } String app = request.getApp(); String ip = request.getIp(); int port = request.getPort(); return sentinelApiClient.modifyClusterClientConfig(app, ip, port, request.getClientConfig()) .thenCompose(v -> sentinelApiClient.modifyClusterMode(ip, port, ClusterStateManager.CLUSTER_CLIENT)); } private boolean notClientRequestValid(/*@NonNull */ ClusterClientModifyRequest request) { ClusterClientConfig config = request.getClientConfig(); return config == null || StringUtil.isEmpty(config.getServerHost()) || config.getServerPort() == null || config.getServerPort() <= 0 || config.getRequestTimeout() == null || config.getRequestTimeout() <= 0; } public CompletableFuture modifyClusterServerConfig(ClusterServerModifyRequest request) { ServerTransportConfig transportConfig = request.getTransportConfig(); ServerFlowConfig flowConfig = request.getFlowConfig(); Set namespaceSet = request.getNamespaceSet(); if (invalidTransportConfig(transportConfig)) { throw new IllegalArgumentException("Invalid transport config in request"); } if (invalidFlowConfig(flowConfig)) { throw new IllegalArgumentException("Invalid flow config in request"); } if (namespaceSet == null) { throw new IllegalArgumentException("namespace set cannot be null"); } String app = request.getApp(); String ip = request.getIp(); int port = request.getPort(); return sentinelApiClient.modifyClusterServerNamespaceSet(app, ip, port, namespaceSet) .thenCompose(v -> sentinelApiClient.modifyClusterServerTransportConfig(app, ip, port, transportConfig)) .thenCompose(v -> sentinelApiClient.modifyClusterServerFlowConfig(app, ip, port, flowConfig)) .thenCompose(v -> sentinelApiClient.modifyClusterMode(ip, port, ClusterStateManager.CLUSTER_SERVER)); } /** * Get cluster state list of all available machines of provided application. * * @param app application name * @return cluster state list of all available machines of the application * @since 1.4.1 */ public CompletableFuture> getClusterUniversalState(String app) { if (StringUtil.isBlank(app)) { return AsyncUtils.newFailedFuture(new IllegalArgumentException("app cannot be empty")); } AppInfo appInfo = appManagement.getDetailApp(app); if (appInfo == null || appInfo.getMachines() == null) { return CompletableFuture.completedFuture(new ArrayList<>()); } List> futures = appInfo.getMachines().stream() .filter(e -> e.isHealthy()) .map(machine -> getClusterUniversalState(app, machine.getIp(), machine.getPort()) .thenApply(e -> new ClusterUniversalStatePairVO(machine.getIp(), machine.getPort(), e))) .collect(Collectors.toList()); return AsyncUtils.sequenceSuccessFuture(futures); } public CompletableFuture getClusterUniversalStateForAppMachine(String app, String machineId) { if (StringUtil.isBlank(app)) { return AsyncUtils.newFailedFuture(new IllegalArgumentException("app cannot be empty")); } AppInfo appInfo = appManagement.getDetailApp(app); if (appInfo == null || appInfo.getMachines() == null) { return AsyncUtils.newFailedFuture(new IllegalArgumentException("app does not have machines")); } boolean machineOk = appInfo.getMachines().stream() .filter(e -> e.isHealthy()) .map(e -> e.getIp() + '@' + e.getPort()) .anyMatch(e -> e.equals(machineId)); if (!machineOk) { return AsyncUtils.newFailedFuture(new IllegalStateException("machine does not exist or disconnected")); } return getClusterUniversalState(app) .thenApply(ClusterEntityUtils::wrapToClusterGroup) .thenCompose(e -> e.stream() .filter(e1 -> e1.getMachineId().equals(machineId)) .findAny() .map(CompletableFuture::completedFuture) .orElse(AsyncUtils.newFailedFuture(new IllegalStateException("not a server: " + machineId))) ); } public CompletableFuture getClusterUniversalState(String app, String ip, int port) { return sentinelApiClient.fetchClusterMode(ip, port) .thenApply(e -> new ClusterUniversalStateVO().setStateInfo(e)) .thenCompose(vo -> { if (vo.getStateInfo().getClientAvailable()) { return sentinelApiClient.fetchClusterClientInfoAndConfig(ip, port) .thenApply(cc -> vo.setClient(new ClusterClientStateVO().setClientConfig(cc))); } else { return CompletableFuture.completedFuture(vo); } }).thenCompose(vo -> { if (vo.getStateInfo().getServerAvailable()) { return sentinelApiClient.fetchClusterServerBasicInfo(ip, port) .thenApply(vo::setServer); } else { return CompletableFuture.completedFuture(vo); } }); } private boolean invalidTransportConfig(ServerTransportConfig transportConfig) { return transportConfig == null || transportConfig.getPort() == null || transportConfig.getPort() <= 0 || transportConfig.getIdleSeconds() == null || transportConfig.getIdleSeconds() <= 0; } private boolean invalidFlowConfig(ServerFlowConfig flowConfig) { return flowConfig == null || flowConfig.getSampleCount() == null || flowConfig.getSampleCount() <= 0 || flowConfig.getIntervalMs() == null || flowConfig.getIntervalMs() <= 0 || flowConfig.getIntervalMs() % flowConfig.getSampleCount() != 0 || flowConfig.getMaxAllowedQps() == null || flowConfig.getMaxAllowedQps() < 0; } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/util/AsyncUtils.java ================================================ /* * 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.util; import java.util.List; import java.util.Objects; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author Eric Zhao * @since 1.4.1 */ public final class AsyncUtils { private static final Logger LOG = LoggerFactory.getLogger(AsyncUtils.class); public static CompletableFuture newFailedFuture(Throwable ex) { CompletableFuture future = new CompletableFuture<>(); future.completeExceptionally(ex); return future; } public static CompletableFuture> sequenceFuture(List> futures) { return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])) .thenApply(v -> futures.stream() .map(AsyncUtils::getValue) .filter(Objects::nonNull) .collect(Collectors.toList()) ); } public static CompletableFuture> sequenceSuccessFuture(List> futures) { return CompletableFuture.supplyAsync(() -> futures.parallelStream() .map(AsyncUtils::getValue) .filter(Objects::nonNull) .collect(Collectors.toList()) ); } public static T getValue(CompletableFuture future) { try { return future.get(10, TimeUnit.SECONDS); } catch (Exception ex) { LOG.error("getValue for async result failed", ex); } return null; } public static boolean isSuccessFuture(CompletableFuture future) { return future.isDone() && !future.isCompletedExceptionally() && !future.isCancelled(); } private AsyncUtils() {} } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/util/ClusterEntityUtils.java ================================================ /* * 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.util; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import com.alibaba.csp.sentinel.cluster.ClusterStateManager; import com.alibaba.csp.sentinel.util.StringUtil; import com.alibaba.csp.sentinel.dashboard.domain.cluster.ClusterGroupEntity; import com.alibaba.csp.sentinel.dashboard.domain.cluster.ConnectionGroupVO; import com.alibaba.csp.sentinel.dashboard.domain.cluster.state.AppClusterClientStateWrapVO; import com.alibaba.csp.sentinel.dashboard.domain.cluster.state.AppClusterServerStateWrapVO; import com.alibaba.csp.sentinel.dashboard.domain.cluster.state.ClusterClientStateVO; import com.alibaba.csp.sentinel.dashboard.domain.cluster.state.ClusterServerStateVO; import com.alibaba.csp.sentinel.dashboard.domain.cluster.state.ClusterUniversalStatePairVO; /** * @author Eric Zhao * @since 1.4.1 */ public final class ClusterEntityUtils { public static List wrapToAppClusterServerState( List list) { if (list == null || list.isEmpty()) { return new ArrayList<>(); } Map map = new HashMap<>(); Set tokenServerSet = new HashSet<>(); // Handle token servers that belong to current app. for (ClusterUniversalStatePairVO stateVO : list) { int mode = stateVO.getState().getStateInfo().getMode(); if (mode == ClusterStateManager.CLUSTER_SERVER) { String ip = stateVO.getIp(); String serverId = ip + '@' + stateVO.getCommandPort(); ClusterServerStateVO serverStateVO = stateVO.getState().getServer(); map.computeIfAbsent(serverId, v -> new AppClusterServerStateWrapVO() .setId(serverId) .setIp(ip) .setPort(serverStateVO.getPort()) .setState(serverStateVO) .setBelongToApp(true) .setConnectedCount(serverStateVO.getConnection().stream() .mapToInt(ConnectionGroupVO::getConnectedCount) .sum() ) ); tokenServerSet.add(ip + ":" + serverStateVO.getPort()); } } // Handle token servers from other app. for (ClusterUniversalStatePairVO stateVO : list) { int mode = stateVO.getState().getStateInfo().getMode(); if (mode == ClusterStateManager.CLUSTER_CLIENT) { ClusterClientStateVO clientState = stateVO.getState().getClient(); if (clientState == null) { continue; } String serverIp = clientState.getClientConfig().getServerHost(); int serverPort = clientState.getClientConfig().getServerPort(); if (tokenServerSet.contains(serverIp + ":" + serverPort)) { continue; } // We are not able to get the commandPort of foreign token server directly. String serverId = String.format("%s:%d", serverIp, serverPort); map.computeIfAbsent(serverId, v -> new AppClusterServerStateWrapVO() .setId(serverId) .setIp(serverIp) .setPort(serverPort) .setBelongToApp(false) ); } } return new ArrayList<>(map.values()); } public static List wrapToAppClusterClientState( List list) { if (list == null || list.isEmpty()) { return new ArrayList<>(); } Map map = new HashMap<>(); for (ClusterUniversalStatePairVO stateVO : list) { int mode = stateVO.getState().getStateInfo().getMode(); if (mode == ClusterStateManager.CLUSTER_CLIENT) { String ip = stateVO.getIp(); String clientId = ip + '@' + stateVO.getCommandPort(); ClusterClientStateVO clientStateVO = stateVO.getState().getClient(); map.computeIfAbsent(clientId, v -> new AppClusterClientStateWrapVO() .setId(clientId) .setIp(ip) .setState(clientStateVO) .setCommandPort(stateVO.getCommandPort()) ); } } return new ArrayList<>(map.values()); } public static List wrapToClusterGroup(List list) { if (list == null || list.isEmpty()) { return new ArrayList<>(); } Map map = new HashMap<>(); for (ClusterUniversalStatePairVO stateVO : list) { int mode = stateVO.getState().getStateInfo().getMode(); String ip = stateVO.getIp(); if (mode == ClusterStateManager.CLUSTER_SERVER) { String serverAddress = getIp(ip); int port = stateVO.getState().getServer().getPort(); map.computeIfAbsent(serverAddress, v -> new ClusterGroupEntity() .setBelongToApp(true).setMachineId(ip + '@' + stateVO.getCommandPort()) .setIp(ip).setPort(port) ); } } for (ClusterUniversalStatePairVO stateVO : list) { int mode = stateVO.getState().getStateInfo().getMode(); String ip = stateVO.getIp(); if (mode == ClusterStateManager.CLUSTER_CLIENT) { String targetServer = stateVO.getState().getClient().getClientConfig().getServerHost(); Integer targetPort = stateVO.getState().getClient().getClientConfig().getServerPort(); if (StringUtil.isBlank(targetServer) || targetPort == null || targetPort <= 0) { continue; } ClusterGroupEntity group = map.computeIfAbsent(targetServer, v -> new ClusterGroupEntity() .setBelongToApp(true).setMachineId(targetServer) .setIp(targetServer).setPort(targetPort) ); group.getClientSet().add(ip + '@' + stateVO.getCommandPort()); } } return new ArrayList<>(map.values()); } private static String getIp(String str) { if (str.contains(":")) { return str.split(":")[0]; } return str; } private ClusterEntityUtils() {} } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/util/MachineUtils.java ================================================ /* * 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.util; import java.util.Optional; import com.alibaba.csp.sentinel.util.StringUtil; import com.alibaba.csp.sentinel.util.function.Tuple2; /** * @author Eric Zhao */ public final class MachineUtils { public static Optional parseCommandPort(String machineIp) { try { if (!machineIp.contains("@")) { return Optional.empty(); } String[] str = machineIp.split("@"); if (str.length <= 1) { return Optional.empty(); } return Optional.of(Integer.parseInt(str[1])); } catch (Exception ex) { return Optional.empty(); } } public static Optional> parseCommandIpAndPort(String machineIp) { try { if (StringUtil.isEmpty(machineIp) || !machineIp.contains("@")) { return Optional.empty(); } String[] str = machineIp.split("@"); if (str.length <= 1) { return Optional.empty(); } return Optional.of(Tuple2.of(str[0], Integer.parseInt(str[1]))); } catch (Exception ex) { return Optional.empty(); } } private MachineUtils() {} } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/java/com/alibaba/csp/sentinel/dashboard/util/VersionUtils.java ================================================ /* * 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.util; import java.util.Optional; import com.alibaba.csp.sentinel.util.StringUtil; import com.alibaba.csp.sentinel.dashboard.datasource.entity.SentinelVersion; /** * Util class for parsing version. * * @author Eric Zhao * @since 0.2.1 */ public final class VersionUtils { /** * Parse version of Sentinel from raw string. * * @param versionFull version string * @return parsed {@link SentinelVersion} if the version is valid; empty if * there is something wrong with the format */ public static Optional parseVersion(String s) { if (StringUtil.isBlank(s)) { return Optional.empty(); } try { String versionFull = s; SentinelVersion version = new SentinelVersion(); // postfix int index = versionFull.indexOf("-"); if (index == 0) { // Start with "-" return Optional.empty(); } if (index == versionFull.length() - 1) { // End with "-" } else if (index > 0) { version.setPostfix(versionFull.substring(index + 1)); } if (index >= 0) { versionFull = versionFull.substring(0, index); } // x.x.x int segment = 0; int[] ver = new int[3]; while (segment < ver.length) { index = versionFull.indexOf('.'); if (index < 0) { if (versionFull.length() > 0) { ver[segment] = Integer.valueOf(versionFull); } break; } ver[segment] = Integer.valueOf(versionFull.substring(0, index)); versionFull = versionFull.substring(index + 1); segment ++; } if (ver[0] < 1) { // Wrong format, return empty. return Optional.empty(); } else { return Optional.of(version .setMajorVersion(ver[0]) .setMinorVersion(ver[1]) .setFixVersion(ver[2])); } } catch (Exception ex) { // Parse fail, return empty. return Optional.empty(); } } private VersionUtils() {} } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/resources/application.properties ================================================ #spring settings spring.http.encoding.force=true spring.http.encoding.charset=UTF-8 spring.http.encoding.enabled=true #logging settings logging.level.org.springframework.web=INFO logging.file=${user.home}/logs/csp/sentinel-dashboard.log logging.pattern.file= %d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n #logging.pattern.console= %d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n #auth settings auth.filter.exclude-urls=/,/auth/login,/auth/logout,/registry/machine auth.filter.exclude-url-suffixes=htm,html,js,css,map,ico,ttf,woff,png auth.username=sentinel auth.password=sentinel ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/.gitignore ================================================ node_modules/ tmp/ ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/.jshintrc ================================================ { /* * ENVIRONMENTS * ================= */ // Define globals exposed by modern browsers. "browser": true, // Define globals exposed by jQuery. "jquery": true, // Define globals exposed by Node.js. "node": true, // Allow ES6. "esversion": 6, /* * ENFORCING OPTIONS * ================= */ // Force all variable names to use either camelCase style or UPPER_CASE // with underscores. "camelcase": true, // Prohibit use of == and != in favor of === and !==. "eqeqeq": true, // Enforce tab width of 2 spaces. "indent": 2, // Prohibit use of a variable before it is defined. "latedef": true, // Enforce line length to 100 characters "maxlen": 100, // Require capitalized names for constructor functions. "newcap": true, // Enforce use of single quotation marks for strings. "quotmark": "single", // Enforce placing 'use strict' at the top function scope // 前端项目中外层使用 strict 即可,覆盖此条规则 "strict": false, // Prohibit use of explicitly undeclared variables. "undef": true, // Warn when variables are defined but never used. "unused": true, /* * RELAXING OPTIONS * ================= */ // Suppress warnings about == null comparisons. "eqnull": true, "globals": { "$": false, "angular": false } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/README.md ================================================ # Sentinel Dashboard Frontend ## Env Requirement - Node.js > 6.x ## Code Guide - [Code Style Guide for HTML/CSS](https://codeguide.bootcss.com/) - [Airbnb JavaScript Style Guide](https://github.com/airbnb/javascript/tree/es5-deprecated/es5) ## Install Packages ``` npm install ``` ## Start Development ``` npm start ``` ## Build for production ``` npm run build ``` ## Credit - [sb-admin-angular](https://github.com/start-angular/sb-admin-angular) ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/README_zh.md ================================================ # Sentinel Dashboard Frontend ## 环境要求 - Node.js > 6.x ## 编码规范 - HTML/CSS 遵循 [Bootstrap 编码规范](https://codeguide.bootcss.com/) - JavaScript 遵循 [Airbnb JavaScript Style Guide](https://github.com/airbnb/javascript/tree/es5-deprecated/es5) 以及最新的 ES 6 标准 ## 安装依赖 ``` npm i ``` ## 开始本地开发 ``` npm start ``` ## 构建前端资源 ``` npm run build ``` ## Credit - [sb-admin-angular](https://github.com/start-angular/sb-admin-angular) ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/scripts/app.js ================================================ 'use strict'; /** * @ngdoc overview * @name sentinelDashboardApp * @description * # sentinelDashboardApp * * Main module of the application. */ angular .module('sentinelDashboardApp', [ 'oc.lazyLoad', 'ui.router', 'ui.bootstrap', 'angular-loading-bar', 'ngDialog', 'ui.bootstrap.datetimepicker', 'ui-notification', 'rzTable', 'angular-clipboard', 'selectize', 'angularUtils.directives.dirPagination' ]) .factory('AuthInterceptor', ['$window', '$state', function ($window, $state) { var authInterceptor = { 'responseError' : function(response) { if (response.status == 401) { // If not auth, clear session in localStorage and jump to the login page $window.localStorage.removeItem("session_sentinel_admin"); $state.go('login'); } return response; }, 'response' : function(response) { return response; }, 'request' : function(config) { return config; }, 'requestError' : function(config){ return config; } }; return authInterceptor; }]) .config(['$stateProvider', '$urlRouterProvider', '$ocLazyLoadProvider', '$httpProvider', function ($stateProvider, $urlRouterProvider, $ocLazyLoadProvider, $httpProvider) { $httpProvider.interceptors.push('AuthInterceptor'); $ocLazyLoadProvider.config({ debug: false, events: true, }); $urlRouterProvider.otherwise('/dashboard/home'); $stateProvider .state('login', { url: '/login', templateUrl: 'app/views/login.html', controller: 'LoginCtl', resolve: { loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) { return $ocLazyLoad.load({ name: 'sentinelDashboardApp', files: [ 'app/scripts/controllers/login.js', ] }); }] } }) .state('dashboard', { url: '/dashboard', templateUrl: 'app/views/dashboard/main.html', resolve: { loadMyDirectives: ['$ocLazyLoad', function ($ocLazyLoad) { return $ocLazyLoad.load( { name: 'sentinelDashboardApp', files: [ 'app/scripts/directives/header/header.js', 'app/scripts/directives/sidebar/sidebar.js', 'app/scripts/directives/sidebar/sidebar-search/sidebar-search.js', ] }); }] } }) .state('dashboard.home', { url: '/home', templateUrl: 'app/views/dashboard/home.html', resolve: { loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) { return $ocLazyLoad.load({ name: 'sentinelDashboardApp', files: [ 'app/scripts/controllers/main.js', ] }); }] } }) .state('dashboard.flowV1', { templateUrl: 'app/views/flow_v1.html', url: '/flow/:app', controller: 'FlowControllerV1', resolve: { loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) { return $ocLazyLoad.load({ name: 'sentinelDashboardApp', files: [ 'app/scripts/controllers/flow_v1.js', ] }); }] } }) .state('dashboard.flow', { templateUrl: 'app/views/flow_v2.html', url: '/v2/flow/:app', controller: 'FlowControllerV2', resolve: { loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) { return $ocLazyLoad.load({ name: 'sentinelDashboardApp', files: [ 'app/scripts/controllers/flow_v2.js', ] }); }] } }) .state('dashboard.paramFlow', { templateUrl: 'app/views/param_flow.html', url: '/paramFlow/:app', controller: 'ParamFlowController', resolve: { loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) { return $ocLazyLoad.load({ name: 'sentinelDashboardApp', files: [ 'app/scripts/controllers/param_flow.js', ] }); }] } }) .state('dashboard.clusterAppAssignManage', { templateUrl: 'app/views/cluster_app_assign_manage.html', url: '/cluster/assign_manage/:app', controller: 'SentinelClusterAppAssignManageController', resolve: { loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) { return $ocLazyLoad.load({ name: 'sentinelDashboardApp', files: [ 'app/scripts/controllers/cluster_app_assign_manage.js', ] }); }] } }) .state('dashboard.clusterAppServerList', { templateUrl: 'app/views/cluster_app_server_list.html', url: '/cluster/server/:app', controller: 'SentinelClusterAppServerListController', resolve: { loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) { return $ocLazyLoad.load({ name: 'sentinelDashboardApp', files: [ 'app/scripts/controllers/cluster_app_server_list.js', ] }); }] } }) .state('dashboard.clusterAppClientList', { templateUrl: 'app/views/cluster_app_client_list.html', url: '/cluster/client/:app', controller: 'SentinelClusterAppTokenClientListController', resolve: { loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) { return $ocLazyLoad.load({ name: 'sentinelDashboardApp', files: [ 'app/scripts/controllers/cluster_app_token_client_list.js', ] }); }] } }) .state('dashboard.clusterSingle', { templateUrl: 'app/views/cluster_single_config.html', url: '/cluster/single/:app', controller: 'SentinelClusterSingleController', resolve: { loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) { return $ocLazyLoad.load({ name: 'sentinelDashboardApp', files: [ 'app/scripts/controllers/cluster_single.js', ] }); }] } }) .state('dashboard.authority', { templateUrl: 'app/views/authority.html', url: '/authority/:app', controller: 'AuthorityRuleController', resolve: { loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) { return $ocLazyLoad.load({ name: 'sentinelDashboardApp', files: [ 'app/scripts/controllers/authority.js', ] }); }] } }) .state('dashboard.degrade', { templateUrl: 'app/views/degrade.html', url: '/degrade/:app', controller: 'DegradeCtl', resolve: { loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) { return $ocLazyLoad.load({ name: 'sentinelDashboardApp', files: [ 'app/scripts/controllers/degrade.js', ] }); }] } }) .state('dashboard.system', { templateUrl: 'app/views/system.html', url: '/system/:app', controller: 'SystemCtl', resolve: { loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) { return $ocLazyLoad.load({ name: 'sentinelDashboardApp', files: [ 'app/scripts/controllers/system.js', ] }); }] } }) .state('dashboard.machine', { templateUrl: 'app/views/machine.html', url: '/app/:app', controller: 'MachineCtl', resolve: { loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) { return $ocLazyLoad.load({ name: 'sentinelDashboardApp', files: [ 'app/scripts/controllers/machine.js', ] }); }] } }) .state('dashboard.identity', { templateUrl: 'app/views/identity.html', url: '/identity/:app', controller: 'IdentityCtl', resolve: { loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) { return $ocLazyLoad.load({ name: 'sentinelDashboardApp', files: [ 'app/scripts/controllers/identity.js', ] }); }] } }) .state('dashboard.metric', { templateUrl: 'app/views/metric.html', url: '/metric/:app', controller: 'MetricCtl', resolve: { loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) { return $ocLazyLoad.load({ name: 'sentinelDashboardApp', files: [ 'app/scripts/controllers/metric.js', ] }); }] } }); }]); ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/scripts/controllers/authority.js ================================================ /** * Authority rule controller. */ angular.module('sentinelDashboardApp').controller('AuthorityRuleController', ['$scope', '$stateParams', 'AuthorityRuleService', 'ngDialog', 'MachineService', function ($scope, $stateParams, AuthorityRuleService, ngDialog, MachineService) { $scope.app = $stateParams.app; $scope.rulesPageConfig = { pageSize: 10, currentPageIndex: 1, totalPage: 1, totalCount: 0, }; $scope.macsInputConfig = { searchField: ['text', 'value'], persist: true, create: false, maxItems: 1, render: { item: function (data, escape) { return '
    ' + escape(data.text) + '
    '; } }, onChange: function (value, oldValue) { $scope.macInputModel = value; } }; function getMachineRules() { if (!$scope.macInputModel) { return; } let mac = $scope.macInputModel.split(':'); AuthorityRuleService.queryMachineRules($scope.app, mac[0], mac[1]) .success(function (data) { if (data.code === 0 && data.data) { $scope.loadError = undefined; $scope.rules = data.data; $scope.rulesPageConfig.totalCount = $scope.rules.length; } else { $scope.rules = []; $scope.rulesPageConfig.totalCount = 0; $scope.loadError = {message: data.msg}; } }) .error((data, header, config, status) => { $scope.loadError = {message: "未知错误"}; }); }; $scope.getMachineRules = getMachineRules; getMachineRules(); var authorityRuleDialog; $scope.editRule = function (rule) { $scope.currentRule = angular.copy(rule); $scope.authorityRuleDialog = { title: '编辑授权规则', type: 'edit', confirmBtnText: '保存', }; authorityRuleDialog = ngDialog.open({ template: '/app/views/dialog/authority-rule-dialog.html', width: 680, overlay: true, scope: $scope }); }; $scope.addNewRule = function () { var mac = $scope.macInputModel.split(':'); $scope.currentRule = { app: $scope.app, ip: mac[0], port: mac[1], rule: { strategy: 0, limitApp: '', } }; $scope.authorityRuleDialog = { title: '新增授权规则', type: 'add', confirmBtnText: '新增', showAdvanceButton: true, }; authorityRuleDialog = ngDialog.open({ template: '/app/views/dialog/authority-rule-dialog.html', width: 680, overlay: true, scope: $scope }); }; $scope.saveRule = function () { if (!AuthorityRuleService.checkRuleValid($scope.currentRule.rule)) { return; } if ($scope.authorityRuleDialog.type === 'add') { addNewRuleAndPush($scope.currentRule); } else if ($scope.authorityRuleDialog.type === 'edit') { saveRuleAndPush($scope.currentRule, true); } }; function addNewRuleAndPush(rule) { AuthorityRuleService.addNewRule(rule).success((data) => { if (data.success) { getMachineRules(); authorityRuleDialog.close(); } else { alert('添加规则失败:' + data.msg); } }).error((data) => { if (data) { alert('添加规则失败:' + data.msg); } else { alert("添加规则失败:未知错误"); } }); } function saveRuleAndPush(rule, edit) { AuthorityRuleService.saveRule(rule).success(function (data) { if (data.success) { alert("修改规则成功"); getMachineRules(); if (edit) { authorityRuleDialog.close(); } else { confirmDialog.close(); } } else { alert('修改规则失败:' + data.msg); } }).error((data) => { if (data) { alert('修改规则失败:' + data.msg); } else { alert("修改规则失败:未知错误"); } }); } function deleteRuleAndPush(entity) { if (entity.id === undefined || isNaN(entity.id)) { alert('规则 ID 不合法!'); return; } AuthorityRuleService.deleteRule(entity).success((data) => { if (data.code == 0) { getMachineRules(); confirmDialog.close(); } else { alert('删除规则失败:' + data.msg); } }).error((data) => { if (data) { alert('删除规则失败:' + data.msg); } else { alert("删除规则失败:未知错误"); } }); }; var confirmDialog; $scope.deleteRule = function (ruleEntity) { $scope.currentRule = ruleEntity; $scope.confirmDialog = { title: '删除授权规则', type: 'delete_rule', attentionTitle: '请确认是否删除如下授权限流规则', attention: '资源名: ' + ruleEntity.rule.resource + ', 流控应用: ' + ruleEntity.rule.limitApp + ', 类型: ' + (ruleEntity.rule.strategy === 0 ? '白名单' : '黑名单'), confirmBtnText: '删除', }; confirmDialog = ngDialog.open({ template: '/app/views/dialog/confirm-dialog.html', scope: $scope, overlay: true }); }; $scope.confirm = function () { if ($scope.confirmDialog.type === 'delete_rule') { deleteRuleAndPush($scope.currentRule); } else { console.error('error'); } }; queryAppMachines(); function queryAppMachines() { MachineService.getAppMachines($scope.app).success( function (data) { if (data.code == 0) { // $scope.machines = data.data; if (data.data) { $scope.machines = []; $scope.macsInputOptions = []; data.data.forEach(function (item) { if (item.healthy) { $scope.macsInputOptions.push({ text: item.ip + ':' + item.port, value: item.ip + ':' + item.port }); } }); } if ($scope.macsInputOptions.length > 0) { $scope.macInputModel = $scope.macsInputOptions[0].value; } } else { $scope.macsInputOptions = []; } } ); }; $scope.$watch('macInputModel', function () { if ($scope.macInputModel) { getMachineRules(); } }); }]); ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/scripts/controllers/cluster_app_assign_manage.js ================================================ var app = angular.module('sentinelDashboardApp'); app.controller('SentinelClusterAppAssignManageController', ['$scope', '$stateParams', 'ngDialog', 'MachineService', 'ClusterStateService', function ($scope, $stateParams, ngDialog, MachineService, ClusterStateService) { $scope.app = $stateParams.app; const UNSUPPORTED_CODE = 4041; const CLUSTER_MODE_CLIENT = 0; const CLUSTER_MODE_SERVER = 1; const DEFAULT_CLUSTER_SERVER_PORT = 18730; $scope.tmp = { curClientChosen: [], curRemainingClientChosen: [], curChosenServer: {}, }; function convertSetToString(set) { if (set === undefined) { return ''; } let s = ''; for (let i = 0; i < set.length; i++) { s = s + set[i]; if (i < set.length - 1) { s = s + ','; } } return s; } function convertStrToNamespaceSet(str) { if (str === undefined || str === '') { return []; } let arr = []; let spliced = str.split(','); spliced.forEach((v) => { arr.push(v.trim()); }); return arr; } function processAppSingleData(data) { if (data.state.server && data.state.server.namespaceSet) { data.state.server.namespaceSetStr = convertSetToString(data.state.server.namespaceSet); data.mode = data.state.stateInfo.mode; } } function removeFromArr(arr, v) { for (let i = 0; i < arr.length; i++) { if (arr[i] === v) { arr.splice(i, 1); break; } } } function resetChosen() { $scope.tmp.curClientChosen = []; $scope.tmp.curRemainingClientChosen = []; } function generateMachineId(e) { return e.ip + '@' + e.commandPort; } function applyClusterMap(appClusterMachineList) { if (!appClusterMachineList) { return; } let tmpMap = new Map(); $scope.clusterMap = []; $scope.remainingClientAddressList = []; let tmpServerList = []; let tmpClientList = []; appClusterMachineList.forEach((e) => { if (e.mode === CLUSTER_MODE_CLIENT) { tmpClientList.push(e); } else if (e.mode === CLUSTER_MODE_SERVER) { tmpServerList.push(e); } else { $scope.remainingClientAddressList.push(generateMachineId(e)); } }); tmpServerList.forEach((e) => { let ip = e.ip; let machineId = ip + '@' + e.commandPort; let group = { ip: ip, machineId: machineId, port: e.state.server.port, clientSet: [], namespaceSetStr: e.state.server.namespaceSetStr, belongToApp: true, }; if (!tmpMap.has(ip)) { tmpMap.set(ip, group); } }); tmpClientList.forEach((e) => { let ip = e.ip; let machineId = ip + '@' + e.commandPort; let targetServer = e.state.client.clientConfig.serverHost; let targetPort = e.state.client.clientConfig.serverPort; if (targetServer === undefined || targetServer === '' || targetPort === undefined || targetPort <= 0) { $scope.remainingClientAddressList.push(generateMachineId(e)); return; } if (!tmpMap.has(targetServer)) { let group = { ip: targetServer, machineId: targetServer, port: targetPort, clientSet: [machineId], belongToApp: false, }; tmpMap.set(targetServer, group); } else { let g = tmpMap.get(targetServer); g.clientSet.push(machineId); } }); tmpMap.forEach((v) => { if (v !== undefined) { $scope.clusterMap.push(v); } }); } $scope.onCurrentServerChange = () => { resetChosen(); }; $scope.remainingClientAddressList = []; $scope.moveToServerGroup = () => { let chosenServer = $scope.tmp.curChosenServer; if (!chosenServer || !chosenServer.machineId) { return; } $scope.tmp.curRemainingClientChosen.forEach(e => { chosenServer.clientSet.push(e); removeFromArr($scope.remainingClientAddressList, e); }); resetChosen(); }; $scope.moveToRemainingSharePool = () => { $scope.tmp.curClientChosen.forEach(e => { $scope.remainingClientAddressList.push(e); removeFromArr($scope.tmp.curChosenServer.clientSet, e); }); resetChosen(); }; function parseIpFromMachineId(machineId) { if (machineId.indexOf('@') === -1) { return machineId; } let arr = machineId.split('@'); return arr[0]; } $scope.addToServerList = () => { let group; $scope.tmp.curRemainingClientChosen.forEach(e => { group = { machineId: e, ip: parseIpFromMachineId(e), port: DEFAULT_CLUSTER_SERVER_PORT, clientSet: [], namespaceSetStr: 'default,' + $scope.app, belongToApp: true, }; $scope.clusterMap.push(group); removeFromArr($scope.remainingClientAddressList, e); $scope.tmp.curChosenServer = group; }); resetChosen(); }; $scope.removeFromServerList = () => { let chosenServer = $scope.tmp.curChosenServer; if (!chosenServer || !chosenServer.machineId) { return; } chosenServer.clientSet.forEach((e) => { if (e !== undefined) { $scope.remainingClientAddressList.push(e); } }); if (chosenServer.belongToApp || chosenServer.machineId.indexOf('@') !== -1) { $scope.remainingClientAddressList.push(chosenServer.machineId); } else { alert('提示:非本应用内机器将不会置回空闲列表中'); } removeFromArr($scope.clusterMap, chosenServer); resetChosen(); if ($scope.clusterMap.length > 0) { $scope.tmp.curChosenServer = $scope.clusterMap[0]; $scope.onCurrentServerChange(); } else { $scope.tmp.curChosenServer = {}; } }; function retrieveClusterAppInfo() { ClusterStateService.fetchClusterUniversalStateOfApp($scope.app).success(function (data) { if (data.code === 0 && data.data) { $scope.loadError = undefined; $scope.appClusterMachineList = data.data; $scope.appClusterMachineList.forEach(processAppSingleData); applyClusterMap($scope.appClusterMachineList); if ($scope.clusterMap.length > 0) { $scope.tmp.curChosenServer = $scope.clusterMap[0]; $scope.onCurrentServerChange(); } } else { $scope.appClusterMachineList = {}; if (data.code === UNSUPPORTED_CODE) { $scope.loadError = {message: '该应用的 Sentinel 客户端不支持集群限流,请升级至 1.4.0 以上版本并引入相关依赖。'} } else { $scope.loadError = {message: data.msg}; } } }).error(() => { $scope.loadError = {message: '未知错误'}; }); } retrieveClusterAppInfo(); $scope.saveAndApplyAssign = () => { let ok = confirm('是否确认执行变更?'); if (!ok) { return; } let cm = $scope.clusterMap; if (!cm) { cm = []; } cm.forEach((e) => { e.namespaceSet = convertStrToNamespaceSet(e.namespaceSetStr); }); cm.namespaceSet = convertStrToNamespaceSet(cm.namespaceSetStr); let request = { clusterMap: cm, remainingList: $scope.remainingClientAddressList, }; ClusterStateService.applyClusterFullAssignOfApp($scope.app, request).success((data) => { if (data.code === 0 && data.data) { let failedServerSet = data.data.failedServerSet; let failedClientSet = data.data.failedClientSet; if (failedClientSet.length === 0 && failedServerSet.length === 0) { alert('全部推送成功'); } else { alert('推送完毕。token server 失败列表:' + JSON.stringify(failedServerSet) + '; token client 失败列表:' + JSON.stringify(failedClientSet)); } retrieveClusterAppInfo(); } else { if (data.code === UNSUPPORTED_CODE) { alert('该应用的 Sentinel 客户端不支持集群限流,请升级至 1.4.0 以上版本并引入相关依赖。'); } else { alert('推送失败:' + data.msg); } } }).error(() => { alert('未知错误'); }); }; }]); ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/scripts/controllers/cluster_app_server_list.js ================================================ var app = angular.module('sentinelDashboardApp'); app.controller('SentinelClusterAppServerListController', ['$scope', '$stateParams', 'ngDialog', 'MachineService', 'ClusterStateService', function ($scope, $stateParams, ngDialog, MachineService, ClusterStateService) { $scope.app = $stateParams.app; const UNSUPPORTED_CODE = 4041; const CLUSTER_MODE_CLIENT = 0; const CLUSTER_MODE_SERVER = 1; const DEFAULT_CLUSTER_SERVER_PORT = 18730; const DEFAULT_NAMESPACE = 'default'; const DEFAULT_MAX_ALLOWED_QPS = 20000; // tmp for dialog temporary data. $scope.tmp = { curClientChosen: [], curRemainingClientChosen: [], curChosenServer: {}, }; $scope.remainingMachineList = []; function convertSetToString(set) { if (set === undefined) { return ''; } if (set.length === 1 && set[0] === DEFAULT_NAMESPACE) { return DEFAULT_NAMESPACE; } let s = ''; for (let i = 0; i < set.length; i++) { let ns = set[i]; if (ns !== DEFAULT_NAMESPACE) { s = s + ns; if (i < set.length - 1) { s = s + ','; } } } return s; } function convertStrToNamespaceSet(str) { if (str === undefined || str === '') { return []; } let arr = []; let spliced = str.split(','); spliced.forEach((v) => { arr.push(v.trim()); }); return arr; } function processAppSingleData(data) { if (data.state.server && data.state.server.namespaceSet) { data.state.server.namespaceSetStr = convertSetToString(data.state.server.namespaceSet); data.mode = data.state.stateInfo.mode; } } function removeFromArr(arr, v) { for (let i = 0; i < arr.length; i++) { if (arr[i] === v) { arr.splice(i, 1); break; } } } function removeFromArrIf(arr, f) { for (let i = 0; i < arr.length; i++) { if (f(arr[i]) === true) { arr.splice(i, 1); break; } } } function resetAssignDialogChosen() { $scope.tmp.curClientChosen = []; $scope.tmp.curRemainingClientChosen = []; } function generateMachineId(e) { return e.ip + '@' + e.commandPort; } function applyClusterMap(appClusterMachineList) { if (!appClusterMachineList) { return; } let tmpMap = new Map(); let serverCommandPortMap = new Map(); $scope.clusterMap = []; $scope.remainingMachineList = []; let tmpServerList = []; let tmpClientList = []; appClusterMachineList.forEach((e) => { if (e.mode === CLUSTER_MODE_CLIENT) { tmpClientList.push(e); } else if (e.mode === CLUSTER_MODE_SERVER) { tmpServerList.push(e); } else { $scope.remainingMachineList.push(generateMachineId(e)); } }); tmpServerList.forEach((e) => { let ip = e.ip; let machineId = ip + '@' + e.commandPort; let group = { ip: ip, machineId: machineId, port: e.state.server.port, clientSet: [], namespaceSetStr: e.state.server.namespaceSetStr, maxAllowedQps: e.state.server.flow.maxAllowedQps, belongToApp: true, }; if (!tmpMap.has(machineId)) { tmpMap.set(machineId, group); } serverCommandPortMap.set(ip + ':' + e.state.server.port, e.commandPort); }); tmpClientList.forEach((e) => { let ip = e.ip; let machineId = ip + '@' + e.commandPort; let targetServer = e.state.client.clientConfig.serverHost; let targetPort = e.state.client.clientConfig.serverPort; if (targetServer === undefined || targetServer === '' || targetPort === undefined || targetPort <= 0) { $scope.remainingMachineList.push(generateMachineId(e)); return; } let serverHostPort = targetServer + ':' + targetPort; if (serverCommandPortMap.has(serverHostPort)) { let serverCommandPort = serverCommandPortMap.get(serverHostPort); let g; if (serverCommandPort < 0) { // Not belong to this app. g = tmpMap.get(serverHostPort); } else { // Belong to this app. g = tmpMap.get(targetServer + '@' + serverCommandPort); } g.clientSet.push(machineId); } else { let group = { ip: targetServer, machineId: serverHostPort, port: targetPort, clientSet: [machineId], belongToApp: false, }; tmpMap.set(serverHostPort, group); // Indicates that it's not belonging to current app. serverCommandPortMap.set(serverHostPort, -1); } // if (!tmpMap.has(serverHostPort)) { // let group = { // ip: targetServer, // machineId: targetServer, // port: targetPort, // clientSet: [machineId], // belongToApp: false, // }; // tmpMap.set(targetServer, group); // } else { // let g = tmpMap.get(targetServer); // g.clientSet.push(machineId); // } }); tmpMap.forEach((v) => { if (v !== undefined) { $scope.clusterMap.push(v); } }); } $scope.notChosenServer = (id) => { return id !== $scope.serverAssignDialogData.serverData.currentServer; }; $scope.onCurrentServerChange = () => { resetAssignDialogChosen(); }; $scope.moveToServerGroup = () => { $scope.tmp.curRemainingClientChosen.forEach(e => { $scope.serverAssignDialogData.serverData.clientSet.push(e); removeFromArr($scope.remainingMachineList, e); }); resetAssignDialogChosen(); }; $scope.moveToRemainingSharePool = () => { $scope.tmp.curClientChosen.forEach(e => { $scope.remainingMachineList.push(e); removeFromArr($scope.serverAssignDialogData.serverData.clientSet, e); }); resetAssignDialogChosen(); }; function parseIpFromMachineId(machineId) { if (machineId.indexOf(':') !== -1) { return machineId.split(':')[0]; } if (machineId.indexOf('@') === -1) { return machineId; } let arr = machineId.split('@'); return arr[0]; } function retrieveClusterAssignInfoOfApp() { ClusterStateService.fetchClusterUniversalStateOfApp($scope.app).success(function (data) { if (data.code === 0 && data.data) { $scope.loadError = undefined; $scope.appClusterMachineList = data.data; $scope.appClusterMachineList.forEach(processAppSingleData); applyClusterMap($scope.appClusterMachineList); } else { $scope.appClusterMachineList = {}; if (data.code === UNSUPPORTED_CODE) { $scope.loadError = {message: '该应用的 Sentinel 客户端不支持集群限流,请升级至 1.4.0 以上版本并引入相关依赖。'} } else { $scope.loadError = {message: data.msg}; } } }).error(() => { $scope.loadError = {message: '未知错误'}; }); } $scope.newServerDialog = () => { retrieveClusterAssignInfoOfApp(); $scope.serverAssignDialogData = { title: '新增 Token Server', type: 'add', confirmBtnText: '保存', serverData: { serverType: 0, clientSet: [], serverPort: DEFAULT_CLUSTER_SERVER_PORT, maxAllowedQps: DEFAULT_MAX_ALLOWED_QPS, } }; $scope.serverAssignDialog = ngDialog.open({ template: '/app/views/dialog/cluster/cluster-server-assign-dialog.html', width: 1000, overlay: true, scope: $scope }); }; $scope.modifyServerAssignConfig = (serverVO) => { let id = serverVO.id; ClusterStateService.fetchClusterUniversalStateOfApp($scope.app).success(function (data) { if (data.code === 0 && data.data) { $scope.loadError = undefined; $scope.appClusterMachineList = data.data; $scope.appClusterMachineList.forEach(processAppSingleData); applyClusterMap($scope.appClusterMachineList); let clusterMap = $scope.clusterMap; let d; for (let i = 0; i < clusterMap.length; i++) { if (clusterMap[i].machineId === id) { d = clusterMap[i]; } } if (!d) { alert('状态错误'); return; } $scope.serverAssignDialogData = { title: 'Token Server 分配编辑', type: 'edit', confirmBtnText: '保存', serverData: { currentServer: d.machineId, belongToApp: serverVO.belongToApp, serverPort: d.port, clientSet: d.clientSet, } }; if (d.maxAllowedQps !== undefined) { $scope.serverAssignDialogData.serverData.maxAllowedQps = d.maxAllowedQps; } $scope.serverAssignDialog = ngDialog.open({ template: '/app/views/dialog/cluster/cluster-server-assign-dialog.html', width: 1000, overlay: true, scope: $scope }); } else { if (data.code === UNSUPPORTED_CODE) { $scope.loadError = {message: '该应用的 Sentinel 客户端不支持集群限流,请升级至 1.4.0 以上版本并引入相关依赖。'} } else { $scope.loadError = {message: data.msg}; } } }).error(() => { $scope.loadError = {message: '未知错误'}; }); }; function getRemainingMachineList() { return $scope.remainingMachineList.filter((e) => $scope.notChosenServer(e)); } function doApplyNewSingleServerAssign() { let ok = confirm('是否确认执行变更?'); if (!ok) { return; } let serverData = $scope.serverAssignDialogData.serverData; let belongToApp = serverData.serverType == 0; // don't modify here! let machineId = serverData.currentServer; let request = { clusterMap: { machineId: machineId, ip: parseIpFromMachineId(machineId), port: serverData.serverPort, clientSet: serverData.clientSet, belongToApp: belongToApp, maxAllowedQps: serverData.maxAllowedQps, }, remainingList: getRemainingMachineList(), }; ClusterStateService.applyClusterSingleServerAssignOfApp($scope.app, request).success((data) => { if (data.code === 0 && data.data) { let failedServerSet = data.data.failedServerSet; let failedClientSet = data.data.failedClientSet; if (failedClientSet.length === 0 && failedServerSet.length === 0) { alert('全部推送成功'); } else { let failedSet = []; if (failedServerSet) { failedServerSet.forEach((e) => { failedSet.push(e); }); } if (failedClientSet) { failedClientSet.forEach((e) => { failedSet.push(e); }); } alert('推送完毕。失败机器列表:' + JSON.stringify(failedSet)); } location.reload(); } else { if (data.code === UNSUPPORTED_CODE) { alert('该应用的 Sentinel 客户端不支持集群限流,请升级至 1.4.0 以上版本并引入相关依赖。'); } else { alert('推送失败:' + data.msg); } } }).error(() => { alert('未知错误'); }); } function doApplySingleServerAssignEdit() { let ok = confirm('是否确认执行变更?'); if (!ok) { return; } let serverData = $scope.serverAssignDialogData.serverData; let machineId = serverData.currentServer; let request = { clusterMap: { machineId: machineId, ip: parseIpFromMachineId(machineId), port: serverData.serverPort, clientSet: serverData.clientSet, belongToApp: serverData.belongToApp, }, remainingList: $scope.remainingMachineList, }; if (serverData.maxAllowedQps !== undefined) { request.clusterMap.maxAllowedQps = serverData.maxAllowedQps; } ClusterStateService.applyClusterSingleServerAssignOfApp($scope.app, request).success((data) => { if (data.code === 0 && data.data) { let failedServerSet = data.data.failedServerSet; let failedClientSet = data.data.failedClientSet; if (failedClientSet.length === 0 && failedServerSet.length === 0) { alert('全部推送成功'); } else { let failedSet = []; failedServerSet.forEach(failedSet.push); failedClientSet.forEach(failedSet.push); alert('推送完毕。失败机器列表:' + JSON.stringify(failedSet)); } location.reload(); } else { if (data.code === UNSUPPORTED_CODE) { alert('该应用的 Sentinel 客户端不支持集群限流,请升级至 1.4.0 以上版本并引入相关依赖。'); } else { alert('推送失败:' + data.msg); } } }).error(() => { alert('未知错误'); }); } $scope.saveAssignForDialog = () => { if (!checkAssignDialogValid()) { return; } if ($scope.serverAssignDialogData.type === 'add') { doApplyNewSingleServerAssign(); } else if ($scope.serverAssignDialogData.type === 'edit') { doApplySingleServerAssignEdit(); } else { alert('未知的操作'); } }; function checkAssignDialogValid() { let serverData = $scope.serverAssignDialogData.serverData; if (serverData.currentServer === undefined || serverData.currentServer === '') { alert('请指定有效的 Token Server'); return false; } if (serverData.serverPort === undefined || serverData.serverPort <= 0 || serverData.serverPort > 65535) { alert('请输入合法的端口值'); return false; } if (serverData.maxAllowedQps !== undefined && serverData.maxAllowedQps < 0) { alert('请输入合法的最大允许 QPS'); return false; } return true; } $scope.viewConnectionDetail = (serverVO) => { $scope.connectionDetailDialogData = { serverData: serverVO }; $scope.connectionDetailDialog = ngDialog.open({ template: '/app/views/dialog/cluster/cluster-server-connection-detail-dialog.html', width: 700, overlay: true, scope: $scope }); }; function generateRequestLimitDataStr(limitData) { if (limitData.length === 1 && limitData[0].namespace === DEFAULT_NAMESPACE) { return 'default: ' + limitData[0].currentQps + ' / ' + limitData[0].maxAllowedQps; } for (let i = 0; i < limitData.length; i++) { let crl = limitData[i]; if (crl.namespace === $scope.app) { return '' + crl.currentQps + ' / ' + crl.maxAllowedQps; } } return '0'; } function processServerListData(serverVO) { if (serverVO.state && serverVO.state.namespaceSet) { serverVO.state.namespaceSetStr = convertSetToString(serverVO.state.namespaceSet); } if (serverVO.state && serverVO.state.requestLimitData) { serverVO.state.requestLimitDataStr = generateRequestLimitDataStr(serverVO.state.requestLimitData); } } $scope.generateConnectionSet = (data) => { let connectionSet = data; let s = ''; if (connectionSet) { s = s + '['; for (let i = 0; i < connectionSet.length; i++) { s = s + connectionSet[i].address; if (i < connectionSet.length - 1) { s = s + ', '; } } s = s + ']'; } else { s = '[]'; } return s; }; function retrieveClusterServerInfo() { ClusterStateService.fetchClusterServerStateOfApp($scope.app).success(function (data) { if (data.code === 0 && data.data) { $scope.loadError = undefined; $scope.serverVOList = data.data; $scope.serverVOList.forEach(processServerListData); } else { $scope.serverVOList = {}; if (data.code === UNSUPPORTED_CODE) { $scope.loadError = {message: '该应用的 Sentinel 客户端不支持集群限流,请升级至 1.4.0 以上版本并引入相关依赖。'} } else { $scope.loadError = {message: data.msg}; } } }).error(() => { $scope.loadError = {message: '未知错误'}; }); } retrieveClusterServerInfo(); let confirmUnbindServerDialog; $scope.unbindServer = (id) => { $scope.pendingUnbindIds = [id]; $scope.confirmDialog = { title: '移除 Token Server', type: 'unbind_token_server', attentionTitle: '请确认是否移除以下 Token Server(该 server 下的 client 也会解除分配)', attention: id + '', confirmBtnText: '移除', }; confirmUnbindServerDialog = ngDialog.open({ template: '/app/views/dialog/confirm-dialog.html', scope: $scope, overlay: true }); }; function apiUnbindServerAssign(ids) { ClusterStateService.applyClusterServerBatchUnbind($scope.app, ids).success((data) => { if (data.code === 0 && data.data) { let failedServerSet = data.data.failedServerSet; let failedClientSet = data.data.failedClientSet; if (failedClientSet.length === 0 && failedServerSet.length === 0) { alert('成功'); } else { alert('操作推送完毕,部分失败机器列表:' + JSON.stringify(failedClientSet)); } location.reload(); } else { if (data.code === UNSUPPORTED_CODE) { alert('该应用的 Sentinel 客户端不支持集群限流,请升级至 1.4.0 以上版本并引入相关依赖。'); } else { alert('推送失败:' + data.msg); } } }).error(() => { alert('未知错误'); }); // confirmUnbindServerDialog.close(); } // Confirm function for confirm dialog. $scope.confirm = () => { if ($scope.confirmDialog.type === 'unbind_token_server') { apiUnbindServerAssign($scope.pendingUnbindIds); } else { console.error('Error dialog when unbinding token server'); } }; }]); ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/scripts/controllers/cluster_app_server_manage.js ================================================ var app = angular.module('sentinelDashboardApp'); app.controller('SentinelClusterAppAssignManageController', ['$scope', '$stateParams', 'ngDialog', 'MachineService', 'ClusterStateService', function ($scope, $stateParams, ngDialog, MachineService, ClusterStateService) { $scope.app = $stateParams.app; const UNSUPPORTED_CODE = 4041; const CLUSTER_MODE_CLIENT = 0; const CLUSTER_MODE_SERVER = 1; const DEFAULT_CLUSTER_SERVER_PORT = 18730; $scope.tmp = { curClientChosen: [], curRemainingClientChosen: [], curChosenServer: {}, }; function convertSetToString(set) { if (set === undefined) { return ''; } let s = ''; for (let i = 0; i < set.length; i++) { s = s + set[i]; if (i < set.length - 1) { s = s + ','; } } return s; } function convertStrToNamespaceSet(str) { if (str === undefined || str === '') { return []; } let arr = []; let spliced = str.split(','); spliced.forEach((v) => { arr.push(v.trim()); }); return arr; } function processAppSingleData(data) { if (data.state.server && data.state.server.namespaceSet) { data.state.server.namespaceSetStr = convertSetToString(data.state.server.namespaceSet); data.mode = data.state.stateInfo.mode; } } function removeFromArr(arr, v) { for (let i = 0; i < arr.length; i++) { if (arr[i] === v) { arr.splice(i, 1); break; } } } function resetChosen() { $scope.tmp.curClientChosen = []; $scope.tmp.curRemainingClientChosen = []; } function generateMachineId(e) { return e.ip + '@' + e.commandPort; } function applyClusterMap(appClusterMachineList) { if (!appClusterMachineList) { return; } let tmpMap = new Map(); $scope.clusterMap = []; $scope.remainingClientAddressList = []; let tmpServerList = []; let tmpClientList = []; appClusterMachineList.forEach((e) => { if (e.mode === CLUSTER_MODE_CLIENT) { tmpClientList.push(e); } else if (e.mode === CLUSTER_MODE_SERVER) { tmpServerList.push(e); } else { $scope.remainingClientAddressList.push(generateMachineId(e)); } }); tmpServerList.forEach((e) => { let ip = e.ip; let machineId = ip + '@' + e.commandPort; let group = { ip: ip, machineId: machineId, port: e.state.server.port, clientSet: [], namespaceSetStr: e.state.server.namespaceSetStr, belongToApp: true, }; if (!tmpMap.has(ip)) { tmpMap.set(ip, group); } }); tmpClientList.forEach((e) => { let ip = e.ip; let machineId = ip + '@' + e.commandPort; let targetServer = e.state.client.clientConfig.serverHost; let targetPort = e.state.client.clientConfig.serverPort; if (targetServer === undefined || targetServer === '' || targetPort === undefined || targetPort <= 0) { $scope.remainingClientAddressList.push(generateMachineId(e)); return; } if (!tmpMap.has(targetServer)) { let group = { ip: targetServer, machineId: targetServer, port: targetPort, clientSet: [machineId], belongToApp: false, }; tmpMap.set(targetServer, group); } else { let g = tmpMap.get(targetServer); g.clientSet.push(machineId); } }); tmpMap.forEach((v) => { if (v !== undefined) { $scope.clusterMap.push(v); } }); } $scope.onCurrentServerChange = () => { resetChosen(); }; $scope.remainingClientAddressList = []; $scope.moveToServerGroup = () => { let chosenServer = $scope.tmp.curChosenServer; if (!chosenServer || !chosenServer.machineId) { return; } $scope.tmp.curRemainingClientChosen.forEach(e => { chosenServer.clientSet.push(e); removeFromArr($scope.remainingClientAddressList, e); }); resetChosen(); }; $scope.moveToRemainingSharePool = () => { $scope.tmp.curClientChosen.forEach(e => { $scope.remainingClientAddressList.push(e); removeFromArr($scope.tmp.curChosenServer.clientSet, e); }); resetChosen(); }; function parseIpFromMachineId(machineId) { if (machineId.indexOf('@') === -1) { return machineId; } let arr = machineId.split('@'); return arr[0]; } $scope.addToServerList = () => { let group; $scope.tmp.curRemainingClientChosen.forEach(e => { group = { machineId: e, ip: parseIpFromMachineId(e), port: DEFAULT_CLUSTER_SERVER_PORT, clientSet: [], namespaceSetStr: 'default,' + $scope.app, belongToApp: true, }; $scope.clusterMap.push(group); removeFromArr($scope.remainingClientAddressList, e); $scope.tmp.curChosenServer = group; }); resetChosen(); }; $scope.removeFromServerList = () => { let chosenServer = $scope.tmp.curChosenServer; if (!chosenServer || !chosenServer.machineId) { return; } chosenServer.clientSet.forEach((e) => { if (e !== undefined) { $scope.remainingClientAddressList.push(e); } }); if (chosenServer.belongToApp || chosenServer.machineId.indexOf('@') !== -1) { $scope.remainingClientAddressList.push(chosenServer.machineId); } else { alert('提示:非本应用内机器将不会置回空闲列表中'); } removeFromArr($scope.clusterMap, chosenServer); resetChosen(); if ($scope.clusterMap.length > 0) { $scope.tmp.curChosenServer = $scope.clusterMap[0]; $scope.onCurrentServerChange(); } else { $scope.tmp.curChosenServer = {}; } }; function retrieveClusterAppInfo() { ClusterStateService.fetchClusterUniversalStateOfApp($scope.app).success(function (data) { if (data.code === 0 && data.data) { $scope.loadError = undefined; $scope.appClusterMachineList = data.data; $scope.appClusterMachineList.forEach(processAppSingleData); applyClusterMap($scope.appClusterMachineList); if ($scope.clusterMap.length > 0) { $scope.tmp.curChosenServer = $scope.clusterMap[0]; $scope.onCurrentServerChange(); } } else { $scope.appClusterMachineList = {}; if (data.code === UNSUPPORTED_CODE) { $scope.loadError = {message: '该应用的 Sentinel 客户端不支持集群限流,请升级至 1.4.0 以上版本并引入相关依赖。'} } else { $scope.loadError = {message: data.msg}; } } }).error(() => { $scope.loadError = {message: '未知错误'}; }); } retrieveClusterAppInfo(); $scope.saveAndApplyAssign = () => { let ok = confirm('是否确认执行变更?'); if (!ok) { return; } let cm = $scope.clusterMap; if (!cm) { cm = []; } cm.forEach((e) => { e.namespaceSet = convertStrToNamespaceSet(e.namespaceSetStr); }); cm.namespaceSet = convertStrToNamespaceSet(cm.namespaceSetStr); let request = { clusterMap: cm, remainingList: $scope.remainingClientAddressList, }; ClusterStateService.applyClusterFullAssignOfApp($scope.app, request).success((data) => { if (data.code === 0 && data.data) { let failedServerSet = data.data.failedServerSet; let failedClientSet = data.data.failedClientSet; if (failedClientSet.length === 0 && failedServerSet.length === 0) { alert('全部推送成功'); } else { alert('推送完毕。token server 失败列表:' + JSON.stringify(failedServerSet) + '; token client 失败列表:' + JSON.stringify(failedClientSet)); } retrieveClusterAppInfo(); } else { if (data.code === UNSUPPORTED_CODE) { alert('该应用的 Sentinel 客户端不支持集群限流,请升级至 1.4.0 以上版本并引入相关依赖。'); } else { alert('推送失败:' + data.msg); } } }).error(() => { alert('未知错误'); }); }; }]); ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/scripts/controllers/cluster_app_server_monitor.js ================================================ var app = angular.module('sentinelDashboardApp'); app.controller('SentinelClusterAppServerMonitorController', ['$scope', '$stateParams', 'ngDialog', 'MachineService', 'ClusterStateService', function ($scope, $stateParams, ngDialog, MachineService, ClusterStateService) { $scope.app = $stateParams.app; const UNSUPPORTED_CODE = 4041; const CLUSTER_MODE_SERVER = 1; $scope.tmp = { curChosenServer: {}, }; function convertSetToString(set) { if (set === undefined) { return ''; } let s = ''; for (let i = 0; i < set.length; i++) { s = s + set[i]; if (i < set.length - 1) { s = s + ','; } } return s; } function processServerData(serverVO) { if (serverVO.state && serverVO.state.namespaceSet) { serverVO.state.namespaceSetStr = convertSetToString(serverVO.state.namespaceSet); } } $scope.generateConnectionSet = (data) => { let connectionSet = data; let s = ''; if (connectionSet) { s = s + '['; for (let i = 0; i < connectionSet.length; i++) { s = s + connectionSet[i].address; if (i < connectionSet.length - 1) { s = s + ', '; } } s = s + ']'; } else { s = '[]'; } return s; }; $scope.onChosenServerChange = () => { }; function retrieveClusterServerInfo() { ClusterStateService.fetchClusterServerStateOfApp($scope.app).success(function (data) { if (data.code === 0 && data.data) { $scope.loadError = undefined; $scope.serverVOList = data.data; $scope.serverVOList.forEach(processServerData); if ($scope.serverVOList.length > 0) { $scope.tmp.curChosenServer = $scope.serverVOList[0]; $scope.onChosenServerChange(); } } else { $scope.serverVOList = {}; if (data.code === UNSUPPORTED_CODE) { $scope.loadError = {message: '该应用的 Sentinel 客户端不支持集群限流,请升级至 1.4.0 以上版本并引入相关依赖。'} } else { $scope.loadError = {message: data.msg}; } } }).error(() => { $scope.loadError = {message: '未知错误'}; }); } retrieveClusterServerInfo(); $scope.macsInputConfig = { searchField: ['text', 'value'], persist: true, create: false, maxItems: 1, render: { item: function (data, escape) { return '
    ' + escape(data.text) + '
    '; } }, onChange: function (value, oldValue) { $scope.macInputModel = value; } }; }]); ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/scripts/controllers/cluster_app_token_client_list.js ================================================ var app = angular.module('sentinelDashboardApp'); app.controller('SentinelClusterAppTokenClientListController', ['$scope', '$stateParams', 'ngDialog', 'MachineService', 'ClusterStateService', function ($scope, $stateParams, ngDialog, MachineService, ClusterStateService) { $scope.app = $stateParams.app; const UNSUPPORTED_CODE = 4041; const CLUSTER_MODE_CLIENT = 0; const CLUSTER_MODE_SERVER = 1; function processClientData(clientVO) { } $scope.modifyClientConfigDialog = (clientVO) => { if (!clientVO) { return; } $scope.ccDialogData = { ip: clientVO.ip, commandPort: clientVO.commandPort, clientId: clientVO.id, serverHost: clientVO.state.clientConfig.serverHost, serverPort: clientVO.state.clientConfig.serverPort, requestTimeout: clientVO.state.clientConfig.requestTimeout, }; $scope.ccDialog = ngDialog.open({ template: '/app/views/dialog/cluster/cluster-client-config-dialog.html', width: 700, overlay: true, scope: $scope }); }; function checkValidClientConfig(config) { if (!config.serverHost || config.serverHost.trim() == '') { alert('请输入有效的 Token Server IP'); return false; } if (config.serverPort === undefined || config.serverPort <= 0 || config.serverPort > 65535) { alert('请输入有效的 Token Server 端口'); return false; } if (config.requestTimeout === undefined || config.requestTimeout <= 0) { alert('请输入有效的请求超时时长'); return false; } return true; } $scope.doModifyClientConfig = () => { if (!checkValidClientConfig($scope.ccDialogData)) { return; } let id = $scope.ccDialogData.id; let request = { app: $scope.app, ip: $scope.ccDialogData.ip, port: $scope.ccDialogData.commandPort, mode: CLUSTER_MODE_CLIENT, clientConfig: { serverHost: $scope.ccDialogData.serverHost, serverPort: $scope.ccDialogData.serverPort, requestTimeout: $scope.ccDialogData.requestTimeout, } }; ClusterStateService.modifyClusterConfig(request).success((data) => { if (data.code === 0 && data.data) { alert('修改 Token Client 配置成功'); window.location.reload(); } else { if (data.code === UNSUPPORTED_CODE) { alert('机器 ' + id + ' 的 Sentinel 没有引入集群限流客户端,请升级至 1.4.0 以上版本并引入相关依赖。'); } else { alert('修改失败:' + data.msg); } } }).error((data, header, config, status) => { alert('未知错误'); }); }; function retrieveClusterTokenClientInfo() { ClusterStateService.fetchClusterClientStateOfApp($scope.app) .success((data) => { if (data.code === 0 && data.data) { $scope.loadError = undefined; $scope.clientVOList = data.data; $scope.clientVOList.forEach(processClientData); } else { $scope.clientVOList = []; if (data.code === UNSUPPORTED_CODE) { $scope.loadError = {message: '该应用的 Sentinel 客户端不支持集群限流,请升级至 1.4.0 以上版本并引入相关依赖。'} } else { $scope.loadError = {message: data.msg}; } } }) .error(() => { $scope.loadError = {message: '未知错误'}; }); } retrieveClusterTokenClientInfo(); $scope.macsInputConfig = { searchField: ['text', 'value'], persist: true, create: false, maxItems: 1, render: { item: function (data, escape) { return '
    ' + escape(data.text) + '
    '; } }, onChange: function (value, oldValue) { $scope.macInputModel = value; } }; }]); ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/scripts/controllers/cluster_single.js ================================================ var app = angular.module('sentinelDashboardApp'); app.controller('SentinelClusterSingleController', ['$scope', '$stateParams', 'ngDialog', 'MachineService', 'ClusterStateService', function ($scope, $stateParams, ngDialog, MachineService, ClusterStateService) { $scope.app = $stateParams.app; const UNSUPPORTED_CODE = 4041; const CLUSTER_MODE_CLIENT = 0; const CLUSTER_MODE_SERVER = 1; $scope.macsInputConfig = { searchField: ['text', 'value'], persist: true, create: false, maxItems: 1, render: { item: function (data, escape) { return '
    ' + escape(data.text) + '
    '; } }, onChange: function (value, oldValue) { $scope.macInputModel = value; } }; function convertSetToString(set) { if (set === undefined) { return ''; } let s = ''; for (let i = 0; i < set.length; i++) { s = s + set[i]; if (i < set.length - 1) { s = s + ','; } } return s; } function convertStrToNamespaceSet(str) { if (str === undefined || str === '') { return []; } let arr = []; let spliced = str.split(','); spliced.forEach((v) => { arr.push(v.trim()); }); return arr; } function fetchMachineClusterState() { if (!$scope.macInputModel || $scope.macInputModel === '') { return; } let mac = $scope.macInputModel.split(':'); ClusterStateService.fetchClusterUniversalStateSingle($scope.app, mac[0], mac[1]).success(function (data) { if (data.code == 0 && data.data) { $scope.loadError = undefined; $scope.stateVO = data.data; $scope.stateVO.currentMode = $scope.stateVO.stateInfo.mode; if ($scope.stateVO.server && $scope.stateVO.server.namespaceSet) { $scope.stateVO.server.namespaceSetStr = convertSetToString($scope.stateVO.server.namespaceSet); } } else { $scope.stateVO = {}; if (data.code === UNSUPPORTED_CODE) { $scope.loadError = {message: '机器 ' + mac[0] + ':' + mac[1] + ' 的 Sentinel 客户端版本不支持集群限流,请升级至 1.4.0 以上版本并引入相关依赖。'} } else { $scope.loadError = {message: data.msg}; } } }).error((data, header, config, status) => { $scope.loadError = {message: '未知错误'}; }); } fetchMachineClusterState(); function checkValidClientConfig(stateVO) { if (!stateVO.client || !stateVO.client.clientConfig) { alert('不合法的配置'); return false; } let config = stateVO.client.clientConfig; if (!config.serverHost || config.serverHost.trim() == '') { alert('请输入有效的 Token Server IP'); return false; } if (config.serverPort === undefined || config.serverPort <= 0 || config.serverPort > 65535) { alert('请输入有效的 Token Server 端口'); return false; } if (config.requestTimeout === undefined || config.requestTimeout <= 0) { alert('请输入有效的请求超时时长'); return false; } return true; } function sendClusterClientRequest(stateVO) { if (!checkValidClientConfig(stateVO)) { return; } if (!$scope.macInputModel) { return; } let mac = $scope.macInputModel.split(':'); let request = { app: $scope.app, ip: mac[0], port: mac[1], }; request.mode = CLUSTER_MODE_CLIENT; request.clientConfig = stateVO.client.clientConfig; ClusterStateService.modifyClusterConfig(request).success(function (data) { if (data.code == 0 && data.data) { alert('修改集群限流客户端配置成功'); window.location.reload(); } else { if (data.code === UNSUPPORTED_CODE) { alert('机器 ' + mac[0] + ':' + mac[1] + ' 的 Sentinel 客户端版本不支持集群限流客户端,请升级至 1.4.0 以上版本并引入相关依赖。'); } else { alert('修改失败:' + data.msg); } } }).error((data, header, config, status) => { alert('未知错误'); }); } function checkValidServerConfig(stateVO) { if (!stateVO.server || !stateVO.server.transport) { alert('不合法的配置'); return false; } if (stateVO.server.namespaceSetStr === undefined || stateVO.server.namespaceSetStr == '') { alert('请输入有效的命名空间集合(多个 namespace 以 , 分隔)'); return false; } let transportConfig = stateVO.server.transport; if (transportConfig.port === undefined || transportConfig.port <= 0 || transportConfig.port > 65535) { alert('请输入有效的 Token Server 端口'); return false; } let flowConfig = stateVO.server.flow; if (flowConfig.maxAllowedQps === undefined || flowConfig.maxAllowedQps < 0) { alert('请输入有效的最大允许 QPS'); return false; } // if (transportConfig.idleSeconds === undefined || transportConfig.idleSeconds <= 0) { // alert('请输入有效的连接清理时长 (idleSeconds)'); // return false; // } return true; } function sendClusterServerRequest(stateVO) { if (!checkValidServerConfig(stateVO)) { return; } if (!$scope.macInputModel) { return; } let mac = $scope.macInputModel.split(':'); let request = { app: $scope.app, ip: mac[0], port: mac[1], }; request.mode = CLUSTER_MODE_SERVER; request.flowConfig = stateVO.server.flow; request.transportConfig = stateVO.server.transport; request.namespaceSet = convertStrToNamespaceSet(stateVO.server.namespaceSetStr); ClusterStateService.modifyClusterConfig(request).success(function (data) { if (data.code == 0 && data.data) { alert('修改集群限流服务端配置成功'); window.location.reload(); } else { if (data.code === UNSUPPORTED_CODE) { alert('机器 ' + mac[0] + ':' + mac[1] + ' 的 Sentinel 客户端版本不支持集群限流服务端,请升级至 1.4.0 以上版本并引入相关依赖。'); } else { alert('修改失败:' + data.msg); } } }).error((data, header, config, status) => { alert('未知错误'); }); } $scope.saveConfig = () => { let ok = confirm('是否确定修改集群限流配置?'); if (!ok) { return; } let mode = $scope.stateVO.stateInfo.mode; if (mode != 1 && mode != 0) { alert('未知的集群限流模式'); return; } if (mode == 0) { sendClusterClientRequest($scope.stateVO); } else { sendClusterServerRequest($scope.stateVO); } }; function queryAppMachines() { MachineService.getAppMachines($scope.app).success( function (data) { if (data.code === 0) { // $scope.machines = data.data; if (data.data) { $scope.machines = []; $scope.macsInputOptionsOrigin = []; $scope.macsInputOptions = []; data.data.forEach(function (item) { if (item.healthy) { $scope.macsInputOptionsOrigin.push({ text: item.ip + ':' + item.port, value: item.ip + ':' + item.port }); } }); $scope.macsInputOptions = $scope.macsInputOptionsOrigin; } if ($scope.macsInputOptions.length > 0) { $scope.macInputModel = $scope.macsInputOptions[0].value; } } else { $scope.macsInputOptions = []; } } ); } queryAppMachines(); $scope.$watch('searchKey', function () { if (!$scope.macsInputOptions) { return; } if ($scope.searchKey) { $scope.macsInputOptions = $scope.macsInputOptionsOrigin .filter((e) => e.value.indexOf($scope.searchKey) !== -1); } else { $scope.macsInputOptions = $scope.macsInputOptionsOrigin; } if ($scope.macsInputOptions.length > 0) { $scope.macInputModel = $scope.macsInputOptions[0].value; } else { $scope.macInputModel = ''; } }); $scope.$watch('macInputModel', function () { if ($scope.macInputModel) { fetchMachineClusterState(); } }); }]); ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/scripts/controllers/degrade.js ================================================ var app = angular.module('sentinelDashboardApp'); app.controller('DegradeCtl', ['$scope', '$stateParams', 'DegradeService', 'ngDialog', 'MachineService', function ($scope, $stateParams, DegradeService, ngDialog, MachineService) { //初始化 $scope.app = $stateParams.app; $scope.rulesPageConfig = { pageSize: 10, currentPageIndex: 1, totalPage: 1, totalCount: 0, }; $scope.macsInputConfig = { searchField: ['text', 'value'], persist: true, create: false, maxItems: 1, render: { item: function (data, escape) { return '
    ' + escape(data.text) + '
    '; } }, onChange: function (value, oldValue) { $scope.macInputModel = value; } }; getMachineRules(); function getMachineRules() { if (!$scope.macInputModel) { return; } var mac = $scope.macInputModel.split(':'); DegradeService.queryMachineRules($scope.app, mac[0], mac[1]).success( function (data) { if (data.code == 0 && data.data) { $scope.rules = data.data; $scope.rulesPageConfig.totalCount = $scope.rules.length; } else { $scope.rules = []; $scope.rulesPageConfig.totalCount = 0; } }); }; $scope.getMachineRules = getMachineRules; var degradeRuleDialog; $scope.editRule = function (rule) { $scope.currentRule = angular.copy(rule); $scope.degradeRuleDialog = { title: '编辑降级规则', type: 'edit', confirmBtnText: '保存' }; degradeRuleDialog = ngDialog.open({ template: '/app/views/dialog/degrade-rule-dialog.html', width: 680, overlay: true, scope: $scope }); }; $scope.addNewRule = function () { var mac = $scope.macInputModel.split(':'); $scope.currentRule = { grade: 0, app: $scope.app, ip: mac[0], port: mac[1], limitApp: 'default' }; $scope.degradeRuleDialog = { title: '新增降级规则', type: 'add', confirmBtnText: '新增' }; degradeRuleDialog = ngDialog.open({ template: '/app/views/dialog/degrade-rule-dialog.html', width: 680, overlay: true, scope: $scope }); }; $scope.saveRule = function () { if (!DegradeService.checkRuleValid($scope.currentRule)) { return; } if ($scope.degradeRuleDialog.type === 'add') { addNewRule($scope.currentRule); } else if ($scope.degradeRuleDialog.type === 'edit') { saveRule($scope.currentRule, true); } }; function parseDegradeMode(grade) { switch (grade) { case 0: return 'RT'; case 1: return '异常比例'; case 2: return '异常数'; default: return '未知'; } } var confirmDialog; $scope.deleteRule = function (rule) { $scope.currentRule = rule; $scope.confirmDialog = { title: '删除降级规则', type: 'delete_rule', attentionTitle: '请确认是否删除如下降级规则', attention: '资源名: ' + rule.resource + ', 降级模式: ' + parseDegradeMode(rule.grade) + ', 阈值: ' + rule.count, confirmBtnText: '删除', }; confirmDialog = ngDialog.open({ template: '/app/views/dialog/confirm-dialog.html', scope: $scope, overlay: true }); }; $scope.confirm = function () { if ($scope.confirmDialog.type == 'delete_rule') { deleteRule($scope.currentRule); } else { console.error('error'); } }; function deleteRule(rule) { DegradeService.deleteRule(rule).success(function (data) { if (data.code == 0) { getMachineRules(); confirmDialog.close(); } else { alert('失败!'); } }); }; function addNewRule(rule) { DegradeService.newRule(rule).success(function (data) { if (data.code == 0) { getMachineRules(); degradeRuleDialog.close(); } else { alert('失败!'); } }); }; function saveRule(rule, edit) { DegradeService.saveRule(rule).success(function (data) { if (data.code == 0) { getMachineRules(); if (edit) { degradeRuleDialog.close(); } else { confirmDialog.close(); } } else { alert('失败!'); } }); } queryAppMachines(); function queryAppMachines() { MachineService.getAppMachines($scope.app).success( function (data) { if (data.code == 0) { // $scope.machines = data.data; if (data.data) { $scope.machines = []; $scope.macsInputOptions = []; data.data.forEach(function (item) { if (item.healthy) { $scope.macsInputOptions.push({ text: item.ip + ':' + item.port, value: item.ip + ':' + item.port }); } }); } if ($scope.macsInputOptions.length > 0) { $scope.macInputModel = $scope.macsInputOptions[0].value; } } else { $scope.macsInputOptions = []; } } ); }; $scope.$watch('macInputModel', function () { if ($scope.macInputModel) { getMachineRules(); } }); }]); ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/scripts/controllers/flow_v1.js ================================================ var app = angular.module('sentinelDashboardApp'); app.controller('FlowControllerV1', ['$scope', '$stateParams', 'FlowServiceV1', 'ngDialog', 'MachineService', function ($scope, $stateParams, FlowService, ngDialog, MachineService) { $scope.app = $stateParams.app; $scope.rulesPageConfig = { pageSize: 10, currentPageIndex: 1, totalPage: 1, totalCount: 0, }; $scope.macsInputConfig = { searchField: ['text', 'value'], persist: true, create: false, maxItems: 1, render: { item: function (data, escape) { return '
    ' + escape(data.text) + '
    '; } }, onChange: function (value, oldValue) { $scope.macInputModel = value; } }; $scope.generateThresholdTypeShow = (rule) => { if (!rule.clusterMode) { return '单机'; } if (rule.clusterConfig.thresholdType === 0) { return '集群均摊'; } else if (rule.clusterConfig.thresholdType === 1) { return '集群总体'; } else { return '集群'; } }; getMachineRules(); function getMachineRules() { if (!$scope.macInputModel) { return; } var mac = $scope.macInputModel.split(':'); FlowService.queryMachineRules($scope.app, mac[0], mac[1]).success( function (data) { if (data.code == 0 && data.data) { $scope.rules = data.data; $scope.rulesPageConfig.totalCount = $scope.rules.length; } else { $scope.rules = []; $scope.rulesPageConfig.totalCount = 0; } }); }; $scope.getMachineRules = getMachineRules; var flowRuleDialog; $scope.editRule = function (rule) { $scope.currentRule = angular.copy(rule); $scope.flowRuleDialog = { title: '编辑流控规则', type: 'edit', confirmBtnText: '保存', showAdvanceButton: rule.controlBehavior == 0 && rule.strategy == 0 }; flowRuleDialog = ngDialog.open({ template: '/app/views/dialog/flow-rule-dialog.html', width: 680, overlay: true, scope: $scope }); }; $scope.addNewRule = function () { var mac = $scope.macInputModel.split(':'); $scope.currentRule = { grade: 1, strategy: 0, controlBehavior: 0, app: $scope.app, ip: mac[0], port: mac[1], limitApp: 'default', clusterMode: false, clusterConfig: { thresholdType: 0 } }; $scope.flowRuleDialog = { title: '新增流控规则', type: 'add', confirmBtnText: '新增', showAdvanceButton: true, }; flowRuleDialog = ngDialog.open({ template: '/app/views/dialog/flow-rule-dialog.html', width: 680, overlay: true, scope: $scope }); }; $scope.saveRule = function () { if (!FlowService.checkRuleValid($scope.currentRule)) { return; } if ($scope.flowRuleDialog.type === 'add') { addNewRule($scope.currentRule); } else if ($scope.flowRuleDialog.type === 'edit') { saveRule($scope.currentRule, true); } }; var confirmDialog; $scope.deleteRule = function (rule) { $scope.currentRule = rule; $scope.confirmDialog = { title: '删除流控规则', type: 'delete_rule', attentionTitle: '请确认是否删除如下流控规则', attention: '资源名: ' + rule.resource + ', 流控应用: ' + rule.limitApp + ', 阈值类型: ' + (rule.grade == 0 ? '线程数' : 'QPS') + ', 阈值: ' + rule.count, confirmBtnText: '删除', }; confirmDialog = ngDialog.open({ template: '/app/views/dialog/confirm-dialog.html', scope: $scope, overlay: true }); }; $scope.confirm = function () { if ($scope.confirmDialog.type === 'delete_rule') { deleteRule($scope.currentRule); } else { console.error('error'); } }; function deleteRule(rule) { FlowService.deleteRule(rule).success(function (data) { if (data.code == 0) { getMachineRules(); confirmDialog.close(); } else { alert('失败!'); } }); }; function addNewRule(rule) { FlowService.newRule(rule).success(function (data) { if (data.code == 0) { getMachineRules(); flowRuleDialog.close(); } else { alert('失败!'); } }); }; $scope.onOpenAdvanceClick = function () { $scope.flowRuleDialog.showAdvanceButton = false; }; $scope.onCloseAdvanceClick = function () { $scope.flowRuleDialog.showAdvanceButton = true; }; function saveRule(rule, edit) { FlowService.saveRule(rule).success(function (data) { if (data.code == 0) { getMachineRules(); if (edit) { flowRuleDialog.close(); } else { confirmDialog.close(); } } else { alert('失败!'); } }); } queryAppMachines(); function queryAppMachines() { MachineService.getAppMachines($scope.app).success( function (data) { if (data.code == 0) { // $scope.machines = data.data; if (data.data) { $scope.machines = []; $scope.macsInputOptions = []; data.data.forEach(function (item) { if (item.healthy) { $scope.macsInputOptions.push({ text: item.ip + ':' + item.port, value: item.ip + ':' + item.port }); } }); } if ($scope.macsInputOptions.length > 0) { $scope.macInputModel = $scope.macsInputOptions[0].value; } } else { $scope.macsInputOptions = []; } } ); }; $scope.$watch('macInputModel', function () { if ($scope.macInputModel) { getMachineRules(); } }); }]); ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/scripts/controllers/flow_v2.js ================================================ var app = angular.module('sentinelDashboardApp'); app.controller('FlowControllerV2', ['$scope', '$stateParams', 'FlowServiceV2', 'ngDialog', 'MachineService', function ($scope, $stateParams, FlowService, ngDialog, MachineService) { $scope.app = $stateParams.app; $scope.rulesPageConfig = { pageSize: 10, currentPageIndex: 1, totalPage: 1, totalCount: 0, }; $scope.macsInputConfig = { searchField: ['text', 'value'], persist: true, create: false, maxItems: 1, render: { item: function (data, escape) { return '
    ' + escape(data.text) + '
    '; } }, onChange: function (value, oldValue) { $scope.macInputModel = value; } }; $scope.generateThresholdTypeShow = (rule) => { if (!rule.clusterMode) { return '单机'; } if (rule.clusterConfig.thresholdType === 0) { return '集群均摊'; } else if (rule.clusterConfig.thresholdType === 1) { return '集群总体'; } else { return '集群'; } }; getMachineRules(); function getMachineRules() { if (!$scope.macInputModel) { return; } var mac = $scope.macInputModel.split(':'); FlowService.queryMachineRules($scope.app, mac[0], mac[1]).success( function (data) { if (data.code == 0 && data.data) { $scope.rules = data.data; $scope.rulesPageConfig.totalCount = $scope.rules.length; } else { $scope.rules = []; $scope.rulesPageConfig.totalCount = 0; } }); }; $scope.getMachineRules = getMachineRules; var flowRuleDialog; $scope.editRule = function (rule) { $scope.currentRule = angular.copy(rule); $scope.flowRuleDialog = { title: '编辑流控规则', type: 'edit', confirmBtnText: '保存', showAdvanceButton: rule.controlBehavior == 0 && rule.strategy == 0 }; flowRuleDialog = ngDialog.open({ template: '/app/views/dialog/flow-rule-dialog.html', width: 680, overlay: true, scope: $scope }); }; $scope.addNewRule = function () { var mac = $scope.macInputModel.split(':'); $scope.currentRule = { grade: 1, strategy: 0, controlBehavior: 0, app: $scope.app, ip: mac[0], port: mac[1], limitApp: 'default', clusterMode: false, clusterConfig: { thresholdType: 0, fallbackToLocalWhenFail: true } }; $scope.flowRuleDialog = { title: '新增流控规则', type: 'add', confirmBtnText: '新增', showAdvanceButton: true, }; flowRuleDialog = ngDialog.open({ template: '/app/views/dialog/flow-rule-dialog.html', width: 680, overlay: true, scope: $scope }); }; $scope.saveRule = function () { if (!FlowService.checkRuleValid($scope.currentRule)) { return; } if ($scope.flowRuleDialog.type === 'add') { addNewRule($scope.currentRule); } else if ($scope.flowRuleDialog.type === 'edit') { saveRule($scope.currentRule, true); } }; var confirmDialog; $scope.deleteRule = function (rule) { $scope.currentRule = rule; $scope.confirmDialog = { title: '删除流控规则', type: 'delete_rule', attentionTitle: '请确认是否删除如下流控规则', attention: '资源名: ' + rule.resource + ', 流控应用: ' + rule.limitApp + ', 阈值类型: ' + (rule.grade == 0 ? '线程数' : 'QPS') + ', 阈值: ' + rule.count, confirmBtnText: '删除', }; confirmDialog = ngDialog.open({ template: '/app/views/dialog/confirm-dialog.html', scope: $scope, overlay: true }); }; $scope.confirm = function () { if ($scope.confirmDialog.type === 'delete_rule') { deleteRule($scope.currentRule); } else { console.error('error'); } }; function deleteRule(rule) { FlowService.deleteRule(rule).success(function (data) { if (data.code == 0) { getMachineRules(); confirmDialog.close(); } else { alert('失败!'); } }); }; function addNewRule(rule) { FlowService.newRule(rule).success(function (data) { if (data.code == 0) { getMachineRules(); flowRuleDialog.close(); } else { alert('失败!'); } }); }; $scope.onOpenAdvanceClick = function () { $scope.flowRuleDialog.showAdvanceButton = false; }; $scope.onCloseAdvanceClick = function () { $scope.flowRuleDialog.showAdvanceButton = true; }; function saveRule(rule, edit) { FlowService.saveRule(rule).success(function (data) { if (data.code == 0) { getMachineRules(); if (edit) { flowRuleDialog.close(); } else { confirmDialog.close(); } } else { alert('失败!'); } }); } queryAppMachines(); function queryAppMachines() { MachineService.getAppMachines($scope.app).success( function (data) { if (data.code == 0) { // $scope.machines = data.data; if (data.data) { $scope.machines = []; $scope.macsInputOptions = []; data.data.forEach(function (item) { if (item.healthy) { $scope.macsInputOptions.push({ text: item.ip + ':' + item.port, value: item.ip + ':' + item.port }); } }); } if ($scope.macsInputOptions.length > 0) { $scope.macInputModel = $scope.macsInputOptions[0].value; } } else { $scope.macsInputOptions = []; } } ); }; $scope.$watch('macInputModel', function () { if ($scope.macInputModel) { getMachineRules(); } }); }]); ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/scripts/controllers/home.js ================================================ /** * @ngdoc function * @name sentinelDashboardApp.controller:MainCtrl * @description * # MainCtrl * Controller of the sentinelDashboardApp */ angular.module('sentinelDashboardApp') .controller('HomeCtrl', ['$scope', '$position', function ($scope, $position) { // do noting }]); ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/scripts/controllers/identity.js ================================================ var app = angular.module('sentinelDashboardApp'); app.controller('IdentityCtl', ['$scope', '$stateParams', 'IdentityService', 'ngDialog', 'FlowServiceV1', 'DegradeService', 'AuthorityRuleService', 'ParamFlowService', 'MachineService', '$interval', '$location', '$timeout', function ($scope, $stateParams, IdentityService, ngDialog, FlowService, DegradeService, AuthorityRuleService, ParamFlowService, MachineService, $interval, $location, $timeout) { $scope.app = $stateParams.app; $scope.currentPage = 1; $scope.pageSize = 16; $scope.totalPage = 1; $scope.totalCount = 0; $scope.identities = []; // 数据自动刷新频率, 默认10s var DATA_REFRESH_INTERVAL = 30; $scope.isExpand = true; $scope.searchKey = ''; $scope.firstExpandAll = false; $scope.isTreeView = true; $scope.macsInputConfig = { searchField: ['text', 'value'], persist: true, create: false, maxItems: 1, render: { item: function (data, escape) { return '
    ' + escape(data.text) + '
    '; } }, onChange: function (value, oldValue) { $scope.macInputModel = value; } }; $scope.table = null; var flowRuleDialog; var flowRuleDialogScope; $scope.addNewFlowRule = function (resource) { if (!$scope.macInputModel) { return; } var mac = $scope.macInputModel.split(':'); flowRuleDialogScope = $scope.$new(true); flowRuleDialogScope.currentRule = { enable: false, strategy: 0, grade: 1, controlBehavior: 0, resource: resource, limitApp: 'default', clusterMode: false, clusterConfig: { thresholdType: 0 }, app: $scope.app, ip: mac[0], port: mac[1] }; flowRuleDialogScope.flowRuleDialog = { title: '新增流控规则', type: 'add', confirmBtnText: '新增', saveAndContinueBtnText: '新增并继续添加', showAdvanceButton: true }; // $scope.flowRuleDialog = { // showAdvanceButton : true // }; flowRuleDialogScope.saveRule = saveFlowRule; flowRuleDialogScope.saveRuleAndContinue = saveFlowRuleAndContinue; flowRuleDialogScope.onOpenAdvanceClick = function () { flowRuleDialogScope.flowRuleDialog.showAdvanceButton = false; }; flowRuleDialogScope.onCloseAdvanceClick = function () { flowRuleDialogScope.flowRuleDialog.showAdvanceButton = true; }; flowRuleDialog = ngDialog.open({ template: '/app/views/dialog/flow-rule-dialog.html', width: 680, overlay: true, scope: flowRuleDialogScope }); }; function saveFlowRule() { if (!FlowService.checkRuleValid(flowRuleDialogScope.currentRule)) { return; } FlowService.newRule(flowRuleDialogScope.currentRule).success(function (data) { if (data.code === 0) { flowRuleDialog.close(); let url = '/dashboard/flow/' + $scope.app; $location.path(url); } else { alert('失败!'); } }).error((data, header, config, status) => { alert('未知错误'); }); } function saveFlowRuleAndContinue() { if (!FlowService.checkRuleValid(flowRuleDialogScope.currentRule)) { return; } FlowService.newRule(flowRuleDialogScope.currentRule).success(function (data) { if (data.code == 0) { flowRuleDialog.close(); } else { alert('失败!'); } }); } var degradeRuleDialog; var degradeRuleDialogScope; $scope.addNewDegradeRule = function (resource) { if (!$scope.macInputModel) { return; } var mac = $scope.macInputModel.split(':'); degradeRuleDialogScope = $scope.$new(true); degradeRuleDialogScope.currentRule = { enable: false, grade: 0, strategy: 0, resource: resource, limitApp: 'default', app: $scope.app, ip: mac[0], port: mac[1] }; degradeRuleDialogScope.degradeRuleDialog = { title: '新增降级规则', type: 'add', confirmBtnText: '新增', saveAndContinueBtnText: '新增并继续添加' }; degradeRuleDialogScope.saveRule = saveDegradeRule; degradeRuleDialogScope.saveRuleAndContinue = saveDegradeRuleAndContinue; degradeRuleDialog = ngDialog.open({ template: '/app/views/dialog/degrade-rule-dialog.html', width: 680, overlay: true, scope: degradeRuleDialogScope }); }; function saveDegradeRule() { if (!DegradeService.checkRuleValid(degradeRuleDialogScope.currentRule)) { return; } DegradeService.newRule(degradeRuleDialogScope.currentRule).success(function (data) { if (data.code == 0) { degradeRuleDialog.close(); var url = '/dashboard/degrade/' + $scope.app; $location.path(url); } else { alert('失败!'); } }); } function saveDegradeRuleAndContinue() { if (!DegradeService.checkRuleValid(degradeRuleDialogScope.currentRule)) { return; } DegradeService.newRule(degradeRuleDialogScope.currentRule).success(function (data) { if (data.code == 0) { degradeRuleDialog.close(); } else { alert('失败!'); } }); } let authorityRuleDialog; let authorityRuleDialogScope; function saveAuthorityRule() { let ruleEntity = authorityRuleDialogScope.currentRule; if (!AuthorityRuleService.checkRuleValid(ruleEntity.rule)) { return; } AuthorityRuleService.addNewRule(ruleEntity).success((data) => { if (data.success) { authorityRuleDialog.close(); let url = '/dashboard/authority/' + $scope.app; $location.path(url); } else { alert('添加规则失败:' + data.msg); } }).error((data) => { if (data) { alert('添加规则失败:' + data.msg); } else { alert("添加规则失败:未知错误"); } }); } function saveAuthorityRuleAndContinue() { let ruleEntity = authorityRuleDialogScope.currentRule; if (!AuthorityRuleService.checkRuleValid(ruleEntity.rule)) { return; } AuthorityRuleService.addNewRule(ruleEntity).success((data) => { if (data.success) { authorityRuleDialog.close(); } else { alert('添加规则失败:' + data.msg); } }).error((data) => { if (data) { alert('添加规则失败:' + data.msg); } else { alert("添加规则失败:未知错误"); } }); } $scope.addNewAuthorityRule = function (resource) { if (!$scope.macInputModel) { return; } let mac = $scope.macInputModel.split(':'); authorityRuleDialogScope = $scope.$new(true); authorityRuleDialogScope.currentRule = { app: $scope.app, ip: mac[0], port: mac[1], rule: { resource: resource, strategy: 0, limitApp: '', } }; authorityRuleDialogScope.authorityRuleDialog = { title: '新增授权规则', type: 'add', confirmBtnText: '新增', saveAndContinueBtnText: '新增并继续添加' }; authorityRuleDialogScope.saveRule = saveAuthorityRule; authorityRuleDialogScope.saveRuleAndContinue = saveAuthorityRuleAndContinue; authorityRuleDialog = ngDialog.open({ template: '/app/views/dialog/authority-rule-dialog.html', width: 680, overlay: true, scope: authorityRuleDialogScope }); }; let paramFlowRuleDialog; let paramFlowRuleDialogScope; function saveParamFlowRule() { let ruleEntity = paramFlowRuleDialogScope.currentRule; if (!ParamFlowService.checkRuleValid(ruleEntity.rule)) { return; } ParamFlowService.addNewRule(ruleEntity).success((data) => { if (data.success) { paramFlowRuleDialog.close(); let url = '/dashboard/paramFlow/' + $scope.app; $location.path(url); } else { alert('添加热点规则失败:' + data.msg); } }).error((data) => { if (data) { alert('添加热点规则失败:' + data.msg); } else { alert("添加热点规则失败:未知错误"); } }); } function saveParamFlowRuleAndContinue() { let ruleEntity = paramFlowRuleDialogScope.currentRule; if (!ParamFlowService.checkRuleValid(ruleEntity.rule)) { return; } ParamFlowService.addNewRule(ruleEntity).success((data) => { if (data.success) { paramFlowRuleDialog.close(); } else { alert('添加热点规则失败:' + data.msg); } }).error((data) => { if (data) { alert('添加热点规则失败:' + data.msg); } else { alert("添加热点规则失败:未知错误"); } }); } $scope.addNewParamFlowRule = function (resource) { if (!$scope.macInputModel) { return; } let mac = $scope.macInputModel.split(':'); paramFlowRuleDialogScope = $scope.$new(true); paramFlowRuleDialogScope.currentRule = { app: $scope.app, ip: mac[0], port: mac[1], rule: { resource: resource, grade: 1, paramFlowItemList: [], count: 0, limitApp: 'default', } }; paramFlowRuleDialogScope.paramFlowRuleDialog = { title: '新增热点规则', type: 'add', confirmBtnText: '新增', saveAndContinueBtnText: '新增并继续添加', supportAdvanced: false, showAdvanceButton: true }; paramFlowRuleDialogScope.saveRule = saveParamFlowRule; paramFlowRuleDialogScope.saveRuleAndContinue = saveParamFlowRuleAndContinue; // paramFlowRuleDialogScope.onOpenAdvanceClick = function () { // paramFlowRuleDialogScope.paramFlowRuleDialog.showAdvanceButton = false; // }; // paramFlowRuleDialogScope.onCloseAdvanceClick = function () { // paramFlowRuleDialogScope.paramFlowRuleDialog.showAdvanceButton = true; // }; paramFlowRuleDialog = ngDialog.open({ template: '/app/views/dialog/param-flow-rule-dialog.html', width: 680, overlay: true, scope: paramFlowRuleDialogScope }); }; var searchHandler; $scope.searchChange = function (searchKey) { $timeout.cancel(searchHandler); searchHandler = $timeout(function () { $scope.searchKey = searchKey; $scope.isExpand = true; $scope.firstExpandAll = true; reInitIdentityDatas(); $scope.firstExpandAll = false; }, 600); }; $scope.initTreeTable = function () { // if (!$scope.table) { com_github_culmat_jsTreeTable.register(window); $scope.table = window.treeTable($('#identities')); // } }; $scope.expandAll = function () { $scope.isExpand = true; }; $scope.collapseAll = function () { $scope.isExpand = false; }; $scope.treeView = function () { $scope.isTreeView = true; queryIdentities(); }; $scope.listView = function () { $scope.isTreeView = false; queryIdentities(); }; function queryAppMachines() { MachineService.getAppMachines($scope.app).success( function (data) { if (data.code === 0) { if (data.data) { $scope.machines = []; $scope.macsInputOptions = []; data.data.forEach(function (item) { if (item.healthy) { $scope.macsInputOptions.push({ text: item.ip + ':' + item.port, value: item.ip + ':' + item.port }); } }); } if ($scope.macsInputOptions.length > 0) { $scope.macInputModel = $scope.macsInputOptions[0].value; } } else { $scope.macsInputOptions = []; } } ); } // Fetch all machines by current app name. queryAppMachines(); $scope.$watch('macInputModel', function () { if ($scope.macInputModel) { reInitIdentityDatas(); } }); $scope.$on('$destroy', function () { $interval.cancel(intervalId); }); var intervalId; function reInitIdentityDatas() { // $interval.cancel(intervalId); queryIdentities(); // intervalId = $interval(function () { // queryIdentities(); // }, DATA_REFRESH_INTERVAL * 1000); }; function queryIdentities() { var mac = $scope.macInputModel.split(':'); if (mac == null || mac.length < 2) { return; } if ($scope.isTreeView) { IdentityService.fetchIdentityOfMachine(mac[0], mac[1], $scope.searchKey).success( function (data) { if (data.code == 0 && data.data) { $scope.identities = data.data; $scope.totalCount = $scope.identities.length; } else { $scope.identities = []; $scope.totalCount = 0; } } ); } else { IdentityService.fetchClusterNodeOfMachine(mac[0], mac[1], $scope.searchKey).success( function (data) { if (data.code == 0 && data.data) { $scope.identities = data.data; $scope.totalCount = $scope.identities.length; } else { $scope.identities = []; $scope.totalCount = 0; } } ); } }; $scope.queryIdentities = queryIdentities; }]); ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/scripts/controllers/login.js ================================================ var app = angular.module('sentinelDashboardApp'); app.controller('LoginCtl', ['$scope', '$state', '$window', 'AuthService', function ($scope, $state, $window, LoginService) { // If auth, jump to the index page directly if ($window.localStorage.getItem('session_sentinel_admin')) { $state.go('dashboard'); } $scope.login = function () { if (!$scope.username) { alert('请输入用户名'); return; } if (!$scope.password) { alert('请输入密码'); return; } var param = {"username": $scope.username, "password": $scope.password}; LoginService.login(param).success(function (data) { if (data.code == 0) { $window.localStorage.setItem('session_sentinel_admin', { username: data.data }); $state.go('dashboard'); } else { alert(data.msg); } }); }; }] ); ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/scripts/controllers/machine.js ================================================ var app = angular.module('sentinelDashboardApp'); app.controller('MachineCtl', ['$scope', '$stateParams', 'MachineService', function ($scope, $stateParams, MachineService) { $scope.app = $stateParams.app; $scope.propertyName = ''; $scope.reverse = false; $scope.currentPage = 1; $scope.machines = []; $scope.machinesPageConfig = { pageSize: 10, currentPageIndex: 1, totalPage: 1, totalCount: 0, }; $scope.sortBy = function (propertyName) { // console.log('machine sortBy ' + propertyName); $scope.reverse = ($scope.propertyName === propertyName) ? !$scope.reverse : false; $scope.propertyName = propertyName; }; $scope.reloadMachines = function() { MachineService.getAppMachines($scope.app).success( function (data) { // console.log('get machines: ' + data.data[0].hostname) if (data.code == 0 && data.data) { $scope.machines = data.data; var healthy = 0; $scope.machines.forEach(function (item) { if (item.healthy) { healthy++; } if (!item.hostname) { item.hostname = '未知' } }) $scope.healthyCount = healthy; $scope.machinesPageConfig.totalCount = $scope.machines.length; } else { $scope.machines = []; $scope.healthyCount = 0; } } ); }; $scope.removeMachine = function(ip, port) { if (!confirm("confirm to remove machine [" + ip + ":" + port + "]?")) { return; } MachineService.removeAppMachine($scope.app, ip, port).success( function(data) { if (data.code == 0) { $scope.reloadMachines(); } else { alert("remove failed"); } } ); }; $scope.reloadMachines(); }]); ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/scripts/controllers/main.js ================================================ /** * @ngdoc function * @name sentinelDashboardApp.controller:MainCtrl * @description * # MainCtrl * Controller of the sentinelDashboardApp */ angular.module('sentinelDashboardApp') .controller('DashboardCtrl', ['$scope', '$position', function ($scope, $position) { }]); ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/scripts/controllers/metric.js ================================================ var app = angular.module('sentinelDashboardApp'); app.controller('MetricCtl', ['$scope', '$stateParams', 'MetricService', '$interval', '$timeout', function ($scope, $stateParams, MetricService, $interval, $timeout) { $scope.endTime = new Date(); $scope.startTime = new Date(); $scope.startTime.setMinutes($scope.endTime.getMinutes() - 30); $scope.startTimeFmt = formatDate($scope.startTime); $scope.endTimeFmt = formatDate($scope.endTime); function formatDate(date) { return moment(date).format('YYYY/MM/DD HH:mm:ss'); } $scope.changeStartTime = function (startTime) { $scope.startTime = new Date(startTime); $scope.startTimeFmt = formatDate(startTime); }; $scope.changeEndTime = function (endTime) { $scope.endTime = new Date(endTime); $scope.endTimeFmt = formatDate(endTime); }; $scope.app = $stateParams.app; // 数据自动刷新频率 var DATA_REFRESH_INTERVAL = 1000 * 10; $scope.servicePageConfig = { pageSize: 6, currentPageIndex: 1, totalPage: 1, totalCount: 0, }; $scope.servicesChartConfigs = []; $scope.pageChanged = function (newPageNumber) { $scope.servicePageConfig.currentPageIndex = newPageNumber; reInitIdentityDatas(); }; var searchT; $scope.searchService = function () { $timeout.cancel(searchT); searchT = $timeout(function () { reInitIdentityDatas(); }, 600); } var intervalId; reInitIdentityDatas(); function reInitIdentityDatas() { $interval.cancel(intervalId); queryIdentityDatas(); intervalId = $interval(function () { queryIdentityDatas(); }, DATA_REFRESH_INTERVAL); }; $scope.$on('$destroy', function () { $interval.cancel(intervalId); }); $scope.initAllChart = function () { $.each($scope.metrics, function (idx, metric) { if (idx == $scope.metrics.length - 1) { return; } const chart = new G2.Chart({ container: 'chart' + idx, forceFit: true, width: 100, height: 250, padding: [10, 30, 70, 50] }); var maxQps = 0; for (var i in metric.data) { var item = metric.data[i]; if (item.passQps > maxQps) { maxQps = item.passQps; } if (item.blockQps > maxQps) { maxQps = item.blockQps; } } chart.source(metric.data); chart.scale('timestamp', { type: 'time', mask: 'YYYY-MM-DD HH:mm:ss' }); chart.scale('passQps', { min: 0, max: maxQps, fine: true, alias: '通过 QPS' // max: 10 }); chart.scale('blockQps', { min: 0, max: maxQps, fine: true, alias: '拒绝 QPS', }); chart.scale('rt', { min: 0, fine: true, }); chart.axis('rt', { grid: null, label: null }); chart.axis('blockQps', { grid: null, label: null }); chart.axis('timestamp', { label: { textStyle: { textAlign: 'center', // 文本对齐方向,可取值为: start center end fill: '#404040', // 文本的颜色 fontSize: '11', // 文本大小 //textBaseline: 'top', // 文本基准线,可取 top middle bottom,默认为middle }, autoRotate: false, formatter: function (text, item, index) { return text.substring(11, 11 + 5); } } }); chart.legend({ custom: true, position: 'bottom', allowAllCanceled: true, itemFormatter: function (val) { if ('passQps' === val) { return '通过 QPS'; } if ('blockQps' === val) { return '拒绝 QPS'; } return val; }, items: [ { value: 'passQps', marker: { symbol: 'hyphen', stroke: 'green', radius: 5, lineWidth: 2 } }, { value: 'blockQps', marker: { symbol: 'hyphen', stroke: 'blue', radius: 5, lineWidth: 2 } }, //{ value: 'rt', marker: {symbol: 'hyphen', stroke: 'gray', radius: 5, lineWidth: 2} }, ], onClick: function (ev) { const item = ev.item; const value = item.value; const checked = ev.checked; const geoms = chart.getAllGeoms(); for (var i = 0; i < geoms.length; i++) { const geom = geoms[i]; if (geom.getYScale().field === value) { if (checked) { geom.show(); } else { geom.hide(); } } } } }); chart.line().position('timestamp*passQps').size(1).color('green').shape('smooth'); chart.line().position('timestamp*blockQps').size(1).color('blue').shape('smooth'); //chart.line().position('timestamp*rt').size(1).color('gray').shape('smooth'); G2.track(false); chart.render(); }); }; $scope.metrics = []; $scope.emptyObjs = []; function queryIdentityDatas() { var params = { app: $scope.app, pageIndex: $scope.servicePageConfig.currentPageIndex, pageSize: $scope.servicePageConfig.pageSize, desc: $scope.isDescOrder, searchKey: $scope.serviceQuery }; MetricService.queryAppSortedIdentities(params).success(function (data) { $scope.metrics = []; $scope.emptyObjs = []; if (data.code === 0 && data.data) { var metricsObj = data.data.metric; var identityNames = Object.keys(metricsObj); if (identityNames.length < 1) { $scope.emptyServices = true; } else { $scope.emptyServices = false; } $scope.servicePageConfig.totalPage = data.data.totalPage; $scope.servicePageConfig.pageSize = data.data.pageSize; var totalCount = data.data.totalCount; $scope.servicePageConfig.totalCount = totalCount; for (i = 0; i < totalCount; i++) { $scope.emptyObjs.push({}); } $.each(identityNames, function (idx, identityName) { var identityDatas = metricsObj[identityName]; var metrics = {}; metrics.resource = identityName; // metrics.data = identityDatas; metrics.data = fillZeros(identityDatas); metrics.shortData = lastOfArray(identityDatas, 6); $scope.metrics.push(metrics); }); // push an empty element in the last, for ng-init reasons. $scope.metrics.push([]); } else { $scope.emptyServices = true; console.log(data.msg); } }); }; function fillZeros(metricData) { if (!metricData || metricData.length == 0) { return []; } var filledData = []; filledData.push(metricData[0]); var lastTime = metricData[0].timestamp / 1000; for (var i = 1; i < metricData.length; i++) { var curTime = metricData[i].timestamp / 1000; if (curTime > lastTime + 1) { for (var j = lastTime + 1; j < curTime; j++) { filledData.push({ "timestamp": j * 1000, "passQps": 0, "blockQps": 0, "successQps": 0, "exception": 0, "rt": 0, "count": 0 }) } } filledData.push(metricData[i]); lastTime = curTime; } return filledData; } function lastOfArray(arr, n) { if (!arr.length) { return []; } var rs = []; for (i = 0; i < n && i < arr.length; i++) { rs.push(arr[arr.length - 1 - i]); } return rs; } $scope.isDescOrder = true; $scope.setDescOrder = function () { $scope.isDescOrder = true; reInitIdentityDatas(); } $scope.setAscOrder = function () { $scope.isDescOrder = false; reInitIdentityDatas(); } }]); ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/scripts/controllers/param_flow.js ================================================ /** * Parameter flow control controller. * * @author Eric Zhao */ angular.module('sentinelDashboardApp').controller('ParamFlowController', ['$scope', '$stateParams', 'ParamFlowService', 'ngDialog', 'MachineService', function ($scope, $stateParams, ParamFlowService, ngDialog, MachineService) { const UNSUPPORTED_CODE = 4041; $scope.app = $stateParams.app; $scope.curExItem = {}; $scope.paramItemClassTypeList = [ 'int', 'double', 'java.lang.String', 'long', 'float', 'char', 'byte' ]; $scope.rulesPageConfig = { pageSize: 10, currentPageIndex: 1, totalPage: 1, totalCount: 0, }; $scope.macsInputConfig = { searchField: ['text', 'value'], persist: true, create: false, maxItems: 1, render: { item: function (data, escape) { return '
    ' + escape(data.text) + '
    '; } }, onChange: function (value, oldValue) { $scope.macInputModel = value; } }; function updateSingleParamItem(arr, v, t, c) { for (let i = 0; i < arr.length; i++) { if (arr[i].object === v && arr[i].classType === t) { arr[i].count = c; return; } } arr.push({object: v, classType: t, count: c}); } function removeSingleParamItem(arr, v, t) { for (let i = 0; i < arr.length; i++) { if (arr[i].object === v && arr[i].classType === t) { arr.splice(i, 1); break; } } } function isNumberClass(classType) { return classType === 'int' || classType === 'double' || classType === 'float' || classType === 'long' || classType === 'short'; } function isByteClass(classType) { return classType === 'byte'; } function notNumberAtLeastZero(num) { return num === undefined || num === '' || isNaN(num) || num < 0; } function notGoodNumber(num) { return num === undefined || num === '' || isNaN(num); } function notGoodNumberBetweenExclusive(num, l ,r) { return num === undefined || num === '' || isNaN(num) || num < l || num > r; } $scope.notValidParamItem = (curExItem) => { if (isNumberClass(curExItem.classType) && notGoodNumber(curExItem.object)) { return true; } if (isByteClass(curExItem.classType) && notGoodNumberBetweenExclusive(curExItem.object, -128, 127)) { return true; } return curExItem.object === undefined || curExItem.classType === undefined || notNumberAtLeastZero(curExItem.count); }; $scope.addParamItem = () => { updateSingleParamItem($scope.currentRule.rule.paramFlowItemList, $scope.curExItem.object, $scope.curExItem.classType, $scope.curExItem.count); let oldItem = $scope.curExItem; $scope.curExItem = {classType: oldItem.classType}; }; $scope.removeParamItem = (v, t) => { removeSingleParamItem($scope.currentRule.rule.paramFlowItemList, v, t); }; function getMachineRules() { if (!$scope.macInputModel) { return; } let mac = $scope.macInputModel.split(':'); ParamFlowService.queryMachineRules($scope.app, mac[0], mac[1]) .success(function (data) { if (data.code === 0 && data.data) { $scope.loadError = undefined; $scope.rules = data.data; $scope.rulesPageConfig.totalCount = $scope.rules.length; } else { $scope.rules = []; $scope.rulesPageConfig.totalCount = 0; if (data.code === UNSUPPORTED_CODE) { $scope.loadError = {message: "机器 " + mac[0] + ":" + mac[1] + " 的 Sentinel 客户端版本不支持热点参数限流功能,请升级至 0.2.0 以上版本并引入 sentinel-parameter-flow-control 依赖。"} } else { $scope.loadError = {message: data.msg} } } }) .error((data, header, config, status) => { $scope.loadError = {message: "未知错误"} }); } $scope.getMachineRules = getMachineRules; getMachineRules(); var paramFlowRuleDialog; $scope.editRule = function (rule) { $scope.currentRule = angular.copy(rule); $scope.paramFlowRuleDialog = { title: '编辑热点规则', type: 'edit', confirmBtnText: '保存', supportAdvanced: true, showAdvanceButton: rule.rule.paramFlowItemList === undefined || rule.rule.paramFlowItemList.length <= 0 }; paramFlowRuleDialog = ngDialog.open({ template: '/app/views/dialog/param-flow-rule-dialog.html', width: 680, overlay: true, scope: $scope }); $scope.curExItem = {}; }; $scope.addNewRule = function () { var mac = $scope.macInputModel.split(':'); $scope.currentRule = { app: $scope.app, ip: mac[0], port: mac[1], rule: { grade: 1, paramFlowItemList: [], count: 0, limitApp: 'default', clusterMode: false, clusterConfig: { thresholdType: 0 } } }; $scope.paramFlowRuleDialog = { title: '新增热点规则', type: 'add', confirmBtnText: '新增', showAdvanceButton: true, }; paramFlowRuleDialog = ngDialog.open({ template: '/app/views/dialog/param-flow-rule-dialog.html', width: 680, overlay: true, scope: $scope }); $scope.curExItem = {}; }; $scope.onOpenAdvanceClick = function () { $scope.paramFlowRuleDialog.showAdvanceButton = false; }; $scope.onCloseAdvanceClick = function () { $scope.paramFlowRuleDialog.showAdvanceButton = true; }; $scope.saveRule = function () { if (!ParamFlowService.checkRuleValid($scope.currentRule.rule)) { return; } if ($scope.paramFlowRuleDialog.type === 'add') { addNewRuleAndPush($scope.currentRule); } else if ($scope.paramFlowRuleDialog.type === 'edit') { saveRuleAndPush($scope.currentRule, true); } }; function addNewRuleAndPush(rule) { ParamFlowService.addNewRule(rule).success((data) => { if (data.success) { getMachineRules(); paramFlowRuleDialog.close(); } else { alert('添加规则失败:' + data.msg); } }).error((data) => { if (data) { alert('添加规则失败:' + data.msg); } else { alert("添加规则失败:未知错误"); } }); } function saveRuleAndPush(rule, edit) { ParamFlowService.saveRule(rule).success(function (data) { if (data.success) { alert("修改规则成功"); getMachineRules(); if (edit) { paramFlowRuleDialog.close(); } else { confirmDialog.close(); } } else { alert('修改规则失败:' + data.msg); } }).error((data) => { if (data) { alert('修改规则失败:' + data.msg); } else { alert("修改规则失败:未知错误"); } }); } function deleteRuleAndPush(entity) { if (entity.id === undefined || isNaN(entity.id)) { alert('规则 ID 不合法!'); return; } ParamFlowService.deleteRule(entity).success((data) => { if (data.code == 0) { getMachineRules(); confirmDialog.close(); } else { alert('删除规则失败:' + data.msg); } }).error((data) => { if (data) { alert('删除规则失败:' + data.msg); } else { alert("删除规则失败:未知错误"); } }); }; var confirmDialog; $scope.deleteRule = function (ruleEntity) { $scope.currentRule = ruleEntity; console.log('deleting: ' + ruleEntity); $scope.confirmDialog = { title: '删除热点规则', type: 'delete_rule', attentionTitle: '请确认是否删除如下热点参数限流规则', attention: '资源名: ' + ruleEntity.rule.resource + ', 热点参数索引: ' + ruleEntity.rule.paramIdx + ', 限流模式: ' + (ruleEntity.rule.grade === 1 ? 'QPS' : '未知') + ', 限流阈值: ' + ruleEntity.rule.count, confirmBtnText: '删除', }; confirmDialog = ngDialog.open({ template: '/app/views/dialog/confirm-dialog.html', scope: $scope, overlay: true }); }; $scope.confirm = function () { if ($scope.confirmDialog.type === 'delete_rule') { deleteRuleAndPush($scope.currentRule); } else { console.error('error'); } }; queryAppMachines(); function queryAppMachines() { MachineService.getAppMachines($scope.app).success( function (data) { if (data.code == 0) { // $scope.machines = data.data; if (data.data) { $scope.machines = []; $scope.macsInputOptions = []; data.data.forEach(function (item) { if (item.healthy) { $scope.macsInputOptions.push({ text: item.ip + ':' + item.port, value: item.ip + ':' + item.port }); } }); } if ($scope.macsInputOptions.length > 0) { $scope.macInputModel = $scope.macsInputOptions[0].value; } } else { $scope.macsInputOptions = []; } } ); }; $scope.$watch('macInputModel', function () { if ($scope.macInputModel) { getMachineRules(); } }); }]); ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/scripts/controllers/system.js ================================================ var app = angular.module('sentinelDashboardApp'); app.controller('SystemCtl', ['$scope', '$stateParams', 'SystemService', 'ngDialog', 'MachineService', function ($scope, $stateParams, SystemService, ngDialog, MachineService) { //初始化 $scope.app = $stateParams.app; $scope.rulesPageConfig = { pageSize: 10, currentPageIndex: 1, totalPage: 1, totalCount: 0, }; $scope.macsInputConfig = { searchField: ['text', 'value'], persist: true, create: false, maxItems: 1, render: { item: function (data, escape) { return '
    ' + escape(data.text) + '
    '; } }, onChange: function (value, oldValue) { $scope.macInputModel = value; } }; getMachineRules(); function getMachineRules() { if (!$scope.macInputModel) { return; } var mac = $scope.macInputModel.split(':'); SystemService.queryMachineRules($scope.app, mac[0], mac[1]).success( function (data) { if (data.code == 0 && data.data) { $scope.rules = data.data; $.each($scope.rules, function (idx, rule) { // rule.orginEnable = rule.enable; if (rule.avgLoad >= 0) { rule.grade = 0; } else if (rule.avgRt >= 0) { rule.grade = 1; } else if (rule.maxThread >= 0) { rule.grade = 2; } else if (rule.qps >= 0) { rule.grade = 3; } }); $scope.rulesPageConfig.totalCount = $scope.rules.length; } else { $scope.rules = []; $scope.rulesPageConfig.totalCount = 0; } }); }; $scope.getMachineRules = getMachineRules; var systemRuleDialog; $scope.editRule = function (rule) { $scope.currentRule = angular.copy(rule); $scope.systemRuleDialog = { title: '编辑系统保护规则', type: 'edit', confirmBtnText: '保存' }; systemRuleDialog = ngDialog.open({ template: '/app/views/dialog/system-rule-dialog.html', width: 680, overlay: true, scope: $scope }); }; $scope.addNewRule = function () { var mac = $scope.macInputModel.split(':'); $scope.currentRule = { grade: 0, app: $scope.app, ip: mac[0], port: mac[1], }; $scope.systemRuleDialog = { title: '新增系统保护规则', type: 'add', confirmBtnText: '新增' }; systemRuleDialog = ngDialog.open({ template: '/app/views/dialog/system-rule-dialog.html', width: 680, overlay: true, scope: $scope }); }; $scope.saveRule = function () { if ($scope.systemRuleDialog.type == 'add') { addNewRule($scope.currentRule); } else if ($scope.systemRuleDialog.type == 'edit') { saveRule($scope.currentRule, true); } } var confirmDialog; $scope.deleteRule = function (rule) { $scope.currentRule = rule; var ruleTypeDesc = ''; var ruleTypeCount = null; if (rule.avgLoad != -1) { ruleTypeDesc = 'LOAD'; ruleTypeCount = rule.avgLoad; } else if (rule.avgRt != -1) { ruleTypeDesc = 'RT'; ruleTypeCount = rule.avgRt; } else if (rule.maxThread != -1) { ruleTypeDesc = '线程数'; ruleTypeCount = rule.maxThread; } else if (rule.qps != -1) { ruleTypeDesc = 'QPS'; ruleTypeCount = rule.qps; } $scope.confirmDialog = { title: '删除系统保护规则', type: 'delete_rule', attentionTitle: '请确认是否删除如下系统保护规则', attention: '阈值类型: ' + ruleTypeDesc + ', 阈值: ' + ruleTypeCount, confirmBtnText: '删除', }; confirmDialog = ngDialog.open({ template: '/app/views/dialog/confirm-dialog.html', scope: $scope, overlay: true }); }; $scope.confirm = function () { if ($scope.confirmDialog.type == 'delete_rule') { deleteRule($scope.currentRule); // } else if ($scope.confirmDialog.type == 'enable_rule') { // $scope.currentRule.enable = true; // saveRule($scope.currentRule); // } else if ($scope.confirmDialog.type == 'disable_rule') { // $scope.currentRule.enable = false; // saveRule($scope.currentRule); // } else if ($scope.confirmDialog.type == 'enable_all') { // enableAll($scope.app); // } else if ($scope.confirmDialog.type == 'disable_all') { // disableAll($scope.app); } else { console.error('error'); } }; function deleteRule(rule) { SystemService.deleteRule(rule).success(function (data) { if (data.code == 0) { getMachineRules(); confirmDialog.close(); } else { alert } }); }; function addNewRule(rule) { SystemService.newRule(rule).success(function (data) { if (data.code == 0) { getMachineRules(); systemRuleDialog.close(); } else { alert('失败!'); } }); }; function saveRule(rule, edit) { SystemService.saveRule(rule).success(function (data) { if (data.code == 0) { // if (rule.enable) { // rule.orginEnable = true; // } else { // rule.orginEnable = false; // } getMachineRules(); if (edit) { systemRuleDialog.close(); } else { confirmDialog.close(); } } else { alert('失败!'); } }); } queryAppMachines(); function queryAppMachines() { MachineService.getAppMachines($scope.app).success( function (data) { if (data.code == 0) { // $scope.machines = data.data; if (data.data) { $scope.machines = []; $scope.macsInputOptions = []; data.data.forEach(function (item) { if (item.healthy) { $scope.macsInputOptions.push({ text: item.ip + ':' + item.port, value: item.ip + ':' + item.port }); } }); } if ($scope.macsInputOptions.length > 0) { $scope.macInputModel = $scope.macsInputOptions[0].value; } } else { $scope.macsInputOptions = []; } } ); }; $scope.$watch('macInputModel', function () { if ($scope.macInputModel) { getMachineRules(); } }); }]); ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/scripts/directives/header/header.html ================================================
    ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/scripts/directives/header/header.js ================================================ /** * @ngdoc directive * @name izzyposWebApp.directive:adminPosHeader * @description * # adminPosHeader */ angular.module('sentinelDashboardApp') .directive('header', ['AuthService', function () { return { templateUrl: 'app/scripts/directives/header/header.html', restrict: 'E', replace: true, controller: function ($scope, $state, $window, AuthService) { if (!$window.localStorage.getItem('session_sentinel_admin')) { $state.go('login'); } $scope.logout = function () { AuthService.logout().success(function (data) { if (data.code == 0) { $window.localStorage.removeItem("session_sentinel_admin"); $state.go('login'); } else { alert('logout error'); } }); } } } }]); ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/scripts/directives/sidebar/sidebar-search/sidebar-search.html ================================================ ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/scripts/directives/sidebar/sidebar-search/sidebar-search.js ================================================ /** * @ngdoc directive * @name izzyposWebApp.directive:adminPosHeader * @description * # adminPosHeader */ angular.module('sentinelDashboardApp') .directive('sidebarSearch', function () { return { templateUrl: 'app/scripts/directives/sidebar/sidebar-search/sidebar-search.html', restrict: 'E', replace: true, scope: { }, controller: function ($scope) { $scope.selectedMenu = 'home'; } } }); ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/scripts/directives/sidebar/sidebar.html ================================================ ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/scripts/directives/sidebar/sidebar.js ================================================ /** * @ngdoc directive * @name izzyposWebApp.directive:adminPosHeader * @description # adminPosHeader */ angular.module('sentinelDashboardApp') .directive('sidebar', ['$location', '$stateParams', 'AppService', function () { return { templateUrl: 'app/scripts/directives/sidebar/sidebar.html', restrict: 'E', replace: true, scope: { }, controller: function ($scope, $stateParams, $location, AppService) { $scope.app = $stateParams.app; $scope.collapseVar = 0; // app AppService.getApps().success( function (data) { if (data.code === 0) { let initHashApp = $location.path().split('/')[3]; $scope.apps = data.data; $scope.apps = $scope.apps.map(function (item) { if (item.app === initHashApp) { item.active = true; } var heathyCount = 0; for (var i in item.machines) { if (item.machines[i].healthy) { heathyCount++; } } item.heathyCount = heathyCount; if (item.shown) { return item; } }); } } ); // toggle side bar $scope.click = function ($event) { let entry = angular.element($event.target).scope().entry; entry.active = !entry.active;// toggle this clicked app bar $scope.apps.forEach(function (item) {// collapse other app bars if (item != entry) { item.active = false; } }); }; /** * @deprecated */ $scope.addSearchApp = function () { let findApp = false; for (let i = 0; i < $scope.apps.length; i++) { if ($scope.apps[i].app === $scope.searchApp) { findApp = true; break; } } if (!findApp) { $scope.apps.push({ app: $scope.searchApp }); } }; } }; }]); ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/scripts/filters/filters.js ================================================ var app = angular.module('sentinelDashboardApp'); app.filter('range', [function () { return function (input, length) { if (isNaN(length) || length <= 0) { return []; } input = []; for (var index = 1; index <= length; index++) { input.push(index); } return input; }; }]); ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/scripts/libs/treeTable.js ================================================ var com_github_culmat_jsTreeTable = (function(){ function depthFirst(tree, func, childrenAttr) { childrenAttr = childrenAttr || 'children' function i_depthFirst(node) { if (node[childrenAttr]) { $.each(node[childrenAttr], function(i, child) { i_depthFirst(child) }) } func(node) } $.each(tree, function(i, root) { i_depthFirst(root) }) return tree } /* * make a deep copy of the object */ function copy(data){ return JSON.parse(JSON.stringify(data)) } function makeTree (data, idAttr, refAttr, childrenAttr) { var data_tmp = data idAttr = idAttr || 'id' refAttr = refAttr || 'parent' childrenAttr = childrenAttr || 'children' var byName = [] $.each(data_tmp, function(i, entry) { byName[entry[idAttr]] = entry }) var tree = [] $.each(data_tmp, function(i, entry) { var parents = entry[refAttr] if(!$.isArray(parents)){ parents = [parents] } if(parents.length == 0){ tree.push(entry) } else { var inTree = false; $.each(parents, function(i,parentID){ var parent = byName[parentID] if (parent) { if (!parent[childrenAttr]) { parent[childrenAttr] = [] } if($.inArray(entry, parent[childrenAttr])< 0) parent[childrenAttr].push(entry) inTree = true } }) if(!inTree){ tree.push(entry) } } }) return tree } function renderTree(tree, childrenAttr, idAttr, attrs, renderer, tableAttributes) { childrenAttr = childrenAttr || 'children' idAttr = idAttr || 'id' tableAttributes = tableAttributes || {} var maxLevel = 0; var ret = [] var table = $("
    ") $.each(tableAttributes, function(key, value){ if(key == 'class' && value != 'jsTT') { table.addClass(value) } else { table.attr(key, value) } }) var thead = $("") var tr = $("") var tbody = $("") table.append(thead) thead.append(tr) table.append(tbody) if (attrs) { $.each(attrs, function(attr, desc) { $(tr).append($('')) }) } else { $(tr).append($('')) $.each(tree[0], function(key, value) { if (key != childrenAttr && key != idAttr) $(tr).append($('')) }) } function render(node, parent) { var tr = $("") $(tr).attr('data-tt-id', node[idAttr]) $(tr).attr('data-tt-level', node['data-tt-level']) if(!node[childrenAttr] || node[childrenAttr].length == 0) $(tr).attr('data-tt-isleaf', true) else $(tr).attr('data-tt-isnode', true) if (parent) { $(tr).attr('data-tt-parent-id', parent[idAttr]) } if (renderer) { renderer($(tr), node) }else if (attrs) { $.each(attrs, function(attr, desc) { $(tr).append($('')) }) } else { $(tr).append($('')) $.each(node, function(key, value) { if (key != childrenAttr && key != idAttr && key != 'data-tt-level') $(tr).append($('')) }) } tbody.append(tr) } function i_renderTree(subTree, childrenAttr, level, parent) { maxLevel = Math.max(maxLevel, level) $.each(subTree, function(i, node) { node['data-tt-level'] = level render(node, parent) if (node[childrenAttr]) { $.each(node[childrenAttr], function(i, child) { i_renderTree([ child ], childrenAttr, level + 1, node) }) } }) } i_renderTree(tree, childrenAttr, 1) if (tree[0]) tree[0].maxLevel = maxLevel return table } function attr2attr(nodes, attrs){ $.each(nodes, function(i, node) { $.each(attrs, function(j, at) { node[at] = $(node).attr(at) }) }) return nodes } function treeTable(table){ table.addClass('jsTT') table.expandLevel = function (n) { $("tr[data-tt-level]", table).each(function(index) { var level = parseInt($(this).attr('data-tt-level')) if (level > n-1) { this.trCollapse(true) } else if (level == n-1){ this.trExpand(true) } }) } function getLevel(node){ var level = node.attr('data-tt-level') if(level != undefined ) return parseInt(level) var parentID = node.attr('data-tt-parent-id') if( parentID == undefined){ return 0 } else { return getLevel($('tr[data-tt-id="'+parentID+'"]', table).first()) + 1 } } $("tr[data-tt-id]", table).each(function(i,node){ node = $(node) node.attr('data-tt-level', getLevel(node)) }) var dat = $("tr[data-tt-level]", table).get() $.each(dat, function(j, d) { d.trChildrenVisible = true d.trChildren = [] }) dat = attr2attr(dat, ['data-tt-id', 'data-tt-parent-id']) dat = makeTree(dat, 'data-tt-id', 'data-tt-parent-id', 'trChildren') var imgExpand = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAsTAAALEwEAmpwYAAAKT2lDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVNnVFPpFj333vRCS4iAlEtvUhUIIFJCi4AUkSYqIQkQSoghodkVUcERRUUEG8igiAOOjoCMFVEsDIoK2AfkIaKOg6OIisr74Xuja9a89+bN/rXXPues852zzwfACAyWSDNRNYAMqUIeEeCDx8TG4eQuQIEKJHAAEAizZCFz/SMBAPh+PDwrIsAHvgABeNMLCADATZvAMByH/w/qQplcAYCEAcB0kThLCIAUAEB6jkKmAEBGAYCdmCZTAKAEAGDLY2LjAFAtAGAnf+bTAICd+Jl7AQBblCEVAaCRACATZYhEAGg7AKzPVopFAFgwABRmS8Q5ANgtADBJV2ZIALC3AMDOEAuyAAgMADBRiIUpAAR7AGDIIyN4AISZABRG8lc88SuuEOcqAAB4mbI8uSQ5RYFbCC1xB1dXLh4ozkkXKxQ2YQJhmkAuwnmZGTKBNA/g88wAAKCRFRHgg/P9eM4Ors7ONo62Dl8t6r8G/yJiYuP+5c+rcEAAAOF0ftH+LC+zGoA7BoBt/qIl7gRoXgugdfeLZrIPQLUAoOnaV/Nw+H48PEWhkLnZ2eXk5NhKxEJbYcpXff5nwl/AV/1s+X48/Pf14L7iJIEyXYFHBPjgwsz0TKUcz5IJhGLc5o9H/LcL//wd0yLESWK5WCoU41EScY5EmozzMqUiiUKSKcUl0v9k4t8s+wM+3zUAsGo+AXuRLahdYwP2SycQWHTA4vcAAPK7b8HUKAgDgGiD4c93/+8//UegJQCAZkmScQAAXkQkLlTKsz/HCAAARKCBKrBBG/TBGCzABhzBBdzBC/xgNoRCJMTCQhBCCmSAHHJgKayCQiiGzbAdKmAv1EAdNMBRaIaTcA4uwlW4Dj1wD/phCJ7BKLyBCQRByAgTYSHaiAFiilgjjggXmYX4IcFIBBKLJCDJiBRRIkuRNUgxUopUIFVIHfI9cgI5h1xGupE7yAAygvyGvEcxlIGyUT3UDLVDuag3GoRGogvQZHQxmo8WoJvQcrQaPYw2oefQq2gP2o8+Q8cwwOgYBzPEbDAuxsNCsTgsCZNjy7EirAyrxhqwVqwDu4n1Y8+xdwQSgUXACTYEd0IgYR5BSFhMWE7YSKggHCQ0EdoJNwkDhFHCJyKTqEu0JroR+cQYYjIxh1hILCPWEo8TLxB7iEPENyQSiUMyJ7mQAkmxpFTSEtJG0m5SI+ksqZs0SBojk8naZGuyBzmULCAryIXkneTD5DPkG+Qh8lsKnWJAcaT4U+IoUspqShnlEOU05QZlmDJBVaOaUt2ooVQRNY9aQq2htlKvUYeoEzR1mjnNgxZJS6WtopXTGmgXaPdpr+h0uhHdlR5Ol9BX0svpR+iX6AP0dwwNhhWDx4hnKBmbGAcYZxl3GK+YTKYZ04sZx1QwNzHrmOeZD5lvVVgqtip8FZHKCpVKlSaVGyovVKmqpqreqgtV81XLVI+pXlN9rkZVM1PjqQnUlqtVqp1Q61MbU2epO6iHqmeob1Q/pH5Z/YkGWcNMw09DpFGgsV/jvMYgC2MZs3gsIWsNq4Z1gTXEJrHN2Xx2KruY/R27iz2qqaE5QzNKM1ezUvOUZj8H45hx+Jx0TgnnKKeX836K3hTvKeIpG6Y0TLkxZVxrqpaXllirSKtRq0frvTau7aedpr1Fu1n7gQ5Bx0onXCdHZ4/OBZ3nU9lT3acKpxZNPTr1ri6qa6UbobtEd79up+6Ynr5egJ5Mb6feeb3n+hx9L/1U/W36p/VHDFgGswwkBtsMzhg8xTVxbzwdL8fb8VFDXcNAQ6VhlWGX4YSRudE8o9VGjUYPjGnGXOMk423GbcajJgYmISZLTepN7ppSTbmmKaY7TDtMx83MzaLN1pk1mz0x1zLnm+eb15vft2BaeFostqi2uGVJsuRaplnutrxuhVo5WaVYVVpds0atna0l1rutu6cRp7lOk06rntZnw7Dxtsm2qbcZsOXYBtuutm22fWFnYhdnt8Wuw+6TvZN9un2N/T0HDYfZDqsdWh1+c7RyFDpWOt6azpzuP33F9JbpL2dYzxDP2DPjthPLKcRpnVOb00dnF2e5c4PziIuJS4LLLpc+Lpsbxt3IveRKdPVxXeF60vWdm7Obwu2o26/uNu5p7ofcn8w0nymeWTNz0MPIQ+BR5dE/C5+VMGvfrH5PQ0+BZ7XnIy9jL5FXrdewt6V3qvdh7xc+9j5yn+M+4zw33jLeWV/MN8C3yLfLT8Nvnl+F30N/I/9k/3r/0QCngCUBZwOJgUGBWwL7+Hp8Ib+OPzrbZfay2e1BjKC5QRVBj4KtguXBrSFoyOyQrSH355jOkc5pDoVQfujW0Adh5mGLw34MJ4WHhVeGP45wiFga0TGXNXfR3ENz30T6RJZE3ptnMU85ry1KNSo+qi5qPNo3ujS6P8YuZlnM1VidWElsSxw5LiquNm5svt/87fOH4p3iC+N7F5gvyF1weaHOwvSFpxapLhIsOpZATIhOOJTwQRAqqBaMJfITdyWOCnnCHcJnIi/RNtGI2ENcKh5O8kgqTXqS7JG8NXkkxTOlLOW5hCepkLxMDUzdmzqeFpp2IG0yPTq9MYOSkZBxQqohTZO2Z+pn5mZ2y6xlhbL+xW6Lty8elQfJa7OQrAVZLQq2QqboVFoo1yoHsmdlV2a/zYnKOZarnivN7cyzytuQN5zvn//tEsIS4ZK2pYZLVy0dWOa9rGo5sjxxedsK4xUFK4ZWBqw8uIq2Km3VT6vtV5eufr0mek1rgV7ByoLBtQFr6wtVCuWFfevc1+1dT1gvWd+1YfqGnRs+FYmKrhTbF5cVf9go3HjlG4dvyr+Z3JS0qavEuWTPZtJm6ebeLZ5bDpaql+aXDm4N2dq0Dd9WtO319kXbL5fNKNu7g7ZDuaO/PLi8ZafJzs07P1SkVPRU+lQ27tLdtWHX+G7R7ht7vPY07NXbW7z3/T7JvttVAVVN1WbVZftJ+7P3P66Jqun4lvttXa1ObXHtxwPSA/0HIw6217nU1R3SPVRSj9Yr60cOxx++/p3vdy0NNg1VjZzG4iNwRHnk6fcJ3/ceDTradox7rOEH0x92HWcdL2pCmvKaRptTmvtbYlu6T8w+0dbq3nr8R9sfD5w0PFl5SvNUyWna6YLTk2fyz4ydlZ19fi753GDborZ752PO32oPb++6EHTh0kX/i+c7vDvOXPK4dPKy2+UTV7hXmq86X23qdOo8/pPTT8e7nLuarrlca7nuer21e2b36RueN87d9L158Rb/1tWeOT3dvfN6b/fF9/XfFt1+cif9zsu72Xcn7q28T7xf9EDtQdlD3YfVP1v+3Njv3H9qwHeg89HcR/cGhYPP/pH1jw9DBY+Zj8uGDYbrnjg+OTniP3L96fynQ89kzyaeF/6i/suuFxYvfvjV69fO0ZjRoZfyl5O/bXyl/erA6xmv28bCxh6+yXgzMV70VvvtwXfcdx3vo98PT+R8IH8o/2j5sfVT0Kf7kxmTk/8EA5jz/GMzLdsAAAAgY0hSTQAAeiUAAICDAAD5/wAAgOkAAHUwAADqYAAAOpgAABdvkl/FRgAAAHlJREFUeNrcU1sNgDAQ6wgmcAM2MICGGlg1gJnNzWQcvwQGy1j4oUl/7tH0mpwzM7SgQyO+EZAUWh2MkkzSWhJwuRAlHYsJwEwyvs1gABDuzqoJcTw5qxaIJN0bgQRgIjnlmn1heSO5PE6Y2YXe+5Cr5+h++gs12AcAS6FS+7YOsj4AAAAASUVORK5CYII=" var imgCollapse = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAsTAAALEwEAmpwYAAAKT2lDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVNnVFPpFj333vRCS4iAlEtvUhUIIFJCi4AUkSYqIQkQSoghodkVUcERRUUEG8igiAOOjoCMFVEsDIoK2AfkIaKOg6OIisr74Xuja9a89+bN/rXXPues852zzwfACAyWSDNRNYAMqUIeEeCDx8TG4eQuQIEKJHAAEAizZCFz/SMBAPh+PDwrIsAHvgABeNMLCADATZvAMByH/w/qQplcAYCEAcB0kThLCIAUAEB6jkKmAEBGAYCdmCZTAKAEAGDLY2LjAFAtAGAnf+bTAICd+Jl7AQBblCEVAaCRACATZYhEAGg7AKzPVopFAFgwABRmS8Q5ANgtADBJV2ZIALC3AMDOEAuyAAgMADBRiIUpAAR7AGDIIyN4AISZABRG8lc88SuuEOcqAAB4mbI8uSQ5RYFbCC1xB1dXLh4ozkkXKxQ2YQJhmkAuwnmZGTKBNA/g88wAAKCRFRHgg/P9eM4Ors7ONo62Dl8t6r8G/yJiYuP+5c+rcEAAAOF0ftH+LC+zGoA7BoBt/qIl7gRoXgugdfeLZrIPQLUAoOnaV/Nw+H48PEWhkLnZ2eXk5NhKxEJbYcpXff5nwl/AV/1s+X48/Pf14L7iJIEyXYFHBPjgwsz0TKUcz5IJhGLc5o9H/LcL//wd0yLESWK5WCoU41EScY5EmozzMqUiiUKSKcUl0v9k4t8s+wM+3zUAsGo+AXuRLahdYwP2SycQWHTA4vcAAPK7b8HUKAgDgGiD4c93/+8//UegJQCAZkmScQAAXkQkLlTKsz/HCAAARKCBKrBBG/TBGCzABhzBBdzBC/xgNoRCJMTCQhBCCmSAHHJgKayCQiiGzbAdKmAv1EAdNMBRaIaTcA4uwlW4Dj1wD/phCJ7BKLyBCQRByAgTYSHaiAFiilgjjggXmYX4IcFIBBKLJCDJiBRRIkuRNUgxUopUIFVIHfI9cgI5h1xGupE7yAAygvyGvEcxlIGyUT3UDLVDuag3GoRGogvQZHQxmo8WoJvQcrQaPYw2oefQq2gP2o8+Q8cwwOgYBzPEbDAuxsNCsTgsCZNjy7EirAyrxhqwVqwDu4n1Y8+xdwQSgUXACTYEd0IgYR5BSFhMWE7YSKggHCQ0EdoJNwkDhFHCJyKTqEu0JroR+cQYYjIxh1hILCPWEo8TLxB7iEPENyQSiUMyJ7mQAkmxpFTSEtJG0m5SI+ksqZs0SBojk8naZGuyBzmULCAryIXkneTD5DPkG+Qh8lsKnWJAcaT4U+IoUspqShnlEOU05QZlmDJBVaOaUt2ooVQRNY9aQq2htlKvUYeoEzR1mjnNgxZJS6WtopXTGmgXaPdpr+h0uhHdlR5Ol9BX0svpR+iX6AP0dwwNhhWDx4hnKBmbGAcYZxl3GK+YTKYZ04sZx1QwNzHrmOeZD5lvVVgqtip8FZHKCpVKlSaVGyovVKmqpqreqgtV81XLVI+pXlN9rkZVM1PjqQnUlqtVqp1Q61MbU2epO6iHqmeob1Q/pH5Z/YkGWcNMw09DpFGgsV/jvMYgC2MZs3gsIWsNq4Z1gTXEJrHN2Xx2KruY/R27iz2qqaE5QzNKM1ezUvOUZj8H45hx+Jx0TgnnKKeX836K3hTvKeIpG6Y0TLkxZVxrqpaXllirSKtRq0frvTau7aedpr1Fu1n7gQ5Bx0onXCdHZ4/OBZ3nU9lT3acKpxZNPTr1ri6qa6UbobtEd79up+6Ynr5egJ5Mb6feeb3n+hx9L/1U/W36p/VHDFgGswwkBtsMzhg8xTVxbzwdL8fb8VFDXcNAQ6VhlWGX4YSRudE8o9VGjUYPjGnGXOMk423GbcajJgYmISZLTepN7ppSTbmmKaY7TDtMx83MzaLN1pk1mz0x1zLnm+eb15vft2BaeFostqi2uGVJsuRaplnutrxuhVo5WaVYVVpds0atna0l1rutu6cRp7lOk06rntZnw7Dxtsm2qbcZsOXYBtuutm22fWFnYhdnt8Wuw+6TvZN9un2N/T0HDYfZDqsdWh1+c7RyFDpWOt6azpzuP33F9JbpL2dYzxDP2DPjthPLKcRpnVOb00dnF2e5c4PziIuJS4LLLpc+Lpsbxt3IveRKdPVxXeF60vWdm7Obwu2o26/uNu5p7ofcn8w0nymeWTNz0MPIQ+BR5dE/C5+VMGvfrH5PQ0+BZ7XnIy9jL5FXrdewt6V3qvdh7xc+9j5yn+M+4zw33jLeWV/MN8C3yLfLT8Nvnl+F30N/I/9k/3r/0QCngCUBZwOJgUGBWwL7+Hp8Ib+OPzrbZfay2e1BjKC5QRVBj4KtguXBrSFoyOyQrSH355jOkc5pDoVQfujW0Adh5mGLw34MJ4WHhVeGP45wiFga0TGXNXfR3ENz30T6RJZE3ptnMU85ry1KNSo+qi5qPNo3ujS6P8YuZlnM1VidWElsSxw5LiquNm5svt/87fOH4p3iC+N7F5gvyF1weaHOwvSFpxapLhIsOpZATIhOOJTwQRAqqBaMJfITdyWOCnnCHcJnIi/RNtGI2ENcKh5O8kgqTXqS7JG8NXkkxTOlLOW5hCepkLxMDUzdmzqeFpp2IG0yPTq9MYOSkZBxQqohTZO2Z+pn5mZ2y6xlhbL+xW6Lty8elQfJa7OQrAVZLQq2QqboVFoo1yoHsmdlV2a/zYnKOZarnivN7cyzytuQN5zvn//tEsIS4ZK2pYZLVy0dWOa9rGo5sjxxedsK4xUFK4ZWBqw8uIq2Km3VT6vtV5eufr0mek1rgV7ByoLBtQFr6wtVCuWFfevc1+1dT1gvWd+1YfqGnRs+FYmKrhTbF5cVf9go3HjlG4dvyr+Z3JS0qavEuWTPZtJm6ebeLZ5bDpaql+aXDm4N2dq0Dd9WtO319kXbL5fNKNu7g7ZDuaO/PLi8ZafJzs07P1SkVPRU+lQ27tLdtWHX+G7R7ht7vPY07NXbW7z3/T7JvttVAVVN1WbVZftJ+7P3P66Jqun4lvttXa1ObXHtxwPSA/0HIw6217nU1R3SPVRSj9Yr60cOxx++/p3vdy0NNg1VjZzG4iNwRHnk6fcJ3/ceDTradox7rOEH0x92HWcdL2pCmvKaRptTmvtbYlu6T8w+0dbq3nr8R9sfD5w0PFl5SvNUyWna6YLTk2fyz4ydlZ19fi753GDborZ752PO32oPb++6EHTh0kX/i+c7vDvOXPK4dPKy2+UTV7hXmq86X23qdOo8/pPTT8e7nLuarrlca7nuer21e2b36RueN87d9L158Rb/1tWeOT3dvfN6b/fF9/XfFt1+cif9zsu72Xcn7q28T7xf9EDtQdlD3YfVP1v+3Njv3H9qwHeg89HcR/cGhYPP/pH1jw9DBY+Zj8uGDYbrnjg+OTniP3L96fynQ89kzyaeF/6i/suuFxYvfvjV69fO0ZjRoZfyl5O/bXyl/erA6xmv28bCxh6+yXgzMV70VvvtwXfcdx3vo98PT+R8IH8o/2j5sfVT0Kf7kxmTk/8EA5jz/GMzLdsAAAAgY0hSTQAAeiUAAICDAAD5/wAAgOkAAHUwAADqYAAAOpgAABdvkl/FRgAAAHFJREFUeNpi/P//PwMlgImBQsA44C6gvhfa29v3MzAwOODRc6CystIRbxi0t7fjDJjKykpGYrwwi1hxnLHQ3t7+jIGBQRJJ6HllZaUUKYEYRYBPOB0gBShKwKGA////48VtbW3/8clTnBIH3gCKkzJgAGvBX0dDm0sCAAAAAElFTkSuQmCC" $("tr[data-tt-level]", table).each(function(index, tr) { var level = $(tr).attr('data-tt-level') var td = $("td",tr).first() if(tr.trChildren.length>0){ td.prepend($('')) } else { td.prepend($('')) } td.prepend($('')) // td.css('white-space','nowrap') tr.trExpand = function(changeState){ if(this.trChildren.length < 1) return if(changeState) { this.trChildrenVisible = true $('#state', this).get(0).src= imgCollapse } var doit = changeState || this.trChildrenVisible $.each(this.trChildren, function(i, ctr) { if(doit) $(ctr).css('display', 'table-row') ctr.trExpand() }) } tr.trCollapse = function(changeState){ if(this.trChildren.length < 1) return if(changeState) { this.trChildrenVisible = false $('#state', this).get(0).src= imgExpand } $.each(this.trChildren, function(i, ctr) { $(ctr).css('display', 'none') ctr.trCollapse() }) } $(tr).click(function() { this.trChildrenVisible ? this.trCollapse(true) : this.trExpand(true) }) }) return table } function appendTreetable(tree, options) { function inALine(nodes) { var tr = $('') $.each(nodes, function(i, node){ tr.append($('
    ' + desc + '' + idAttr + '' + key + '
    ' + node[attr] + '' + node[idAttr] + '' + value + '
    ').append(node)) }) return $('').append(tr) } options = options || {} options.idAttr = (options.idAttr || 'id') options.childrenAttr = (options.childrenAttr || 'children') var controls = (options.controls || []) if (!options.mountPoint) options.mountPoint = $('body') if (options.depthFirst) depthFirst(tree, options.depthFirst, options.childrenAttr) var rendered = renderTree(tree, options.childrenAttr, options.idAttr, options.renderedAttr, options.renderer, options.tableAttributes) treeTable(rendered) if (options.replaceContent) { options.mountPoint.html('') } var initialExpandLevel = options.initialExpandLevel ? parseInt(options.initialExpandLevel) : -1 initialExpandLevel = Math.min(initialExpandLevel, tree[0].maxLevel) rendered.expandLevel(initialExpandLevel) if(options.slider){ var slider = $('
    ') slider.width('200px') slider.slider({ min : 1, max : tree[0].maxLevel, range : "min", value : initialExpandLevel, slide : function(event, ui) { rendered.expandLevel(ui.value) } }) controls = [slider].concat(options.controls) } if(controls.length >0){ options.mountPoint.append(inALine(controls)) } options.mountPoint.append(rendered) return rendered } return { depthFirst : depthFirst, makeTree : makeTree, renderTree : renderTree, attr2attr : attr2attr, treeTable : treeTable, appendTreetable : appendTreetable, jsTreeTable : '1.0', register : function(target){ $.each(this, function(key, value){ if(key != 'register') target[key] = value}) } } })(); ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/scripts/services/appservice.js ================================================ var app = angular.module('sentinelDashboardApp'); app.service('AppService', ['$http', function ($http) { this.getApps = function () { return $http({ // url: 'app/mock_infos', url: 'app/briefinfos.json', method: 'GET' }); }; }]); ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/scripts/services/auth_service.js ================================================ var app = angular.module('sentinelDashboardApp'); app.service('AuthService', ['$http', function ($http) { this.login = function (param) { return $http({ url: '/auth/login', params: param, method: 'POST' }) } this.logout = function () { return $http({ url: '/auth/logout', method: 'POST' }) } }]); ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/scripts/services/authority_service.js ================================================ /** * Authority rule service. */ angular.module('sentinelDashboardApp').service('AuthorityRuleService', ['$http', function ($http) { this.queryMachineRules = function(app, ip, port) { var param = { app: app, ip: ip, port: port }; return $http({ url: '/authority/rules', params: param, method: 'GET' }); }; this.addNewRule = function(rule) { return $http({ url: '/authority/rule', data: rule, method: 'POST' }); }; this.saveRule = function (entity) { return $http({ url: '/authority/rule/' + entity.id, data: entity, method: 'PUT' }); }; this.deleteRule = function (entity) { return $http({ url: '/authority/rule/' + entity.id, method: 'DELETE' }); }; this.checkRuleValid = function checkRuleValid(rule) { if (rule.resource === undefined || rule.resource === '') { alert('资源名称不能为空'); return false; } if (rule.limitApp === undefined || rule.limitApp === '') { alert('流控针对应用不能为空'); return false; } if (rule.strategy === undefined) { alert('必须选择黑白名单模式'); return false; } return true; }; }]); ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/scripts/services/cluster_state_service.js ================================================ /** * Cluster state control service. * * @author Eric Zhao */ angular.module('sentinelDashboardApp').service('ClusterStateService', ['$http', function ($http) { this.fetchClusterUniversalStateSingle = function(app, ip, port) { var param = { app: app, ip: ip, port: port }; return $http({ url: '/cluster/state_single', params: param, method: 'GET' }); }; this.fetchClusterUniversalStateOfApp = function(app) { return $http({ url: '/cluster/state/' + app, method: 'GET' }); }; this.fetchClusterServerStateOfApp = function(app) { return $http({ url: '/cluster/server_state/' + app, method: 'GET' }); }; this.fetchClusterClientStateOfApp = function(app) { return $http({ url: '/cluster/client_state/' + app, method: 'GET' }); }; this.modifyClusterConfig = function(config) { return $http({ url: '/cluster/config/modify_single', data: config, method: 'POST' }); }; this.applyClusterFullAssignOfApp = function(app, clusterMap) { return $http({ url: '/cluster/assign/all_server/' + app, data: clusterMap, method: 'POST' }); }; this.applyClusterSingleServerAssignOfApp = function(app, request) { return $http({ url: '/cluster/assign/single_server/' + app, data: request, method: 'POST' }); }; this.applyClusterServerBatchUnbind = function(app, machineSet) { return $http({ url: '/cluster/assign/unbind_server/' + app, data: machineSet, method: 'POST' }); }; }]); ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/scripts/services/degradeservice.js ================================================ var app = angular.module('sentinelDashboardApp'); app.service('DegradeService', ['$http', function ($http) { this.queryMachineRules = function (app, ip, port) { var param = { app: app, ip: ip, port: port }; return $http({ url: 'degrade/rules.json', params: param, method: 'GET' }); }; this.newRule = function (rule) { var param = { id: rule.id, resource: rule.resource, limitApp: rule.limitApp, count: rule.count, timeWindow: rule.timeWindow, grade: rule.grade, app: rule.app, ip: rule.ip, port: rule.port }; return $http({ url: '/degrade/new.json', params: param, method: 'GET' }); }; this.saveRule = function (rule) { var param = { id: rule.id, resource: rule.resource, limitApp: rule.limitApp, grade: rule.grade, count: rule.count, timeWindow: rule.timeWindow, }; return $http({ url: '/degrade/save.json', params: param, method: 'GET' }); }; this.deleteRule = function (rule) { var param = { id: rule.id, app: rule.app }; return $http({ url: '/degrade/delete.json', params: param, method: 'GET' }); }; this.checkRuleValid = function (rule) { if (rule.resource === undefined || rule.resource === '') { alert('资源名称不能为空'); return false; } if (rule.grade === undefined || rule.grade < 0) { alert('未知的降级策略'); return false; } if (rule.count === undefined || rule.count === '' || rule.count < 0) { alert('降级阈值不能为空或小于 0'); return false; } if (rule.timeWindow === undefined || rule.timeWindow === '' || rule.timeWindow <= 0) { alert('降级时间窗口必须大于 0'); return false; } // 异常比率类型. if (rule.grade == 1 && rule.count > 1) { alert('异常比率超出范围:[0.0 - 1.0]'); return false; } return true; }; }]); ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/scripts/services/flow_service_v1.js ================================================ var app = angular.module('sentinelDashboardApp'); app.service('FlowServiceV1', ['$http', function ($http) { this.queryMachineRules = function (app, ip, port) { var param = { app: app, ip: ip, port: port }; return $http({ url: '/v1/flow/rules', params: param, method: 'GET' }); }; this.newRule = function (rule) { var param = { resource: rule.resource, limitApp: rule.limitApp, grade: rule.grade, count: rule.count, strategy: rule.strategy, refResource: rule.refResource, controlBehavior: rule.controlBehavior, warmUpPeriodSec: rule.warmUpPeriodSec, maxQueueingTimeMs: rule.maxQueueingTimeMs, app: rule.app, ip: rule.ip, port: rule.port }; return $http({ url: '/v1/flow/rule', data: rule, method: 'POST' }); }; this.saveRule = function (rule) { var param = { id: rule.id, resource: rule.resource, limitApp: rule.limitApp, grade: rule.grade, count: rule.count, strategy: rule.strategy, refResource: rule.refResource, controlBehavior: rule.controlBehavior, warmUpPeriodSec: rule.warmUpPeriodSec, maxQueueingTimeMs: rule.maxQueueingTimeMs, }; return $http({ url: '/v1/flow/save.json', params: param, method: 'PUT' }); }; this.deleteRule = function (rule) { var param = { id: rule.id, app: rule.app }; return $http({ url: '/v1/flow/delete.json', params: param, method: 'DELETE' }); }; function notNumberAtLeastZero(num) { return num === undefined || num === '' || isNaN(num) || num < 0; } function notNumberGreaterThanZero(num) { return num === undefined || num === '' || isNaN(num) || num <= 0; } this.checkRuleValid = function (rule) { if (rule.resource === undefined || rule.resource === '') { alert('资源名称不能为空'); return false; } if (rule.count === undefined || rule.count < 0) { alert('限流阈值必须大于等于 0'); return false; } if (rule.strategy === undefined || rule.strategy < 0) { alert('无效的流控模式'); return false; } if (rule.strategy == 1 || rule.strategy == 2) { if (rule.refResource === undefined || rule.refResource == '') { alert('请填写关联资源或入口'); return false; } } if (rule.controlBehavior === undefined || rule.controlBehavior < 0) { alert('无效的流控整形方式'); return false; } if (rule.controlBehavior == 1 && notNumberGreaterThanZero(rule.warmUpPeriodSec)) { alert('预热时长必须大于 0'); return false; } if (rule.controlBehavior == 2 && notNumberGreaterThanZero(rule.maxQueueingTimeMs)) { alert('排队超时时间必须大于 0'); return false; } if (rule.clusterMode && (rule.clusterConfig === undefined || rule.clusterConfig.thresholdType === undefined)) { alert('集群限流配置不正确'); return false; } return true; }; }]); ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/scripts/services/flow_service_v2.js ================================================ var app = angular.module('sentinelDashboardApp'); app.service('FlowServiceV2', ['$http', function ($http) { this.queryMachineRules = function (app, ip, port) { var param = { app: app, ip: ip, port: port }; return $http({ url: '/v2/flow/rules', params: param, method: 'GET' }); }; this.newRule = function (rule) { return $http({ url: '/v2/flow/rule', data: rule, method: 'POST' }); }; this.saveRule = function (rule) { return $http({ url: '/v2/flow/rule/' + rule.id, data: rule, method: 'PUT' }); }; this.deleteRule = function (rule) { return $http({ url: '/v2/flow/rule/' + rule.id, method: 'DELETE' }); }; function notNumberAtLeastZero(num) { return num === undefined || num === '' || isNaN(num) || num < 0; } function notNumberGreaterThanZero(num) { return num === undefined || num === '' || isNaN(num) || num <= 0; } this.checkRuleValid = function (rule) { if (rule.resource === undefined || rule.resource === '') { alert('资源名称不能为空'); return false; } if (rule.count === undefined || rule.count < 0) { alert('限流阈值必须大于等于 0'); return false; } if (rule.strategy === undefined || rule.strategy < 0) { alert('无效的流控模式'); return false; } if (rule.strategy == 1 || rule.strategy == 2) { if (rule.refResource === undefined || rule.refResource == '') { alert('请填写关联资源或入口'); return false; } } if (rule.controlBehavior === undefined || rule.controlBehavior < 0) { alert('无效的流控整形方式'); return false; } if (rule.controlBehavior == 1 && notNumberGreaterThanZero(rule.warmUpPeriodSec)) { alert('预热时长必须大于 0'); return false; } if (rule.controlBehavior == 2 && notNumberGreaterThanZero(rule.maxQueueingTimeMs)) { alert('排队超时时间必须大于 0'); return false; } if (rule.clusterMode && (rule.clusterConfig === undefined || rule.clusterConfig.thresholdType === undefined)) { alert('集群限流配置不正确'); return false; } return true; }; }]); ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/scripts/services/identityservice.js ================================================ var app = angular.module('sentinelDashboardApp'); app.service('IdentityService', ['$http', function ($http) { this.fetchIdentityOfMachine = function (ip, port, searchKey) { var param = { ip: ip, port: port, searchKey: searchKey }; return $http({ url: 'resource/machineResource.json', params: param, method: 'GET' }); }; this.fetchClusterNodeOfMachine = function (ip, port, searchKey) { var param = { ip: ip, port: port, type: 'cluster', searchKey: searchKey }; return $http({ url: 'resource/machineResource.json', params: param, method: 'GET' }); }; }]); ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/scripts/services/machineservice.js ================================================ var app = angular.module('sentinelDashboardApp'); app.service('MachineService', ['$http', '$httpParamSerializerJQLike', function ($http, $httpParamSerializerJQLike) { this.getAppMachines = function (app) { return $http({ url: 'app/' + app + '/machines.json', method: 'GET' }); }; this.removeAppMachine = function (app, ip, port) { return $http({ url: 'app/' + app + '/machine/remove.json', method: 'POST', headers: { "Content-type": 'application/x-www-form-urlencoded; charset=UTF-8' }, data: $httpParamSerializerJQLike({ ip: ip, port: port }) }); }; }]); ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/scripts/services/metricservice.js ================================================ var app = angular.module('sentinelDashboardApp'); app.service('MetricService', ['$http', function ($http) { this.queryAppSortedIdentities = function (params) { return $http({ url: '/metric/queryTopResourceMetric.json', params: params, method: 'GET' }); }; this.queryByAppAndIdentity = function (params) { return $http({ url: '/metric/queryByAppAndResource.json', params: params, method: 'GET' }); }; this.queryByMachineAndIdentity = function (ip, port, identity, startTime, endTime) { var param = { ip: ip, port: port, identity: identity, startTime: startTime.getTime(), endTime: endTime.getTime() }; return $http({ url: '/metric/queryByAppAndResource.json', params: param, method: 'GET' }); }; }]); ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/scripts/services/param_flow_service.js ================================================ /** * Parameter flow control service. * * @author Eric Zhao */ angular.module('sentinelDashboardApp').service('ParamFlowService', ['$http', function ($http) { this.queryMachineRules = function(app, ip, port) { var param = { app: app, ip: ip, port: port }; return $http({ url: '/paramFlow/rules', params: param, method: 'GET' }); }; this.addNewRule = function(rule) { return $http({ url: '/paramFlow/rule', data: rule, method: 'POST' }); }; this.saveRule = function (entity) { return $http({ url: '/paramFlow/rule/' + entity.id, data: entity, method: 'PUT' }); }; this.deleteRule = function (entity) { return $http({ url: '/paramFlow/rule/' + entity.id, method: 'DELETE' }); }; function isNumberClass(classType) { return classType === 'int' || classType === 'double' || classType === 'float' || classType === 'long' || classType === 'short'; } function isByteClass(classType) { return classType === 'byte'; } function notNumberAtLeastZero(num) { return num === undefined || num === '' || isNaN(num) || num < 0; } function notGoodNumber(num) { return num === undefined || num === '' || isNaN(num); } function notGoodNumberBetweenExclusive(num, l ,r) { return num === undefined || num === '' || isNaN(num) || num < l || num > r; } function notValidParamItem(curExItem) { if (isNumberClass(curExItem.classType) && notGoodNumber(curExItem.object)) { return true; } if (isByteClass(curExItem.classType) && notGoodNumberBetweenExclusive(curExItem.object, -128, 127)) { return true; } return curExItem.object === undefined || curExItem.classType === undefined || notNumberAtLeastZero(curExItem.count); } this.checkRuleValid = function (rule) { if (!rule.resource || rule.resource === '') { alert('资源名称不能为空'); return false; } if (rule.grade != 1) { alert('未知的限流模式'); return false; } if (rule.count < 0) { alert('限流阈值必须大于等于 0'); return false; } if (rule.paramIdx === undefined || rule.paramIdx === '' || isNaN(rule.paramIdx) || rule.paramIdx < 0) { alert('热点参数索引必须大于等于 0'); return false; } if (rule.paramFlowItemList !== undefined) { for (var i = 0; i < rule.paramFlowItemList.length; i++) { var item = rule.paramFlowItemList[i]; if (notValidParamItem(item)) { alert('热点参数例外项不合法,请检查值和类型是否正确:参数为 ' + item.object + ', 类型为 ' + item.classType + ', 限流阈值为 ' + item.count); return false; } } } return true; }; }]); ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/scripts/services/systemservice.js ================================================ var app = angular.module('sentinelDashboardApp'); app.service('SystemService', ['$http', function ($http) { this.queryMachineRules = function (app, ip, port) { var param = { app: app, ip: ip, port: port }; return $http({ url: 'system/rules.json', params: param, method: 'GET' }); }; this.newRule = function (rule) { var param = { app: rule.app, ip: rule.ip, port: rule.port }; if (rule.grade == 0) {// avgLoad param.avgLoad = rule.avgLoad; } else if (rule.grade == 1) {// avgRt param.avgRt = rule.avgRt; } else if (rule.grade == 2) {// maxThread param.maxThread = rule.maxThread; } else if (rule.grade == 3) {// qps param.qps = rule.qps; } return $http({ url: '/system/new.json', params: param, method: 'GET' }); }; this.saveRule = function (rule) { var param = { id: rule.id, }; if (rule.grade == 0) {// avgLoad param.avgLoad = rule.avgLoad; } else if (rule.grade == 1) {// avgRt param.avgRt = rule.avgRt; } else if (rule.grade == 2) {// maxThread param.maxThread = rule.maxThread; } else if (rule.grade == 3) {// qps param.qps = rule.qps; } return $http({ url: '/system/save.json', params: param, method: 'GET' }); }; this.deleteRule = function (rule) { var param = { id: rule.id, app: rule.app }; return $http({ url: '/system/delete.json', params: param, method: 'GET' }); }; }]); ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/styles/main.css ================================================ .browsehappy { margin: 0.2em 0; background: #ccc; color: #000; padding: 0.2em 0; } body { padding: 0; } /* Everything but the jumbotron gets side spacing for mobile first views */ .header, .marketing, .footer { padding-left: 15px; padding-right: 15px; } /* Custom page header */ .header { border-bottom: 1px solid #e5e5e5; margin-bottom: 10px; } /* Make the masthead heading the same height as the navigation */ .header h3 { margin-top: 0; margin-bottom: 0; line-height: 40px; padding-bottom: 19px; } /* Custom page footer */ .footer { padding-top: 19px; color: #777; border-top: 1px solid #e5e5e5; } .container-narrow > hr { margin: 30px 0; } /* Main marketing message and sign up button */ .jumbotron { text-align: center; border-bottom: 1px solid #e5e5e5; } .jumbotron .btn { font-size: 21px; padding: 14px 24px; } /* Supporting marketing content */ .marketing { margin: 40px 0; } .marketing p + h4 { margin-top: 28px; } /* Responsive: Portrait tablets and up */ @media screen and (min-width: 768px) { .container { width: inherit; margin-left: 60px; margin-right: 5px; } /* Remove the padding we set earlier */ .header, .marketing, .footer { padding-left: 0; padding-right: 0; } /* Space out the masthead */ .header { margin-bottom: 30px; } /* Remove the bottom border on the jumbotron for visual effect */ .jumbotron { border-bottom: 0; } } .navbar-inverse { background-color: #1d9d74; border-color: #1b926c; } .navbar-inverse .navbar-nav > li > a { color: #b0ddce; font-size: 15px; } .navbar-inverse .navbar-nav>.open>a, .navbar-inverse .navbar-nav>.open>a:focus, .navbar-inverse .navbar-nav>.open>a:hover { background-color: #1b926c; } @media (min-width: 900px) { .navbar-left { float: left !important; } .navbar-right { float: right !important; margin-right: 0%; } .navbar-right ~ .navbar-right { margin-right: 0; } } .dropdown-menu { min-width: 100px !important; } .nav-sidebar li.active a { background: #DDD; } .dropdown-menu>li>a:hover, .dropdown-menu>li>a:focus { background: #1d9d74; /*background: #d9d9d9;*/ color: white; } .broadcast-message, .broadcast-message-preview { padding: 10px; text-align: center; background: #555; color: #BBB; margin-top: 50px; } .card { position: relative; border: 1px solid #d9d9d9; border-radius: 3px; color: #666; background-color: #fff; width: 100%; border-radius: 5px; } .card .card-header { padding: 9px 0; height: 40px; background: #555; color: #fff; text-align: center; border-top-left-radius: 4px; border-top-right-radius: 4px; } .card .card-body { padding: 12px 10px; } .card .card-footer { height: 20px; font-size: 10px; color: #777; margin-top: -15px; margin-bottom: 5px; margin-left: 20px; margin-right: 20px; } .card .detail-brand { float: left; width: 30%; line-height: 98px; font-size: 30px; text-align: center; color: white; } .card .default { background: #1d9d74; } .card .info { background: #6EBEE7; } .card .warn { background: #ED7F54; } .card .danger { background: #6583BE; } .card .detail .text-default { color: #1d9d74; } .card .detail .text-info { color: #6EBEE7; } .card .detail .text-warn { color: #ED7F54; } .card .detail .text-danger { color: #6583BE; } .card .detail { float: right; width: 70%; line-height: 98px; text-align: center; } .card .detail .text { font-size: 12px; } .card .detail .number { font-size: 30px; font-weight: 500; } .h100 { height: 100px; } .inline { display: inline; } .separator { height: 1px; background-color: #e5e5e5; margin-top: 10px; } .card > .card-body > table > thead > tr > td, .card > .card-body > table > tbody > tr > td { word-wrap: break-word; word-break: break-all; } .card > .card-body > table > thead > tr > td { font-weight: 500; font-size: 13px; text-align: center; } .card > .card-body > table > thead > tr > td > span { font-weight: 500; font-size: 10px; } .card > .card-body > table > tbody > tr > td { font-size: 12px; text-align: center; } .card > .card-body > table > tbody > tr > td > a { color: #666; } .thumbnails > .card > .card-body > table > thead > tr > td, .thumbnails > .card > .card-body > table > tbody > tr > td { font-size: 12px; color: #777; word-wrap: break-word; word-break: break-all; } .thumbnails > .card > .card-body > table > thead > tr > td:nth-child(n+2) { text-align: center; } .thumbnails > .card > .card-body > table > tbody > tr > td:nth-child(n+2) { font-weight: 700; text-align: center; } .thumbnails > .card > .card-body > table > thead > tr > td:nth-child(1), .thumbnails > .card > .card-body > table > tbody > tr > td:nth-child(1) { text-align: left; } .tools-header { background: whitesmoke; padding: 9px 0; height: 40px; border-top-left-radius: 4px; border-top-right-radius: 4px; } .tools-header .brand { font-size: 13px; margin: 2px 10px; font-weight: 700; float: left; } .tools-header .brand > a { color: #666; } .tools-header > button, .tools-header > select, .tools-header > a { float: right; max-width: 80px; margin: 1px 10px; height: 25px; padding: 0 10px; line-height: 25px; color: #666; } .tools-header .paged { margin-right: 0px; } .btn { height: 32px; } .btn.btn-main { color: #ffffff; background-color: #337ab7; border-color: #337ab7; } .btn:focus, .btn:active { outline: none !important; } .btn-default:hover, .btn-default:focus, .btn-default:active { color: #1d9d74; border-color: #1d9d74; background: white; } .btn.btn-danger-tag { color: #ffffff; background-color: #d9534f; border-color: #d43f3a; line-height: 1px; font-size: 11px; padding: 4px 4px; } .btn.btn-danger { color: #333; background-color: #fff; border-color: #ccc; } .btn.btn-danger:hover, .btn.btn-danger:focus, .btn.btn-danger:active { color: #d9534f; border-color: #d9534f; background: white; } .form-control { height : 32px; } .form-control:focus { border-color: #337ab7; box-shadow: 0px 0px 0px rgba(0, 0, 0, 0.075) inset, 0px 0px 0px rgba(29, 157, 116, 1); } .form-control { border-radius: 8px; } .input-label:before { display: inline-block; content: "*"; color: #f44336; font-family: SimSun; font-size: 12px; -webkit-transform: TranslateX(-10px); -ms-transform: TranslateX(-10px); transform: TranslateX(-10px); } .label.label-main { color: #ffffff; background-color: #1d9d74; border-color: #1d9d74; } .badge-main { color: #ffffff; background-color: #1d9d74; border-color: #1d9d74; } .bootstrap-tagsinput { background-color: #fff; border: 1px solid #ccc; box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); display: inline-block; padding: 4px 6px; color: #555; vertical-align: middle; border-radius: 4px; /* max-width: 100%; */ width: 85%; height: 100px; line-height: 20px; cursor: text; } .bootstrap-tagsinput > .dropdown-menu { min-width: 40px; font-size: 12px; } .bootstrap-tagsinput > .dropdown-menu>.active>a, .bootstrap-tagsinput > .dropdown-menu>.active>a:focus, .bootstrap-tagsinput > .dropdown-menu>.active>a:hover { background-color: #1d9d74; background-image: -webkit-linear-gradient(top, #1d9d74 0, #1d9d74 100%); background-image: -o-linear-gradient(top, #1d9d74 0, #1d9d74 100%); background-image: -webkit-gradient(linear, left top, left bottom, from(#1d9d74), to(#1d9d74)); background-image: linear-gradient(to bottom, #1d9d74 0, #1d9d74 100%); filter: progid: DXImageTransform.Microsoft.gradient(startColorstr='#1d9d74', endColorstr='#1d9d74', GradientType=0); background-repeat: repeat-x; } .bootstrap-tagsinput > .dropdown-menu>.active>a, .bootstrap-tagsinput > .dropdown-menu>.active>a:focus, .bootstrap-tagsinput > .dropdown-menu>.active>a:hover { color: #fff; text-decoration: none; background-color: #1d9d74; outline: 0; } .bootstrap-tagsinput > .dropdown-menu>.active>a, .bootstrap-tagsinput > .dropdown-menu>.active>a:hover, .bootstrap-tagsinput > .dropdown-menu>.active>a:focus { color: white; text-decoration: none; outline: 0; background-color: #1d9d74; } .inputs-header { padding: 9px 0; height: 50px; border-top-left-radius: 4px; border-top-right-radius: 4px; } .inputs-header .brand { font-size: 13px; margin: 2px 10px; font-weight: 700; float: left; } .inputs-header .brand > a { color: #666; } .inputs-header > input { float: right; margin: 1px 10px; height: 30px; padding: 0 10px; color: #666; } .inputs-header > a { float: right; margin: 1px 10px; height: 30px; padding: 5 5px; } .inputs-header > select { float: right; max-width: 80px; margin: 1px 10px; height: 30px; padding: 0 10px; color: #666; height: 25px; font-size: 12px; } .witdh-150 { max-width: 150px; } .witdh-200 { max-width: 200px; } .width-200 { max-width: 200px; } .witdh-300 { max-width: 300px; } .width-300 { max-width: 300px; } .card.highlight { border-color: #d9534f; } .card .pagination-footer { height: 40px; font-size: 10px; color: #777; margin-top: -15px; margin-bottom: 5px; margin-left: 20px; margin-right: 20px; } .card .pagination-footer .tools { font-size: 12px; margin: 11px 0; float: right; display: inline; margin-right: 20px; } .card > .pagination-footer > .tools > span > input { height: 25px; max-width: 50px; display: inline; } .pagination { display: inline-block; padding-left: 0; margin: 8px 0; float: right; border-radius: 4px; } .pagination > a { margin-right: 5px; height: 28px; width: 28px; padding: 5px 0px; } .pagination > .btn.active { color: #ffffff; background-color: #1d9d74; border-color: #1d9d74; } .datepicker > .table > thead > tr > td, .datepicker > .table > tbody > tr > td, .timepicker > .table > thead > tr > td, .timepicker > .table > tbody > tr > td { padding: 5px 3px; } .datepicker > .table > thead > tr > td > .btn, .datepicker > .table > tbody > tr > td > .btn, .timepicker > .table > thead > tr > td > .btn, .timepicker > .table > tbody > tr > td > .btn { border: 1px solid #FFFDFD; } .datepicker > .table > thead > tr > td > .btn-default:hover, .datepicker > .table > thead > tr > td > .btn-default:focus, .datepicker > .table > thead > tr > td > .btn-default:active, .datepicker > .table > tbody > tr > td > .btn-default:hover, .datepicker > .table > tbody > tr > td > .btn-default:focus, .datepicker > .table > tbody > tr > td > .btn-default:active, .timepicker > .table > thead > tr > td > .btn-default:hover, .timepicker > .table > thead > tr > td > .btn-default:focus, .timepicker > .table > thead > tr > td > .btn-default:active, .timepicker > .table > tbody > tr > td > .btn-default:hover, .timepicker > .table > tbody > tr > td > .btn-default:focus, .timepicker > .table > tbody > tr > td > .btn-default:active { color: #1d9d74; border-color: #1d9d74; background: white; } .datepicker > .table > thead > tr > td > a, .datepicker > .table > tbody > tr > td > a, .timepicker > .table > thead > tr > td > a, .timepicker > .table > tbody > tr > td > a { height: 25px; width: 25px; padding: 3px 0px; } .datepicker > .table > tbody > tr:first-child > td > a { padding: 4px 0px; } .datepicker > .table > thead > tr > td > a.btn.active, .datepicker > .table > tbody > tr > td > a.btn.active, .timepicker > .table > thead > tr > td > a.btn.active, .timepicker > .table > tbody > tr > td > a.btn.active { /* color: #ffffff; background-color: #1d9d74; border-color: #1d9d74;*/ color: #1d9d74; border-color: #1d9d74; background: white; box-shadow: inset 0 0px 0px rgba(0,0,0,0.125); } .datepicker > .table > thead > tr > td:not(:first-child):last-child > a, .timepicker > .table > thead > tr > td:not(:first-child):last-child > a { height: 25px; width: 50px; padding: 5px 0px; } .datepicker > .table > tbody > tr > td > a, .timepicker > .table > tbody > tr > td > a { margin-left: 8px; } .selectize-input-200 > .selectize-input { min-width: 250px; } .highlight-border { border-color: #337ab7; box-shadow: 0px 0px 0px rgba(0, 0, 0, 0.075) inset, 0px 0px 0px rgba(29, 157, 116, 1); }.browsehappy { margin: 0.2em 0; background: #ccc; color: #000; padding: 0.2em 0; } body { padding: 0; } /* Everything but the jumbotron gets side spacing for mobile first views */ .header, .marketing, .footer { padding-left: 15px; padding-right: 15px; } /* Custom page header */ .header { border-bottom: 1px solid #e5e5e5; margin-bottom: 10px; } /* Make the masthead heading the same height as the navigation */ .header h3 { margin-top: 0; margin-bottom: 0; line-height: 40px; padding-bottom: 19px; } /* Custom page footer */ .footer { padding-top: 19px; color: #777; border-top: 1px solid #e5e5e5; } .container-narrow > hr { margin: 30px 0; } /* Main marketing message and sign up button */ .jumbotron { text-align: center; border-bottom: 1px solid #e5e5e5; } .jumbotron .btn { font-size: 21px; padding: 14px 24px; } /* Supporting marketing content */ .marketing { margin: 40px 0; } .marketing p + h4 { margin-top: 28px; } /* Responsive: Portrait tablets and up */ @media screen and (min-width: 768px) { .container { width: inherit; margin-left: 60px; margin-right: 5px; } /* Remove the padding we set earlier */ .header, .marketing, .footer { padding-left: 0; padding-right: 0; } /* Space out the masthead */ .header { margin-bottom: 30px; } /* Remove the bottom border on the jumbotron for visual effect */ .jumbotron { border-bottom: 0; } } .navbar-inverse { background-color: #1d9d74; border-color: #1b926c; } .navbar-inverse .navbar-nav > li > a { color: #b0ddce; font-size: 15px; } .navbar-inverse .navbar-nav>.open>a, .navbar-inverse .navbar-nav>.open>a:focus, .navbar-inverse .navbar-nav>.open>a:hover { background-color: #1b926c; } @media (min-width: 900px) { .navbar-left { float: left !important; } .navbar-right { float: right !important; margin-right: 0%; } .navbar-right ~ .navbar-right { margin-right: 0; } } .dropdown-menu { min-width: 100px !important; } .nav-sidebar li.active a { background: #DDD; } .dropdown-menu>li>a:hover, .dropdown-menu>li>a:focus { background: #1d9d74; /*background: #d9d9d9;*/ color: white; } .broadcast-message, .broadcast-message-preview { padding: 10px; text-align: center; background: #555; color: #BBB; margin-top: 50px; } .card { position: relative; border: 1px solid #d9d9d9; border-radius: 3px; color: #666; background-color: #fff; width: 100%; border-radius: 5px; } .card .card-header { padding: 9px 0; height: 40px; background: #555; color: #fff; text-align: center; border-top-left-radius: 4px; border-top-right-radius: 4px; } .card .card-body { padding: 12px 10px; } .card .card-footer { height: 20px; font-size: 10px; color: #777; margin-top: -15px; margin-bottom: 5px; margin-left: 20px; margin-right: 20px; } .card .detail-brand { float: left; width: 30%; line-height: 98px; font-size: 30px; text-align: center; color: white; } .card .default { background: #1d9d74; } .card .info { background: #6EBEE7; } .card .warn { background: #ED7F54; } .card .danger { background: #6583BE; } .card .detail .text-default { color: #1d9d74; } .card .detail .text-info { color: #6EBEE7; } .card .detail .text-warn { color: #ED7F54; } .card .detail .text-danger { color: #6583BE; } .card .detail { float: right; width: 70%; line-height: 98px; text-align: center; } .card .detail .text { font-size: 12px; } .card .detail .number { font-size: 30px; font-weight: 500; } .h100 { height: 100px; } .inline { display: inline; } .separator { height: 1px; background-color: #e5e5e5; margin-top: 10px; } .card > .card-body > table > thead > tr > td, .card > .card-body > table > tbody > tr > td { word-wrap: break-word; word-break: break-all; } .card > .card-body > table > thead > tr > td { font-weight: 500; font-size: 13px; text-align: center; } .card > .card-body > table > thead > tr > td > span { font-weight: 500; font-size: 10px; } .card > .card-body > table > tbody > tr > td { font-size: 12px; text-align: center; } .card > .card-body > table > tbody > tr > td > a { color: #666; } .thumbnails > .card > .card-body > table > thead > tr > td, .thumbnails > .card > .card-body > table > tbody > tr > td { font-size: 12px; color: #777; word-wrap: break-word; word-break: break-all; } .thumbnails > .card > .card-body > table > thead > tr > td:nth-child(n+2) { text-align: center; } .thumbnails > .card > .card-body > table > tbody > tr > td:nth-child(n+2) { font-weight: 700; text-align: center; } .thumbnails > .card > .card-body > table > thead > tr > td:nth-child(1), .thumbnails > .card > .card-body > table > tbody > tr > td:nth-child(1) { text-align: left; } .tools-header { background: whitesmoke; padding: 9px 0; height: 40px; border-top-left-radius: 4px; border-top-right-radius: 4px; } .tools-header .brand { font-size: 13px; margin: 2px 10px; font-weight: 700; float: left; } .tools-header .brand > a { color: #666; } .tools-header > button, .tools-header > select, .tools-header > a { float: right; max-width: 80px; margin: 1px 10px; height: 25px; padding: 0 10px; line-height: 25px; color: #666; } .tools-header .paged { margin-right: 0px; } .btn.btn-main { color: #ffffff; background-color: #1d9d74; border-color: #1d9d74; } .btn:focus, .btn:active { outline: none !important; } .btn-default:hover, .btn-default:focus, .btn-default:active { color: #1d9d74; border-color: #1d9d74; background: white; } .btn-default-inverse { color: #1d9d74; border-color: #1d9d74; background: white; } .btn-default-inverse:hover, .btn-default-inverse:focus, .btn-default:active { color: #1d9d74; border-color: #1d9d74; background: white; } .btn.btn-danger-tag { color: #ffffff; background-color: #d9534f; border-color: #d43f3a; line-height: 1px; font-size: 11px; padding: 4px 4px; } .btn.btn-danger { color: #333; background-color: #fff; border-color: #ccc; } .btn.btn-danger:hover, .btn.btn-danger:focus, .btn.btn-danger:active { color: #d9534f; border-color: #d9534f; background: white; } .form-control { height : 32px; } .form-control:focus { border-color: #1d9d74; box-shadow: 0px 0px 0px rgba(0, 0, 0, 0.075) inset, 0px 0px 0px rgba(29, 157, 116, 1); } .form-control { border-radius: 8px; } .input-label:before { display: inline-block; content: "*"; color: #f44336; font-family: SimSun; font-size: 12px; -webkit-transform: TranslateX(-10px); -ms-transform: TranslateX(-10px); transform: TranslateX(-10px); } .label.label-main { color: #ffffff; background-color: #1d9d74; border-color: #1d9d74; } .badge-main { color: #ffffff; background-color: #1d9d74; border-color: #1d9d74; } .bootstrap-tagsinput { background-color: #fff; border: 1px solid #ccc; box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); display: inline-block; padding: 4px 6px; color: #555; vertical-align: middle; border-radius: 4px; /* max-width: 100%; */ width: 85%; height: 100px; line-height: 20px; cursor: text; } .bootstrap-tagsinput > .dropdown-menu { min-width: 40px; font-size: 12px; } .bootstrap-tagsinput > .dropdown-menu>.active>a, .bootstrap-tagsinput > .dropdown-menu>.active>a:focus, .bootstrap-tagsinput > .dropdown-menu>.active>a:hover { background-color: #1d9d74; background-image: -webkit-linear-gradient(top, #1d9d74 0, #1d9d74 100%); background-image: -o-linear-gradient(top, #1d9d74 0, #1d9d74 100%); background-image: -webkit-gradient(linear, left top, left bottom, from(#1d9d74), to(#1d9d74)); background-image: linear-gradient(to bottom, #1d9d74 0, #1d9d74 100%); filter: progid: DXImageTransform.Microsoft.gradient(startColorstr='#1d9d74', endColorstr='#1d9d74', GradientType=0); background-repeat: repeat-x; } .bootstrap-tagsinput > .dropdown-menu>.active>a, .bootstrap-tagsinput > .dropdown-menu>.active>a:focus, .bootstrap-tagsinput > .dropdown-menu>.active>a:hover { color: #fff; text-decoration: none; background-color: #1d9d74; outline: 0; } .bootstrap-tagsinput > .dropdown-menu>.active>a, .bootstrap-tagsinput > .dropdown-menu>.active>a:hover, .bootstrap-tagsinput > .dropdown-menu>.active>a:focus { color: white; text-decoration: none; outline: 0; background-color: #1d9d74; } .inputs-header { padding: 9px 0; height: 50px; border-top-left-radius: 4px; border-top-right-radius: 4px; } .inputs-header .brand { font-size: 13px; margin: 2px 10px; font-weight: 700; float: left; } .inputs-header .brand > a { color: #666; } .inputs-header > input { float: right; margin: 1px 10px; height: 30px; padding: 0 10px; color: #666; } .inputs-header > a { float: right; margin: 1px 10px; height: 30px; padding: 5 5px; } .inputs-header > select { float: right; max-width: 80px; margin: 1px 10px; height: 30px; padding: 0 10px; color: #666; height: 25px; font-size: 12px; } .witdh-150 { max-width: 150px; } .witdh-200 { max-width: 200px; } .card.highlight { border-color: #d9534f; } .card .pagination-footer { height: 40px; font-size: 10px; color: #777; margin-top: -15px; margin-bottom: 5px; margin-left: 20px; margin-right: 20px; } .card .pagination-footer .tools { font-size: 12px; margin: 11px 0; float: right; display: inline; margin-right: 20px; } .card > .pagination-footer > .tools > span > input { height: 25px; max-width: 50px; display: inline; } .pagination { display: inline-block; padding-left: 0; margin: 8px 0; float: right; border-radius: 4px; } .pagination > a { margin-right: 5px; height: 28px; width: 28px; padding: 5px 0px; } .pagination > .btn.active { color: #ffffff; background-color: #449d44; border-color: #449d44; } .datepicker > .table > thead > tr > td, .datepicker > .table > tbody > tr > td, .timepicker > .table > thead > tr > td, .timepicker > .table > tbody > tr > td { padding: 5px 3px; } .datepicker > .table > thead > tr > td > .btn, .datepicker > .table > tbody > tr > td > .btn, .timepicker > .table > thead > tr > td > .btn, .timepicker > .table > tbody > tr > td > .btn { border: 1px solid #FFFDFD; } .datepicker > .table > thead > tr > td > .btn-default:hover, .datepicker > .table > thead > tr > td > .btn-default:focus, .datepicker > .table > thead > tr > td > .btn-default:active, .datepicker > .table > tbody > tr > td > .btn-default:hover, .datepicker > .table > tbody > tr > td > .btn-default:focus, .datepicker > .table > tbody > tr > td > .btn-default:active, .timepicker > .table > thead > tr > td > .btn-default:hover, .timepicker > .table > thead > tr > td > .btn-default:focus, .timepicker > .table > thead > tr > td > .btn-default:active, .timepicker > .table > tbody > tr > td > .btn-default:hover, .timepicker > .table > tbody > tr > td > .btn-default:focus, .timepicker > .table > tbody > tr > td > .btn-default:active { color: #1d9d74; border-color: #1d9d74; background: white; } .datepicker > .table > thead > tr > td > a, .datepicker > .table > tbody > tr > td > a, .timepicker > .table > thead > tr > td > a, .timepicker > .table > tbody > tr > td > a { height: 25px; width: 25px; padding: 3px 0px; } .datepicker > .table > tbody > tr:first-child > td > a { padding: 4px 0px; } .datepicker > .table > thead > tr > td > a.btn.active, .datepicker > .table > tbody > tr > td > a.btn.active, .timepicker > .table > thead > tr > td > a.btn.active, .timepicker > .table > tbody > tr > td > a.btn.active { /* color: #ffffff; background-color: #1d9d74; border-color: #1d9d74;*/ color: #1d9d74; border-color: #1d9d74; background: white; box-shadow: inset 0 0px 0px rgba(0,0,0,0.125); } .datepicker > .table > thead > tr > td:not(:first-child):last-child > a, .timepicker > .table > thead > tr > td:not(:first-child):last-child > a { height: 25px; width: 50px; padding: 5px 0px; } .datepicker > .table > tbody > tr > td > a, .timepicker > .table > tbody > tr > td > a { margin-left: 8px; } .selectize-input-200 > .selectize-input { min-width: 250px; } .highlight-border { border-color: #1d9d74; box-shadow: 0px 0px 0px rgba(0, 0, 0, 0.075) inset, 0px 0px 0px rgba(29, 157, 116, 1); } .sortorder:after { content: '\25b2'; } .sortorder.reverse:after { content: '\25bc'; } .input-control select { -moz-appearance: none; -webkit-appearance: none; appearance: none; position: relative; border: 1px #d9d9d9 solid; width: 100%; height: 100%; padding: .3125rem; z-index: 0; } .navbar-inverse { background-color: #337ab7; border-color: #337ab7; } .sidebar { z-index: 1; width: 220px; /*position: fixed;*/ top: 0; left: 0; height: 100%; } #page-wrapper { position: inherit; margin: 70px 0 0 220px; padding: 12px 30px; border-left: 0px solid #e7e7e7; } .sidebar .sidebar-nav.navbar-collapse { padding-right: 0; padding-left: 0; background-color: #F5F5F5; position: relative; color: black; width: 100%; padding: 0; margin: 0; list-style: none inside none; } .sidebar a { color: #555; } .sidebar ul li:hover { color:red; } .form-control { border-radius: 8px; } .form-control:focus { border-color: #337ab7; box-shadow: 0px 0px 0px rgba(0, 0, 0, 0.075) inset, 0px 0px 0px rgba(29, 157, 116, 1); } .highlight-border { border-color: #337ab7; box-shadow: 0px 0px 0px rgba(0, 0, 0, 0.075) inset, 0px 0px 0px rgba(29, 157, 116, 1); }.browsehappy { margin: 0.2em 0; background: #ccc; color: #000; padding: 0.2em 0; } .btn.btn-main { color: #ffffff; background-color: #337ab7; border-color: #337ab7; } .btn-default-inverse { color: #337ab7; border-color: #337ab7; background: white; } .btn-default-inverse:hover, .btn-default-inverse:focus, .btn-default:active { color: #337ab7; border-color: #337ab7; background: white; } .btn-danger-inverse { color: #d9534f; border-color: #d9534f; background: white; } .btn-danger-inverse:hover, .btn-danger-inverse:focus, .btn-danger:active { color: #d9534f; border-color: #d9534f; background: white; } .btn-tab-active, .btn-tab-active:hover, .btn-tab-active:focus, .btn-tab-default:hover, .btn-tab-default:focus, .btn-tab-default:active { color: #337ab7; border-color: #337ab7; background: white; font-weight: 600; } .btn-tab-default { color: #777; background: white; font-weight: 600; } .pagination > .btn.active { color: #ffffff; background-color: #337ab7; border-color: #337ab7; } .btn-default:hover, .btn-default:focus, .btn-default:active { color: #337ab7; border-color: #337ab7; background: white; } .bootstrap-switch.bootstrap-switch-on { border-color: #337ab7; } .bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-success, .bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-success { color: #fff; background: #337ab7; } .selectize-input-200 > .selectize-input { min-width: 200px; border-color: #337ab7; } .btn-outline-primary { color: #007bff; background-color: transparent; background-image: none; border-color: #007bff; } .btn-outline-primary:hover { color: #fff; background-color: #007bff; border-color: #007bff; } .btn-outline-primary:focus, .btn-outline-primary.focus { box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.5); } .btn-outline-primary.disabled, .btn-outline-primary:disabled { color: #007bff; background-color: transparent; } .btn-outline-primary:not(:disabled):not(.disabled):active, .btn-outline-primary:not(:disabled):not(.disabled).active, .show > .btn-outline-primary.dropdown-toggle { color: #fff; background-color: #007bff; border-color: #007bff; } .btn-outline-primary:not(:disabled):not(.disabled):active:focus, .btn-outline-primary:not(:disabled):not(.disabled).active:focus, .show > .btn-outline-primary.dropdown-toggle:focus { box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.5); } .btn-outline-secondary { color: #6c757d; background-color: transparent; background-image: none; border-color: #6c757d; } .btn-outline-secondary:hover { color: #fff; background-color: #6c757d; border-color: #6c757d; } .btn-outline-secondary:focus, .btn-outline-secondary.focus { box-shadow: 0 0 0 0.2rem rgba(108, 117, 125, 0.5); } .btn-outline-secondary.disabled, .btn-outline-secondary:disabled { color: #6c757d; background-color: transparent; } .btn-outline-secondary:not(:disabled):not(.disabled):active, .btn-outline-secondary:not(:disabled):not(.disabled).active, .show > .btn-outline-secondary.dropdown-toggle { color: #fff; background-color: #6c757d; border-color: #6c757d; } .btn-outline-secondary:not(:disabled):not(.disabled):active:focus, .btn-outline-secondary:not(:disabled):not(.disabled).active:focus, .show > .btn-outline-secondary.dropdown-toggle:focus { box-shadow: 0 0 0 0.2rem rgba(108, 117, 125, 0.5); } .btn-outline-success { color: #28a745; background-color: transparent; background-image: none; border-color: #28a745; } .btn-outline-success:hover { color: #fff; background-color: #28a745; border-color: #28a745; } .btn-outline-success:focus, .btn-outline-success.focus { box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.5); } .btn-outline-success.disabled, .btn-outline-success:disabled { color: #28a745; background-color: transparent; } .btn-outline-success:not(:disabled):not(.disabled):active, .btn-outline-success:not(:disabled):not(.disabled).active, .show > .btn-outline-success.dropdown-toggle { color: #fff; background-color: #28a745; border-color: #28a745; } .btn-outline-success:not(:disabled):not(.disabled):active:focus, .btn-outline-success:not(:disabled):not(.disabled).active:focus, .show > .btn-outline-success.dropdown-toggle:focus { box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.5); } .btn-outline-info { color: #17a2b8; background-color: transparent; background-image: none; border-color: #17a2b8; } .btn-outline-info:hover { color: #fff; background-color: #17a2b8; border-color: #17a2b8; } .btn-outline-info:focus, .btn-outline-info.focus { box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5); } .btn-outline-info.disabled, .btn-outline-info:disabled { color: #17a2b8; background-color: transparent; } .btn-outline-info:not(:disabled):not(.disabled):active, .btn-outline-info:not(:disabled):not(.disabled).active, .show > .btn-outline-info.dropdown-toggle { color: #fff; background-color: #17a2b8; border-color: #17a2b8; } .btn-outline-info:not(:disabled):not(.disabled):active:focus, .btn-outline-info:not(:disabled):not(.disabled).active:focus, .show > .btn-outline-info.dropdown-toggle:focus { box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5); } .btn-outline-warning { color: #ffc107; background-color: transparent; background-image: none; border-color: #ffc107; } .btn-outline-warning:hover { color: #212529; background-color: #ffc107; border-color: #ffc107; } .btn-outline-warning:focus, .btn-outline-warning.focus { box-shadow: 0 0 0 0.2rem rgba(255, 193, 7, 0.5); } .btn-outline-warning.disabled, .btn-outline-warning:disabled { color: #ffc107; background-color: transparent; } .btn-outline-warning:not(:disabled):not(.disabled):active, .btn-outline-warning:not(:disabled):not(.disabled).active, .show > .btn-outline-warning.dropdown-toggle { color: #212529; background-color: #ffc107; border-color: #ffc107; } .btn-outline-warning:not(:disabled):not(.disabled):active:focus, .btn-outline-warning:not(:disabled):not(.disabled).active:focus, .show > .btn-outline-warning.dropdown-toggle:focus { box-shadow: 0 0 0 0.2rem rgba(255, 193, 7, 0.5); } .btn-outline-danger { color: #dc3545; background-color: transparent; background-image: none; border-color: #dc3545; } .btn-outline-danger:hover { color: #fff; background-color: #dc3545; border-color: #dc3545; } .btn-outline-danger:focus, .btn-outline-danger.focus { box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.5); } .btn-outline-danger.disabled, .btn-outline-danger:disabled { color: #dc3545; background-color: transparent; } .btn-outline-danger:not(:disabled):not(.disabled):active, .btn-outline-danger:not(:disabled):not(.disabled).active, .show > .btn-outline-danger.dropdown-toggle { color: #fff; background-color: #dc3545; border-color: #dc3545; } .btn-outline-danger:not(:disabled):not(.disabled):active:focus, .btn-outline-danger:not(:disabled):not(.disabled).active:focus, .show > .btn-outline-danger.dropdown-toggle:focus { box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.5); } .btn-outline-light { color: #f8f9fa; background-color: transparent; background-image: none; border-color: #f8f9fa; } .btn-outline-light:hover { color: #212529; background-color: #f8f9fa; border-color: #f8f9fa; } .btn-outline-light:focus, .btn-outline-light.focus { box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5); } .btn-outline-light.disabled, .btn-outline-light:disabled { color: #f8f9fa; background-color: transparent; } .btn-outline-light:not(:disabled):not(.disabled):active, .btn-outline-light:not(:disabled):not(.disabled).active, .show > .btn-outline-light.dropdown-toggle { color: #212529; background-color: #f8f9fa; border-color: #f8f9fa; } .btn-outline-light:not(:disabled):not(.disabled):active:focus, .btn-outline-light:not(:disabled):not(.disabled).active:focus, .show > .btn-outline-light.dropdown-toggle:focus { box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5); } .btn-outline-dark { color: #343a40; background-color: transparent; background-image: none; border-color: #343a40; } .btn-outline-dark:hover { color: #fff; background-color: #343a40; border-color: #343a40; } .btn-outline-dark:focus, .btn-outline-dark.focus { box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5); } .btn-outline-dark.disabled, .btn-outline-dark:disabled { color: #343a40; background-color: transparent; } .btn-outline-dark:not(:disabled):not(.disabled):active, .btn-outline-dark:not(:disabled):not(.disabled).active, .show > .btn-outline-dark.dropdown-toggle { color: #fff; background-color: #343a40; border-color: #343a40; } .btn-outline-dark:not(:disabled):not(.disabled):active:focus, .btn-outline-dark:not(:disabled):not(.disabled).active:focus, .show > .btn-outline-dark.dropdown-toggle:focus { box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5); } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/styles/page.css ================================================ /*! * Start Bootstrap - SB Admin 2 Bootstrap Admin Theme (http://startbootstrap.com) * Code licensed under the Apache License v2.0. * For details, see http://www.apache.org/licenses/LICENSE-2.0. */ body { background-color: #f8f8f8; } .example { padding: .625rem 1.825rem .625rem 2.5rem; border: 1px #ccc dashed; position: relative; margin: 0 0 .625rem 0; background-color: #ffffff; } dl dt, dl dd { line-height: 1.25rem; } dl dt { font-style: normal; font-weight: 700; } dl dd { margin-left: .9375rem; } dl.horizontal dt { float: left; width: 10rem; overflow: hidden; clear: left; text-align: right; text-overflow: ellipsis; white-space: nowrap; } dl.horizontal dd { margin-left: 11.25rem; } #wrapper { width: 100%; } #page-wrapper { padding: 0 15px; min-height: 568px; background-color: #fff; } @media(min-width:768px) { #page-wrapper { position: inherit; margin: 0 0 0 250px; padding: 0 30px; border-left: 1px solid #e7e7e7; } } .navbar-top-links { margin-right: 0; } .navbar-top-links li { display: inline-block; } .navbar-top-links li:last-child { margin-right: 15px; } .navbar-top-links li a { padding: 15px; min-height: 50px; } .navbar-top-links .dropdown-menu li { display: block; } .navbar-top-links .dropdown-menu li:last-child { margin-right: 0; } .navbar-top-links .dropdown-menu li a { padding: 3px 20px; min-height: 0; } .navbar-top-links .dropdown-menu li a div { white-space: normal; } .navbar-top-links .dropdown-messages, .navbar-top-links .dropdown-tasks, .navbar-top-links .dropdown-alerts { width: 310px; min-width: 0; } .navbar-top-links .dropdown-messages { margin-left: 5px; } .navbar-top-links .dropdown-tasks { margin-left: -59px; } .navbar-top-links .dropdown-alerts { margin-left: -123px; } .navbar-top-links .dropdown-user { right: 0; left: auto; } .sidebar .sidebar-nav.navbar-collapse { padding-right: 0; padding-left: 0; background-color: #71b1d1; color: #ffffff; position: relative; width: 100%; padding: 0; margin: 0; list-style: none inside none; } .sidebar .sidebar-search { padding: 15px; } .sidebar ul li { border-bottom: 1px solid #e7e7e7; } .sidebar ul li a.active { background-color: #ffffff; color: #ffffff; } .sidebar a{ color: #fff; } .sidebar .arrow { float: right; } .sidebar .fa.arrow:before { content: "\f104"; } .sidebar .active>a>.fa.arrow:before { content: "\f107"; } .sidebar .nav-second-level li, .sidebar .nav-third-level li { border-bottom: 0!important; } .sidebar .nav-second-level li a { padding-left: 37px; } .sidebar .nav-third-level li a { padding-left: 52px; } @media(min-width:768px) { .sidebar { z-index: 1; position: absolute; width: 250px; margin-top: 51px; } .navbar-top-links .dropdown-messages, .navbar-top-links .dropdown-tasks, .navbar-top-links .dropdown-alerts { margin-left: auto; } } .btn-outline { color: inherit; background-color: transparent; transition: all .5s; } .btn-primary.btn-outline { color: #428bca; } .btn-success.btn-outline { color: #5cb85c; } .btn-info.btn-outline { color: #5bc0de; } .btn-warning.btn-outline { color: #f0ad4e; } .btn-danger.btn-outline { color: #d9534f; } .btn-primary.btn-outline:hover, .btn-success.btn-outline:hover, .btn-info.btn-outline:hover, .btn-warning.btn-outline:hover, .btn-danger.btn-outline:hover { color: #fff; } .chat { margin: 0; padding: 0; list-style: none; } .chat li { margin-bottom: 10px; padding-bottom: 5px; border-bottom: 1px dotted #999; } .chat li.left .chat-body { margin-left: 60px; } .chat li.right .chat-body { margin-right: 60px; } .chat li .chat-body p { margin: 0; } .panel .slidedown .glyphicon, .chat .glyphicon { margin-right: 5px; } .chat-panel .panel-body { height: 350px; overflow-y: scroll; } .login-panel { margin-top: 25%; } .flot-chart { display: block; height: 400px; } .flot-chart-content { width: 100%; height: 100%; } .dataTables_wrapper { position: relative; clear: both; } table.dataTable thead .sorting, table.dataTable thead .sorting_asc, table.dataTable thead .sorting_desc, table.dataTable thead .sorting_asc_disabled, table.dataTable thead .sorting_desc_disabled { background: 0 0; } table.dataTable thead .sorting_asc:after { content: "\f0de"; float: right; font-family: fontawesome; } table.dataTable thead .sorting_desc:after { content: "\f0dd"; float: right; font-family: fontawesome; } table.dataTable thead .sorting:after { content: "\f0dc"; float: right; font-family: fontawesome; color: rgba(50,50,50,.5); } .btn-circle { width: 30px; height: 30px; padding: 6px 0; border-radius: 15px; text-align: center; font-size: 12px; line-height: 1.428571429; } .btn-circle.btn-lg { width: 50px; height: 50px; padding: 10px 16px; border-radius: 25px; font-size: 18px; line-height: 1.33; } .btn-circle.btn-xl { width: 70px; height: 70px; padding: 10px 16px; border-radius: 35px; font-size: 24px; line-height: 1.33; } .show-grid [class^=col-] { padding-top: 10px; padding-bottom: 10px; border: 1px solid #ddd; background-color: #eee!important; } .show-grid { margin: 15px 0; } .huge { font-size: 40px; } .panel-green { border-color: #5cb85c; } .panel-green .panel-heading { border-color: #5cb85c; color: #fff; background-color: #5cb85c; } .panel-green a { color: #5cb85c; } .panel-green a:hover { color: #3d8b3d; } .panel-red { border-color: #d9534f; } .panel-red .panel-heading { border-color: #d9534f; color: #fff; background-color: #d9534f; } .panel-red a { color: #d9534f; } .panel-red a:hover { color: #b52b27; } .panel-yellow { border-color: #f0ad4e; } .panel-yellow .panel-heading { border-color: #f0ad4e; color: #fff; background-color: #f0ad4e; } .panel-yellow a { color: #f0ad4e; } .panel-yellow a:hover { color: #df8a13; } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/styles/timeline.css ================================================ .timeline { position: relative; padding: 20px 0 20px; list-style: none; } .timeline:before { content: " "; position: absolute; top: 0; bottom: 0; left: 50%; width: 3px; margin-left: -1.5px; background-color: #eeeeee; } .timeline > li { position: relative; margin-bottom: 20px; } .timeline > li:before, .timeline > li:after { content: " "; display: table; } .timeline > li:after { clear: both; } .timeline > li:before, .timeline > li:after { content: " "; display: table; } .timeline > li:after { clear: both; } .timeline > li > .timeline-panel { float: left; position: relative; width: 46%; padding: 20px; border: 1px solid #d4d4d4; border-radius: 2px; -webkit-box-shadow: 0 1px 6px rgba(0,0,0,0.175); box-shadow: 0 1px 6px rgba(0,0,0,0.175); } .timeline > li > .timeline-panel:before { content: " "; display: inline-block; position: absolute; top: 26px; right: -15px; border-top: 15px solid transparent; border-right: 0 solid #ccc; border-bottom: 15px solid transparent; border-left: 15px solid #ccc; } .timeline > li > .timeline-panel:after { content: " "; display: inline-block; position: absolute; top: 27px; right: -14px; border-top: 14px solid transparent; border-right: 0 solid #fff; border-bottom: 14px solid transparent; border-left: 14px solid #fff; } .timeline > li > .timeline-badge { z-index: 100; position: absolute; top: 16px; left: 50%; width: 50px; height: 50px; margin-left: -25px; border-radius: 50% 50% 50% 50%; text-align: center; font-size: 1.4em; line-height: 50px; color: #fff; background-color: #999999; } .timeline > li.timeline-inverted > .timeline-panel { float: right; } .timeline > li.timeline-inverted > .timeline-panel:before { right: auto; left: -15px; border-right-width: 15px; border-left-width: 0; } .timeline > li.timeline-inverted > .timeline-panel:after { right: auto; left: -14px; border-right-width: 14px; border-left-width: 0; } .timeline-badge.primary { background-color: #2e6da4 !important; } .timeline-badge.success { background-color: #3f903f !important; } .timeline-badge.warning { background-color: #f0ad4e !important; } .timeline-badge.danger { background-color: #d9534f !important; } .timeline-badge.info { background-color: #5bc0de !important; } .timeline-title { margin-top: 0; color: inherit; } .timeline-body > p, .timeline-body > ul { margin-bottom: 0; } .timeline-body > p + p { margin-top: 5px; } @media(max-width:767px) { ul.timeline:before { left: 40px; } ul.timeline > li > .timeline-panel { width: calc(100% - 90px); width: -moz-calc(100% - 90px); width: -webkit-calc(100% - 90px); } ul.timeline > li > .timeline-badge { top: 16px; left: 15px; margin-left: 0; } ul.timeline > li > .timeline-panel { float: right; } ul.timeline > li > .timeline-panel:before { right: auto; left: -15px; border-right-width: 15px; border-left-width: 0; } ul.timeline > li > .timeline-panel:after { right: auto; left: -14px; border-right-width: 14px; border-left-width: 0; } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/views/authority.html ================================================
    {{app}}
    授权规则
    资源名 流控应用 授权类型 操作
    {{ruleEntity.rule.resource}} {{ruleEntity.rule.limitApp }} 白名单 黑名单
    ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/views/cluster/client.html ================================================

    未连接

    连接中

    已连接

    ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/views/cluster/server.html ================================================

    独立模式 (Alone)

    嵌入模式 (Embedded)

    ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/views/cluster_app_assign_manage.html ================================================
    {{app}}
    集群限流 - 机器分配/管控

    {{loadError.message}}

    ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/views/cluster_app_client_list.html ================================================
    集群限流 - Token Client 列表

    {{loadError.message}}

    Client ID Server IP Server 端口 连接状态 操作
    {{clientVO.id}} {{clientVO.state.clientConfig.serverHost}} {{clientVO.state.clientConfig.serverPort}} 未连接 连接中 已连接
    ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/views/cluster_app_server_list.html ================================================
    {{app}}
    Token Client 列表
    集群限流 - Token Server 列表

    {{loadError.message}}

    Server ID Port 命名空间集合 运行模式 总连接数 QPS 总览 操作
    {{serverVO.id}} {{serverVO.id}}(自主指定) {{serverVO.port}} {{serverVO.state.namespaceSetStr}} 未知 未知 嵌入模式 独立模式 {{serverVO.connectedCount}} 未知 {{serverVO.state.requestLimitDataStr}} 未知
    ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/views/cluster_app_server_overview.html ================================================
    {{app}}
    集群限流 - Token Server 总览

    {{loadError.message}}

    Server ID Port 命名空间集合 总连接数 连接情况 QPS 总览
    {{serverVO.id}} {{serverVO.port}} {{serverVO.state.namespaceSetStr}} {{serverVO.connectedCount}}

    namespace: {{cg.namespace}}, 连接数: {{cg.connectedCount}}, clients: {{generateConnectionSet(cg.connectionSet)}}

    namespace: {{crl.namespace}}, 当前 QPS: {{crl.currentQps}}, 最大允许 QPS: {{crl.maxAllowedQps}}

    ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/views/cluster_single_config.html ================================================
    {{app}}
    集群限流

    {{loadError.message}}

    Client

    Server

    未开启

     Client    Server

    该机器未引入 Sentinel 集群限流客户端或服务端的相关依赖,请引入相关依赖。

    ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/views/dashboard/home.html ================================================

    欢迎使用 Sentinel 控制台

    ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/views/dashboard/main.html ================================================
    ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/views/degrade.html ================================================
    {{app}}
    降级规则
    资源名 降级模式 阈值 时间窗口(s) 操作
    {{rule.resource}} RT 异常比例 异常数 {{rule.count}} {{rule.timeWindow}}s
    ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/views/dialog/authority-rule-dialog.html ================================================
    {{authorityRuleDialog.title}}
     白名单    黑名单
    ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/views/dialog/cluster/cluster-client-config-dialog.html ================================================
    修改 Token Client 配置

    {{ccDialogData.clientId}}

    ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/views/dialog/cluster/cluster-server-assign-dialog.html ================================================
    {{serverAssignDialogData.title}}

    {{serverAssignDialogData.serverData.currentServer}}

     应用内机器    外部指定机器

    若指定外部 server,请先在相应页面对外部 server 进行配置,然后在此页面指定。

    ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/views/dialog/cluster/cluster-server-connection-detail-dialog.html ================================================
    连接详情

    {{connectionDetailDialogData.serverData.id}}

    命名空间 连接数 连接详情
    {{cg.namespace}} {{cg.connectedCount}} {{generateConnectionSet(cg.connectionSet)}}
    ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/views/dialog/confirm-dialog.html ================================================
    {{confirmDialog.title}}

    {{confirmDialog.attentionTitle}}:

    {{confirmDialog.attention}}

    ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/views/dialog/degrade-rule-dialog.html ================================================
    {{degradeRuleDialog.title}}
     RT    异常比例    异常数
    ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/views/dialog/flow-rule-dialog.html ================================================
    {{flowRuleDialog.title}}
     QPS    线程数
     单机均摊    总体阈值
     如果 Token Server 不可用是否退化到单机限流
     直接    关联    链路  
     快速失败    Warm Up    排队等待
    ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/views/dialog/param-flow-rule-dialog.html ================================================
    {{paramFlowRuleDialog.title}}

    QPS 模式

     单机均摊    总体阈值

    参数值 参数类型 限流阈值 操作

    {{paramItem.classType}}

    ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/views/dialog/system-rule-dialog.html ================================================
    {{systemRuleDialog.title}}
     LOAD    RT    线程数    入口 QPS
     LOAD    RT    线程数    入口 QPS
    ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/views/flow_v1.html ================================================
    {{app}}
    流控规则
    资源名 来源应用 流控模式 阈值类型 阈值 阈值模式 流控效果 操作
    {{rule.resource}} {{rule.limitApp }} 直接 关联 链路 {{rule.grade==0 ? '线程数' : 'QPS'}} {{rule.count}} {{generateThresholdTypeShow(rule)}} 快速失败 Warm Up 排队等待 预热排队
    ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/views/flow_v2.html ================================================
    {{app}}
    回到单机页面
    流控规则
    资源名 来源应用 流控模式 阈值类型 阈值 阈值模式 流控效果 操作
    {{rule.resource}} {{rule.limitApp }} 直接 关联 链路 {{rule.grade == 0 ? '线程数' : 'QPS'}} {{rule.count}} {{generateThresholdTypeShow(rule)}} 快速失败 Warm Up 排队等待 预热排队
    ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/views/identity.html ================================================
    {{app}}
    簇点链路
    资源名 通过QPS 拒绝QPS 线程数 平均RT 分钟通过 分钟拒绝 操作
    {{resource.resource}} {{resource.passQps}} {{resource.blockQps}} {{resource.threadNum}} {{resource.averageRt}} {{resource.oneMinutePass}} {{resource.oneMinuteBlock}} {{resource.oneMinuteBlock}}
    ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/views/login.html ================================================
    Sentinel Logo
    ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/views/machine.html ================================================
    {{app}}
    机器列表 实例总数 {{machines.length}}, 健康 {{healthyCount}}, 失联 {{machines.length - healthyCount}}.
    机器名 IP 地址 端口号 Sentinel 客户端版本 健康状态 心跳时间 操作
    {{entry.hostname}} {{entry.ip}} {{entry.port}} {{entry.version}} 健康 失联 {{entry.lastHeartbeat | date: 'yyyy/MM/dd HH:mm:ss'}}
    ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/views/metric.html ================================================
    {{app}}
    {{metricTypeDesc}} 实时监控
     {{metric.resource}}
    时间 通过 QPS 拒绝QPS 响应时间(ms)
    {{tableObj.timestamp | date: 'HH:mm:ss'}} {{tableObj.passQps | number : 1}} {{tableObj.blockQps | number : 1}} {{tableObj.rt | number : 1}}
    -
  • ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/views/pagination.tpl.html ================================================ ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/views/param_flow.html ================================================
    {{app}}
    热点参数限流规则

    {{loadError.message}}

    资源名 参数索引 流控模式 阈值 是否集群 例外项数目 操作
    {{ruleEntity.rule.resource}} {{ruleEntity.rule.paramIdx}} {{ruleEntity.rule.grade == 1 ? 'QPS' : '未知'}} {{ruleEntity.rule.count}} {{ruleEntity.rule.paramFlowItemList == undefined ? 0 : ruleEntity.rule.paramFlowItemList.length}}
    ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/app/views/system.html ================================================
    {{app}}
    系统规则
    阈值类型 单机阈值 操作
    LOAD RT 线程数 QPS {{rule.avgLoad}} {{rule.avgRt}} {{rule.maxThread}} {{rule.qps}}
    ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/dist/css/app.css ================================================ .chat,.timeline{list-style:none}#loading-bar,#loading-bar-spinner{pointer-events:none;-webkit-pointer-events:none;-webkit-transition:350ms linear all;-moz-transition:350ms linear all;-o-transition:350ms linear all;transition:350ms linear all}#loading-bar-spinner.ng-enter,#loading-bar-spinner.ng-leave.ng-leave-active,#loading-bar.ng-enter,#loading-bar.ng-leave.ng-leave-active{opacity:0}#loading-bar-spinner.ng-enter.ng-enter-active,#loading-bar-spinner.ng-leave,#loading-bar.ng-enter.ng-enter-active,#loading-bar.ng-leave{opacity:1}#loading-bar .bar{-webkit-transition:width 350ms;-moz-transition:width 350ms;-o-transition:width 350ms;transition:width 350ms;background:#29d;position:fixed;z-index:10002;top:0;left:0;width:100%;height:2px;border-bottom-right-radius:1px;border-top-right-radius:1px}#loading-bar .peg{position:absolute;width:70px;right:0;top:0;height:2px;opacity:.45;-moz-box-shadow:#29d 1px 0 6px 1px;-ms-box-shadow:#29d 1px 0 6px 1px;-webkit-box-shadow:#29d 1px 0 6px 1px;box-shadow:#29d 1px 0 6px 1px;-moz-border-radius:100%;-webkit-border-radius:100%;border-radius:100%}#loading-bar-spinner{display:block;position:fixed;z-index:10002;top:10px;left:10px}#loading-bar-spinner .spinner-icon{width:14px;height:14px;border:2px solid transparent;border-top-color:#29d;border-left-color:#29d;border-radius:50%;-webkit-animation:loading-bar-spinner .4s linear infinite;-moz-animation:loading-bar-spinner .4s linear infinite;-ms-animation:loading-bar-spinner .4s linear infinite;-o-animation:loading-bar-spinner .4s linear infinite;animation:loading-bar-spinner .4s linear infinite}@-webkit-keyframes loading-bar-spinner{0%{-webkit-transform:rotate(0);transform:rotate(0)}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@-moz-keyframes loading-bar-spinner{0%{-moz-transform:rotate(0);transform:rotate(0)}100%{-moz-transform:rotate(360deg);transform:rotate(360deg)}}@-o-keyframes loading-bar-spinner{0%{-o-transform:rotate(0);transform:rotate(0)}100%{-o-transform:rotate(360deg);transform:rotate(360deg)}}@-ms-keyframes loading-bar-spinner{0%{-ms-transform:rotate(0);transform:rotate(0)}100%{-ms-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes loading-bar-spinner{0%{transform:rotate(0)}100%{transform:rotate(360deg)}}.bootstrap-switch{display:inline-block;direction:ltr;cursor:pointer;border-radius:4px;border:1px solid #ccc;position:relative;text-align:left;overflow:hidden;line-height:8px;z-index:0;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;vertical-align:middle;-webkit-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;-o-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.bootstrap-switch .bootstrap-switch-container{display:inline-block;top:0;border-radius:4px;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}.bootstrap-switch .bootstrap-switch-handle-off,.bootstrap-switch .bootstrap-switch-handle-on,.bootstrap-switch .bootstrap-switch-label{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;cursor:pointer;display:table-cell;vertical-align:middle;padding:6px 12px;font-size:14px;line-height:20px}.bootstrap-switch .bootstrap-switch-handle-off,.bootstrap-switch .bootstrap-switch-handle-on{text-align:center;z-index:1}.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-primary,.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-primary{color:#fff;background:#337ab7}.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-info,.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-info{color:#fff;background:#5bc0de}.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-warning,.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-warning{background:#f0ad4e;color:#fff}.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-danger,.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-danger{color:#fff;background:#d9534f}.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-default,.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-default{color:#000;background:#eee}.bootstrap-switch .bootstrap-switch-label{text-align:center;margin-top:-1px;margin-bottom:-1px;z-index:100;color:#333;background:#fff}.bootstrap-switch span::before{content:"\200b"}.bootstrap-switch .bootstrap-switch-handle-on{border-bottom-left-radius:3px;border-top-left-radius:3px}.bootstrap-switch .bootstrap-switch-handle-off{border-bottom-right-radius:3px;border-top-right-radius:3px}.bootstrap-switch input[type=radio],.bootstrap-switch input[type=checkbox]{position:absolute!important;top:0;left:0;margin:0;z-index:-1;opacity:0;filter:alpha(opacity=0);visibility:hidden}.bootstrap-switch.bootstrap-switch-mini .bootstrap-switch-handle-off,.bootstrap-switch.bootstrap-switch-mini .bootstrap-switch-handle-on,.bootstrap-switch.bootstrap-switch-mini .bootstrap-switch-label{padding:1px 5px;font-size:12px;line-height:1.5}.bootstrap-switch.bootstrap-switch-small .bootstrap-switch-handle-off,.bootstrap-switch.bootstrap-switch-small .bootstrap-switch-handle-on,.bootstrap-switch.bootstrap-switch-small .bootstrap-switch-label{padding:5px 10px;font-size:12px;line-height:1.5}.bootstrap-switch.bootstrap-switch-large .bootstrap-switch-handle-off,.bootstrap-switch.bootstrap-switch-large .bootstrap-switch-handle-on,.bootstrap-switch.bootstrap-switch-large .bootstrap-switch-label{padding:6px 16px;font-size:18px;line-height:1.3333333}.bootstrap-switch.bootstrap-switch-disabled,.bootstrap-switch.bootstrap-switch-indeterminate,.bootstrap-switch.bootstrap-switch-readonly{cursor:default!important}.bootstrap-switch.bootstrap-switch-disabled .bootstrap-switch-handle-off,.bootstrap-switch.bootstrap-switch-disabled .bootstrap-switch-handle-on,.bootstrap-switch.bootstrap-switch-disabled .bootstrap-switch-label,.bootstrap-switch.bootstrap-switch-indeterminate .bootstrap-switch-handle-off,.bootstrap-switch.bootstrap-switch-indeterminate .bootstrap-switch-handle-on,.bootstrap-switch.bootstrap-switch-indeterminate .bootstrap-switch-label,.bootstrap-switch.bootstrap-switch-readonly .bootstrap-switch-handle-off,.bootstrap-switch.bootstrap-switch-readonly .bootstrap-switch-handle-on,.bootstrap-switch.bootstrap-switch-readonly .bootstrap-switch-label{opacity:.5;filter:alpha(opacity=50);cursor:default!important}.bootstrap-switch.bootstrap-switch-animate .bootstrap-switch-container{-webkit-transition:margin-left .5s;-o-transition:margin-left .5s;transition:margin-left .5s}.bootstrap-switch.bootstrap-switch-inverse .bootstrap-switch-handle-on{border-radius:0 3px 3px 0}.bootstrap-switch.bootstrap-switch-inverse .bootstrap-switch-handle-off{border-radius:3px 0 0 3px}.bootstrap-switch.bootstrap-switch-focused{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)}.bootstrap-switch.bootstrap-switch-inverse.bootstrap-switch-off .bootstrap-switch-label,.bootstrap-switch.bootstrap-switch-on .bootstrap-switch-label{border-bottom-right-radius:3px;border-top-right-radius:3px}.bootstrap-switch.bootstrap-switch-inverse.bootstrap-switch-on .bootstrap-switch-label,.bootstrap-switch.bootstrap-switch-off .bootstrap-switch-label{border-bottom-left-radius:3px;border-top-left-radius:3px}.ngdialog,.ngdialog-overlay{position:fixed;top:0;right:0;bottom:0;left:0}@-webkit-keyframes ngdialog-fadeout{0%{opacity:1}100%{opacity:0}}@keyframes ngdialog-fadeout{0%{opacity:1}100%{opacity:0}}@-webkit-keyframes ngdialog-fadein{0%{opacity:0}100%{opacity:1}}@keyframes ngdialog-fadein{0%{opacity:0}100%{opacity:1}}.ngdialog{box-sizing:border-box;overflow:auto;-webkit-overflow-scrolling:touch;z-index:10000}.ngdialog *,.ngdialog :after,.ngdialog :before{box-sizing:inherit}.ngdialog.ngdialog-disabled-animation,.ngdialog.ngdialog-disabled-animation .ngdialog-content,.ngdialog.ngdialog-disabled-animation .ngdialog-overlay{-webkit-animation:none!important;animation:none!important}.ngdialog-overlay{background:rgba(0,0,0,.4);-webkit-backface-visibility:hidden;-webkit-animation:ngdialog-fadein .5s;animation:ngdialog-fadein .5s}.ngdialog-no-overlay{pointer-events:none}.ngdialog.ngdialog-closing .ngdialog-overlay{-webkit-backface-visibility:hidden;-webkit-animation:ngdialog-fadeout .5s;animation:ngdialog-fadeout .5s}.ngdialog-content{background:#fff;-webkit-backface-visibility:hidden;-webkit-animation:ngdialog-fadein .5s;animation:ngdialog-fadein .5s;pointer-events:all}.ngdialog.ngdialog-closing .ngdialog-content{-webkit-backface-visibility:hidden;-webkit-animation:ngdialog-fadeout .5s;animation:ngdialog-fadeout .5s}.ngdialog-close:before{font-family:Helvetica,Arial,sans-serif;content:'\00D7';cursor:pointer}body.ngdialog-open,html.ngdialog-open{overflow:hidden}@-webkit-keyframes ngdialog-flyin{0%{opacity:0;-webkit-transform:translateY(-40px);transform:translateY(-40px)}100%{opacity:1;-webkit-transform:translateY(0);transform:translateY(0)}}@keyframes ngdialog-flyin{0%{opacity:0;-webkit-transform:translateY(-40px);transform:translateY(-40px)}100%{opacity:1;-webkit-transform:translateY(0);transform:translateY(0)}}@-webkit-keyframes ngdialog-flyout{0%{opacity:1;-webkit-transform:translateY(0);transform:translateY(0)}100%{opacity:0;-webkit-transform:translateY(-40px);transform:translateY(-40px)}}@keyframes ngdialog-flyout{0%{opacity:1;-webkit-transform:translateY(0);transform:translateY(0)}100%{opacity:0;-webkit-transform:translateY(-40px);transform:translateY(-40px)}}.ngdialog.ngdialog-theme-default{padding-bottom:160px;padding-top:160px}.ngdialog.ngdialog-theme-default.ngdialog-closing .ngdialog-content{-webkit-animation:ngdialog-flyout .5s;animation:ngdialog-flyout .5s}.ngdialog.ngdialog-theme-default .ngdialog-content{-webkit-animation:ngdialog-flyin .5s;animation:ngdialog-flyin .5s;background:#f0f0f0;border-radius:5px;color:#444;font-family:Helvetica,sans-serif;font-size:1.1em;line-height:1.5em;margin:0 auto;max-width:100%;padding:1em;position:relative;width:450px}.ngdialog.ngdialog-theme-default .ngdialog-close{border-radius:5px;cursor:pointer;position:absolute;right:0;top:0}.ngdialog.ngdialog-theme-default .ngdialog-close:before{background:0 0;border-radius:3px;color:#bbb;content:'\00D7';font-size:26px;font-weight:400;height:30px;line-height:26px;position:absolute;right:3px;text-align:center;top:3px;width:30px}.ngdialog.ngdialog-theme-default .ngdialog-close:active:before,.ngdialog.ngdialog-theme-default .ngdialog-close:hover:before{color:#777}.ngdialog.ngdialog-theme-default .ngdialog-message{margin-bottom:.5em}.ngdialog.ngdialog-theme-default .ngdialog-input{margin-bottom:1em}.ngdialog.ngdialog-theme-default .ngdialog-input input[type=text],.ngdialog.ngdialog-theme-default .ngdialog-input input[type=password],.ngdialog.ngdialog-theme-default .ngdialog-input input[type=email],.ngdialog.ngdialog-theme-default .ngdialog-input input[type=url],.ngdialog.ngdialog-theme-default .ngdialog-input textarea{background:#fff;border:0;border-radius:3px;font-family:inherit;font-size:inherit;font-weight:inherit;margin:0 0 .25em;min-height:2.5em;padding:.25em .67em;width:100%}.ngdialog.ngdialog-theme-default .ngdialog-input input[type=text]:focus,.ngdialog.ngdialog-theme-default .ngdialog-input input[type=password]:focus,.ngdialog.ngdialog-theme-default .ngdialog-input input[type=email]:focus,.ngdialog.ngdialog-theme-default .ngdialog-input input[type=url]:focus,.ngdialog.ngdialog-theme-default .ngdialog-input textarea:focus{box-shadow:inset 0 0 0 2px #8dbdf1;outline:0}.ngdialog.ngdialog-theme-default .ngdialog-buttons:after{content:'';display:table;clear:both}.ngdialog.ngdialog-theme-default .ngdialog-button{border:0;border-radius:3px;cursor:pointer;float:right;font-family:inherit;font-size:.8em;letter-spacing:.1em;line-height:1em;margin:0 0 0 .5em;padding:.75em 2em;text-transform:uppercase}.ngdialog.ngdialog-theme-default .ngdialog-button:focus{-webkit-animation:ngdialog-pulse 1.1s infinite;animation:ngdialog-pulse 1.1s infinite;outline:0}.btn:active,.btn:focus,.selectize-input>input:focus{outline:0!important}@media (max-width:568px){.ngdialog.ngdialog-theme-default .ngdialog-button:focus{-webkit-animation:none;animation:none}}.ngdialog.ngdialog-theme-default .ngdialog-button.ngdialog-button-primary{background:#3288e6;color:#fff}.ngdialog.ngdialog-theme-default .ngdialog-button.ngdialog-button-secondary{background:#e0e0e0;color:#777}.datetimepicker{border-radius:4px;direction:ltr;display:block;margin-top:1px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;width:320px}.datetimepicker>div{display:none}.datetimepicker .hour,.datetimepicker .minute{height:34px;line-height:34px;margin:0;width:25%}.datetimepicker .table{margin:0}.datetimepicker .table td,.datetimepicker .table th{border:0;border-radius:4px;height:20px;text-align:center}.datetimepicker .day:hover,.datetimepicker .hour:hover,.datetimepicker .left:hover,.datetimepicker .minute:hover,.datetimepicker .right:hover,.datetimepicker .switch:hover{background:#eee;cursor:pointer}.datetimepicker .disabled,.datetimepicker .disabled:hover{background:0 0;color:#ebebeb;cursor:default}.datetimepicker .active,.datetimepicker .active.disabled,.datetimepicker .active.disabled:hover,.datetimepicker .active:hover{background-color:#04c;background-image:linear-gradient(to bottom,#08c,#04c);background-repeat:repeat-x;border-color:#04c #04c #002a80;color:#fff;filter:progid:dximagetransform.microsoft.gradient(startColorstr='#08c', endColorstr='#04c', GradientType=0);text-shadow:0 -1px 0 rgba(0,0,0,.25)}.datetimepicker .current,.datetimepicker .current.disabled,.datetimepicker .current.disabled:hover,.datetimepicker .current:hover{background-color:#e5e5e5}.datetimepicker .active.active,.datetimepicker .active.disabled,.datetimepicker .active.disabled.active,.datetimepicker .active.disabled.disabled,.datetimepicker .active.disabled:active,.datetimepicker .active.disabled:hover,.datetimepicker .active.disabled:hover.active,.datetimepicker .active.disabled:hover.disabled,.datetimepicker .active.disabled:hover:active,.datetimepicker .active.disabled:hover:hover,.datetimepicker .active:active,.datetimepicker .active:hover,.datetimepicker .active:hover.active,.datetimepicker .active:hover.disabled,.datetimepicker .active:hover:active,.datetimepicker .active:hover:hover,.datetimepicker span.active.disabled:hover[disabled],.datetimepicker span.active.disabled[disabled],.datetimepicker span.active:hover[disabled],.datetimepicker span.active[disabled],.datetimepicker td.active.disabled:hover[disabled],.datetimepicker td.active.disabled[disabled],.datetimepicker td.active:hover[disabled],.datetimepicker td.active[disabled]{background-color:#04c}.datetimepicker span{border-radius:4px;cursor:pointer;display:block;float:left;height:54px;line-height:54px;margin:1%;width:23%}.datetimepicker span:hover{background:#eee}.datetimepicker .future,.datetimepicker .past{color:#999}.ui-notification{position:fixed;z-index:9999;width:300px;-webkit-transition:all ease .5s;-o-transition:all ease .5s;transition:all ease .5s;color:#fff;border-radius:0;background:#337ab7;box-shadow:5px 5px 10px rgba(0,0,0,.3)}.ui-notification.clickable{cursor:pointer}.ui-notification.clickable:hover{opacity:.7}.ui-notification.killed{-webkit-transition:opacity ease 1s;-o-transition:opacity ease 1s;transition:opacity ease 1s;opacity:0}.ui-notification>h3{font-size:14px;font-weight:700;display:block;margin:10px 10px 0;padding:0 0 5px;text-align:left;border-bottom:1px solid rgba(255,255,255,.3)}.ui-notification a{color:#fff}.ui-notification a:hover{text-decoration:underline}.ui-notification>.message{margin:10px}.ui-notification.warning{color:#fff;background:#f0ad4e}.ui-notification.error{color:#fff;background:#d9534f}.ui-notification.success{color:#fff;background:#5cb85c}.ui-notification.info{color:#fff;background:#5bc0de}table.rz-table{table-layout:fixed;border-collapse:collapse}table.rz-table th{position:relative;min-width:25px}table.rz-table th .rz-handle{width:10px;height:100%;position:absolute;top:0;right:0;cursor:ew-resize!important}table.rz-table th .rz-handle.rz-handle-active{border-right:1px dotted #000}.selectize-control.plugin-drag_drop.multi>.selectize-input>div.ui-sortable-placeholder{visibility:visible!important;background:#f2f2f2!important;background:rgba(0,0,0,.06)!important;border:0!important;-webkit-box-shadow:inset 0 0 12px 4px #fff;box-shadow:inset 0 0 12px 4px #fff}.selectize-control.plugin-drag_drop .ui-sortable-placeholder::after{content:'!';visibility:hidden}.selectize-control.plugin-drag_drop .ui-sortable-helper{-webkit-box-shadow:0 2px 5px rgba(0,0,0,.2);box-shadow:0 2px 5px rgba(0,0,0,.2)}.selectize-dropdown-header{position:relative;padding:5px 8px;border-bottom:1px solid #d0d0d0;background:#f8f8f8;-webkit-border-radius:3px 3px 0 0;-moz-border-radius:3px 3px 0 0;border-radius:3px 3px 0 0}.selectize-dropdown-header-close{position:absolute;right:8px;top:50%;color:#303030;opacity:.4;margin-top:-12px;line-height:20px;font-size:20px!important}.selectize-dropdown-header-close:hover{color:#000}.selectize-dropdown.plugin-optgroup_columns .optgroup{border-right:1px solid #f2f2f2;border-top:0 none;float:left;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.selectize-control.plugin-remove_button [data-value] .remove,.selectize-input{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;display:inline-block}.selectize-dropdown.plugin-optgroup_columns .optgroup:last-child{border-right:0 none}.selectize-dropdown.plugin-optgroup_columns .optgroup:before{display:none}.selectize-dropdown.plugin-optgroup_columns .optgroup-header{border-top:0 none}.selectize-control.plugin-remove_button [data-value]{position:relative;padding-right:24px!important}.selectize-control.plugin-remove_button [data-value] .remove{z-index:1;position:absolute;top:0;right:0;bottom:0;width:17px;text-align:center;font-weight:700;font-size:12px;color:inherit;text-decoration:none;vertical-align:middle;padding:2px 0 0;border-left:1px solid #d0d0d0;-webkit-border-radius:0 2px 2px 0;-moz-border-radius:0 2px 2px 0;border-radius:0 2px 2px 0;box-sizing:border-box}.selectize-control.plugin-remove_button [data-value] .remove:hover{background:rgba(0,0,0,.05)}.selectize-control.plugin-remove_button [data-value].active .remove{border-left-color:#cacaca}.selectize-control.plugin-remove_button .disabled [data-value] .remove:hover{background:0 0}.selectize-control.plugin-remove_button .disabled [data-value] .remove{border-left-color:#fff}.selectize-control.plugin-remove_button .remove-single{position:absolute;right:0;top:0;font-size:23px}.selectize-control,.selectize-input{position:relative}.selectize-dropdown,.selectize-input,.selectize-input input{color:#303030;font-family:inherit;font-size:13px;line-height:18px;-webkit-font-smoothing:inherit}.selectize-control.single .selectize-input.input-active,.selectize-input{background:#fff;cursor:text;display:inline-block}.selectize-input{border:1px solid #d0d0d0;padding:8px;width:100%;overflow:hidden;z-index:1;box-sizing:border-box;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.1);box-shadow:inset 0 1px 1px rgba(0,0,0,.1);-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.selectize-control.multi .selectize-input.has-items{padding:6px 8px 3px}.selectize-input.full{background-color:#fff}.selectize-input.disabled,.selectize-input.disabled *{cursor:default!important}.selectize-input.focus{-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.15);box-shadow:inset 0 1px 2px rgba(0,0,0,.15)}.selectize-input.dropdown-active{-webkit-border-radius:3px 3px 0 0;-moz-border-radius:3px 3px 0 0;border-radius:3px 3px 0 0}.selectize-input>*{vertical-align:baseline;display:-moz-inline-stack;display:inline-block;zoom:1}.selectize-control.multi .selectize-input>div{cursor:pointer;margin:0 3px 3px 0;padding:2px 6px;background:#f2f2f2;color:#303030;border:0 solid #d0d0d0}.selectize-control.multi .selectize-input>div.active{background:#e8e8e8;color:#303030;border:0 solid #cacaca}.selectize-control.multi .selectize-input.disabled>div,.selectize-control.multi .selectize-input.disabled>div.active{color:#7d7d7d;background:#fff;border:0 solid #fff}.selectize-input>input{display:inline-block!important;padding:0!important;min-height:0!important;max-height:none!important;max-width:100%!important;margin:0 2px 0 0!important;text-indent:0!important;border:0!important;background:0 0!important;line-height:inherit!important;-webkit-user-select:auto!important;-webkit-box-shadow:none!important;box-shadow:none!important}.selectize-input>input::-ms-clear{display:none}.selectize-input::after{content:' ';display:block;clear:left}.selectize-input.dropdown-active::before{content:' ';display:block;position:absolute;background:#f0f0f0;height:1px;bottom:0;left:0;right:0}.selectize-dropdown{position:absolute;z-index:10;border:1px solid #d0d0d0;background:#fff;margin:-1px 0 0;border-top:0 none;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;-webkit-box-shadow:0 1px 3px rgba(0,0,0,.1);box-shadow:0 1px 3px rgba(0,0,0,.1);-webkit-border-radius:0 0 3px 3px;-moz-border-radius:0 0 3px 3px;border-radius:0 0 3px 3px}.selectize-dropdown [data-selectable]{cursor:pointer;overflow:hidden}.selectize-dropdown [data-selectable] .highlight{background:rgba(125,168,208,.2);-webkit-border-radius:1px;-moz-border-radius:1px;border-radius:1px}.selectize-dropdown .optgroup-header,.selectize-dropdown .option{padding:5px 8px}.selectize-dropdown .option,.selectize-dropdown [data-disabled],.selectize-dropdown [data-disabled] [data-selectable].option{cursor:inherit;opacity:.5}.selectize-dropdown [data-selectable].option{opacity:1}.selectize-dropdown .optgroup:first-child .optgroup-header{border-top:0 none}.selectize-dropdown .optgroup-header{color:#303030;background:#fff;cursor:default}.selectize-dropdown .active{background-color:#f5fafd;color:#495c68}.selectize-dropdown .active.create{color:#495c68}.selectize-dropdown .create{color:rgba(48,48,48,.5)}.selectize-dropdown-content{overflow-y:auto;overflow-x:hidden;max-height:200px;-webkit-overflow-scrolling:touch}.selectize-control.single .selectize-input,.selectize-control.single .selectize-input input{cursor:pointer}.selectize-control.single .selectize-input.input-active,.selectize-control.single .selectize-input.input-active input{cursor:text}.selectize-control.single .selectize-input:after{content:' ';display:block;position:absolute;top:50%;right:15px;margin-top:-3px;width:0;height:0;border-style:solid;border-width:5px 5px 0;border-color:grey transparent transparent}.selectize-control.single .selectize-input.dropdown-active:after{margin-top:-4px;border-width:0 5px 5px;border-color:transparent transparent grey}.selectize-control.rtl.single .selectize-input:after{left:15px;right:auto}.selectize-control.rtl .selectize-input>input{margin:0 4px 0 -2px!important}.selectize-control .selectize-input.disabled{opacity:.5;background-color:#fafafa}/*! * Start Bootstrap - SB Admin 2 Bootstrap Admin Theme (http://startbootstrap.com) * Code licensed under the Apache License v2.0. * For details, see http://www.apache.org/licenses/LICENSE-2.0. */body{background-color:#f8f8f8}.example{padding:.625rem 1.825rem .625rem 2.5rem;border:1px dashed #ccc;position:relative;margin:0 0 .625rem;background-color:#fff}dl dd,dl dt{line-height:1.25rem}dl dt{font-style:normal;font-weight:700}dl dd{margin-left:.9375rem}dl.horizontal dt{float:left;width:10rem;overflow:hidden;clear:left;text-align:right;text-overflow:ellipsis;white-space:nowrap}dl.horizontal dd{margin-left:11.25rem}#wrapper{width:100%}#page-wrapper{min-height:568px;background-color:#fff}@media(min-width:768px){#page-wrapper{position:inherit;margin:0 0 0 250px;padding:0 30px;border-left:1px solid #e7e7e7}}.navbar-top-links{margin-right:0}.navbar-top-links li{display:inline-block}.flot-chart,.navbar-top-links .dropdown-menu li{display:block}.navbar-top-links li:last-child{margin-right:15px}.navbar-top-links li a{padding:15px;min-height:50px}.navbar-top-links .dropdown-menu li:last-child{margin-right:0}.navbar-top-links .dropdown-menu li a{padding:3px 20px;min-height:0}.navbar-top-links .dropdown-menu li a div{white-space:normal}.navbar-top-links .dropdown-alerts,.navbar-top-links .dropdown-messages,.navbar-top-links .dropdown-tasks{width:310px;min-width:0}.navbar-top-links .dropdown-messages{margin-left:5px}.navbar-top-links .dropdown-tasks{margin-left:-59px}.navbar-top-links .dropdown-alerts{margin-left:-123px}.navbar-top-links .dropdown-user{right:0;left:auto}.sidebar .sidebar-search{padding:15px}.sidebar ul li{border-bottom:1px solid #e7e7e7}.sidebar ul li a.active{background-color:#fff;color:#fff}.sidebar .arrow{float:right}.sidebar .fa.arrow:before{content:"\f104"}.sidebar .active>a>.fa.arrow:before{content:"\f107"}.sidebar .nav-second-level li,.sidebar .nav-third-level li{border-bottom:0!important}.sidebar .nav-second-level li a{padding-left:37px}.sidebar .nav-third-level li a{padding-left:52px}@media(min-width:768px){.sidebar{z-index:1;position:absolute;width:250px;margin-top:51px}.navbar-top-links .dropdown-alerts,.navbar-top-links .dropdown-messages,.navbar-top-links .dropdown-tasks{margin-left:auto}}.btn-outline{color:inherit;background-color:transparent;transition:all .5s}.btn-primary.btn-outline{color:#428bca}.btn-success.btn-outline{color:#5cb85c}.btn-info.btn-outline{color:#5bc0de}.btn-warning.btn-outline{color:#f0ad4e}.btn-danger.btn-outline{color:#d9534f}.btn-danger.btn-outline:hover,.btn-info.btn-outline:hover,.btn-primary.btn-outline:hover,.btn-success.btn-outline:hover,.btn-warning.btn-outline:hover{color:#fff}.chat{margin:0;padding:0}.chat li{margin-bottom:10px;padding-bottom:5px;border-bottom:1px dotted #999}.chat li.left .chat-body{margin-left:60px}.chat li.right .chat-body{margin-right:60px}.chat li .chat-body p{margin:0}.chat .glyphicon,.panel .slidedown .glyphicon{margin-right:5px}.chat-panel .panel-body{height:350px;overflow-y:scroll}.login-panel{margin-top:25%}.flot-chart{height:400px}.flot-chart-content{width:100%;height:100%}.dataTables_wrapper{position:relative;clear:both}table.dataTable thead .sorting,table.dataTable thead .sorting_asc,table.dataTable thead .sorting_asc_disabled,table.dataTable thead .sorting_desc,table.dataTable thead .sorting_desc_disabled{background:0 0}table.dataTable thead .sorting_asc:after{content:"\f0de";float:right;font-family:fontawesome}table.dataTable thead .sorting_desc:after{content:"\f0dd";float:right;font-family:fontawesome}table.dataTable thead .sorting:after{content:"\f0dc";float:right;font-family:fontawesome;color:rgba(50,50,50,.5)}.btn-circle{width:30px;height:30px;padding:6px 0;border-radius:15px;text-align:center;font-size:12px;line-height:1.428571429}.btn-circle.btn-lg{width:50px;height:50px;padding:10px 16px;border-radius:25px;font-size:18px;line-height:1.33}.btn-circle.btn-xl{width:70px;height:70px;padding:10px 16px;border-radius:35px;font-size:24px;line-height:1.33}.show-grid [class^=col-]{padding-top:10px;padding-bottom:10px;border:1px solid #ddd;background-color:#eee!important}.show-grid{margin:15px 0}.huge{font-size:40px}.panel-green{border-color:#5cb85c}.panel-green .panel-heading{border-color:#5cb85c;color:#fff;background-color:#5cb85c}.panel-green a{color:#5cb85c}.panel-green a:hover{color:#3d8b3d}.panel-red{border-color:#d9534f}.panel-red .panel-heading{border-color:#d9534f;color:#fff;background-color:#d9534f}.panel-red a{color:#d9534f}.panel-red a:hover{color:#b52b27}.panel-yellow{border-color:#f0ad4e}.panel-yellow .panel-heading{border-color:#f0ad4e;color:#fff;background-color:#f0ad4e}.panel-yellow a{color:#f0ad4e}.panel-yellow a:hover{color:#df8a13}.timeline{position:relative;padding:20px 0}.timeline:before{content:" ";position:absolute;top:0;bottom:0;left:50%;width:3px;margin-left:-1.5px;background-color:#eee}.timeline>li{position:relative;margin-bottom:20px}.timeline>li:after,.timeline>li:before{content:" ";display:table}.timeline>li:after{clear:both}.timeline>li>.timeline-panel{float:left;position:relative;width:46%;padding:20px;border:1px solid #d4d4d4;border-radius:2px;-webkit-box-shadow:0 1px 6px rgba(0,0,0,.175);box-shadow:0 1px 6px rgba(0,0,0,.175)}.timeline>li>.timeline-panel:before{content:" ";display:inline-block;position:absolute;top:26px;right:-15px;border-top:15px solid transparent;border-right:0 solid #ccc;border-bottom:15px solid transparent;border-left:15px solid #ccc}.timeline>li>.timeline-panel:after{content:" ";display:inline-block;position:absolute;top:27px;right:-14px;border-top:14px solid transparent;border-right:0 solid #fff;border-bottom:14px solid transparent;border-left:14px solid #fff}.timeline>li>.timeline-badge{z-index:100;position:absolute;top:16px;left:50%;width:50px;height:50px;margin-left:-25px;border-radius:50%;text-align:center;font-size:1.4em;line-height:50px;color:#fff;background-color:#999}.timeline>li.timeline-inverted>.timeline-panel{float:right}.timeline>li.timeline-inverted>.timeline-panel:before{right:auto;left:-15px;border-right-width:15px;border-left-width:0}.timeline>li.timeline-inverted>.timeline-panel:after{right:auto;left:-14px;border-right-width:14px;border-left-width:0}.timeline-badge.primary{background-color:#2e6da4!important}.timeline-badge.success{background-color:#3f903f!important}.timeline-badge.warning{background-color:#f0ad4e!important}.timeline-badge.danger{background-color:#d9534f!important}.timeline-badge.info{background-color:#5bc0de!important}.timeline-title{margin-top:0;color:inherit}.timeline-body>p,.timeline-body>ul{margin-bottom:0}.timeline-body>p+p{margin-top:5px}@media(max-width:767px){ul.timeline:before{left:40px}ul.timeline>li>.timeline-panel{width:calc(100% - 90px);width:-moz-calc(100% - 90px);width:-webkit-calc(100% - 90px);float:right}ul.timeline>li>.timeline-badge{top:16px;left:15px;margin-left:0}ul.timeline>li>.timeline-panel:before{right:auto;left:-15px;border-right-width:15px;border-left-width:0}ul.timeline>li>.timeline-panel:after{right:auto;left:-14px;border-right-width:14px;border-left-width:0}}.header,.jumbotron{border-bottom:1px solid #e5e5e5}.btn{height:32px}.width-200{max-width:200px}.width-300,.witdh-300{max-width:300px}body{padding:0}.footer,.header,.marketing{padding-left:15px;padding-right:15px}.header{margin-bottom:10px}.header h3{margin-top:0;margin-bottom:0;line-height:40px;padding-bottom:19px}.card .detail,.card .detail-brand{line-height:98px;text-align:center}.footer{padding-top:19px;color:#777;border-top:1px solid #e5e5e5}.container-narrow>hr{margin:30px 0}.jumbotron{text-align:center}.jumbotron .btn{font-size:21px;padding:14px 24px}.marketing{margin:40px 0}.marketing p+h4{margin-top:28px}@media screen and (min-width:768px){.container{width:inherit;margin-left:60px;margin-right:5px}.footer,.header,.marketing{padding-left:0;padding-right:0}.header{margin-bottom:30px}.jumbotron{border-bottom:0}}.navbar-inverse .navbar-nav>li>a{color:#b0ddce;font-size:15px}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:focus,.navbar-inverse .navbar-nav>.open>a:hover{background-color:#1b926c}@media (min-width:900px){.navbar-right,.navbar-right~.navbar-right{margin-right:0}.navbar-left{float:left!important}.navbar-right{float:right!important}}.dropdown-menu{min-width:100px!important}.nav-sidebar li.active a{background:#DDD}.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{background:#1d9d74;color:#fff}.broadcast-message,.broadcast-message-preview{padding:10px;text-align:center;background:#555;color:#BBB;margin-top:50px}.card{position:relative;border:1px solid #d9d9d9;color:#666;background-color:#fff;width:100%;border-radius:5px}.card .card-header,.tools-header{border-top-left-radius:4px;border-top-right-radius:4px}.card .card-header{padding:9px 0;height:40px;background:#555;color:#fff;text-align:center}.card .card-body{padding:12px 10px}.card .card-footer{height:20px;font-size:10px;color:#777;margin:-15px 20px 5px}.card .detail-brand{float:left;width:30%;font-size:30px;color:#fff}.card .default{background:#1d9d74}.card .info{background:#6EBEE7}.card .warn{background:#ED7F54}.card .danger{background:#6583BE}.card .detail .text-default{color:#1d9d74}.card .detail .text-info{color:#6EBEE7}.card .detail .text-warn{color:#ED7F54}.card .detail .text-danger{color:#6583BE}.card .detail{float:right;width:70%}.card .detail .text{font-size:12px}.card .detail .number{font-size:30px;font-weight:500}.h100{height:100px}.inline{display:inline}.separator{height:1px;background-color:#e5e5e5;margin-top:10px}.card>.card-body>table>tbody>tr>td,.card>.card-body>table>thead>tr>td{word-wrap:break-word;word-break:break-all}.card>.card-body>table>thead>tr>td{font-weight:500;font-size:13px;text-align:center}.card>.card-body>table>thead>tr>td>span{font-weight:500;font-size:10px}.card>.card-body>table>tbody>tr>td{font-size:12px;text-align:center}.card>.card-body>table>tbody>tr>td>a{color:#666}.thumbnails>.card>.card-body>table>tbody>tr>td,.thumbnails>.card>.card-body>table>thead>tr>td{font-size:12px;color:#777;word-wrap:break-word;word-break:break-all}.thumbnails>.card>.card-body>table>thead>tr>td:nth-child(n+2){text-align:center}.thumbnails>.card>.card-body>table>tbody>tr>td:nth-child(n+2){font-weight:700;text-align:center}.thumbnails>.card>.card-body>table>tbody>tr>td:nth-child(1),.thumbnails>.card>.card-body>table>thead>tr>td:nth-child(1){text-align:left}.tools-header{background:#f5f5f5;padding:9px 0;height:40px}.tools-header .brand{font-size:13px;margin:2px 10px;font-weight:700;float:left}.tools-header .brand>a{color:#666}.tools-header>a,.tools-header>button,.tools-header>select{float:right;max-width:80px;margin:1px 10px;height:25px;padding:0 10px;line-height:25px;color:#666}.tools-header .paged{margin-right:0}.btn.btn-danger-tag{color:#fff;background-color:#d9534f;border-color:#d43f3a;line-height:1px;font-size:11px;padding:4px}.btn.btn-danger{color:#333;background-color:#fff;border-color:#ccc}.btn.btn-danger:active,.btn.btn-danger:focus,.btn.btn-danger:hover{color:#d9534f;border-color:#d9534f;background:#fff}.form-control{height:32px}.input-label:before{display:inline-block;content:"*";color:#f44336;font-family:SimSun;font-size:12px;-webkit-transform:TranslateX(-10px);-ms-transform:TranslateX(-10px);transform:TranslateX(-10px)}.badge-main,.label.label-main{color:#fff;background-color:#1d9d74;border-color:#1d9d74}.bootstrap-tagsinput{background-color:#fff;border:1px solid #ccc;box-shadow:inset 0 1px 1px rgba(0,0,0,.075);display:inline-block;padding:4px 6px;color:#555;vertical-align:middle;border-radius:4px;width:85%;height:100px;line-height:20px;cursor:text}.bootstrap-tagsinput>.dropdown-menu{min-width:40px;font-size:12px}.bootstrap-tagsinput>.dropdown-menu>.active>a,.bootstrap-tagsinput>.dropdown-menu>.active>a:focus,.bootstrap-tagsinput>.dropdown-menu>.active>a:hover{background-image:-webkit-linear-gradient(top,#1d9d74 0,#1d9d74 100%);background-image:-o-linear-gradient(top,#1d9d74 0,#1d9d74 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#1d9d74),to(#1d9d74));background-image:linear-gradient(to bottom,#1d9d74 0,#1d9d74 100%);filter:progid: DXImageTransform.Microsoft.gradient(startColorstr='#1d9d74', endColorstr='#1d9d74', GradientType=0);background-repeat:repeat-x;color:#fff;text-decoration:none;outline:0;background-color:#1d9d74}.inputs-header{padding:9px 0;height:50px;border-top-left-radius:4px;border-top-right-radius:4px}.inputs-header .brand{font-size:13px;margin:2px 10px;font-weight:700;float:left}.inputs-header .brand>a{color:#666}.inputs-header>input{float:right;margin:1px 10px;height:30px;padding:0 10px;color:#666}.inputs-header>a{float:right;margin:1px 10px;height:30px;padding:5 5px}.inputs-header>select{float:right;max-width:80px;margin:1px 10px;padding:0 10px;color:#666;height:25px;font-size:12px}.witdh-150{max-width:150px}.witdh-200{max-width:200px}.card.highlight{border-color:#d9534f}.card .pagination-footer{height:40px;font-size:10px;color:#777;margin:-15px 20px 5px}.card .pagination-footer .tools{font-size:12px;margin:11px 20px 11px 0;float:right;display:inline}.card>.pagination-footer>.tools>span>input{height:25px;max-width:50px;display:inline}.pagination{display:inline-block;padding-left:0;margin:8px 0;float:right;border-radius:4px}.pagination>a{margin-right:5px;height:28px;width:28px;padding:5px 0}.datepicker>.table>tbody>tr>td,.datepicker>.table>thead>tr>td,.timepicker>.table>tbody>tr>td,.timepicker>.table>thead>tr>td{padding:5px 3px}.datepicker>.table>tbody>tr>td>.btn,.datepicker>.table>thead>tr>td>.btn,.timepicker>.table>tbody>tr>td>.btn,.timepicker>.table>thead>tr>td>.btn{border:1px solid #FFFDFD}.datepicker>.table>tbody>tr>td>.btn-default:active,.datepicker>.table>tbody>tr>td>.btn-default:focus,.datepicker>.table>tbody>tr>td>.btn-default:hover,.datepicker>.table>thead>tr>td>.btn-default:active,.datepicker>.table>thead>tr>td>.btn-default:focus,.datepicker>.table>thead>tr>td>.btn-default:hover,.timepicker>.table>tbody>tr>td>.btn-default:active,.timepicker>.table>tbody>tr>td>.btn-default:focus,.timepicker>.table>tbody>tr>td>.btn-default:hover,.timepicker>.table>thead>tr>td>.btn-default:active,.timepicker>.table>thead>tr>td>.btn-default:focus,.timepicker>.table>thead>tr>td>.btn-default:hover{color:#1d9d74;border-color:#1d9d74;background:#fff}.datepicker>.table>tbody>tr>td>a,.datepicker>.table>thead>tr>td>a,.timepicker>.table>tbody>tr>td>a,.timepicker>.table>thead>tr>td>a{height:25px;width:25px;padding:3px 0}.datepicker>.table>tbody>tr:first-child>td>a{padding:4px 0}.datepicker>.table>tbody>tr>td>a.btn.active,.datepicker>.table>thead>tr>td>a.btn.active,.timepicker>.table>tbody>tr>td>a.btn.active,.timepicker>.table>thead>tr>td>a.btn.active{color:#1d9d74;border-color:#1d9d74;background:#fff;box-shadow:inset 0 0 0 rgba(0,0,0,.125)}.datepicker>.table>thead>tr>td:not(:first-child):last-child>a,.timepicker>.table>thead>tr>td:not(:first-child):last-child>a{height:25px;width:50px;padding:5px 0}.datepicker>.table>tbody>tr>td>a,.timepicker>.table>tbody>tr>td>a{margin-left:8px}.sortorder:after{content:'\25b2'}.sortorder.reverse:after{content:'\25bc'}.input-control select{-moz-appearance:none;-webkit-appearance:none;appearance:none;position:relative;border:1px solid #d9d9d9;width:100%;height:100%;padding:.3125rem;z-index:0}.navbar-inverse{background-color:#337ab7;border-color:#337ab7}.sidebar{z-index:1;width:220px;top:0;left:0;height:100%}#page-wrapper{position:inherit;margin:70px 0 0 220px;padding:12px 30px;border-left:0 solid #e7e7e7}.sidebar .sidebar-nav.navbar-collapse{background-color:#F5F5F5;position:relative;color:#000;width:100%;padding:0;margin:0;list-style:none inside}.sidebar a{color:#555}.sidebar ul li:hover{color:red}.form-control{border-radius:8px}.form-control:focus,.highlight-border{border-color:#337ab7;box-shadow:0 0 0 rgba(0,0,0,.075) inset,0 0 0 rgba(29,157,116,1)}.btn-outline-primary.focus,.btn-outline-primary:focus,.btn-outline-primary:not(:disabled):not(.disabled).active:focus,.btn-outline-primary:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-primary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.browsehappy{margin:.2em 0;background:#ccc;color:#000;padding:.2em 0}.btn.btn-main{color:#fff;background-color:#337ab7;border-color:#337ab7}.btn-default-inverse,.btn-default-inverse:focus,.btn-default-inverse:hover,.btn-default:active{color:#337ab7;border-color:#337ab7;background:#fff}.btn-danger-inverse,.btn-danger-inverse:focus,.btn-danger-inverse:hover,.btn-danger:active{color:#d9534f;border-color:#d9534f;background:#fff}.btn-tab-active,.btn-tab-active:focus,.btn-tab-active:hover,.btn-tab-default:active,.btn-tab-default:focus,.btn-tab-default:hover{color:#337ab7;border-color:#337ab7;background:#fff;font-weight:600}.btn-tab-default{color:#777;background:#fff;font-weight:600}.pagination>.btn.active{color:#fff;background-color:#337ab7;border-color:#337ab7}.btn-default:active,.btn-default:focus,.btn-default:hover{color:#337ab7;border-color:#337ab7;background:#fff}.bootstrap-switch.bootstrap-switch-on{border-color:#337ab7}.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-success,.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-success{color:#fff;background:#337ab7}.selectize-input-200>.selectize-input{min-width:200px;border-color:#337ab7}.btn-outline-primary{color:#007bff;background-color:transparent;background-image:none;border-color:#007bff}.btn-outline-primary:hover{color:#fff;background-color:#007bff;border-color:#007bff}.btn-outline-primary.disabled,.btn-outline-primary:disabled{color:#007bff;background-color:transparent}.btn-outline-primary:not(:disabled):not(.disabled).active,.btn-outline-primary:not(:disabled):not(.disabled):active,.show>.btn-outline-primary.dropdown-toggle{color:#fff;background-color:#007bff;border-color:#007bff}.btn-outline-secondary.focus,.btn-outline-secondary:focus,.btn-outline-secondary:not(:disabled):not(.disabled).active:focus,.btn-outline-secondary:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.btn-outline-secondary{color:#6c757d;background-color:transparent;background-image:none;border-color:#6c757d}.btn-outline-secondary:hover{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-outline-secondary.disabled,.btn-outline-secondary:disabled{color:#6c757d;background-color:transparent}.btn-outline-secondary:not(:disabled):not(.disabled).active,.btn-outline-secondary:not(:disabled):not(.disabled):active,.show>.btn-outline-secondary.dropdown-toggle{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-outline-success.focus,.btn-outline-success:focus,.btn-outline-success:not(:disabled):not(.disabled).active:focus,.btn-outline-success:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-success.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-outline-success{color:#28a745;background-color:transparent;background-image:none;border-color:#28a745}.btn-outline-success:hover{color:#fff;background-color:#28a745;border-color:#28a745}.btn-outline-success.disabled,.btn-outline-success:disabled{color:#28a745;background-color:transparent}.btn-outline-success:not(:disabled):not(.disabled).active,.btn-outline-success:not(:disabled):not(.disabled):active,.show>.btn-outline-success.dropdown-toggle{color:#fff;background-color:#28a745;border-color:#28a745}.btn-outline-info.focus,.btn-outline-info:focus,.btn-outline-info:not(:disabled):not(.disabled).active:focus,.btn-outline-info:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-info.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-outline-info{color:#17a2b8;background-color:transparent;background-image:none;border-color:#17a2b8}.btn-outline-info:hover{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-outline-info.disabled,.btn-outline-info:disabled{color:#17a2b8;background-color:transparent}.btn-outline-info:not(:disabled):not(.disabled).active,.btn-outline-info:not(:disabled):not(.disabled):active,.show>.btn-outline-info.dropdown-toggle{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-outline-warning.focus,.btn-outline-warning:focus,.btn-outline-warning:not(:disabled):not(.disabled).active:focus,.btn-outline-warning:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-warning.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-outline-warning{color:#ffc107;background-color:transparent;background-image:none;border-color:#ffc107}.btn-outline-warning:hover{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-outline-warning.disabled,.btn-outline-warning:disabled{color:#ffc107;background-color:transparent}.btn-outline-warning:not(:disabled):not(.disabled).active,.btn-outline-warning:not(:disabled):not(.disabled):active,.show>.btn-outline-warning.dropdown-toggle{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-outline-danger.focus,.btn-outline-danger:focus,.btn-outline-danger:not(:disabled):not(.disabled).active:focus,.btn-outline-danger:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-danger.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-outline-danger{color:#dc3545;background-color:transparent;background-image:none;border-color:#dc3545}.btn-outline-danger:hover{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-outline-danger.disabled,.btn-outline-danger:disabled{color:#dc3545;background-color:transparent}.btn-outline-danger:not(:disabled):not(.disabled).active,.btn-outline-danger:not(:disabled):not(.disabled):active,.show>.btn-outline-danger.dropdown-toggle{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-outline-light.focus,.btn-outline-light:focus,.btn-outline-light:not(:disabled):not(.disabled).active:focus,.btn-outline-light:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-light.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-outline-light{color:#f8f9fa;background-color:transparent;background-image:none;border-color:#f8f9fa}.btn-outline-light:hover{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light.disabled,.btn-outline-light:disabled{color:#f8f9fa;background-color:transparent}.btn-outline-light:not(:disabled):not(.disabled).active,.btn-outline-light:not(:disabled):not(.disabled):active,.show>.btn-outline-light.dropdown-toggle{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-outline-dark.focus,.btn-outline-dark:focus,.btn-outline-dark:not(:disabled):not(.disabled).active:focus,.btn-outline-dark:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-dark.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-outline-dark{color:#343a40;background-color:transparent;background-image:none;border-color:#343a40}.btn-outline-dark:hover{color:#fff;background-color:#343a40;border-color:#343a40}.btn-outline-dark.disabled,.btn-outline-dark:disabled{color:#343a40;background-color:transparent}.btn-outline-dark:not(:disabled):not(.disabled).active,.btn-outline-dark:not(:disabled):not(.disabled):active,.show>.btn-outline-dark.dropdown-toggle{color:#fff;background-color:#343a40;border-color:#343a40} ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/dist/js/app.js ================================================ "use strict";var app;angular.module("sentinelDashboardApp",["oc.lazyLoad","ui.router","ui.bootstrap","angular-loading-bar","ngDialog","ui.bootstrap.datetimepicker","ui-notification","rzTable","angular-clipboard","selectize","angularUtils.directives.dirPagination"]).factory("AuthInterceptor",["$window","$state",function(r,t){return{responseError:function(e){return 401==e.status&&(r.localStorage.removeItem("session_sentinel_admin"),t.go("login")),e},response:function(e){return e},request:function(e){return e},requestError:function(e){return e}}}]).config(["$stateProvider","$urlRouterProvider","$ocLazyLoadProvider","$httpProvider",function(e,r,t,a){a.interceptors.push("AuthInterceptor"),t.config({debug:!1,events:!0}),r.otherwise("/dashboard/home"),e.state("login",{url:"/login",templateUrl:"app/views/login.html",controller:"LoginCtl",resolve:{loadMyFiles:["$ocLazyLoad",function(e){return e.load({name:"sentinelDashboardApp",files:["app/scripts/controllers/login.js"]})}]}}).state("dashboard",{url:"/dashboard",templateUrl:"app/views/dashboard/main.html",resolve:{loadMyDirectives:["$ocLazyLoad",function(e){return e.load({name:"sentinelDashboardApp",files:["app/scripts/directives/header/header.js","app/scripts/directives/sidebar/sidebar.js","app/scripts/directives/sidebar/sidebar-search/sidebar-search.js"]})}]}}).state("dashboard.home",{url:"/home",templateUrl:"app/views/dashboard/home.html",resolve:{loadMyFiles:["$ocLazyLoad",function(e){return e.load({name:"sentinelDashboardApp",files:["app/scripts/controllers/main.js"]})}]}}).state("dashboard.flowV1",{templateUrl:"app/views/flow_v1.html",url:"/flow/:app",controller:"FlowControllerV1",resolve:{loadMyFiles:["$ocLazyLoad",function(e){return e.load({name:"sentinelDashboardApp",files:["app/scripts/controllers/flow_v1.js"]})}]}}).state("dashboard.flow",{templateUrl:"app/views/flow_v2.html",url:"/v2/flow/:app",controller:"FlowControllerV2",resolve:{loadMyFiles:["$ocLazyLoad",function(e){return e.load({name:"sentinelDashboardApp",files:["app/scripts/controllers/flow_v2.js"]})}]}}).state("dashboard.paramFlow",{templateUrl:"app/views/param_flow.html",url:"/paramFlow/:app",controller:"ParamFlowController",resolve:{loadMyFiles:["$ocLazyLoad",function(e){return e.load({name:"sentinelDashboardApp",files:["app/scripts/controllers/param_flow.js"]})}]}}).state("dashboard.clusterAppAssignManage",{templateUrl:"app/views/cluster_app_assign_manage.html",url:"/cluster/assign_manage/:app",controller:"SentinelClusterAppAssignManageController",resolve:{loadMyFiles:["$ocLazyLoad",function(e){return e.load({name:"sentinelDashboardApp",files:["app/scripts/controllers/cluster_app_assign_manage.js"]})}]}}).state("dashboard.clusterAppServerList",{templateUrl:"app/views/cluster_app_server_list.html",url:"/cluster/server/:app",controller:"SentinelClusterAppServerListController",resolve:{loadMyFiles:["$ocLazyLoad",function(e){return e.load({name:"sentinelDashboardApp",files:["app/scripts/controllers/cluster_app_server_list.js"]})}]}}).state("dashboard.clusterAppClientList",{templateUrl:"app/views/cluster_app_client_list.html",url:"/cluster/client/:app",controller:"SentinelClusterAppTokenClientListController",resolve:{loadMyFiles:["$ocLazyLoad",function(e){return e.load({name:"sentinelDashboardApp",files:["app/scripts/controllers/cluster_app_token_client_list.js"]})}]}}).state("dashboard.clusterSingle",{templateUrl:"app/views/cluster_single_config.html",url:"/cluster/single/:app",controller:"SentinelClusterSingleController",resolve:{loadMyFiles:["$ocLazyLoad",function(e){return e.load({name:"sentinelDashboardApp",files:["app/scripts/controllers/cluster_single.js"]})}]}}).state("dashboard.authority",{templateUrl:"app/views/authority.html",url:"/authority/:app",controller:"AuthorityRuleController",resolve:{loadMyFiles:["$ocLazyLoad",function(e){return e.load({name:"sentinelDashboardApp",files:["app/scripts/controllers/authority.js"]})}]}}).state("dashboard.degrade",{templateUrl:"app/views/degrade.html",url:"/degrade/:app",controller:"DegradeCtl",resolve:{loadMyFiles:["$ocLazyLoad",function(e){return e.load({name:"sentinelDashboardApp",files:["app/scripts/controllers/degrade.js"]})}]}}).state("dashboard.system",{templateUrl:"app/views/system.html",url:"/system/:app",controller:"SystemCtl",resolve:{loadMyFiles:["$ocLazyLoad",function(e){return e.load({name:"sentinelDashboardApp",files:["app/scripts/controllers/system.js"]})}]}}).state("dashboard.machine",{templateUrl:"app/views/machine.html",url:"/app/:app",controller:"MachineCtl",resolve:{loadMyFiles:["$ocLazyLoad",function(e){return e.load({name:"sentinelDashboardApp",files:["app/scripts/controllers/machine.js"]})}]}}).state("dashboard.identity",{templateUrl:"app/views/identity.html",url:"/identity/:app",controller:"IdentityCtl",resolve:{loadMyFiles:["$ocLazyLoad",function(e){return e.load({name:"sentinelDashboardApp",files:["app/scripts/controllers/identity.js"]})}]}}).state("dashboard.metric",{templateUrl:"app/views/metric.html",url:"/metric/:app",controller:"MetricCtl",resolve:{loadMyFiles:["$ocLazyLoad",function(e){return e.load({name:"sentinelDashboardApp",files:["app/scripts/controllers/metric.js"]})}]}})}]),(app=angular.module("sentinelDashboardApp")).filter("range",[function(){return function(e,r){if(isNaN(r)||r<=0)return[];e=[];for(var t=1;t<=r;t++)e.push(t);return e}}]),(app=angular.module("sentinelDashboardApp")).service("AuthService",["$http",function(r){this.login=function(e){return r({url:"/auth/login",params:e,method:"POST"})},this.logout=function(){return r({url:"/auth/logout",method:"POST"})}}]),(app=angular.module("sentinelDashboardApp")).service("AppService",["$http",function(e){this.getApps=function(){return e({url:"app/briefinfos.json",method:"GET"})}}]),(app=angular.module("sentinelDashboardApp")).service("FlowServiceV1",["$http",function(a){function r(e){return void 0===e||""===e||isNaN(e)||e<=0}this.queryMachineRules=function(e,r,t){return a({url:"/v1/flow/rules",params:{app:e,ip:r,port:t},method:"GET"})},this.newRule=function(e){e.resource,e.limitApp,e.grade,e.count,e.strategy,e.refResource,e.controlBehavior,e.warmUpPeriodSec,e.maxQueueingTimeMs,e.app,e.ip,e.port;return a({url:"/v1/flow/rule",data:e,method:"POST"})},this.saveRule=function(e){var r={id:e.id,resource:e.resource,limitApp:e.limitApp,grade:e.grade,count:e.count,strategy:e.strategy,refResource:e.refResource,controlBehavior:e.controlBehavior,warmUpPeriodSec:e.warmUpPeriodSec,maxQueueingTimeMs:e.maxQueueingTimeMs};return a({url:"/v1/flow/save.json",params:r,method:"PUT"})},this.deleteRule=function(e){var r={id:e.id,app:e.app};return a({url:"/v1/flow/delete.json",params:r,method:"DELETE"})},this.checkRuleValid=function(e){return void 0===e.resource||""===e.resource?(alert("资源名称不能为空"),!1):void 0===e.count||e.count<0?(alert("限流阈值必须大于等于 0"),!1):void 0===e.strategy||e.strategy<0?(alert("无效的流控模式"),!1):1!=e.strategy&&2!=e.strategy||void 0!==e.refResource&&""!=e.refResource?void 0===e.controlBehavior||e.controlBehavior<0?(alert("无效的流控整形方式"),!1):1==e.controlBehavior&&r(e.warmUpPeriodSec)?(alert("预热时长必须大于 0"),!1):2==e.controlBehavior&&r(e.maxQueueingTimeMs)?(alert("排队超时时间必须大于 0"),!1):!e.clusterMode||void 0!==e.clusterConfig&&void 0!==e.clusterConfig.thresholdType||(alert("集群限流配置不正确"),!1):(alert("请填写关联资源或入口"),!1)}}]),(app=angular.module("sentinelDashboardApp")).service("FlowServiceV2",["$http",function(a){function r(e){return void 0===e||""===e||isNaN(e)||e<=0}this.queryMachineRules=function(e,r,t){return a({url:"/v2/flow/rules",params:{app:e,ip:r,port:t},method:"GET"})},this.newRule=function(e){return a({url:"/v2/flow/rule",data:e,method:"POST"})},this.saveRule=function(e){return a({url:"/v2/flow/rule/"+e.id,data:e,method:"PUT"})},this.deleteRule=function(e){return a({url:"/v2/flow/rule/"+e.id,method:"DELETE"})},this.checkRuleValid=function(e){return void 0===e.resource||""===e.resource?(alert("资源名称不能为空"),!1):void 0===e.count||e.count<0?(alert("限流阈值必须大于等于 0"),!1):void 0===e.strategy||e.strategy<0?(alert("无效的流控模式"),!1):1!=e.strategy&&2!=e.strategy||void 0!==e.refResource&&""!=e.refResource?void 0===e.controlBehavior||e.controlBehavior<0?(alert("无效的流控整形方式"),!1):1==e.controlBehavior&&r(e.warmUpPeriodSec)?(alert("预热时长必须大于 0"),!1):2==e.controlBehavior&&r(e.maxQueueingTimeMs)?(alert("排队超时时间必须大于 0"),!1):!e.clusterMode||void 0!==e.clusterConfig&&void 0!==e.clusterConfig.thresholdType||(alert("集群限流配置不正确"),!1):(alert("请填写关联资源或入口"),!1)}}]),(app=angular.module("sentinelDashboardApp")).service("DegradeService",["$http",function(a){this.queryMachineRules=function(e,r,t){return a({url:"degrade/rules.json",params:{app:e,ip:r,port:t},method:"GET"})},this.newRule=function(e){var r={id:e.id,resource:e.resource,limitApp:e.limitApp,count:e.count,timeWindow:e.timeWindow,grade:e.grade,app:e.app,ip:e.ip,port:e.port};return a({url:"/degrade/new.json",params:r,method:"GET"})},this.saveRule=function(e){var r={id:e.id,resource:e.resource,limitApp:e.limitApp,grade:e.grade,count:e.count,timeWindow:e.timeWindow};return a({url:"/degrade/save.json",params:r,method:"GET"})},this.deleteRule=function(e){var r={id:e.id,app:e.app};return a({url:"/degrade/delete.json",params:r,method:"GET"})},this.checkRuleValid=function(e){return void 0===e.resource||""===e.resource?(alert("资源名称不能为空"),!1):void 0===e.grade||e.grade<0?(alert("未知的降级策略"),!1):void 0===e.count||""===e.count||e.count<0?(alert("降级阈值不能为空或小于 0"),!1):void 0===e.timeWindow||""===e.timeWindow||e.timeWindow<=0?(alert("降级时间窗口必须大于 0"),!1):!(1==e.grade&&1=r?n.apply(null,t):function(){return e(t.concat([].slice.apply(arguments)))}}(e)}function n(){var n=arguments,r=n.length-1;return function(){for(var e=r,t=n[r].apply(this,arguments);e--;)t=n[e].call(this,t);return t}}function u(){for(var e=[],t=0;tthis._limit&&this.evict(),e},e.prototype.evict=function(){var t=this._items.shift();return this._evictListeners.forEach(function(e){return e(t)}),t},e.prototype.dequeue=function(){if(this.size())return this._items.splice(0,1)[0]},e.prototype.clear=function(){var e=this._items;return this._items=[],e},e.prototype.size=function(){return this._items.length},e.prototype.remove=function(e){var t=this._items.indexOf(e);return-1 "+qe(e))},e.prototype.traceTransitionIgnored=function(e){this.enabled(g.Category.TRANSITION)&&console.log(st(e)+": Ignored <> "+qe(e))},e.prototype.traceHookInvocation=function(e,t,n){if(this.enabled(g.Category.HOOK)){var r=C("traceData.hookType")(n)||"internal",i=C("traceData.context.state.name")(n)||C("traceData.context")(n)||"unknown",o=He(e.registeredHook.callback);console.log(st(t)+": Hook -> "+r+" context: "+i+", "+Le(200,o))}},e.prototype.traceHookResult=function(e,t,n){this.enabled(g.Category.HOOK)&&console.log(st(t)+": <- Hook returned: "+Le(200,qe(e)))},e.prototype.traceResolvePath=function(e,t,n){this.enabled(g.Category.RESOLVE)&&console.log(st(n)+": Resolving "+e+" ("+t+")")},e.prototype.traceResolvableResolved=function(e,t){this.enabled(g.Category.RESOLVE)&&console.log(st(t)+": <- Resolved "+e+" to: "+Le(200,qe(e.data)))},e.prototype.traceError=function(e,t){this.enabled(g.Category.TRANSITION)&&console.log(st(t)+": <- Rejected "+qe(t)+", reason: "+e)},e.prototype.traceSuccess=function(e,t){this.enabled(g.Category.TRANSITION)&&console.log(st(t)+": <- Success "+qe(t)+", final state: "+e.name)},e.prototype.traceUIViewEvent=function(e,t,n){void 0===n&&(n=""),this.enabled(g.Category.UIVIEW)&&console.log("ui-view: "+Fe(30,e)+" "+et(t)+n)},e.prototype.traceUIViewConfigUpdated=function(e,t){this.enabled(g.Category.UIVIEW)&&this.traceUIViewEvent("Updating",e," with ViewConfig from context='"+t+"'")},e.prototype.traceUIViewFill=function(e,t){this.enabled(g.Category.UIVIEW)&&this.traceUIViewEvent("Fill",e," with: "+Le(200,t))},e.prototype.traceViewSync=function(e){if(this.enabled(g.Category.VIEWCONFIG)){var a="uiview component fqn",t=e.map(function(e){var t,n=e.uiView,r=e.viewConfig,i=n&&n.fqn,o=r&&r.viewDecl.$context.name+": ("+r.viewDecl.$name+")";return(t={})[a]=i,t["view config state (view name)"]=o,t}).sort(function(e,t){return(e[a]||"").localeCompare(t[a]||"")});it(t)}},e.prototype.traceViewServiceEvent=function(e,t){this.enabled(g.Category.VIEWCONFIG)&&console.log("VIEWCONFIG: "+e+" "+function(e){var t=e.viewDecl,n=t.$context.name||"(root)";return"[View#"+e.$id+" from '"+n+"' state]: target ui-view: '"+t.$uiViewName+"@"+t.$uiViewContextAnchor+"'"}(t))},e.prototype.traceViewServiceUIViewEvent=function(e,t){this.enabled(g.Category.VIEWCONFIG)&&console.log("VIEWCONFIG: "+e+" "+et(t))},e}(),lt=new ut,ct=function(){function e(e){this.pattern=/.*/,this.inherit=!0,Y(this,e)}return e.prototype.is=function(e,t){return!0},e.prototype.encode=function(e,t){return e},e.prototype.decode=function(e,t){return e},e.prototype.equals=function(e,t){return e==t},e.prototype.$subPattern=function(){var e=this.pattern.toString();return e.substr(1,e.length-2)},e.prototype.toString=function(){return"{ParamType:"+this.name+"}"},e.prototype.$normalize=function(e){return this.is(e)?e:this.decode(e)},e.prototype.$asArray=function(e,t){if(!e)return this;if("auto"===e&&!t)throw new Error("'auto' array mode is for query parameters only");return new dt(this,e)},e}();function dt(r,i){var o=this;function a(e){return A(e)?e:k(e)?[e]:[]}function s(n,r){return function(e){if(A(e)&&0===e.length)return e;var t=ce(a(e),n);return!0===r?0===se(t,function(e){return!e}).length:function(e){switch(e.length){case 0:return;case 1:return"auto"===i?e[0]:e;default:return e}}(t)}}function u(o){return function(e,t){var n=a(e),r=a(t);if(n.length!==r.length)return!1;for(var i=0;i=n.invokeLimit&&n.deregister()}}},o.prototype.handleHookResult=function(e){var t=this,n=this.getNotCurrentRejection();return n||(R(e)?e.then(function(e){return t.handleHookResult(e)}):(lt.traceHookResult(e,this.transition,this.options),!1===e?Ve.aborted("Hook aborted transition").toPromise():c($t)(e)?Ve.redirected(e).toPromise():void 0))},o.prototype.getNotCurrentRejection=function(){var e=this.transition.router;return e._disposed?Ve.aborted("UIRouter instance #"+e.$id+" has been stopped (disposed)").toPromise():this.transition._aborted?Ve.aborted().toPromise():this.isSuperseded()?Ve.superseded(this.options.current()).toPromise():void 0},o.prototype.toString=function(){var e=this.options,t=this.registeredHook;return(C("traceData.hookType")(e)||"internal")+" context: "+(C("traceData.context.state.name")(e)||C("traceData.context")(e)||"unknown")+", "+Le(200,Ne(t.callback))},o.HANDLE_RESULT=function(t){return function(e){return t.handleHookResult(e)}},o.LOG_REJECTED_RESULT=function(t){return function(e){R(e)&&e.catch(function(e){return t.logError(Ve.normalize(e))})}},o.LOG_ERROR=function(t){return function(e){return t.logError(e)}},o.REJECT_ERROR=function(e){return function(e){return Pe(e)}},o.THROW_ERROR=function(e){return function(e){throw e}},o}();function Gt(e,t,n){var i=O(t)?[t]:t;return!!(D(i)?i:function(e){for(var t=i,n=0;n "+(this.valid()?"":"(X) ")+"'"+(T(t)?t.name:t)+"'"+qe(n(this.params()))+" )"},t.diToken=t}();function en(e,t){var n=["",""],r=e.replace(/[\\\[\]\^$*+?.()|{}]/g,"\\$&");if(!t)return r;switch(t.squash){case!1:n=["(",")"+(t.isOptional?"?":"")];break;case!0:r=r.replace(/\/$/,""),n=["(?:/(",")|/)?"];break;default:n=["("+t.squash+"|",")?"]}return r+n[0]+t.type.pattern.source+n[1]}var tn=Xe("/"),nn={state:{params:{}},strict:!0,caseInsensitive:!0},rn=function(){function m(o,a,e,t){var s=this;this._cache={path:[this]},this._children=[],this._params=[],this._segments=[],this._compiled=[],this.config=t=te(t,nn),this.pattern=o;for(var n,r,i,u=/([:*])([\w\[\]]+)|\{([\w\[\]]+)(?:\:\s*((?:[^{}\\]+|\\.|\{(?:[^{}\\]+|\\.)*\})+))?\}/g,l=/([:]?)([\w\[\].-]+)|\{([\w\[\].-]+)(?:\:\s*((?:[^{}\\]+|\\.|\{(?:[^{}\\]+|\\.)*\})+))?\}/g,c=[],d=0,p=function(e){if(!m.nameValidator.test(e))throw new Error("Invalid parameter name '"+e+"' in pattern '"+o+"'");if(ue(s._params,y("id",e)))throw new Error("Duplicate parameter name '"+e+"' in pattern '"+o+"'")},h=function(e,t){var n,r=e[2]||e[3],i=t?e[4]:e[4]||("*"===e[1]?"[\\s\\S]*":null);return{id:r,regexp:i,segment:o.substring(d,e.index),type:i?a.type(i)||(n=i,W(a.type(t?"query":"path"),{pattern:new RegExp(n,s.config.caseInsensitive?"i":void 0)})):null}};(n=u.exec(o))&&!(0<=(r=h(n,!1)).segment.indexOf("?"));)p(r.id),this._params.push(e.fromPath(r.id,r.type,t.state)),this._segments.push(r.segment),c.push([r.segment,De(this._params)]),d=u.lastIndex;var f=(i=o.substring(d)).indexOf("?");if(0<=f){var g=i.substring(f);if(i=i.substring(0,f),0 Registering",e),this._viewConfigs.push(e)},u.prototype.sync=function(){var n=this,r=this._uiViews.map(function(e){return[e.fqn,e]}).reduce(ke,{});function i(e){for(var t=e.viewDecl.$context,n=0;++n&&t.parent;)t=t.parent;return n}var o=l(function(e,t,n,r){return t*(e(n)-e(r))}),e=this._uiViews.sort(o(function(e){var t=function(e){return e&&e.parent?t(e.parent)+1:1};return 1e4*e.fqn.split(".").length+t(e.creationContext)},1)).map(function(e){var t=n._viewConfigs.filter(u.matches(r,e));return 1 Registering",t);var e=this._uiViews;return e.filter(function(e){return e.fqn===t.fqn&&e.$type===t.$type}).length&<.traceViewServiceUIViewEvent("!!!! duplicate uiView named:",t),e.push(t),this.sync(),function(){-1!==e.indexOf(t)?(lt.traceViewServiceUIViewEvent("<- Deregistering",t),Q(e)(t)):lt.traceViewServiceUIViewEvent("Tried removing non-registered uiView",t)}},u.prototype.available=function(){return this._uiViews.map(v("fqn"))},u.prototype.active=function(){return this._uiViews.filter(v("$config")).map(v("name"))},u.matches=function(s,u){return function(e){if(u.$type!==e.viewDecl.$type)return!1;var t=e.viewDecl,n=t.$uiViewName.split("."),r=u.fqn.split(".");if(!U(n,r.slice(0-n.length)))return!1;var i=1-n.length||void 0,o=r.slice(0,i).join("."),a=s[o].creationContext;return t.$uiViewContextAnchor===(a&&a.name)}},u}(),hn=function(){function e(){this.params=new wt,this.lastStartedTransitionId=-1,this.transitionHistory=new Re([],1),this.successfulTransitions=new Re([],1)}return e.prototype.dispose=function(){this.transitionHistory.clear(),this.successfulTransitions.clear(),this.transition=null},e}();function fn(e){if(!(D(e)||O(e)||c($t)(e)||$t.isDef(e)))throw new Error("'handler' must be a string, function, TargetState, or have a state: 'newtarget' property");return D(e)?e:p(e)}cn=function(e,t){var n=function(e,t){return(t.priority||0)-(e.priority||0)}(e,t);return 0!==n?n:0!==(n=function(e,t){var n={STATE:4,URLMATCHER:4,REGEXP:3,RAW:2,OTHER:1};return(n[e.type]||0)-(n[t.type]||0)}(e,t))?n:0!==(n=function(e,t){return e.urlMatcher&&t.urlMatcher?rn.compare(e.urlMatcher,t.urlMatcher):0}(e,t))?n:function(e,t){var n={STATE:!0,URLMATCHER:!0};return n[e.type]&&n[t.type]?0:(e.$id||0)-(t.$id||0)}(e,t)};var gn=function(){function e(e){this.router=e,this._sortFn=cn,this._rules=[],this._id=0,this.urlRuleFactory=new un(e)}return e.prototype.dispose=function(e){this._rules=[],delete this._otherwiseFn},e.prototype.initial=function(e){var t=fn(e);this.rule(this.urlRuleFactory.create(function(e,t){return 0===t.globals.transitionHistory.size()&&!!/^\/?$/.exec(e.path)},t))},e.prototype.otherwise=function(e){var t=fn(e);this._otherwiseFn=this.urlRuleFactory.create(p(!0),t),this._sorted=!1},e.prototype.removeRule=function(e){Q(this._rules,e)},e.prototype.rule=function(e){var t=this;if(!un.isUrlRule(e))throw new Error("invalid rule");return e.$id=this._id++,e.priority=e.priority||0,this._rules.push(e),this._sorted=!1,function(){return t.removeRule(e)}},e.prototype.rules=function(){return this.ensureSorted(),this._rules.concat(this._otherwiseFn?[this._otherwiseFn]:[])},e.prototype.sort=function(e){for(var t=this.stableSort(this._rules,this._sortFn=e||this._sortFn),n=0,r=0;rn.weight?s:n}return n},e}(),yn=0,wn=I("LocationServices",["url","path","search","hash","onChange"]),bn=I("LocationConfig",["port","protocol","host","baseHref","html5Mode","hashPrefix"]),$n=function(){function e(e,t){void 0===e&&(e=wn),void 0===t&&(t=bn),this.locationService=e,this.locationConfig=t,this.$id=yn++,this._disposed=!1,this._disposables=[],this.trace=lt,this.viewService=new pn(this),this.globals=new hn,this.transitionService=new Un(this),this.urlMatcherFactory=new sn(this),this.urlRouter=new dn(this),this.urlService=new vn(this),this.stateRegistry=new zt(this),this.stateService=new qn(this),this._plugins={},this.viewService._pluginapi._rootViewContext(this.stateRegistry.root()),this.globals.$current=this.stateRegistry.root(),this.globals.current=this.globals.$current.self,this.disposable(this.globals),this.disposable(this.stateService),this.disposable(this.stateRegistry),this.disposable(this.transitionService),this.disposable(this.urlService),this.disposable(e),this.disposable(t)}return e.prototype.disposable=function(e){this._disposables.push(e)},e.prototype.dispose=function(e){var t=this;e&&D(e.dispose)?e.dispose(this):(this._disposed=!0,this._disposables.slice().forEach(function(e){try{"function"==typeof e.dispose&&e.dispose(t),Q(t._disposables,e)}catch(e){}}))},e.prototype.plugin=function(e,t){void 0===t&&(t={});var n=new e(this,t);if(!n.name)throw new Error("Required property `name` missing on plugin: "+n);return this._disposables.push(n),this._plugins[n.name]=n},e.prototype.getPlugin=function(e){return e?this._plugins[e]:de(this._plugins)},e}();function _n(t){t.addResolvable(kt.fromData($n,t.router),""),t.addResolvable(kt.fromData(Jt,t),""),t.addResolvable(kt.fromData("$transition$",t),""),t.addResolvable(kt.fromData("$stateParams",t.params()),""),t.entering().forEach(function(e){t.addResolvable(kt.fromData("$state$",e),e)})}var Sn=G(["$transition$",Jt]),Cn=function(e){var t=de(e.treeChanges()).reduce(fe,[]).reduce(ve,[]),n=function(e){return Sn(e.token)?kt.fromData(e.token,null):e};t.forEach(function(e){e.resolvables=e.resolvables.map(n)})},kn=function(t){var e=t.to().redirectTo;if(e){var n=t.router.stateService;return D(e)?V.$q.when(e(t)).then(r):r(e)}function r(e){if(e)return e instanceof $t?e:O(e)?n.target(e,t.params(),t.options()):e.state||e.params?n.target(e.state||t.to(),e.params||t.params(),t.options()):void 0}};function Dn(n){return function(e,t){return(0,t.$$state()[n])(e,t)}}var xn=Dn("onExit"),On=Dn("onRetain"),Tn=Dn("onEnter"),An=function(e){return new At(e.treeChanges().to).resolvePath("EAGER",e).then(z)},En=function(e,t){return new At(e.treeChanges().to).subContext(t.$$state()).resolvePath("LAZY",e).then(z)},Pn=function(e){return new At(e.treeChanges().to).resolvePath("LAZY",e).then(z)},Mn=function(e){var t=V.$q,n=e.views("entering");if(n.length)return t.all(n.map(function(e){return t.when(e.load())})).then(z)},Rn=function(e){var t=e.views("entering"),n=e.views("exiting");if(t.length||n.length){var r=e.router.viewService;n.forEach(function(e){return r.deactivateViewConfig(e)}),t.forEach(function(e){return r.activateViewConfig(e)}),r.sync()}},In=function(e){var t=e.router.globals,n=function(){t.transition===e&&(t.transition=null)};e.onSuccess({},function(){t.successfulTransitions.enqueue(e),t.$current=e.$to(),t.current=t.$current.self,xe(e.params(),t.params)},{priority:1e4}),e.promise.then(n,n)},Vn=function(e){var t=e.options(),n=e.router.stateService,r=e.router.urlRouter;if("url"!==t.source&&t.location&&n.$current.navigable){var i={replace:"replace"===t.location};r.push(n.$current.navigable.url,n.params,i)}r.update(!0)},Ln=function(a){var s=a.router;var e=a.entering().filter(function(e){return!!e.$$state().lazyLoad}).map(function(e){return Fn(a,e)});return V.$q.all(e).then(function(){if("url"!==a.originalTransition().options().source){var e=a.targetState();return s.stateService.target(e.identifier(),e.params(),e.options())}var t=s.urlService,n=t.match(t.parts()),r=n&&n.rule;if(r&&"STATE"===r.type){var i=r.state,o=n.match;return s.stateService.target(i,o,a.options())}s.urlService.sync()})};function Fn(t,n){var r=n.$$state().lazyLoad,e=r._promise;if(!e){e=r._promise=V.$q.when(r(t,n)).then(function(e){e&&Array.isArray(e.states)&&e.states.forEach(function(e){return t.router.stateRegistry.register(e)});return e}).then(function(e){return delete n.lazyLoad,delete n.$$state().lazyLoad,delete r._promise,e},function(e){return delete r._promise,V.$q.reject(e)})}return e}var jn=function(e,t,n,r,i,o,a,s){void 0===i&&(i=!1),void 0===o&&(o=Wt.HANDLE_RESULT),void 0===a&&(a=Wt.REJECT_ERROR),void 0===s&&(s=!1),this.name=e,this.hookPhase=t,this.hookOrder=n,this.criteriaMatchPath=r,this.reverseSort=i,this.getResultHandler=o,this.getErrorHandler=a,this.synchronous=s};function Hn(e){var t=e._ignoredReason();if(t){lt.traceTransitionIgnored(e);var n=e.router.globals.transition;return"SameAsCurrent"===t&&n&&n.abort(),Ve.ignored().toPromise()}}function Nn(e){if(!e.valid())throw new Error(e.error().toString())}var Yn={location:!0,relative:null,inherit:!1,notify:!0,reload:!1,custom:{},current:function(){return null},source:"unknown"},Un=function(){function e(e){this._transitionCount=0,this._eventTypes=[],this._registeredHooks={},this._criteriaPaths={},this._router=e,this.$view=e.viewService,this._deregisterHookFns={},this._pluginapi=B(p(this),{},p(this),["_definePathType","_defineEvent","_getPathTypes","_getEvents","getHooks"]),this._defineCorePaths(),this._defineCoreEvents(),this._registerCoreTransitionHooks(),e.globals.successfulTransitions.onEvict(Cn)}return e.prototype.onCreate=function(e,t,n){},e.prototype.onBefore=function(e,t,n){},e.prototype.onStart=function(e,t,n){},e.prototype.onExit=function(e,t,n){},e.prototype.onRetain=function(e,t,n){},e.prototype.onEnter=function(e,t,n){},e.prototype.onFinish=function(e,t,n){},e.prototype.onSuccess=function(e,t,n){},e.prototype.onError=function(e,t,n){},e.prototype.dispose=function(e){de(this._registeredHooks).forEach(function(t){return t.forEach(function(e){e._deregistered=!0,Q(t,e)})})},e.prototype.create=function(e,t){return new Jt(e,t,this._router)},e.prototype._defineCoreEvents=function(){var e=g.TransitionHookPhase,t=Wt,n=this._criteriaPaths;this._defineEvent("onCreate",e.CREATE,0,n.to,!1,t.LOG_REJECTED_RESULT,t.THROW_ERROR,!0),this._defineEvent("onBefore",e.BEFORE,0,n.to),this._defineEvent("onStart",e.RUN,0,n.to),this._defineEvent("onExit",e.RUN,100,n.exiting,!0),this._defineEvent("onRetain",e.RUN,200,n.retained),this._defineEvent("onEnter",e.RUN,300,n.entering),this._defineEvent("onFinish",e.RUN,400,n.to),this._defineEvent("onSuccess",e.SUCCESS,0,n.to,!1,t.LOG_REJECTED_RESULT,t.LOG_ERROR,!0),this._defineEvent("onError",e.ERROR,0,n.to,!1,t.LOG_REJECTED_RESULT,t.LOG_ERROR,!0)},e.prototype._defineCorePaths=function(){var e=g.TransitionHookScope.STATE,t=g.TransitionHookScope.TRANSITION;this._definePathType("to",t),this._definePathType("from",t),this._definePathType("exiting",e),this._definePathType("retained",e),this._definePathType("entering",e)},e.prototype._defineEvent=function(e,t,n,r,i,o,a,s){void 0===i&&(i=!1),void 0===o&&(o=Wt.HANDLE_RESULT),void 0===a&&(a=Wt.REJECT_ERROR),void 0===s&&(s=!1);var u=new jn(e,t,n,r,i,o,a,s);this._eventTypes.push(u),Qt(this,this,u)},e.prototype._getEvents=function(t){return(k(t)?this._eventTypes.filter(function(e){return e.hookPhase===t}):this._eventTypes.slice()).sort(function(e,t){var n=e.hookPhase-t.hookPhase;return 0==n?e.hookOrder-t.hookOrder:n})},e.prototype._definePathType=function(e,t){this._criteriaPaths[e]={name:e,scope:t}},e.prototype._getPathTypes=function(){return this._criteriaPaths},e.prototype.getHooks=function(e){return this._registeredHooks[e]},e.prototype._registerCoreTransitionHooks=function(){var e=this._deregisterHookFns;e.addCoreResolves=function(e){return e.onCreate({},_n)}(this),e.ignored=function(e){return e.onBefore({},Hn,{priority:-9999})}(this),e.invalid=function(e){return e.onBefore({},Nn,{priority:-1e4})}(this),e.redirectTo=function(e){return e.onStart({to:function(e){return!!e.redirectTo}},kn)}(this),e.onExit=function(e){return e.onExit({exiting:function(e){return!!e.onExit}},xn)}(this),e.onRetain=function(e){return e.onRetain({retained:function(e){return!!e.onRetain}},On)}(this),e.onEnter=function(e){return e.onEnter({entering:function(e){return!!e.onEnter}},Tn)}(this),e.eagerResolve=function(e){return e.onStart({},An,{priority:1e3})}(this),e.lazyResolve=function(e){return e.onEnter({entering:p(!0)},En,{priority:1e3})}(this),e.resolveAll=function(e){return e.onFinish({},Pn,{priority:1e3})}(this),e.loadViews=function(e){return e.onFinish({},Mn)}(this),e.activateViews=function(e){return e.onSuccess({},Rn)}(this),e.updateGlobals=function(e){return e.onCreate({},In)}(this),e.updateUrl=function(e){return e.onSuccess({},Vn,{priority:9999})}(this),e.lazyLoad=function(e){return e.onBefore({entering:function(e){return!!e.lazyLoad}},Ln)}(this)},e}(),qn=function(){function n(e){this.router=e,this.invalidCallbacks=[],this._defaultErrorHandler=function(e){e instanceof Error&&e.stack?(console.error(e),console.error(e.stack)):e instanceof Ve?(console.error(e.toString()),e.detail&&e.detail.stack&&console.error(e.detail.stack)):console.error(e)};var t=Object.keys(n.prototype).filter(d(G(["current","$current","params","transition"])));B(p(n.prototype),this,p(this),t)}return Object.defineProperty(n.prototype,"transition",{get:function(){return this.router.globals.transition},enumerable:!0,configurable:!0}),Object.defineProperty(n.prototype,"params",{get:function(){return this.router.globals.params},enumerable:!0,configurable:!0}),Object.defineProperty(n.prototype,"current",{get:function(){return this.router.globals.current},enumerable:!0,configurable:!0}),Object.defineProperty(n.prototype,"$current",{get:function(){return this.router.globals.$current},enumerable:!0,configurable:!0}),n.prototype.dispose=function(){this.defaultErrorHandler(z),this.invalidCallbacks=[]},n.prototype._handleInvalidTargetState=function(e,n){var r=this,i=_t.makeTargetState(this.router.stateRegistry,e),t=this.router.globals,o=function(){return t.transitionHistory.peekTail()},a=o(),s=new Re(this.invalidCallbacks.slice()),u=new At(e).injector(),l=function(e){if(e instanceof $t){var t=e;return(t=r.target(t.identifier(),t.params(),t.options())).valid()?o()!==a?Ve.superseded().toPromise():r.transitionTo(t.identifier(),t.params(),t.options()):Ve.invalid(t.error()).toPromise()}};return function t(){var e=s.dequeue();return void 0===e?Ve.invalid(n.error()).toPromise():V.$q.when(e(n,i,u)).then(l).then(function(e){return e||t()})}()},n.prototype.onInvalid=function(e){return this.invalidCallbacks.push(e),function(){Q(this.invalidCallbacks)(e)}.bind(this)},n.prototype.reload=function(e){return this.transitionTo(this.current,this.params,{reload:!k(e)||e,inherit:!1,notify:!1})},n.prototype.go=function(e,t,n){var r=te(n,{relative:this.$current,inherit:!0},Yn);return this.transitionTo(e,t,r)},n.prototype.target=function(e,t,n){if(void 0===n&&(n={}),T(n.reload)&&!n.reload.name)throw new Error("Invalid reload state object");var r=this.router.stateRegistry;if(n.reloadState=!0===n.reload?r.root():r.matcher.find(n.reload,n.relative),n.reload&&!n.reloadState)throw new Error("No such reload state '"+(O(n.reload)?n.reload:n.reload.name)+"'");return new $t(this.router.stateRegistry,e,t,n)},n.prototype.getCurrentPath=function(){var e=this,t=this.router.globals.successfulTransitions.peekTail();return t?t.treeChanges().to:[new bt(e.router.stateRegistry.root())]},n.prototype.transitionTo=function(e,t,n){var o=this;void 0===t&&(t={}),void 0===n&&(n={});var a=this.router,s=a.globals;n=te(n,Yn);n=Y(n,{current:function(){return s.transition}});var r=this.target(e,t,n),i=this.getCurrentPath();if(!r.exists())return this._handleInvalidTargetState(i,r);if(!r.valid())return Pe(r.error());var u=function(i){return function(e){if(e instanceof Ve){var t=a.globals.lastStartedTransitionId<=i.$id;if(e.type===g.RejectType.IGNORED)return t&&a.urlRouter.update(),V.$q.when(s.current);var n=e.detail;if(e.type===g.RejectType.SUPERSEDED&&e.redirected&&n instanceof $t){var r=i.redirect(n);return r.run().catch(u(r))}if(e.type===g.RejectType.ABORTED)return t&&a.urlRouter.update(),V.$q.reject(e)}return o.defaultErrorHandler()(e),V.$q.reject(e)}},l=this.router.transitionService.create(i,r),c=l.run().catch(u(l));return Ee(c),Y(c,{transition:l})},n.prototype.is=function(e,t,n){n=te(n,{relative:this.$current});var r=this.router.stateRegistry.matcher.find(e,n.relative);if(k(r)){if(this.$current!==r)return!1;if(!t)return!0;var i=r.parameters({inherit:!0,matchingKeys:t});return vt.equals(i,vt.values(i,t),this.params)}},n.prototype.includes=function(e,t,n){n=te(n,{relative:this.$current});var r=O(e)&&Me.fromString(e);if(r){if(!r.matches(this.$current.name))return!1;e=this.$current.name}var i=this.router.stateRegistry.matcher.find(e,n.relative),o=this.$current.includes;if(k(i)){if(!k(o[i.name]))return!1;if(!t)return!0;var a=i.parameters({inherit:!0,matchingKeys:t});return vt.equals(a,vt.values(a,t),this.params)}},n.prototype.href=function(e,t,n){n=te(n,{lossy:!0,inherit:!0,absolute:!1,relative:this.$current}),t=t||{};var r=this.router.stateRegistry.matcher.find(e,n.relative);if(!k(r))return null;n.inherit&&(t=this.params.$inherit(t,this.$current,r));var i=r&&n.lossy?r.navigable:r;return i&&void 0!==i.url&&null!==i.url?this.router.urlRouter.href(i.url,t,{absolute:n.absolute}):null},n.prototype.defaultErrorHandler=function(e){return this._defaultErrorHandler=e||this._defaultErrorHandler},n.prototype.get=function(e,t){var n=this.router.stateRegistry;return 0===arguments.length?n.get():n.get(e,t||this.$current)},n.prototype.lazyLoad=function(e,t){var n=this.get(e);if(!n||!n.lazyLoad)throw new Error("Can not lazy load "+e);var r=this.getCurrentPath(),i=_t.makeTargetState(this.router.stateRegistry,r);return Fn(t=t||this.router.transitionService.create(r,i),n)},n}(),zn={when:function(n){return new Promise(function(e,t){return e(n)})},reject:function(n){return new Promise(function(e,t){t(n)})},defer:function(){var n={};return n.promise=new Promise(function(e,t){n.resolve=e,n.reject=t}),n},all:function(e){if(A(e))return Promise.all(e);if(T(e)){var t=Object.keys(e).map(function(t){return e[t].then(function(e){return{key:t,val:e}})});return zn.all(t).then(function(e){return e.reduce(function(e,t){return e[t.key]=t.val,e},{})})}}},Bn={},Wn=/((\/\/.*$)|(\/\*[\s\S]*?\*\/))/gm,Gn=/([^\s,]+)/g,Kn={get:function(e){return Bn[e]},has:function(e){return null!=Kn.get(e)},invoke:function(e,t,n){var r=Y({},Bn,n||{}),i=Kn.annotate(e),o=be(function(e){return r.hasOwnProperty(e)},function(e){return"DI can't find injectable: '"+e+"'"}),a=i.filter(o).map(function(e){return r[e]});return D(e)?e.apply(t,a):e.slice(-1)[0].apply(t,a)},annotate:function(e){if(!M(e))throw new Error("Not an injectable function: "+e);if(e&&e.$inject)return e.$inject;if(A(e))return e.slice(0,-1);var t=e.toString().replace(Wn,"");return t.slice(t.indexOf("(")+1,t.indexOf(")")).match(Gn)||[]}},Qn=function(e,t){var n=t[0],r=t[1];return e.hasOwnProperty(n)?A(e[n])?e[n].push(r):e[n]=[e[n],r]:e[n]=r,e},Zn=function(e){return e.split("&").filter(q).map(Qe).reduce(Qn,{})};function Xn(e){var t=function(e){return e||""},n=Ge(e).map(t),r=n[0],i=n[1],o=Ke(r).map(t);return{path:o[0],search:o[1],hash:i,url:e}}var Jn=function(e){var t=e.path(),n=e.search(),r=e.hash(),i=Object.keys(n).map(function(t){var e=n[t];return(A(e)?e:[e]).map(function(e){return t+"="+e})}).reduce(fe,[]).join("&");return t+(i?"?"+i:"")+(r?"#"+r:"")};function er(r,i,o,a){return function(e){var t=e.locationService=new o(e),n=e.locationConfig=new a(e,i);return{name:r,service:t,configuration:n,dispose:function(e){e.dispose(t),e.dispose(n)}}}}var tr,nr,rr,ir=function(){function e(e,t){var n=this;this.fireAfterUpdate=t,this._listeners=[],this._listener=function(t){return n._listeners.forEach(function(e){return e(t)})},this.hash=function(){return Xn(n._get()).hash},this.path=function(){return Xn(n._get()).path},this.search=function(){return Zn(Xn(n._get()).search)},this._location=L.location,this._history=L.history}return e.prototype.url=function(t,e){return void 0===e&&(e=!0),k(t)&&t!==this._get()&&(this._set(null,null,t,e),this.fireAfterUpdate&&this._listeners.forEach(function(e){return e({url:t})})),Jn(this)},e.prototype.onChange=function(e){var t=this;return this._listeners.push(e),function(){return Q(t._listeners,e)}},e.prototype.dispose=function(e){ee(this._listeners)},e}(),or=(tr=function(e,t){return(tr=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(e,t)},function(e,t){function n(){this.constructor=e}tr(e,t),e.prototype=null===t?Object.create(t):(n.prototype=t.prototype,new n)}),ar=function(n){function e(e){var t=n.call(this,e,!1)||this;return L.addEventListener("hashchange",t._listener,!1),t}return or(e,n),e.prototype._get=function(){return Ze(this._location.hash)},e.prototype._set=function(e,t,n,r){this._location.hash=n},e.prototype.dispose=function(e){n.prototype.dispose.call(this,e),L.removeEventListener("hashchange",this._listener)},e}(ir),sr=(nr=function(e,t){return(nr=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(e,t)},function(e,t){function n(){this.constructor=e}nr(e,t),e.prototype=null===t?Object.create(t):(n.prototype=t.prototype,new n)}),ur=function(t){function e(e){return t.call(this,e,!0)||this}return sr(e,t),e.prototype._get=function(){return this._url},e.prototype._set=function(e,t,n,r){this._url=n},e}(ir),lr=(rr=function(e,t){return(rr=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(e,t)},function(e,t){function n(){this.constructor=e}rr(e,t),e.prototype=null===t?Object.create(t):(n.prototype=t.prototype,new n)}),cr=function(n){function e(e){var t=n.call(this,e,!0)||this;return t._config=e.urlService.config,L.addEventListener("popstate",t._listener,!1),t}return lr(e,n),e.prototype._getBasePrefix=function(){return We(this._config.baseHref())},e.prototype._get=function(){var e=this._location,t=e.pathname,n=e.hash,r=e.search;r=Ke(r)[1],n=Ge(n)[1];var i=this._getBasePrefix(),o=t===this._config.baseHref(),a=t.substr(0,i.length)===i;return(t=o?"/":a?t.substring(i.length):t)+(r?"?"+r:"")+(n?"#"+n:"")},e.prototype._set=function(e,t,n,r){var i=this._getBasePrefix(),o=n&&"/"!==n[0]?"/":"",a=""===n||"/"===n?this._config.baseHref():i+o+n;r?this._history.replaceState(e,t,a):this._history.pushState(e,t,a)},e.prototype.dispose=function(e){n.prototype.dispose.call(this,e),L.removeEventListener("popstate",this._listener)},e}(ir),dr=function(){var t=this;this.dispose=z,this._baseHref="",this._port=80,this._protocol="http",this._host="localhost",this._hashPrefix="",this.port=function(){return t._port},this.protocol=function(){return t._protocol},this.host=function(){return t._host},this.baseHref=function(){return t._baseHref},this.html5Mode=function(){return!1},this.hashPrefix=function(e){return k(e)?t._hashPrefix=e:t._hashPrefix}},pr=function(){function e(e,t){void 0===t&&(t=!1),this._isHtml5=t,this._baseHref=void 0,this._hashPrefix=""}return e.prototype.port=function(){return location.port?Number(location.port):"https"===this.protocol()?443:80},e.prototype.protocol=function(){return location.protocol.replace(/:/g,"")},e.prototype.host=function(){return location.hostname},e.prototype.html5Mode=function(){return this._isHtml5},e.prototype.hashPrefix=function(e){return k(e)?this._hashPrefix=e:this._hashPrefix},e.prototype.baseHref=function(e){return k(e)&&(this._baseHref=e),b(this._baseHref)&&(this._baseHref=this.getBaseHref()),this._baseHref},e.prototype.getBaseHref=function(){var e=document.getElementsByTagName("base")[0];return e&&e.href?e.href.replace(/^([^/:]*:)?\/\/[^/]*/,""):this._isHtml5?"/":location.pathname||"/"},e.prototype.dispose=function(){},e}();function hr(e){return V.$injector=Kn,{name:"vanilla.services",$q:V.$q=zn,$injector:Kn,dispose:function(){return null}}}var fr=er("vanilla.hashBangLocation",!1,ar,pr),gr=er("vanilla.pushStateLocation",!0,cr,pr),mr=er("vanilla.memoryLocation",!1,ur,dr),vr=function(){function e(){}return e.prototype.dispose=function(e){},e}(),yr=Object.freeze({root:L,fromJson:j,toJson:H,forEach:N,extend:Y,equals:U,identity:q,noop:z,createProxyFunctions:B,inherit:W,inArray:G,_inArray:K,removeFrom:Q,_removeFrom:Z,pushTo:X,_pushTo:J,deregAll:ee,defaults:te,mergeR:ne,ancestors:re,pick:ie,omit:oe,pluck:ae,filter:se,find:ue,mapObj:le,map:ce,values:de,allTrueR:pe,anyTrueR:he,unnestR:fe,flattenR:ge,pushR:me,uniqR:ve,unnest:ye,flatten:we,assertPredicate:be,assertMap:$e,assertFn:_e,pairs:Se,arrayTuples:Ce,applyPairs:ke,tail:De,copy:xe,_extend:Oe,silenceUncaughtInPromise:Ee,silentRejection:Pe,makeStub:I,services:V,Glob:Me,curry:l,compose:n,pipe:u,prop:v,propEq:y,parse:C,not:d,and:r,or:i,all:a,any:s,is:c,eq:o,val:p,invoke:h,pattern:f,isUndefined:b,isDefined:k,isNull:$,isNullOrUndefined:_,isFunction:D,isNumber:x,isString:O,isObject:T,isArray:A,isDate:E,isRegExp:P,isInjectable:M,isPromise:R,Queue:Re,maxLength:Le,padString:Fe,kebobString:je,functionToString:He,fnToString:Ne,stringify:qe,beforeAfterSubstr:ze,hostRegex:Be,stripLastPathElement:We,splitHash:Ge,splitQuery:Ke,splitEqual:Qe,trimHashVal:Ze,splitOnDelim:Xe,joinNeighborsR:Je,get Category(){return g.Category},Trace:ut,trace:lt,get DefType(){return g.DefType},Param:vt,ParamTypes:yt,StateParams:wt,ParamType:ct,PathNode:bt,PathUtils:_t,resolvePolicies:St,defaultResolvePolicy:Ct,Resolvable:kt,NATIVE_INJECTOR_TOKEN:Tt,ResolveContext:At,resolvablesBuilder:Ft,StateBuilder:Nt,StateObject:Yt,StateMatcher:Ut,StateQueueManager:qt,StateRegistry:zt,StateService:qn,TargetState:$t,get TransitionHookPhase(){return g.TransitionHookPhase},get TransitionHookScope(){return g.TransitionHookScope},HookBuilder:Zt,matchState:Gt,RegisteredHook:Kt,makeEvent:Qt,get RejectType(){return g.RejectType},Rejection:Ve,Transition:Jt,TransitionHook:Wt,TransitionEventType:jn,defaultTransOpts:Yn,TransitionService:Un,UrlRules:gn,UrlConfig:mn,UrlMatcher:rn,ParamFactory:an,UrlMatcherFactory:sn,UrlRouter:dn,UrlRuleFactory:un,BaseUrlRule:ln,UrlService:vn,ViewService:pn,UIRouterGlobals:hn,UIRouter:$n,$q:zn,$injector:Kn,BaseLocationServices:ir,HashLocationService:ar,MemoryLocationService:ur,PushStateLocationService:cr,MemoryLocationConfig:dr,BrowserLocationConfig:pr,keyValsToObjectR:Qn,getParams:Zn,parseUrl:Xn,buildUrl:Jn,locationPluginFactory:er,servicesPlugin:hr,hashLocationPlugin:fr,pushStateLocationPlugin:gr,memoryLocationPlugin:mr,UIRouterPluginBase:vr});function wr(){var n=null;return function(e,t){return n=n||V.$injector.get("$templateFactory"),[new Sr(e,t,n)]}}var br=function(e,n){return e.reduce(function(e,t){return e||k(n[t])},!1)};function $r(r){if(!r.parent)return{};var i=["component","bindings","componentProvider"],o=["templateProvider","templateUrl","template","notify","async"].concat(["controller","controllerProvider","controllerAs","resolveAs"]),e=i.concat(o);if(k(r.views)&&br(e,r))throw new Error("State '"+r.name+"' has a 'views' object. It cannot also have \"view properties\" at the state level. Move the following properties into a view (in the 'views' object): "+e.filter(function(e){return k(r[e])}).join(", "));var a={},t=r.views||{$default:ie(r,e)};return N(t,function(e,t){if(t=t||"$default",O(e)&&(e={component:e}),e=Y({},e),br(i,e)&&br(o,e))throw new Error("Cannot combine: "+i.join("|")+" with: "+o.join("|")+" in stateview: '"+t+"@"+r.name+"'");e.resolveAs=e.resolveAs||"$resolve",e.$type="ng1",e.$context=r,e.$name=t;var n=pn.normalizeUIViewTarget(e.$context,e.$name);e.$uiViewName=n.uiViewName,e.$uiViewContextAnchor=n.uiViewContextAnchor,a[t]=e}),a}var _r=0,Sr=function(){function e(e,t,n){var r=this;this.path=e,this.viewDecl=t,this.factory=n,this.$id=_r++,this.loaded=!1,this.getTemplate=function(e,t){return r.component?r.factory.makeComponentTemplate(e,t,r.component,r.viewDecl.bindings):r.template}}return e.prototype.load=function(){var t=this,e=V.$q,n=new At(this.path),r=this.path.reduce(function(e,t){return Y(e,t.paramValues)},{}),i={template:e.when(this.factory.fromConfig(this.viewDecl,r,n)),controller:e.when(this.getController(n))};return e.all(i).then(function(e){return lt.traceViewServiceEvent("Loaded",t),t.controller=e.controller,Y(t,e.template),t})},e.prototype.getController=function(e){var t=this.viewDecl.controllerProvider;if(!M(t))return this.viewDecl.controller;var n=V.$injector.annotate(t),r=A(t)?De(t):t;return new kt("",r,n).get(e)},e}(),Cr=function(){function e(){var r=this;this._useHttp=S.version.minor<3,this.$get=["$http","$templateCache","$injector",function(e,t,n){return r.$templateRequest=n.has&&n.has("$templateRequest")&&n.get("$templateRequest"),r.$http=e,r.$templateCache=t,r}]}return e.prototype.useHttpService=function(e){this._useHttp=e},e.prototype.fromConfig=function(e,t,n){var r=function(e){return V.$q.when(e).then(function(e){return{template:e}})},i=function(e){return V.$q.when(e).then(function(e){return{component:e}})};return k(e.template)?r(this.fromString(e.template,t)):k(e.templateUrl)?r(this.fromUrl(e.templateUrl,t)):k(e.templateProvider)?r(this.fromProvider(e.templateProvider,t,n)):k(e.component)?i(e.component):k(e.componentProvider)?i(this.fromComponentProvider(e.componentProvider,t,n)):r("")},e.prototype.fromString=function(e,t){return D(e)?e(t):e},e.prototype.fromUrl=function(e,t){return D(e)&&(e=e(t)),null==e?null:this._useHttp?this.$http.get(e,{cache:this.$templateCache,headers:{Accept:"text/html"}}).then(function(e){return e.data}):this.$templateRequest(e)},e.prototype.fromProvider=function(e,t,n){var r=V.$injector.annotate(e),i=A(e)?De(e):e;return new kt("",i,r).get(n)},e.prototype.fromComponentProvider=function(e,t,n){var r=V.$injector.annotate(e),i=A(e)?De(e):e;return new kt("",i,r).get(n)},e.prototype.makeComponentTemplate=function(u,l,e,c){c=c||{};var d=3<=S.version.minor?"::":"",p=function(e){var t=je(e);return/^(x|data)-/.exec(t)?"x-"+t:t},t=function(e){var t=V.$injector.get(e+"Directive");if(!t||!t.length)throw new Error("Unable to find component named '"+e+"'");return t.map(kr).reduce(fe,[])}(e).map(function(e){var t=e.name,n=e.type,r=p(t);if(u.attr(r)&&!c[t])return r+"='"+u.attr(r)+"'";var i=c[t]||t;if("@"===n)return r+"='{{"+d+"$resolve."+i+"}}'";if("&"!==n)return r+"='"+d+"$resolve."+i+"'";var o=l.getResolvable(i),a=o&&o.data,s=a&&V.$injector.annotate(a)||[];return r+"='$resolve."+i+(A(a)?"["+(a.length-1)+"]":"")+"("+s.join(",")+")'"}).join(" "),n=p(e);return"<"+n+" "+t+">"},e}();var kr=function(e){return T(e.bindToController)?Dr(e.bindToController):Dr(e.scope)},Dr=function(t){return Object.keys(t||{}).map(function(e){return[e,/^([=<@&])[?]?(.*)/.exec(t[e])]}).filter(function(e){return k(e)&&A(e[1])}).map(function(e){return{name:e[1][2]||e[0],type:e[1][1]}})},xr=function(){function n(e,t){this.stateRegistry=e,this.stateService=t,B(p(n.prototype),this,p(this))}return n.prototype.decorator=function(e,t){return this.stateRegistry.decorator(e,t)||this},n.prototype.state=function(e,t){return T(e)?t=e:t.name=e,this.stateRegistry.register(t),this},n.prototype.onInvalid=function(e){return this.stateService.onInvalid(e)},n}(),Or=function(n){return function(e,t){var i=e[n],o="onExit"===n?"from":"to";return i?function(e,t){var n=new At(e.treeChanges(o)).subContext(t.$$state()),r=Y(zr(n),{$state$:t,$transition$:e});return V.$injector.invoke(i,this,r)}:void 0}},Tr=function(){function e(e){this._urlListeners=[],this.$locationProvider=e;var t=p(e);B(t,this,t,["hashPrefix"])}return e.monkeyPatchPathParameterType=function(e){var t=e.urlMatcherFactory.type("path");t.encode=function(e){return null!=e?e.toString().replace(/(~|\/)/g,function(e){return{"~":"~~","/":"~2F"}[e]}):e},t.decode=function(e){return null!=e?e.toString().replace(/(~~|~2F)/g,function(e){return{"~~":"~","~2F":"/"}[e]}):e}},e.prototype.dispose=function(){},e.prototype.onChange=function(e){var t=this;return this._urlListeners.push(e),function(){return Q(t._urlListeners)(e)}},e.prototype.html5Mode=function(){var e=this.$locationProvider.html5Mode();return(e=T(e)?e.enabled:e)&&this.$sniffer.history},e.prototype.baseHref=function(){return this._baseHref||(this._baseHref=this.$browser.baseHref()||this.$window.location.pathname)},e.prototype.url=function(e,t,n){return void 0===t&&(t=!1),k(e)&&this.$location.url(e),t&&this.$location.replace(),n&&this.$location.state(n),this.$location.url()},e.prototype._runtimeServices=function(e,t,n,r,i){var o=this;this.$location=t,this.$sniffer=n,this.$browser=r,this.$window=i,e.$on("$locationChangeSuccess",function(t){return o._urlListeners.forEach(function(e){return e(t)})});var a=p(t);B(a,this,a,["replace","path","search","hash"]),B(a,this,a,["port","protocol","host"])},e}(),Ar=function(){function n(e){this.router=e}return n.injectableHandler=function(t,n){return function(e){return V.$injector.invoke(n,null,{$match:e,$stateParams:t.globals.params})}},n.prototype.$get=function(){var e=this.router.urlService;return this.router.urlRouter.update(!0),e.interceptDeferred||e.listen(),this.router.urlRouter},n.prototype.rule=function(e){var t=this;if(!D(e))throw new Error("'rule' must be a function");var n=new ln(function(){return e(V.$injector,t.router.locationService)},q);return this.router.urlService.rules.rule(n),this},n.prototype.otherwise=function(e){var t=this,n=this.router.urlService.rules;if(O(e))n.otherwise(e);else{if(!D(e))throw new Error("'rule' must be a string or function");n.otherwise(function(){return e(V.$injector,t.router.locationService)})}return this},n.prototype.when=function(e,t){return(A(t)||D(t))&&(t=n.injectableHandler(this.router,t)),this.router.urlService.rules.when(e,t),this},n.prototype.deferIntercept=function(e){this.router.urlService.deferIntercept(e)},n}();S.module("ui.router.angular1",[]);var Er=S.module("ui.router.init",["ng"]),Pr=S.module("ui.router.util",["ui.router.init"]),Mr=S.module("ui.router.router",["ui.router.util"]),Rr=S.module("ui.router.state",["ui.router.router","ui.router.util","ui.router.angular1"]),Ir=S.module("ui.router",["ui.router.init","ui.router.state","ui.router.angular1"]),Vr=(S.module("ui.router.compat",["ui.router"]),null);function Lr(e){(Vr=this.router=new $n).stateProvider=new xr(Vr.stateRegistry,Vr.stateService),Vr.stateRegistry.decorator("views",$r),Vr.stateRegistry.decorator("onExit",Or("onExit")),Vr.stateRegistry.decorator("onRetain",Or("onRetain")),Vr.stateRegistry.decorator("onEnter",Or("onEnter")),Vr.viewService._pluginapi._viewConfigFactory("ng1",wr());var s=Vr.locationService=Vr.locationConfig=new Tr(e);function t(e,t,n,r,i,o,a){return s._runtimeServices(i,e,r,t,n),delete Vr.router,delete Vr.$get,Vr}return Tr.monkeyPatchPathParameterType(Vr),((Vr.router=Vr).$get=t).$inject=["$location","$browser","$window","$sniffer","$rootScope","$http","$templateCache"],Vr}Lr.$inject=["$locationProvider"];var Fr=function(n){return["$uiRouterProvider",function(e){var t=e.router[n];return t.$get=function(){return t},t}]};function jr(t,e,n){if(V.$injector=t,V.$q=e,!t.hasOwnProperty("strictDi"))try{t.invoke(function(e){})}catch(e){t.strictDi=!!/strict mode/.exec(e&&e.toString())}n.stateRegistry.get().map(function(e){return e.$$state().resolvables}).reduce(fe,[]).filter(function(e){return"deferred"===e.deps}).forEach(function(e){return e.deps=t.annotate(e.resolveFn,t.strictDi)})}jr.$inject=["$injector","$q","$uiRouter"];function Hr(e){e.$watch(function(){lt.approximateDigests++})}Hr.$inject=["$rootScope"],Er.provider("$uiRouter",Lr),Mr.provider("$urlRouter",["$uiRouterProvider",function(e){return e.urlRouterProvider=new Ar(e)}]),Pr.provider("$urlService",Fr("urlService")),Pr.provider("$urlMatcherFactory",["$uiRouterProvider",function(){return Vr.urlMatcherFactory}]),Pr.provider("$templateFactory",function(){return new Cr}),Rr.provider("$stateRegistry",Fr("stateRegistry")),Rr.provider("$uiRouterGlobals",Fr("globals")),Rr.provider("$transitions",Fr("transitionService")),Rr.provider("$state",["$uiRouterProvider",function(){return Y(Vr.stateProvider,{$get:function(){return Vr.stateService}})}]),Rr.factory("$stateParams",["$uiRouter",function(e){return e.globals.params}]),Ir.factory("$view",function(){return Vr.viewService}),Ir.service("$trace",function(){return lt}),Ir.run(Hr),Pr.run(["$urlMatcherFactory",function(e){}]),Rr.run(["$state",function(e){}]),Mr.run(["$urlRouter",function(e){}]),Er.run(jr);var Nr,Yr,Ur,qr,zr=function(n){return n.getTokens().filter(O).map(function(e){var t=n.getResolvable(e);return[e,"NOWAIT"===n.getPolicy(t).async?t.promise:t.data]}).reduce(ke,{})};function Br(e){var t,n=e.match(/^\s*({[^}]*})\s*$/);if(n&&(e="("+n[1]+")"),!(t=e.replace(/\n/g," ").match(/^\s*([^(]*?)\s*(\((.*)\))?\s*$/))||4!==t.length)throw new Error("Invalid state ref '"+e+"'");return{state:t[1]||null,paramExpr:t[3]||null}}function Wr(e){var t=e.parent().inheritedData("$uiView"),n=C("$cfg.path")(t);return n?De(n).state.name:void 0}function Gr(e,t,n){var r=n.uiState||e.current.name,i=Y(function(e,t){return{relative:Wr(e)||t.$current,inherit:!0,source:"sref"}}(t,e),n.uiStateOpts||{}),o=e.href(r,n.uiStateParams,i);return{uiState:r,uiStateParams:n.uiStateParams,uiStateOpts:i,href:o}}function Kr(e){var t="[object SVGAnimatedString]"===Object.prototype.toString.call(e.prop("href")),n="FORM"===e[0].nodeName;return{attr:n?"action":t?"xlink:href":"href",isAnchor:"A"===e.prop("tagName").toUpperCase(),clickable:!n}}function Qr(o,a,s,u,l){return function(e){var t=e.which||e.button,n=l();if(!(1>>0;if(0==i)return-1;var o=+t||0;if(Math.abs(o)===1/0&&(o=0),i<=o)return-1;for(n=Math.max(0<=o?o:i-Math.abs(o),0);n
    ',this.loadingBarTemplate='
    ',this.$get=["$injector","$document","$timeout","$rootScope",function(i,o,a,s){function u(e){if(m){var t=100*e+"%";f.css("width",t),v=e,y&&(a.cancel(c),c=a(function(){n()},250))}}function n(){if(!(1<=r())){var e,t=r();e=0<=t&&t<.25?(3*Math.random()+3)/100:.25<=t&&t<.65?3*Math.random()/100:.65<=t&&t<.9?2*Math.random()/100:.9<=t&&t<.99?.005:0,u(r()+e)}}function r(){return v}function t(){v=0,m=!1}var l,c,d,p=this.parentSelector,h=angular.element(this.loadingBarTemplate),f=h.find("div").eq(0),g=angular.element(this.spinnerTemplate),m=!1,v=0,y=this.autoIncrement,w=this.includeSpinner,b=this.includeBar,$=this.startSize;return{start:function(){if(l||(l=i.get("$animate")),a.cancel(d),!m){var e=o[0],t=e.querySelector?e.querySelector(p):o.find(p)[0];t||(t=e.getElementsByTagName("body")[0]);var n=angular.element(t),r=t.lastChild&&angular.element(t.lastChild);s.$broadcast("cfpLoadingBar:started"),m=!0,b&&l.enter(h,n,r),w&&l.enter(g,n,h),u($)}},set:u,status:r,inc:n,complete:function(){l||(l=i.get("$animate")),s.$broadcast("cfpLoadingBar:completed"),u(1),a.cancel(d),d=a(function(){var e=l.leave(h,t);e&&e.then&&e.then(t),l.leave(g)},500)},autoIncrement:this.autoIncrement,includeSpinner:this.includeSpinner,latencyThreshold:this.latencyThreshold,parentSelector:this.parentSelector,startSize:this.startSize}}]})}(),angular.module("ui.bootstrap",["ui.bootstrap.tpls","ui.bootstrap.transition","ui.bootstrap.collapse","ui.bootstrap.accordion","ui.bootstrap.alert","ui.bootstrap.bindHtml","ui.bootstrap.buttons","ui.bootstrap.carousel","ui.bootstrap.dateparser","ui.bootstrap.position","ui.bootstrap.datepicker","ui.bootstrap.dropdown","ui.bootstrap.modal","ui.bootstrap.pagination","ui.bootstrap.tooltip","ui.bootstrap.popover","ui.bootstrap.progressbar","ui.bootstrap.rating","ui.bootstrap.tabs","ui.bootstrap.timepicker","ui.bootstrap.typeahead"]),angular.module("ui.bootstrap.tpls",["template/accordion/accordion-group.html","template/accordion/accordion.html","template/alert/alert.html","template/carousel/carousel.html","template/carousel/slide.html","template/datepicker/datepicker.html","template/datepicker/day.html","template/datepicker/month.html","template/datepicker/popup.html","template/datepicker/year.html","template/modal/backdrop.html","template/modal/window.html","template/pagination/pager.html","template/pagination/pagination.html","template/tooltip/tooltip-html-unsafe-popup.html","template/tooltip/tooltip-popup.html","template/popover/popover.html","template/progressbar/bar.html","template/progressbar/progress.html","template/progressbar/progressbar.html","template/rating/rating.html","template/tabs/tab.html","template/tabs/tabset.html","template/timepicker/timepicker.html","template/typeahead/typeahead-match.html","template/typeahead/typeahead-popup.html"]),angular.module("ui.bootstrap.transition",[]).factory("$transition",["$q","$timeout","$rootScope",function(a,s,u){function e(e){for(var t in e)if(void 0!==n.style[t])return e[t]}var l=function(e,t,n){n=n||{};var r=a.defer(),i=l[n.animation?"animationEndEventName":"transitionEndEventName"],o=function(){u.$apply(function(){e.unbind(i,o),r.resolve(e)})};return i&&e.bind(i,o),s(function(){angular.isString(t)?e.addClass(t):angular.isFunction(t)?t(e):angular.isObject(t)&&e.css(t),i||r.resolve(e)}),r.promise.cancel=function(){i&&e.unbind(i,o),r.reject("Transition cancelled")},r.promise},n=document.createElement("trans");return l.transitionEndEventName=e({WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd",transition:"transitionend"}),l.animationEndEventName=e({WebkitTransition:"webkitAnimationEnd",MozTransition:"animationend",OTransition:"oAnimationEnd",transition:"animationend"}),l}]),angular.module("ui.bootstrap.collapse",["ui.bootstrap.transition"]).directive("collapse",["$transition",function(u){return{link:function(e,r,t){function n(e){function t(){a===n&&(a=void 0)}var n=u(r,e);return a&&a.cancel(),(a=n).then(t,t),n}function i(){r.removeClass("collapsing"),r.addClass("collapse in"),r.css({height:"auto"})}function o(){r.removeClass("collapsing"),r.addClass("collapse")}var a,s=!0;e.$watch(t.collapse,function(e){e?s?(s=!1,o(),r.css({height:0})):(r.css({height:r[0].scrollHeight+"px"}),r[0].offsetWidth,r.removeClass("collapse in").addClass("collapsing"),n({height:0}).then(o)):s?(s=!1,i()):(r.removeClass("collapse").addClass("collapsing"),n({height:r[0].scrollHeight+"px"}).then(i))})}}}]),angular.module("ui.bootstrap.accordion",["ui.bootstrap.collapse"]).constant("accordionConfig",{closeOthers:!0}).controller("AccordionController",["$scope","$attrs","accordionConfig",function(e,n,r){this.groups=[],this.closeOthers=function(t){(angular.isDefined(n.closeOthers)?e.$eval(n.closeOthers):r.closeOthers)&&angular.forEach(this.groups,function(e){e!==t&&(e.isOpen=!1)})},this.addGroup=function(e){var t=this;this.groups.push(e),e.$on("$destroy",function(){t.removeGroup(e)})},this.removeGroup=function(e){var t=this.groups.indexOf(e);-1!==t&&this.groups.splice(t,1)}}]).directive("accordion",function(){return{restrict:"EA",controller:"AccordionController",transclude:!0,replace:!1,templateUrl:"template/accordion/accordion.html"}}).directive("accordionGroup",function(){return{require:"^accordion",restrict:"EA",transclude:!0,replace:!0,templateUrl:"template/accordion/accordion-group.html",scope:{heading:"@",isOpen:"=?",isDisabled:"=?"},controller:function(){this.setHeading=function(e){this.heading=e}},link:function(t,e,n,r){r.addGroup(t),t.$watch("isOpen",function(e){e&&r.closeOthers(t)}),t.toggleOpen=function(){t.isDisabled||(t.isOpen=!t.isOpen)}}}}).directive("accordionHeading",function(){return{restrict:"EA",transclude:!0,template:"",replace:!0,require:"^accordionGroup",link:function(e,t,n,r,i){r.setHeading(i(e,function(){}))}}}).directive("accordionTransclude",function(){return{require:"^accordionGroup",link:function(e,t,n,r){e.$watch(function(){return r[n.accordionTransclude]},function(e){e&&(t.html(""),t.append(e))})}}}),angular.module("ui.bootstrap.alert",[]).controller("AlertController",["$scope","$attrs",function(e,t){e.closeable="close"in t,this.close=e.close}]).directive("alert",function(){return{restrict:"EA",controller:"AlertController",templateUrl:"template/alert/alert.html",transclude:!0,replace:!0,scope:{type:"@",close:"&"}}}).directive("dismissOnTimeout",["$timeout",function(i){return{require:"alert",link:function(e,t,n,r){i(function(){r.close()},parseInt(n.dismissOnTimeout,10))}}}]),angular.module("ui.bootstrap.bindHtml",[]).directive("bindHtmlUnsafe",function(){return function(e,t,n){t.addClass("ng-binding").data("$binding",n.bindHtmlUnsafe),e.$watch(n.bindHtmlUnsafe,function(e){t.html(e||"")})}}),angular.module("ui.bootstrap.buttons",[]).constant("buttonConfig",{activeClass:"active",toggleEvent:"click"}).controller("ButtonsController",["buttonConfig",function(e){this.activeClass=e.activeClass||"active",this.toggleEvent=e.toggleEvent||"click"}]).directive("btnRadio",function(){return{require:["btnRadio","ngModel"],controller:"ButtonsController",link:function(t,n,r,e){var i=e[0],o=e[1];o.$render=function(){n.toggleClass(i.activeClass,angular.equals(o.$modelValue,t.$eval(r.btnRadio)))},n.bind(i.toggleEvent,function(){var e=n.hasClass(i.activeClass);(!e||angular.isDefined(r.uncheckable))&&t.$apply(function(){o.$setViewValue(e?null:t.$eval(r.btnRadio)),o.$render()})})}}}).directive("btnCheckbox",function(){return{require:["btnCheckbox","ngModel"],controller:"ButtonsController",link:function(r,e,t,n){function i(){return o(t.btnCheckboxTrue,!0)}function o(e,t){var n=r.$eval(e);return angular.isDefined(n)?n:t}var a=n[0],s=n[1];s.$render=function(){e.toggleClass(a.activeClass,angular.equals(s.$modelValue,i()))},e.bind(a.toggleEvent,function(){r.$apply(function(){s.$setViewValue(e.hasClass(a.activeClass)?o(t.btnCheckboxFalse,!1):i()),s.$render()})})}}}),angular.module("ui.bootstrap.carousel",["ui.bootstrap.transition"]).controller("CarouselController",["$scope","$timeout","$interval","$transition",function(a,t,n,s){function u(){r();var e=+a.interval;!isNaN(e)&&0=d.length?d[t-1]:d[t]):t
    ");e.attr({"ng-model":"date","ng-change":"dateSelection()"});var c=angular.element(e.children()[0]);i.datepickerOptions&&angular.forEach(r.$parent.$eval(i.datepickerOptions),function(e,t){c.attr(o(t),e)}),r.watchData={},angular.forEach(["minDate","maxDate","datepickerMode"],function(t){if(i[t]){var e=g(i[t]);if(r.$parent.$watch(e,function(e){r.watchData[t]=e}),c.attr(o(t),"watchData."+t),"datepickerMode"===t){var n=e.assign;r.$watch("watchData."+t,function(e,t){e!==t&&n(r.$parent,e)})}}}),i.dateDisabled&&c.attr("date-disabled","dateDisabled({ date: date, mode: mode })"),n.$parsers.unshift(a),r.dateSelection=function(e){angular.isDefined(e)&&(r.date=e),n.$setViewValue(r.date),n.$render(),u&&(r.isOpen=!1,t[0].focus())},t.bind("input change keyup",function(){r.$apply(function(){r.date=n.$modelValue})}),n.$render=function(){var e=n.$viewValue?y(n.$viewValue,s):"";t.val(e),r.date=a(n.$modelValue)};var d=function(e){r.isOpen&&e.target!==t[0]&&r.$apply(function(){r.isOpen=!1})},p=function(e){r.keydown(e)};t.bind("keydown",p),r.keydown=function(e){27===e.which?(e.preventDefault(),e.stopPropagation(),r.close()):40!==e.which||r.isOpen||(r.isOpen=!0)},r.$watch("isOpen",function(e){e?(r.$broadcast("datepicker.focus"),r.position=l?v.offset(t):v.position(t),r.position.top=r.position.top+t.prop("offsetHeight"),m.bind("click",d)):m.unbind("click",d)}),r.select=function(e){if("today"===e){var t=new Date;angular.isDate(n.$modelValue)?(e=new Date(n.$modelValue)).setFullYear(t.getFullYear(),t.getMonth(),t.getDate()):e=new Date(t.setHours(0,0,0,0))}r.dateSelection(e)},r.close=function(){r.isOpen=!1,t[0].focus()};var h=f(e)(r);e.remove(),l?m.find("body").append(h):t.after(h),r.$on("$destroy",function(){h.remove(),t.unbind("keydown",p),m.unbind("click",d)})}}}]).directive("datepickerPopupWrap",function(){return{restrict:"EA",replace:!0,transclude:!0,templateUrl:"template/datepicker/popup.html",link:function(e,t){t.bind("click",function(e){e.preventDefault(),e.stopPropagation()})}}}),angular.module("ui.bootstrap.dropdown",[]).constant("dropdownConfig",{openClass:"open"}).service("dropdownService",["$document",function(t){var n=null;this.open=function(e){n||(t.bind("click",r),t.bind("keydown",i)),n&&n!==e&&(n.isOpen=!1),n=e},this.close=function(e){n===e&&(n=null,t.unbind("click",r),t.unbind("keydown",i))};var r=function(e){if(n){var t=n.getToggleElement();e&&t&&t[0].contains(e.target)||n.$apply(function(){n.isOpen=!1})}},i=function(e){27===e.which&&(n.focusToggleElement(),r())}}]).controller("DropdownController",["$scope","$attrs","$parse","dropdownConfig","dropdownService","$animate",function(n,t,r,e,i,o){var a,s=this,u=n.$new(),l=e.openClass,c=angular.noop,d=t.onToggle?r(t.onToggle):angular.noop;this.init=function(e){s.$element=e,t.isOpen&&(a=r(t.isOpen),c=a.assign,n.$watch(a,function(e){u.isOpen=!!e}))},this.toggle=function(e){return u.isOpen=arguments.length?!!e:!u.isOpen},this.isOpen=function(){return u.isOpen},u.getToggleElement=function(){return s.toggleElement},u.focusToggleElement=function(){s.toggleElement&&s.toggleElement[0].focus()},u.$watch("isOpen",function(e,t){o[e?"addClass":"removeClass"](s.$element,l),e?(u.focusToggleElement(),i.open(u)):i.close(u),c(n,e),angular.isDefined(e)&&e!==t&&d(n,{open:!!e})}),n.$on("$locationChangeSuccess",function(){u.isOpen=!1}),n.$on("$destroy",function(){u.$destroy()})}]).directive("dropdown",function(){return{controller:"DropdownController",link:function(e,t,n,r){r.init(t)}}}).directive("dropdownToggle",function(){return{require:"?^dropdown",link:function(t,n,r,i){if(i){i.toggleElement=n;var e=function(e){e.preventDefault(),n.hasClass("disabled")||r.disabled||t.$apply(function(){i.toggle()})};n.bind("click",e),n.attr({"aria-haspopup":!0,"aria-expanded":!1}),t.$watch(i.isOpen,function(e){n.attr("aria-expanded",!!e)}),t.$on("$destroy",function(){n.unbind("click",e)})}}}}),angular.module("ui.bootstrap.modal",["ui.bootstrap.transition"]).factory("$$stackedMap",function(){return{createNew:function(){var r=[];return{add:function(e,t){r.push({key:e,value:t})},get:function(e){for(var t=0;t");i.attr("backdrop-class",t.backdropClass),h=c(i)(f),n.append(h)}var o=angular.element("
    ");o.attr({"template-url":t.windowTemplateUrl,"window-class":t.windowClass,size:t.size,index:m.length()-1,animate:"animate"}).html(t.content);var a=c(o)(t.scope);m.top().value.modalDomEl=a,n.append(a),n.addClass(g)},n.close=function(e,t){var n=m.get(e);n&&(n.value.deferred.resolve(t),r(e))},n.dismiss=function(e,t){var n=m.get(e);n&&(n.value.deferred.reject(t),r(e))},n.dismissAll=function(e){for(var t=this.getTop();t;)this.dismiss(t.key,e),t=this.getTop()},n.getTop=function(){return m.top()},n}]).provider("$modal",function(){var p={options:{backdrop:!0,keyboard:!0},$get:["$injector","$rootScope","$q","$http","$templateCache","$controller","$modalStack",function(n,u,r,i,l,c,d){var e={};return e.open=function(o){var a=r.defer(),e=r.defer(),s={result:a.promise,opened:e.promise,close:function(e){d.close(s,e)},dismiss:function(e){d.dismiss(s,e)}};if((o=angular.extend({},p.options,o)).resolve=o.resolve||{},!o.template&&!o.templateUrl)throw new Error("One of template or templateUrl options is required.");var t=r.all([function(e){return e.template?r.when(e.template):i.get(angular.isFunction(e.templateUrl)?e.templateUrl():e.templateUrl,{cache:l}).then(function(e){return e.data})}(o)].concat(function(e){var t=[];return angular.forEach(e,function(e){(angular.isFunction(e)||angular.isArray(e))&&t.push(r.when(n.invoke(e)))}),t}(o.resolve)));return t.then(function(n){var e=(o.scope||u).$new();e.$close=s.close,e.$dismiss=s.dismiss;var t,r={},i=1;o.controller&&(r.$scope=e,r.$modalInstance=s,angular.forEach(o.resolve,function(e,t){r[t]=n[i++]}),t=c(o.controller,r),o.controllerAs&&(e[o.controllerAs]=t)),d.open(s,{scope:e,deferred:a,content:n[0],backdrop:o.backdrop,keyboard:o.keyboard,backdropClass:o.backdropClass,windowClass:o.windowClass,windowTemplateUrl:o.windowTemplateUrl,size:o.size})},function(e){a.reject(e)}),t.then(function(){e.resolve(!0)},function(){e.reject(!1)}),s},e}]};return p}),angular.module("ui.bootstrap.pagination",[]).controller("PaginationController",["$scope","$attrs","$parse",function(n,r,i){var o=this,a={$setViewValue:angular.noop},t=r.numPages?i(r.numPages).assign:angular.noop;this.init=function(e,t){a=e,this.config=t,a.$render=function(){o.render()},r.itemsPerPage?n.$parent.$watch(i(r.itemsPerPage),function(e){o.itemsPerPage=parseInt(e,10),n.totalPages=o.calculateTotalPages()}):this.itemsPerPage=t.itemsPerPage},this.calculateTotalPages=function(){var e=this.itemsPerPage<1?1:Math.ceil(n.totalItems/this.itemsPerPage);return Math.max(e||0,1)},this.render=function(){n.page=parseInt(a.$viewValue,10)||1},n.selectPage=function(e){n.page!==e&&0e?n.selectPage(e):a.$render()})}]).constant("paginationConfig",{itemsPerPage:10,boundaryLinks:!1,directionLinks:!0,firstText:"First",previousText:"Previous",nextText:"Next",lastText:"Last",rotate:!0}).directive("pagination",["$parse","paginationConfig",function(s,u){return{restrict:"EA",scope:{totalItems:"=",firstText:"@",previousText:"@",nextText:"@",lastText:"@"},require:["pagination","?ngModel"],controller:"PaginationController",templateUrl:"template/pagination/pagination.html",replace:!0,link:function(e,t,n,r){function c(e,t,n){return{number:e,text:t,active:n}}var i=r[0],o=r[1];if(o){var d=angular.isDefined(n.maxSize)?e.$parent.$eval(n.maxSize):u.maxSize,p=angular.isDefined(n.rotate)?e.$parent.$eval(n.rotate):u.rotate;e.boundaryLinks=angular.isDefined(n.boundaryLinks)?e.$parent.$eval(n.boundaryLinks):u.boundaryLinks,e.directionLinks=angular.isDefined(n.directionLinks)?e.$parent.$eval(n.directionLinks):u.directionLinks,i.init(o,u),n.maxSize&&e.$parent.$watch(s(n.maxSize),function(e){d=parseInt(e,10),i.render()});var a=i.render;i.render=function(){a(),0';return{restrict:"EA",compile:function(){var _=o(i);return function(e,t,n){function r(){m.isOpen?o():i()}function i(){(!g||e.$eval(n[C+"Enable"]))&&(function(){var e=n[C+"Placement"];m.placement=angular.isDefined(e)?e:D.placement}(),function(){var e=n[C+"PopupDelay"],t=parseInt(e,10);m.popupDelay=isNaN(t)?D.popupDelay:t}(),m.popupDelay?p||(p=x(a,m.popupDelay,!1)).then(function(e){e()}):a()())}function o(){e.$apply(function(){s()})}function a(){return p=null,d&&(x.cancel(d),d=null),m.content?(l&&u(),c=m.$new(),(l=_(c,function(e){h?O.find("body").append(e):t.after(e)})).css({top:0,left:0,display:"block"}),m.$digest(),v(),m.isOpen=!0,m.$digest(),v):angular.noop}function s(){m.isOpen=!1,x.cancel(p),p=null,m.animation?d||(d=x(u,500)):u()}function u(){d=null,l&&(l.remove(),l=null),c&&(c.$destroy(),c=null)}var l,c,d,p,h=!!angular.isDefined(D.appendToBody)&&D.appendToBody,f=k(void 0),g=angular.isDefined(n[C+"Enable"]),m=e.$new(!0),v=function(){var e=T.positionElements(t,l,m.placement,h);e.top+="px",e.left+="px",l.css(e)};m.isOpen=!1,n.$observe(S,function(e){!(m.content=e)&&m.isOpen&&s()}),n.$observe(C+"Title",function(e){m.title=e});var y,w=function(){t.unbind(f.show,i),t.unbind(f.hide,o)};y=n[C+"Trigger"],w(),(f=k(y)).show===f.hide?t.bind(f.show,r):(t.bind(f.show,i),t.bind(f.hide,o));var b=e.$eval(n[C+"Animation"]);m.animation=angular.isDefined(b)?!!b:D.animation;var $=e.$eval(n[C+"AppendToBody"]);(h=angular.isDefined($)?$:h)&&e.$on("$locationChangeSuccess",function(){m.isOpen&&s()}),e.$on("$destroy",function(){x.cancel(d),x.cancel(p),w(),u(),m=null})}}}}}]}).directive("tooltipPopup",function(){return{restrict:"EA",replace:!0,scope:{content:"@",placement:"@",animation:"&",isOpen:"&"},templateUrl:"template/tooltip/tooltip-popup.html"}}).directive("tooltip",["$tooltip",function(e){return e("tooltip","tooltip","mouseenter")}]).directive("tooltipHtmlUnsafePopup",function(){return{restrict:"EA",replace:!0,scope:{content:"@",placement:"@",animation:"&",isOpen:"&"},templateUrl:"template/tooltip/tooltip-html-unsafe-popup.html"}}).directive("tooltipHtmlUnsafe",["$tooltip",function(e){return e("tooltipHtmlUnsafe","tooltip","mouseenter")}]),angular.module("ui.bootstrap.popover",["ui.bootstrap.tooltip"]).directive("popoverPopup",function(){return{restrict:"EA",replace:!0,scope:{title:"@",content:"@",placement:"@",animation:"&",isOpen:"&"},templateUrl:"template/popover/popover.html"}}).directive("popover",["$tooltip",function(e){return e("popover","popover","click")}]),angular.module("ui.bootstrap.progressbar",[]).constant("progressConfig",{animate:!0,max:100}).controller("ProgressController",["$scope","$attrs","progressConfig",function(n,e,t){var r=this,i=angular.isDefined(e.animate)?n.$parent.$eval(e.animate):t.animate;this.bars=[],n.max=angular.isDefined(e.max)?n.$parent.$eval(e.max):t.max,this.addBar=function(t,e){i||e.css({transition:"none"}),this.bars.push(t),t.$watch("value",function(e){t.percent=+(100*e/n.max).toFixed(2)}),t.$on("$destroy",function(){e=null,r.removeBar(t)})},this.removeBar=function(e){this.bars.splice(this.bars.indexOf(e),1)}}]).directive("progress",function(){return{restrict:"EA",replace:!0,transclude:!0,controller:"ProgressController",require:"progress",scope:{},templateUrl:"template/progressbar/progress.html"}}).directive("bar",function(){return{restrict:"EA",replace:!0,transclude:!0,require:"^progress",scope:{value:"=",type:"@"},templateUrl:"template/progressbar/bar.html",link:function(e,t,n,r){r.addBar(e,t)}}}).directive("progressbar",function(){return{restrict:"EA",replace:!0,transclude:!0,controller:"ProgressController",scope:{value:"=",type:"@"},templateUrl:"template/progressbar/progressbar.html",link:function(e,t,n,r){r.addBar(e,angular.element(t.children()[0]))}}}),angular.module("ui.bootstrap.rating",[]).constant("ratingConfig",{max:5,stateOn:null,stateOff:null}).controller("RatingController",["$scope","$attrs","ratingConfig",function(n,r,i){var o={$setViewValue:angular.noop};this.init=function(e){(o=e).$render=this.render,this.stateOn=angular.isDefined(r.stateOn)?n.$parent.$eval(r.stateOn):i.stateOn,this.stateOff=angular.isDefined(r.stateOff)?n.$parent.$eval(r.stateOff):i.stateOff;var t=angular.isDefined(r.ratingStates)?n.$parent.$eval(r.ratingStates):new Array(angular.isDefined(r.max)?n.$parent.$eval(r.max):i.max);n.range=this.buildTemplateObjects(t)},this.buildTemplateObjects=function(e){for(var t=0,n=e.length;t");v.attr({id:m,matches:"matches",active:"activeIdx",select:"select(activeIdx)",query:"query",position:"position"}),angular.isDefined(e.typeaheadTemplateUrl)&&v.attr("template-url",e.typeaheadTemplateUrl);var y=function(){g.matches=[],g.activeIdx=-1,a.attr("aria-expanded",!1)},w=function(e){return m+"-option-"+e};g.$watch("activeIdx",function(e){e<0?a.removeAttr("aria-activedescendant"):a.attr("aria-activedescendant",w(e))});var b=function(r){var i={$viewValue:r};l(o,!0),x.when(f.source(o,i)).then(function(e){var t=r===s.$viewValue;if(t&&u)if(0=t?0$&"):e}}),angular.module("template/accordion/accordion-group.html",[]).run(["$templateCache",function(e){e.put("template/accordion/accordion-group.html",'
    \n
    \n

    \n {{heading}}\n

    \n
    \n
    \n\t
    \n
    \n
    \n')}]),angular.module("template/accordion/accordion.html",[]).run(["$templateCache",function(e){e.put("template/accordion/accordion.html",'
    ')}]),angular.module("template/alert/alert.html",[]).run(["$templateCache",function(e){e.put("template/alert/alert.html",'\n')}]),angular.module("template/carousel/carousel.html",[]).run(["$templateCache",function(e){e.put("template/carousel/carousel.html",'\n')}]),angular.module("template/carousel/slide.html",[]).run(["$templateCache",function(e){e.put("template/carousel/slide.html","
    \n")}]),angular.module("template/datepicker/datepicker.html",[]).run(["$templateCache",function(e){e.put("template/datepicker/datepicker.html",'
    \n \n \n \n
    ')}]),angular.module("template/datepicker/day.html",[]).run(["$templateCache",function(e){e.put("template/datepicker/day.html",'\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
    {{label.abbr}}
    {{ weekNumbers[$index] }}\n \n
    \n')}]),angular.module("template/datepicker/month.html",[]).run(["$templateCache",function(e){e.put("template/datepicker/month.html",'\n \n \n \n \n \n \n \n \n \n \n \n \n
    \n \n
    \n')}]),angular.module("template/datepicker/popup.html",[]).run(["$templateCache",function(e){e.put("template/datepicker/popup.html",'\n')}]),angular.module("template/datepicker/year.html",[]).run(["$templateCache",function(e){e.put("template/datepicker/year.html",'\n \n \n \n \n \n \n \n \n \n \n \n \n
    \n \n
    \n')}]),angular.module("template/modal/backdrop.html",[]).run(["$templateCache",function(e){e.put("template/modal/backdrop.html",'\n')}]),angular.module("template/modal/window.html",[]).run(["$templateCache",function(e){e.put("template/modal/window.html",'')}]),angular.module("template/pagination/pager.html",[]).run(["$templateCache",function(e){e.put("template/pagination/pager.html",'')}]),angular.module("template/pagination/pagination.html",[]).run(["$templateCache",function(e){e.put("template/pagination/pagination.html",'')}]),angular.module("template/tooltip/tooltip-html-unsafe-popup.html",[]).run(["$templateCache",function(e){e.put("template/tooltip/tooltip-html-unsafe-popup.html",'
    \n
    \n
    \n
    \n')}]),angular.module("template/tooltip/tooltip-popup.html",[]).run(["$templateCache",function(e){e.put("template/tooltip/tooltip-popup.html",'
    \n
    \n
    \n
    \n')}]),angular.module("template/popover/popover.html",[]).run(["$templateCache",function(e){e.put("template/popover/popover.html",'
    \n
    \n\n
    \n

    \n
    \n
    \n
    \n')}]),angular.module("template/progressbar/bar.html",[]).run(["$templateCache",function(e){e.put("template/progressbar/bar.html",'
    ')}]),angular.module("template/progressbar/progress.html",[]).run(["$templateCache",function(e){e.put("template/progressbar/progress.html",'
    ')}]),angular.module("template/progressbar/progressbar.html",[]).run(["$templateCache",function(e){e.put("template/progressbar/progressbar.html",'
    \n
    \n
    ')}]),angular.module("template/rating/rating.html",[]).run(["$templateCache",function(e){e.put("template/rating/rating.html",'\n \n ({{ $index < value ? \'*\' : \' \' }})\n \n')}]),angular.module("template/tabs/tab.html",[]).run(["$templateCache",function(e){e.put("template/tabs/tab.html",'
  • \n {{heading}}\n
  • \n')}]),angular.module("template/tabs/tabset.html",[]).run(["$templateCache",function(e){e.put("template/tabs/tabset.html",'
    \n \n
    \n
    \n
    \n
    \n
    \n')}]),angular.module("template/timepicker/timepicker.html",[]).run(["$templateCache",function(e){e.put("template/timepicker/timepicker.html",'\n\t\n\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\n
     
    \n\t\t\t\t\n\t\t\t:\n\t\t\t\t\n\t\t\t
     
    \n')}]),angular.module("template/typeahead/typeahead-match.html",[]).run(["$templateCache",function(e){e.put("template/typeahead/typeahead-match.html",'')}]),angular.module("template/typeahead/typeahead-popup.html",[]).run(["$templateCache",function(e){e.put("template/typeahead/typeahead-popup.html",'\n')}]),function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):e.moment=t()}(this,function(){"use strict";var e,i;function p(){return e.apply(null,arguments)}function a(e){return e instanceof Array||"[object Array]"===Object.prototype.toString.call(e)}function s(e){return null!=e&&"[object Object]"===Object.prototype.toString.call(e)}function o(e){return void 0===e}function u(e){return"number"==typeof e||"[object Number]"===Object.prototype.toString.call(e)}function l(e){return e instanceof Date||"[object Date]"===Object.prototype.toString.call(e)}function r(e,t){var n,r=[];for(n=0;n>>0,r=0;rSe(e)?(o=e+1,s-Se(e)):(o=e,s),{year:o,dayOfYear:a}}function Ye(e,t,n){var r,i,o=He(e.year(),t,n),a=Math.floor((e.dayOfYear()-o-1)/7)+1;return a<1?r=a+Ue(i=e.year()-1,t,n):a>Ue(e.year(),t,n)?(r=a-Ue(e.year(),t,n),i=e.year()+1):(i=e.year(),r=a),{week:r,year:i}}function Ue(e,t,n){var r=He(e,t,n),i=He(e+1,t,n);return(Se(e)-r+i)/7}Y("w",["ww",2],"wo","week"),Y("W",["WW",2],"Wo","isoWeek"),P("week","w"),P("isoWeek","W"),V("week",5),V("isoWeek",5),ue("w",Q),ue("ww",Q,B),ue("W",Q),ue("WW",Q,B),he(["w","ww","W","WW"],function(e,t,n,r){t[r.substr(0,1)]=_(e)});function qe(e,t){return e.slice(t,7).concat(e.slice(0,t))}Y("d",0,"do","day"),Y("dd",0,0,function(e){return this.localeData().weekdaysMin(this,e)}),Y("ddd",0,0,function(e){return this.localeData().weekdaysShort(this,e)}),Y("dddd",0,0,function(e){return this.localeData().weekdays(this,e)}),Y("e",0,0,"weekday"),Y("E",0,0,"isoWeekday"),P("day","d"),P("weekday","e"),P("isoWeekday","E"),V("day",11),V("weekday",11),V("isoWeekday",11),ue("d",Q),ue("e",Q),ue("E",Q),ue("dd",function(e,t){return t.weekdaysMinRegex(e)}),ue("ddd",function(e,t){return t.weekdaysShortRegex(e)}),ue("dddd",function(e,t){return t.weekdaysRegex(e)}),he(["dd","ddd","dddd"],function(e,t,n,r){var i=n._locale.weekdaysParse(e,r,n._strict);null!=i?t.d=i:f(n).invalidWeekday=e}),he(["d","e","E"],function(e,t,n,r){t[r]=_(e)});var ze="Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_");var Be="Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_");var We="Su_Mo_Tu_We_Th_Fr_Sa".split("_");var Ge=ae;var Ke=ae;var Qe=ae;function Ze(){function e(e,t){return t.length-e.length}var t,n,r,i,o,a=[],s=[],u=[],l=[];for(t=0;t<7;t++)n=d([2e3,1]).day(t),r=this.weekdaysMin(n,""),i=this.weekdaysShort(n,""),o=this.weekdays(n,""),a.push(r),s.push(i),u.push(o),l.push(r),l.push(i),l.push(o);for(a.sort(e),s.sort(e),u.sort(e),l.sort(e),t=0;t<7;t++)s[t]=ce(s[t]),u[t]=ce(u[t]),l[t]=ce(l[t]);this._weekdaysRegex=new RegExp("^("+l.join("|")+")","i"),this._weekdaysShortRegex=this._weekdaysRegex,this._weekdaysMinRegex=this._weekdaysRegex,this._weekdaysStrictRegex=new RegExp("^("+u.join("|")+")","i"),this._weekdaysShortStrictRegex=new RegExp("^("+s.join("|")+")","i"),this._weekdaysMinStrictRegex=new RegExp("^("+a.join("|")+")","i")}function Xe(){return this.hours()%12||12}function Je(e,t){Y(e,0,0,function(){return this.localeData().meridiem(this.hours(),this.minutes(),t)})}function et(e,t){return t._meridiemParse}Y("H",["HH",2],0,"hour"),Y("h",["hh",2],0,Xe),Y("k",["kk",2],0,function(){return this.hours()||24}),Y("hmm",0,0,function(){return""+Xe.apply(this)+L(this.minutes(),2)}),Y("hmmss",0,0,function(){return""+Xe.apply(this)+L(this.minutes(),2)+L(this.seconds(),2)}),Y("Hmm",0,0,function(){return""+this.hours()+L(this.minutes(),2)}),Y("Hmmss",0,0,function(){return""+this.hours()+L(this.minutes(),2)+L(this.seconds(),2)}),Je("a",!0),Je("A",!1),P("hour","h"),V("hour",13),ue("a",et),ue("A",et),ue("H",Q),ue("h",Q),ue("k",Q),ue("HH",Q,B),ue("hh",Q,B),ue("kk",Q,B),ue("hmm",Z),ue("hmmss",X),ue("Hmm",Z),ue("Hmmss",X),pe(["H","HH"],ve),pe(["k","kk"],function(e,t,n){var r=_(e);t[ve]=24===r?0:r}),pe(["a","A"],function(e,t,n){n._isPm=n._locale.isPM(e),n._meridiem=e}),pe(["h","hh"],function(e,t,n){t[ve]=_(e),f(n).bigHour=!0}),pe("hmm",function(e,t,n){var r=e.length-2;t[ve]=_(e.substr(0,r)),t[ye]=_(e.substr(r)),f(n).bigHour=!0}),pe("hmmss",function(e,t,n){var r=e.length-4,i=e.length-2;t[ve]=_(e.substr(0,r)),t[ye]=_(e.substr(r,2)),t[we]=_(e.substr(i)),f(n).bigHour=!0}),pe("Hmm",function(e,t,n){var r=e.length-2;t[ve]=_(e.substr(0,r)),t[ye]=_(e.substr(r))}),pe("Hmmss",function(e,t,n){var r=e.length-4,i=e.length-2;t[ve]=_(e.substr(0,r)),t[ye]=_(e.substr(r,2)),t[we]=_(e.substr(i))});var tt,nt=xe("Hours",!0),rt={calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},longDateFormat:{LTS:"h:mm:ss A",LT:"h:mm A",L:"MM/DD/YYYY",LL:"MMMM D, YYYY",LLL:"MMMM D, YYYY h:mm A",LLLL:"dddd, MMMM D, YYYY h:mm A"},invalidDate:"Invalid date",ordinal:"%d",dayOfMonthOrdinalParse:/\d{1,2}/,relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",ss:"%d seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},months:Pe,monthsShort:Me,week:{dow:0,doy:6},weekdays:ze,weekdaysMin:We,weekdaysShort:Be,meridiemParse:/[ap]\.?m?\.?/i},it={},ot={};function at(e){return e?e.toLowerCase().replace("_","-"):e}function st(e){var t=null;if(!it[e]&&"undefined"!=typeof module&&module&&module.exports)try{t=tt._abbr,require("./locale/"+e),ut(t)}catch(e){}return it[e]}function ut(e,t){var n;return e&&((n=o(t)?ct(e):lt(e,t))?tt=n:"undefined"!=typeof console&&console.warn&&console.warn("Locale "+e+" not found. Did you forget to load it?")),tt._abbr}function lt(e,t){if(null===t)return delete it[e],null;var n,r=rt;if(t.abbr=e,null!=it[e])x("defineLocaleOverride","use moment.updateLocale(localeName, config) to change an existing locale. moment.defineLocale(localeName, config) should only be used for creating a new locale See http://momentjs.com/guides/#/warnings/define-locale/ for more info."),r=it[e]._config;else if(null!=t.parentLocale)if(null!=it[t.parentLocale])r=it[t.parentLocale]._config;else{if(null==(n=st(t.parentLocale)))return ot[t.parentLocale]||(ot[t.parentLocale]=[]),ot[t.parentLocale].push({name:e,config:t}),null;r=n._config}return it[e]=new A(T(r,t)),ot[e]&&ot[e].forEach(function(e){lt(e.name,e.config)}),ut(e),it[e]}function ct(e){var t;if(e&&e._locale&&e._locale._abbr&&(e=e._locale._abbr),!e)return tt;if(!a(e)){if(t=st(e))return t;e=[e]}return function(e){for(var t,n,r,i,o=0;o=t&&S(i,n,!0)>=t-1)break;t--}o++}return tt}(e)}function dt(e){var t,n=e._a;return n&&-2===f(e).overflow&&(t=n[ge]<0||11Ae(n[fe],n[ge])?me:n[ve]<0||24Ue(n,o,a)?f(e)._overflowWeeks=!0:null!=u?f(e)._overflowWeekday=!0:(s=Ne(n,r,i,o,a),e._a[fe]=s.year,e._dayOfYear=s.dayOfYear)}(e),null!=e._dayOfYear&&(o=pt(e._a[fe],r[fe]),(e._dayOfYear>Se(o)||0===e._dayOfYear)&&(f(e)._overflowDayOfYear=!0),n=je(o,0,e._dayOfYear),e._a[ge]=n.getUTCMonth(),e._a[me]=n.getUTCDate()),t=0;t<3&&null==e._a[t];++t)e._a[t]=a[t]=r[t];for(;t<7;t++)e._a[t]=a[t]=null==e._a[t]?2===t?1:0:e._a[t];24===e._a[ve]&&0===e._a[ye]&&0===e._a[we]&&0===e._a[be]&&(e._nextDay=!0,e._a[ve]=0),e._d=(e._useUTC?je:function(e,t,n,r,i,o,a){var s;return e<100&&0<=e?(s=new Date(e+400,t,n,r,i,o,a),isFinite(s.getFullYear())&&s.setFullYear(e)):s=new Date(e,t,n,r,i,o,a),s}).apply(null,a),i=e._useUTC?e._d.getUTCDay():e._d.getDay(),null!=e._tzm&&e._d.setUTCMinutes(e._d.getUTCMinutes()-e._tzm),e._nextDay&&(e._a[ve]=24),e._w&&void 0!==e._w.d&&e._w.d!==i&&(f(e).weekdayMismatch=!0)}}var ft=/^\s*((?:[+-]\d{6}|\d{4})-(?:\d\d-\d\d|W\d\d-\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?::\d\d(?::\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/,gt=/^\s*((?:[+-]\d{6}|\d{4})(?:\d\d\d\d|W\d\d\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?:\d\d(?:\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/,mt=/Z|[+-]\d\d(?::?\d\d)?/,vt=[["YYYYYY-MM-DD",/[+-]\d{6}-\d\d-\d\d/],["YYYY-MM-DD",/\d{4}-\d\d-\d\d/],["GGGG-[W]WW-E",/\d{4}-W\d\d-\d/],["GGGG-[W]WW",/\d{4}-W\d\d/,!1],["YYYY-DDD",/\d{4}-\d{3}/],["YYYY-MM",/\d{4}-\d\d/,!1],["YYYYYYMMDD",/[+-]\d{10}/],["YYYYMMDD",/\d{8}/],["GGGG[W]WWE",/\d{4}W\d{3}/],["GGGG[W]WW",/\d{4}W\d{2}/,!1],["YYYYDDD",/\d{7}/]],yt=[["HH:mm:ss.SSSS",/\d\d:\d\d:\d\d\.\d+/],["HH:mm:ss,SSSS",/\d\d:\d\d:\d\d,\d+/],["HH:mm:ss",/\d\d:\d\d:\d\d/],["HH:mm",/\d\d:\d\d/],["HHmmss.SSSS",/\d\d\d\d\d\d\.\d+/],["HHmmss,SSSS",/\d\d\d\d\d\d,\d+/],["HHmmss",/\d\d\d\d\d\d/],["HHmm",/\d\d\d\d/],["HH",/\d\d/]],wt=/^\/?Date\((\-?\d+)/i;function bt(e){var t,n,r,i,o,a,s=e._i,u=ft.exec(s)||gt.exec(s);if(u){for(f(e).iso=!0,t=0,n=vt.length;tn.valueOf():n.valueOf()this.clone().month(0).utcOffset()||this.utcOffset()>this.clone().month(5).utcOffset()},fn.isLocal=function(){return!!this.isValid()&&!this._isUTC},fn.isUtcOffset=function(){return!!this.isValid()&&this._isUTC},fn.isUtc=Nt,fn.isUTC=Nt,fn.zoneAbbr=function(){return this._isUTC?"UTC":""},fn.zoneName=function(){return this._isUTC?"Coordinated Universal Time":""},fn.dates=n("dates accessor is deprecated. Use date instead.",un),fn.months=n("months accessor is deprecated. Use month instead",Ie),fn.years=n("years accessor is deprecated. Use year instead",De),fn.zone=n("moment().zone is deprecated, use moment().utcOffset instead. http://momentjs.com/guides/#/warnings/zone/",function(e,t){return null!=e?("string"!=typeof e&&(e=-e),this.utcOffset(e,t),this):-this.utcOffset()}),fn.isDSTShifted=n("isDSTShifted is deprecated. See http://momentjs.com/guides/#/warnings/dst-shifted/ for more information",function(){if(!o(this._isDSTShifted))return this._isDSTShifted;var e={};if(y(e,this),(e=Dt(e))._a){var t=e._isUTC?d(e._a):Ot(e._a);this._isDSTShifted=this.isValid()&&0-1/0&&t.selectable&&c[e]){var r=c[e](t.utcDateValue),i=[];if(r.weeks)for(var o=0;o9+n,past:l.year()r.indexOf(e.startView))throw new Error("startView must be greater than minView");if(!a.isNumber(e.minuteStep))throw new Error("minuteStep must be numeric");if(e.minuteStep<=0||60<=e.minuteStep)throw new Error("minuteStep must be greater than zero and less than 60");if(null!==e.configureOn&&!a.isString(e.configureOn))throw new Error("configureOn must be a string");if(null!==e.configureOn&&e.configureOn.length<1)throw new Error("configureOn must not be an empty string");if(null!==e.renderOn&&!a.isString(e.renderOn))throw new Error("renderOn must be a string");if(null!==e.renderOn&&e.renderOn.length<1)throw new Error("renderOn must not be an empty string");if(null!==e.modelType&&!a.isString(e.modelType))throw new Error("modelType must be a string");if(null!==e.modelType&&e.modelType.length<1)throw new Error("modelType must not be an empty string");"Date"!==e.modelType&&"moment"!==e.modelType&&"milliseconds"!==e.modelType&&(e.parseFormat=e.modelType);if(null!==e.dropdownSelector&&!a.isString(e.dropdownSelector))throw new Error("dropdownSelector must be a string");null===e.dropdownSelector||"undefined"!=typeof jQuery&&"function"==typeof jQuery().dropdown||(i.error("Please DO NOT specify the dropdownSelector option unless you are using jQuery AND Bootstrap.js. Please include jQuery AND Bootstrap.js, or write code to close the dropdown in the on-set-time callback. \n\nThe dropdownSelector configuration option is being removed because it will not function properly."),delete e.dropdownSelector)}}}a.module("ui.bootstrap.datetimepicker",[]).service("dateTimePickerConfig",function(){var e={bg:{previous:"предишна",next:"следваща"},ca:{previous:"anterior",next:"següent"},da:{previous:"forrige",next:"næste"},de:{previous:"vorige",next:"weiter"},"en-au":{previous:"previous",next:"next"},"en-gb":{previous:"previous",next:"next"},en:{previous:"previous",next:"next"},"es-us":{previous:"atrás",next:"siguiente"},es:{previous:"atrás",next:"siguiente"},fi:{previous:"edellinen",next:"seuraava"},fr:{previous:"précédent",next:"suivant"},hu:{previous:"előző",next:"következő"},it:{previous:"precedente",next:"successivo"},ja:{previous:"前へ",next:"次へ"},ml:{previous:"മുൻപുള്ളത്",next:"അടുത്തത്"},nl:{previous:"vorige",next:"volgende"},pl:{previous:"poprzednia",next:"następna"},"pt-br":{previous:"anteriores",next:"próximos"},pt:{previous:"anterior",next:"próximo"},ro:{previous:"anterior",next:"următor"},ru:{previous:"предыдущая",next:"следующая"},sk:{previous:"predošlá",next:"ďalšia"},sv:{previous:"föregående",next:"nästa"},tr:{previous:"önceki",next:"sonraki"},uk:{previous:"назад",next:"далі"},"zh-cn":{previous:"上一页",next:"下一页"},"zh-tw":{previous:"上一頁",next:"下一頁"}}[b.locale().toLowerCase()];return a.extend({},{configureOn:null,dropdownSelector:null,minuteStep:5,minView:"minute",modelType:"Date",parseFormat:"YYYY-MM-DDTHH:mm:ss.SSSZZ",renderOn:null,startView:"day"},{screenReader:e})}).service("dateTimePickerValidator",t).directive("datetimepicker",e),e.$inject=["dateTimePickerConfig","dateTimePickerValidator"],t.$inject=["$log"]}),angular.module("rzTable",[]),angular.module("rzTable").directive("rzTable",["resizeStorage","$injector","$parse",function(s,i,o){function e(e){}function r(n,r,i){return function(e,t){!0!==i.busy&&void 0!==t&&t!==e&&(a(n),u(n,r,i))}}function a(e){_=!0,v.map(function(e){e.remove()}),v=[]}function u(e,t,n){if(!n.busy){h=$(e).find("th"),l=n.mode,d=!angular.isDefined(n.saveTableSizes)||n.saveTableSizes,p=n.profile;var r=function(t,e){try{var n=e.rzMode?t.mode:"BasicResizer",r=i.get(n);return r}catch(e){return console.error("The resizer "+t.mode+" was not found"),null}}(n,t);r&&(b=new r(e,h,w),d&&(S=s.loadTableSizes(e,n.mode,n.profile)),g=b.handles(h),f=b.ctrlColumns,b.setup(),function(o){o&&($(y).width("auto"),f.each(function(e,t){var n=angular.element(t).scope(),r=n.rzCol||$(t).attr("id"),i=o[r];$(t).css({width:i})}),b.onTableReady())}(S),g.each(function(e,t){!function(e,t,n){var r=$("
    ",{class:e.options.handleClass||"rz-handle"});$(n).prepend(r),v.push(r);var i=b.handleMiddleware(r,n);!function(i,e,o,a){$(o).mousedown(function(e){_&&(b.onFirstDrag(a,o),b.onTableReady(),_=!1),i.options.onResizeStarted&&i.options.onResizeStarted(a);var t={};b.intervene&&(((t=b.intervene.selector(a)).column=t).orgWidth=$(t).width()),e.preventDefault(),$(o).addClass(i.options.handleClassActive||"rz-handle-active");var n=e.clientX,r=$(a).width();m=function(o,a,s,u,l){return function(e){var t=e.clientX,n=t-s,r=b.calculate(u,n);if(!(rt)||(this.fixedColumn.width()<=this.getMinWidth(this.fixedColumn)?(this.bound=t,$(this.fixedColumn).width(this.minWidth),!0):void 0)},e.prototype.onEndDrag=function(){this.bound=!1},e.prototype.calculate=function(e,t){return e-t},e}]),angular.module("rzTable").factory("OverflowResizer",["ResizerModel",function(r){function e(e,t,n){r.call(this,e,t,n)}return(e.prototype=Object.create(r.prototype)).setup=function(){$(this.container).css({overflow:"auto"})},e.prototype.onTableReady=function(){$(this.table).width(1)},e}]),function(e,t){"function"==typeof define&&define.amd?define(["angular"],t):"object"==typeof module&&module.exports?module.exports=t(require("angular")):e.angularClipboard=t(e.angular)}(this,function(i){return i.module("angular-clipboard",[]).factory("clipboard",["$document","$window",function(o,a){return{copyText:function(e,t){var n=a.pageXOffset||o[0].documentElement.scrollLeft,r=a.pageYOffset||o[0].documentElement.scrollTop,i=function(e,t){var n=o[0].createElement("textarea");return n.style.position="absolute",n.style.fontSize="12pt",n.style.border="0",n.style.padding="0",n.style.margin="0",n.style.left="-10000px",n.style.top=(a.pageYOffset||o[0].documentElement.scrollTop)+"px",n.textContent=e,n}(e);o[0].body.appendChild(i),function(e){try{o[0].body.style.webkitUserSelect="initial";var t=o[0].getSelection();t.removeAllRanges();var n=document.createRange();n.selectNodeContents(e),t.addRange(n),e.select(),e.setSelectionRange(0,999999);try{if(!o[0].execCommand("copy"))throw"failure copy"}finally{t.removeAllRanges()}}finally{o[0].body.style.webkitUserSelect=""}}(i),a.scrollTo(n,r),o[0].body.removeChild(i)},supported:"queryCommandSupported"in o[0]&&o[0].queryCommandSupported("copy")}}]).directive("clipboard",["clipboard",function(r){return{restrict:"A",scope:{onCopied:"&",onError:"&",text:"=",supported:"=?"},link:function(t,n){t.supported=r.supported,n.on("click",function(e){try{r.copyText(t.text,n[0]),i.isFunction(t.onCopied)&&t.$evalAsync(t.onCopied())}catch(e){i.isFunction(t.onError)&&t.$evalAsync(t.onError({err:e}))}})}}}])}),function(e,t){"function"==typeof define&&define.amd?define("sifter",t):"object"==typeof exports?module.exports=t():e.Sifter=t()}(this,function(){var e=function(e,t){this.items=e,this.settings=t||{diacritics:!0}};e.prototype.tokenize=function(e){if(!(e=s(String(e||"").toLowerCase()))||!e.length)return[];var t,n,r,i,o=[],a=e.split(/ +/);for(t=0,n=a.length;t/g,">").replace(/"/g,""")},t={before:function(e,t,n){var r=e[t];e[t]=function(){return n.apply(e,arguments),r.apply(e,arguments)}},after:function(t,e,n){var r=t[e];t[e]=function(){var e=r.apply(t,arguments);return n.apply(t,arguments),e}}},n=function(t,n,e){var r,i=t.trigger,o={};for(r in t.trigger=function(){var e=arguments[0];if(-1===n.indexOf(e))return i.apply(t,arguments);o[e]=arguments},e.apply(t,[]),t.trigger=i,o)o.hasOwnProperty(r)&&i.apply(t,o[r])},p=function(e){var t={};if("selectionStart"in e)t.start=e.selectionStart,t.length=e.selectionEnd-t.start;else if(document.selection){e.focus();var n=document.selection.createRange(),r=document.selection.createRange().text.length;n.moveStart("character",-e.value.length),t.start=n.text.length-r,t.length=r}return t},$=function(c){var d=null,e=function(e,t){var n,r,i,o,a,s,u,l;t=t||{},(e=e||window.event||{}).metaKey||e.altKey||(t.force||!1!==c.data("grow"))&&(n=c.val(),e.type&&"keydown"===e.type.toLowerCase()&&(i=48<=(r=e.keyCode)&&r<=57||65<=r&&r<=90||96<=r&&r<=111||186<=r&&r<=222||32===r,46===r||8===r?(l=p(c[0])).length?n=n.substring(0,l.start)+n.substring(l.start+l.length):8===r&&l.start?n=n.substring(0,l.start-1)+n.substring(l.start+1):46===r&&void 0!==l.start&&(n=n.substring(0,l.start)+n.substring(l.start+1)):i&&(s=e.shiftKey,u=String.fromCharCode(e.keyCode),n+=u=s?u.toUpperCase():u.toLowerCase())),o=c.attr("placeholder"),!n&&o&&(n=o),(a=function(e,t){return e?(_.$testInput||(_.$testInput=C("").css({position:"absolute",top:-99999,left:-99999,width:"auto",padding:0,whiteSpace:"pre"}).appendTo("body")),_.$testInput.text(e),function(e,t,n){var r,i,o={};if(n)for(r=0,i=n.length;r").addClass(d.wrapperClass).addClass(s).addClass(a),t=C("
    ").addClass(d.inputClass).addClass("items").appendTo(e),n=C('').appendTo(t).attr("tabindex",g.is(":disabled")?"-1":c.tabIndex),o=C(d.dropdownParent||e),r=C("
    ").addClass(d.dropdownClass).addClass(a).hide().appendTo(o),i=C("
    ").addClass(d.dropdownContentClass).appendTo(r),(l=g.attr("id"))&&(n.attr("id",l+"-selectized"),C("label[for='"+l+"']").attr("for",l+"-selectized")),c.settings.copyClassesToDropdown&&r.addClass(s),e.css({width:g[0].style.width}),c.plugins.names.length&&(u="plugin-"+c.plugins.names.join(" plugin-"),e.addClass(u),r.addClass(u)),(null===d.maxItems||1[data-selectable]",function(e){e.stopImmediatePropagation()}),r.on("mouseenter","[data-selectable]",function(){return c.onOptionHover.apply(c,arguments)}),r.on("mousedown click","[data-selectable]",function(){return c.onOptionSelect.apply(c,arguments)}),function(n,e,t,r){n.on(e,t,function(e){for(var t=e.target;t&&t.parentNode!==n[0];)t=t.parentNode;return e.currentTarget=t,r.apply(this,[e])})}(t,"mousedown","*:not(input)",function(){return c.onItemSelect.apply(c,arguments)}),$(n),t.on({mousedown:function(){return c.onMouseDown.apply(c,arguments)},click:function(){return c.onClick.apply(c,arguments)}}),n.on({mousedown:function(e){e.stopPropagation()},keydown:function(){return c.onKeyDown.apply(c,arguments)},keyup:function(){return c.onKeyUp.apply(c,arguments)},keypress:function(){return c.onKeyPress.apply(c,arguments)},resize:function(){c.positionDropdown.apply(c,[])},blur:function(){return c.onBlur.apply(c,arguments)},focus:function(){return c.ignoreBlur=!1,c.onFocus.apply(c,arguments)},paste:function(){return c.onPaste.apply(c,arguments)}}),f.on("keydown"+p,function(e){c.isCmdDown=e[v?"metaKey":"ctrlKey"],c.isCtrlDown=e[v?"altKey":"ctrlKey"],c.isShiftDown=e.shiftKey}),f.on("keyup"+p,function(e){e.keyCode===w&&(c.isCtrlDown=!1),16===e.keyCode&&(c.isShiftDown=!1),e.keyCode===y&&(c.isCmdDown=!1)}),f.on("mousedown"+p,function(e){if(c.isFocused){if(e.target===c.$dropdown[0]||e.target.parentNode===c.$dropdown[0])return!1;c.$control.has(e.target).length||e.target===c.$control[0]||c.blur(e.target)}}),h.on(["scroll"+p,"resize"+p].join(" "),function(){c.isOpen&&c.positionDropdown.apply(c,arguments)}),h.on("mousemove"+p,function(){c.ignoreHover=!1}),this.revertSettings={$children:g.children().detach(),tabindex:g.attr("tabindex")},g.attr("tabindex",-1).hide().after(c.$wrapper),C.isArray(d.items)&&(c.setValue(d.items),delete d.items),b&&g.on("invalid"+p,function(e){e.preventDefault(),c.isInvalid=!0,c.refreshState()}),c.updateOriginalInput(),c.refreshItems(),c.refreshState(),c.updatePlaceholder(),c.isSetup=!0,g.is(":disabled")&&c.disable(),c.on("change",this.onChange),g.data("selectize",c),g.addClass("selectized"),c.trigger("initialize"),!0===d.preload&&c.onSearchChange("")},setupTemplates:function(){var n=this.settings.labelField,r=this.settings.optgroupLabelField,e={optgroup:function(e){return'
    '+e.html+"
    "},optgroup_header:function(e,t){return'
    '+t(e[r])+"
    "},option:function(e,t){return'
    '+t(e[n])+"
    "},item:function(e,t){return'
    '+t(e[n])+"
    "},option_create:function(e,t){return'
    Add '+t(e.input)+"
    "}};this.settings.render=C.extend({},e,this.settings.render)},setupCallbacks:function(){var e,t,n={initialize:"onInitialize",change:"onChange",item_add:"onItemAdd",item_remove:"onItemRemove",clear:"onClear",option_add:"onOptionAdd",option_remove:"onOptionRemove",option_clear:"onOptionClear",optgroup_add:"onOptionGroupAdd",optgroup_remove:"onOptionGroupRemove",optgroup_clear:"onOptionGroupClear",dropdown_open:"onDropdownOpen",dropdown_close:"onDropdownClose",type:"onType",load:"onLoad",focus:"onFocus",blur:"onBlur"};for(e in n)n.hasOwnProperty(e)&&(t=this.settings[n[e]])&&this.on(e,t)},onClick:function(e){this.isFocused&&this.isOpen||(this.focus(),e.preventDefault())},onMouseDown:function(e){var t=this,n=e.isDefaultPrevented();C(e.target);if(t.isFocused){if(e.target!==t.$control_input[0])return"single"===t.settings.mode?t.isOpen?t.close():t.open():n||t.setActiveItem(null),!1}else n||window.setTimeout(function(){t.focus()},0)},onChange:function(){this.$input.trigger("change")},onPaste:function(e){var i=this;i.isFull()||i.isInputHidden||i.isLocked?e.preventDefault():i.settings.splitOn&&setTimeout(function(){var e=i.$control_input.val();if(e.match(i.settings.splitOn))for(var t=C.trim(e).split(i.settings.splitOn),n=0,r=t.length;n=this.settings.maxItems},updateOriginalInput:function(e){var t,n,r,i,o=this;if(e=e||{},1===o.tagType){for(r=[],t=0,n=o.items.length;t'+s(i)+"");r.length||this.$input.attr("multiple")||r.push(''),o.$input.html(r.join(""))}else o.$input.val(o.getValue()),o.$input.attr("value",o.$input.val());o.isSetup&&(e.silent||o.trigger("change",o.$input.val()))},updatePlaceholder:function(){if(this.settings.placeholder){var e=this.$control_input;this.items.length?e.removeAttr("placeholder"):e.attr("placeholder",this.settings.placeholder),e.triggerHandler("update",{force:!0})}},open:function(){var e=this;e.isLocked||e.isOpen||"multi"===e.settings.mode&&e.isFull()||(e.focus(),e.isOpen=!0,e.refreshState(),e.$dropdown.css({visibility:"hidden",display:"block"}),e.positionDropdown(),e.$dropdown.css({visibility:"visible"}),e.trigger("dropdown_open",e.$dropdown))},close:function(){var e=this,t=e.isOpen;"single"===e.settings.mode&&e.items.length&&(e.hideInput(),e.isBlurring||e.$control_input.blur()),e.isOpen=!1,e.$dropdown.hide(),e.setActiveOption(null),e.refreshState(),t&&e.trigger("dropdown_close",e.$dropdown)},positionDropdown:function(){var e=this.$control,t="body"===this.settings.dropdownParent?e.offset():e.position();t.top+=e.outerHeight(!0),this.$dropdown.css({width:e[0].getBoundingClientRect().width,top:t.top,left:t.left})},clear:function(e){var t=this;t.items.length&&(t.$control.children(":not(input)").remove(),t.items=[],t.lastQuery=null,t.setCaret(0),t.setActiveItem(null),t.updatePlaceholder(),t.updateOriginalInput({silent:e}),t.refreshState(),t.showInput(),t.trigger("clear"))},insertAtCaret:function(e){var t=Math.min(this.caretPos,this.items.length),n=e[0],r=this.buffer||this.$control[0];0===t?r.insertBefore(n,r.firstChild):r.insertBefore(n,r.childNodes[t]),this.setCaret(t+1)},deleteSelection:function(e){var t,n,r,i,o,a,s,u,l,c=this;if(r=e&&8===e.keyCode?-1:1,i=p(c.$control_input[0]),c.$activeOption&&!c.settings.hideSelected&&(s=c.getAdjacentOption(c.$activeOption,-1).attr("data-value")),o=[],c.$activeItems.length){for(l=c.$control.children(".active:"+(0
    '+e.title+'×
    '}},e),n.setup=(t=n.setup,function(){t.apply(n,arguments),n.$dropdown_header=C(e.html(e)),n.$dropdown.prepend(n.$dropdown_header)})}),_.define("optgroup_columns",function(s){var o,u=this;s=C.extend({equalizeWidth:!0,equalizeHeight:!0},s),this.getAdjacentOption=function(e,t){var n=e.closest("[data-group]").find("[data-selectable]"),r=n.index(e)+t;return 0<=r&&r
    ',e=e.firstChild,n.body.appendChild(e),t=l.width=e.offsetWidth-e.clientWidth,n.body.removeChild(e)),t},e=function(){var e,t,n,r,i,o,a;if((t=(a=C("[data-group]",u.$dropdown_content)).length)&&u.$dropdown_content.width()){if(s.equalizeHeight){for(e=n=0;e'+r.label+"",n.setup=(i=o.setup,function(){if(r.append){var t=o.settings.render.item;o.settings.render.item=function(e){return function(e,t){var n=e.search(/(<\/[^>]+>\s*)$/);return e.substring(0,n)+t+e.substring(n)}(t.apply(n,arguments),a)}}i.apply(n,arguments),n.$control.on("click","."+r.className,function(e){if(e.preventDefault(),!o.isLocked){var t=C(e.currentTarget).parent();o.setActiveItem(t),o.deleteSelection()&&o.setCaret(o.items.length)}})})):function(n,r){r.className="remove-single";var i,o=n,a=''+r.label+"";n.setup=(i=o.setup,function(){if(r.append){var e=C(o.$input.context).attr("id"),t=(C("#"+e),o.settings.render.item);o.settings.render.item=function(e){return function(e,t){return C("").append(e).append(t)}(t.apply(n,arguments),a)}}i.apply(n,arguments),n.$control.on("click","."+r.className,function(e){e.preventDefault(),o.isLocked||o.clear()})})}(this,e)}),_.define("restore_on_backspace",function(r){var i,e=this;r.text=r.text||function(e){return e[this.settings.labelField]},this.onKeyDown=(i=e.onKeyDown,function(e){var t,n;return 8===e.keyCode&&""===this.$control_input.val()&&!this.$activeItems.length&&0<=(t=this.caretPos-1)&&t",{class:function(){var e=[];return e.push(i.options.state?"on":"off"),i.options.size&&e.push(i.options.size),i.options.disabled&&e.push("disabled"),i.options.readonly&&e.push("readonly"),i.options.indeterminate&&e.push("indeterminate"),i.options.inverse&&e.push("inverse"),i.$element.attr("id")&&e.push("id-"+i.$element.attr("id")),e.map(i._getClass.bind(i)).concat([i.options.baseClass],i._getClasses(i.options.wrapperClass)).join(" ")}}),this.$container=s("
    ",{class:this._getClass("container")}),this.$on=s("",{html:this.options.onText,class:this._getClass("handle-on")+" "+this._getClass(this.options.onColor)}),this.$off=s("",{html:this.options.offText,class:this._getClass("handle-off")+" "+this._getClass(this.options.offColor)}),this.$label=s("",{html:this.options.labelText,class:this._getClass("label")}),this.$element.on("init.bootstrapSwitch",this.options.onInit.bind(this,r)),this.$element.on("switchChange.bootstrapSwitch",function(){for(var e=arguments.length,t=Array(e),n=0;n-n._handleWidth/2;n._dragEnd=!1,n.state(n.options.inverse?!t:t)}else n.state(!n.options.state);n._dragStart=!1}},"mouseleave.bootstrapSwitch":function(){n.$label.trigger("mouseup.bootstrapSwitch")}})}},{key:"_externalLabelHandler",value:function(){var t=this,n=this.$element.closest("label");n.on("click",function(e){e.preventDefault(),e.stopImmediatePropagation(),e.target===n[0]&&t.toggleState()})}},{key:"_formHandler",value:function(){var e=this.$element.closest("form");e.data("bootstrap-switch")||e.on("reset.bootstrapSwitch",function(){window.setTimeout(function(){e.find("input").filter(function(){return s(this).data("bootstrap-switch")}).each(function(){return s(this).bootstrapSwitch("state",this.checked)})},1)}).data("bootstrap-switch",!0)}},{key:"_getClass",value:function(e){return this.options.baseClass+"-"+e}},{key:"_getClasses",value:function(e){return s.isArray(e)?e.map(this._getClass.bind(this)):[this._getClass(e)]}}]),t}();s.fn.bootstrapSwitch=function(o){for(var e=arguments.length,a=Array(1
    ');var r,i=f.overlay?"":" ngdialog-no-overlay";if((d=T('
    ')).html(f.overlay?'
    '+t+"
    ":'
    '+t+"
    "),d.data("$ngDialogOptions",f),c.ngDialogId=l,f.data&&O.isString(f.data)){var o=f.data.replace(/^\s*/,"")[0];c.ngDialogData="{"===o||"["===o?O.fromJson(f.data):new String(f.data),c.ngDialogData.ngDialogId=l}else f.data&&O.isObject(f.data)&&(c.ngDialogData=f.data,c.ngDialogData.ngDialogId=l);(f.className&&d.addClass(f.className),f.appendClassName&&d.addClass(f.appendClassName),f.width&&(h=d[0].querySelector(".ngdialog-content"),O.isString(f.width)?h.style.width=f.width:h.style.width=f.width+"px"),f.height&&(h=d[0].querySelector(".ngdialog-content"),O.isString(f.height)?h.style.height=f.height:h.style.height=f.height+"px"),f.disableAnimation&&d.addClass("ngdialog-disabled-animation"),p=f.appendTo&&O.isString(f.appendTo)?O.element(document.querySelector(f.appendTo)):b.body,$.applyAriaAttributes(d,f),f.preCloseCallback)&&(O.isFunction(f.preCloseCallback)?r=f.preCloseCallback:O.isString(f.preCloseCallback)&&c&&(O.isFunction(c[f.preCloseCallback])?r=c[f.preCloseCallback]:c.$parent&&O.isFunction(c.$parent[f.preCloseCallback])?r=c.$parent[f.preCloseCallback]:m&&O.isFunction(m[f.preCloseCallback])&&(r=m[f.preCloseCallback])),r&&d.data("$ngDialogPreCloseCallback",r));if(c.closeThisDialog=function(e){$.closeDialog(d,e)},f.controller&&(O.isString(f.controller)||O.isArray(f.controller)||O.isFunction(f.controller))){var a;f.controllerAs&&O.isString(f.controllerAs)&&(a=f.controllerAs);var s=w(f.controller,O.extend(n,{$scope:c,$element:d}),!0,a);f.bindToController&&O.extend(s.instance,{ngDialogId:c.ngDialogId,ngDialogData:c.ngDialogData,closeThisDialog:c.closeThisDialog,confirm:c.confirm}),"function"==typeof s?d.data("$ngDialogControllerController",s()):d.data("$ngDialogControllerController",s)}if(v(function(){var e=document.querySelectorAll(".ngdialog");$.deactivateAll(e),g(d)(c);var t=y.innerWidth-b.body.prop("clientWidth");b.html.addClass(f.bodyClassName),b.body.addClass(f.bodyClassName);var n=t-(y.innerWidth-b.body.prop("clientWidth"));0window.innerHeight&&(u=v,t++,e=0);var l=u?0===e?u:u+w:v,c=n+t*(b+s);o.css(o._positionY,l+"px"),"center"==o._positionX?o.css("left",parseInt(window.innerWidth/2-s/2)+"px"):o.css(o._positionX,c+"px"),r[o._positionY+o._positionX]=l+a,0m.maxCount&&0===i&&o.scope().kill(!0),e++}}},i=c(e)(n);i._positionY=h.positionY,i._positionX=h.positionX,i._priority=h.priority,i.addClass(h.type);var o=function(e){("click"===(e=e.originalEvent||e).type||"opacity"===e.propertyName&&1<=e.elapsedTime)&&(n.onClose&&n.$apply(n.onClose(i)),i.remove(),$.splice($.indexOf(i),1),n.$destroy(),r())};h.closeOnClick&&(i.addClass("clickable"),i.bind("click",o)),i.bind("webkitTransitionEnd oTransitionEnd otransitionend transitionend msTransitionEnd",o),angular.isNumber(h.delay)&&l(function(){i.addClass("killed")},h.delay),t("none"),angular.element(document.querySelector(h.container)).append(i);var a=-(parseInt(i[0].offsetHeight)+50);if(i.css(i._positionY,a+"px"),$.push(i),"center"==h.positionX){var s=parseInt(i[0].offsetWidth);i.css("left",parseInt(window.innerWidth/2-s/2)+"px")}l(function(){t("")}),n._templateElement=i,n.kill=function(e){e?(n.onClose&&n.$apply(n.onClose(n._templateElement)),$.splice($.indexOf(n._templateElement),1),n._templateElement.remove(),n.$destroy(),l(r)):n._templateElement.addClass("killed")},l(r),_||(angular.element(g).bind("resize",function(e){l(r)}),_=!0),u.resolve(n)}var u=a.defer();"object"==typeof h&&null!==h||(h={message:h}),h.scope=h.scope?h.scope:o,h.template=h.templateUrl?h.templateUrl:m.templateUrl,h.delay=angular.isUndefined(h.delay)?s:h.delay,h.type=e||h.type||m.type||"",h.positionY=h.positionY?h.positionY:m.positionY,h.positionX=h.positionX?h.positionX:m.positionX,h.replaceMessage=h.replaceMessage?h.replaceMessage:m.replaceMessage,h.onClose=h.onClose?h.onClose:m.onClose,h.closeOnClick=null!==h.closeOnClick&&void 0!==h.closeOnClick?h.closeOnClick:m.closeOnClick,h.container=h.container?h.container:m.container,h.priority=h.priority?h.priority:m.priority;var n=i.get(h.template);return n?t(n):r.get(h.template,{cache:!0}).then(function(e){t(e.data)}).catch(function(e){throw new Error("Template ("+h.template+") could not be loaded. "+e)}),u.promise};return t.primary=function(e){return this(e,"primary")},t.error=function(e){return this(e,"error")},t.success=function(e){return this(e,"success")},t.info=function(e){return this(e,"info")},t.warning=function(e){return this(e,"warning")},t.clearAll=function(){angular.forEach($,function(e){e.addClass("killed")})},t}]}),angular.module("ui-notification").run(["$templateCache",function(e){e.put("angular-ui-notification.html",'

    ')}]),function(){var g="__default";angular.module("angularUtils.directives.dirPagination",[]).directive("dirPaginate",["$compile","$parse","paginationService",function(l,c,d){return{terminal:!0,multiElement:!0,priority:100,compile:function(e,t){var s=t.dirPaginate,n=s.match(/^\s*([\s\S]+?)\s+in\s+([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+track\s+by\s+([\s\S]+?))?\s*$/),r=/\|\s*itemsPerPage\s*:\s*(.*\(\s*\w*\)|([^\)]*?(?=\s+as\s+))|[^\)]*)/;if(null===n[2].match(r))throw"pagination directive: the 'itemsPerPage' filter must be set.";var i=n[2].replace(r,""),u=c(i);!function(e){angular.forEach(e,function(e){1===e.nodeType&&angular.element(e).attr("dir-paginate-no-compile",!0)})}(e);var o=t.paginationId||g;return d.registerInstance(o),function(e,t,n){var r=c(n.paginationId)(e)||n.paginationId||g;d.registerInstance(r);var i=function(e,t){var n=!!e.match(/(\|\s*itemsPerPage\s*:[^|]*:[^|]*)/);return t===g||n?e:e.replace(/(\|\s*itemsPerPage\s*:\s*[^|\s]*)/,"$1 : '"+t+"'")}(s,r);!function(e,t,n){e[0].hasAttribute("dir-paginate-start")||e[0].hasAttribute("data-dir-paginate-start")?(t.$set("ngRepeatStart",n),e.eq(e.length-1).attr("ng-repeat-end",!0)):t.$set("ngRepeat",n)}(t,n,i),function(e){angular.forEach(e,function(e){1===e.nodeType&&angular.element(e).removeAttr("dir-paginate-no-compile")}),e.eq(0).removeAttr("dir-paginate-start").removeAttr("dir-paginate").removeAttr("data-dir-paginate-start").removeAttr("data-dir-paginate"),e.eq(e.length-1).removeAttr("dir-paginate-end").removeAttr("data-dir-paginate-end")}(t);var o=l(t),a=function(e,t,n){var r;if(t.currentPage)r=c(t.currentPage);else{var i=(n+"__currentPage").replace(/\W/g,"_");e[i]=1,r=c(i)}return r}(e,n,r);d.setCurrentPageParser(r,a,e),void 0!==n.totalItems?(d.setAsyncModeTrue(r),e.$watch(function(){return c(n.totalItems)(e)},function(e){0<=e&&d.setCollectionLength(r,e)})):(d.setAsyncModeFalse(r),e.$watchCollection(function(){return u(e)},function(e){if(e){var t=e instanceof Array?e.length:Object.keys(e).length;d.setCollectionLength(r,t)}})),o(e)}}}}]).directive("dirPaginateNoCompile",function(){return{priority:5e3,terminal:!0}}).directive("dirPaginationControls",["paginationService","paginationTemplate",function(d,n){var p=/^\d+$/,e={restrict:"AE",scope:{maxSize:"=?",onPageChange:"&?",paginationId:"=?",autoHide:"=?"},link:function(r,e,t){var n=t.paginationId||g,i=r.paginationId||t.paginationId||g;if(!d.isRegistered(i)&&!d.isRegistered(n)){var o=i!==g?" (id: "+i+") ":" ";window.console&&console.warn("Pagination directive: the pagination controls"+o+"cannot be used without the corresponding pagination directive, which was not found at link time.")}r.maxSize||(r.maxSize=9);r.autoHide=void 0===r.autoHide||r.autoHide,r.directionLinks=!angular.isDefined(t.directionLinks)||r.$parent.$eval(t.directionLinks),r.boundaryLinks=!!angular.isDefined(t.boundaryLinks)&&r.$parent.$eval(t.boundaryLinks);var a=Math.max(r.maxSize,5);function s(e){if(d.isRegistered(i)&&c(e)){var t=r.pagination.current;r.pages=h(e,d.getCollectionLength(i),d.getItemsPerPage(i),a),r.pagination.current=e,l(),r.onPageChange&&r.onPageChange({newPageNumber:e,oldPageNumber:t})}}function u(){if(d.isRegistered(i)){var e=parseInt(d.getCurrentPage(i))||1;r.pages=h(e,d.getCollectionLength(i),d.getItemsPerPage(i),a),r.pagination.current=e,r.pagination.last=r.pages[r.pages.length-1],r.pagination.last
  • «
  • {{ pageNumber }}
  • »
  • ')}])}();var com_github_culmat_jsTreeTable=function(){function u(e,r,i){return i=i||"children",$.each(e,function(e,t){!function n(e){e[i]&&$.each(e[i],function(e,t){n(t)}),r(e)}(t)}),e}function t(e,n,o,a){var t=e;n=n||"id",o=o||"parent",a=a||"children";var s=[];$.each(t,function(e,t){s[t[n]]=t});var u=[];return $.each(t,function(e,r){var t=r[o];if($.isArray(t)||(t=[t]),0==t.length)u.push(r);else{var i=!1;$.each(t,function(e,t){var n=s[t];n&&(n[a]||(n[a]=[]),$.inArray(r,n[a])<0&&n[a].push(r),i=!0)}),i||u.push(r)}}),u}function l(e,a,s,u,l,t){a=a||"children",s=s||"id",t=t||{};var n=0,r=$("");$.each(t,function(e,t){"class"==e&&"jsTT"!=t?r.addClass(t):r.attr(e,t)});var i=$(""),o=$(""),c=$("");return r.append(i),i.append(o),r.append(c),u?$.each(u,function(e,t){$(o).append($(""))}):($(o).append($("")),$.each(e[0],function(e,t){e!=a&&e!=s&&$(o).append($(""))})),function r(e,i,o,t){n=Math.max(n,o),$.each(e,function(e,n){n["data-tt-level"]=o,function(n,e){var r=$("");$(r).attr("data-tt-id",n[s]),$(r).attr("data-tt-level",n["data-tt-level"]),n[a]&&0!=n[a].length?$(r).attr("data-tt-isnode",!0):$(r).attr("data-tt-isleaf",!0),e&&$(r).attr("data-tt-parent-id",e[s]),l?l($(r),n):u?$.each(u,function(e,t){$(r).append($(""))}):($(r).append($("")),$.each(n,function(e,t){e!=a&&e!=s&&"data-tt-level"!=e&&$(r).append($(""))})),c.append(r)}(n,t),n[i]&&$.each(n[i],function(e,t){r([t],i,o+1,n)})})}(e,a,1),e[0]&&(e[0].maxLevel=n),r}function n(e,t){return $.each(e,function(e,n){$.each(t,function(e,t){n[t]=$(n).attr(t)})}),e}function c(i){i.addClass("jsTT"),i.expandLevel=function(n){$("tr[data-tt-level]",i).each(function(e){var t=parseInt($(this).attr("data-tt-level"));n-1')):r.prepend($('')),r.prepend($('')),t.trExpand=function(e){if(!(this.trChildren.length<1)){e&&(this.trChildrenVisible=!0,$("#state",this).get(0).src=o);var n=e||this.trChildrenVisible;$.each(this.trChildren,function(e,t){n&&$(t).css("display","table-row"),t.trExpand()})}},t.trCollapse=function(e){this.trChildren.length<1||(e&&(this.trChildrenVisible=!1,$("#state",this).get(0).src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAsTAAALEwEAmpwYAAAKT2lDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVNnVFPpFj333vRCS4iAlEtvUhUIIFJCi4AUkSYqIQkQSoghodkVUcERRUUEG8igiAOOjoCMFVEsDIoK2AfkIaKOg6OIisr74Xuja9a89+bN/rXXPues852zzwfACAyWSDNRNYAMqUIeEeCDx8TG4eQuQIEKJHAAEAizZCFz/SMBAPh+PDwrIsAHvgABeNMLCADATZvAMByH/w/qQplcAYCEAcB0kThLCIAUAEB6jkKmAEBGAYCdmCZTAKAEAGDLY2LjAFAtAGAnf+bTAICd+Jl7AQBblCEVAaCRACATZYhEAGg7AKzPVopFAFgwABRmS8Q5ANgtADBJV2ZIALC3AMDOEAuyAAgMADBRiIUpAAR7AGDIIyN4AISZABRG8lc88SuuEOcqAAB4mbI8uSQ5RYFbCC1xB1dXLh4ozkkXKxQ2YQJhmkAuwnmZGTKBNA/g88wAAKCRFRHgg/P9eM4Ors7ONo62Dl8t6r8G/yJiYuP+5c+rcEAAAOF0ftH+LC+zGoA7BoBt/qIl7gRoXgugdfeLZrIPQLUAoOnaV/Nw+H48PEWhkLnZ2eXk5NhKxEJbYcpXff5nwl/AV/1s+X48/Pf14L7iJIEyXYFHBPjgwsz0TKUcz5IJhGLc5o9H/LcL//wd0yLESWK5WCoU41EScY5EmozzMqUiiUKSKcUl0v9k4t8s+wM+3zUAsGo+AXuRLahdYwP2SycQWHTA4vcAAPK7b8HUKAgDgGiD4c93/+8//UegJQCAZkmScQAAXkQkLlTKsz/HCAAARKCBKrBBG/TBGCzABhzBBdzBC/xgNoRCJMTCQhBCCmSAHHJgKayCQiiGzbAdKmAv1EAdNMBRaIaTcA4uwlW4Dj1wD/phCJ7BKLyBCQRByAgTYSHaiAFiilgjjggXmYX4IcFIBBKLJCDJiBRRIkuRNUgxUopUIFVIHfI9cgI5h1xGupE7yAAygvyGvEcxlIGyUT3UDLVDuag3GoRGogvQZHQxmo8WoJvQcrQaPYw2oefQq2gP2o8+Q8cwwOgYBzPEbDAuxsNCsTgsCZNjy7EirAyrxhqwVqwDu4n1Y8+xdwQSgUXACTYEd0IgYR5BSFhMWE7YSKggHCQ0EdoJNwkDhFHCJyKTqEu0JroR+cQYYjIxh1hILCPWEo8TLxB7iEPENyQSiUMyJ7mQAkmxpFTSEtJG0m5SI+ksqZs0SBojk8naZGuyBzmULCAryIXkneTD5DPkG+Qh8lsKnWJAcaT4U+IoUspqShnlEOU05QZlmDJBVaOaUt2ooVQRNY9aQq2htlKvUYeoEzR1mjnNgxZJS6WtopXTGmgXaPdpr+h0uhHdlR5Ol9BX0svpR+iX6AP0dwwNhhWDx4hnKBmbGAcYZxl3GK+YTKYZ04sZx1QwNzHrmOeZD5lvVVgqtip8FZHKCpVKlSaVGyovVKmqpqreqgtV81XLVI+pXlN9rkZVM1PjqQnUlqtVqp1Q61MbU2epO6iHqmeob1Q/pH5Z/YkGWcNMw09DpFGgsV/jvMYgC2MZs3gsIWsNq4Z1gTXEJrHN2Xx2KruY/R27iz2qqaE5QzNKM1ezUvOUZj8H45hx+Jx0TgnnKKeX836K3hTvKeIpG6Y0TLkxZVxrqpaXllirSKtRq0frvTau7aedpr1Fu1n7gQ5Bx0onXCdHZ4/OBZ3nU9lT3acKpxZNPTr1ri6qa6UbobtEd79up+6Ynr5egJ5Mb6feeb3n+hx9L/1U/W36p/VHDFgGswwkBtsMzhg8xTVxbzwdL8fb8VFDXcNAQ6VhlWGX4YSRudE8o9VGjUYPjGnGXOMk423GbcajJgYmISZLTepN7ppSTbmmKaY7TDtMx83MzaLN1pk1mz0x1zLnm+eb15vft2BaeFostqi2uGVJsuRaplnutrxuhVo5WaVYVVpds0atna0l1rutu6cRp7lOk06rntZnw7Dxtsm2qbcZsOXYBtuutm22fWFnYhdnt8Wuw+6TvZN9un2N/T0HDYfZDqsdWh1+c7RyFDpWOt6azpzuP33F9JbpL2dYzxDP2DPjthPLKcRpnVOb00dnF2e5c4PziIuJS4LLLpc+Lpsbxt3IveRKdPVxXeF60vWdm7Obwu2o26/uNu5p7ofcn8w0nymeWTNz0MPIQ+BR5dE/C5+VMGvfrH5PQ0+BZ7XnIy9jL5FXrdewt6V3qvdh7xc+9j5yn+M+4zw33jLeWV/MN8C3yLfLT8Nvnl+F30N/I/9k/3r/0QCngCUBZwOJgUGBWwL7+Hp8Ib+OPzrbZfay2e1BjKC5QRVBj4KtguXBrSFoyOyQrSH355jOkc5pDoVQfujW0Adh5mGLw34MJ4WHhVeGP45wiFga0TGXNXfR3ENz30T6RJZE3ptnMU85ry1KNSo+qi5qPNo3ujS6P8YuZlnM1VidWElsSxw5LiquNm5svt/87fOH4p3iC+N7F5gvyF1weaHOwvSFpxapLhIsOpZATIhOOJTwQRAqqBaMJfITdyWOCnnCHcJnIi/RNtGI2ENcKh5O8kgqTXqS7JG8NXkkxTOlLOW5hCepkLxMDUzdmzqeFpp2IG0yPTq9MYOSkZBxQqohTZO2Z+pn5mZ2y6xlhbL+xW6Lty8elQfJa7OQrAVZLQq2QqboVFoo1yoHsmdlV2a/zYnKOZarnivN7cyzytuQN5zvn//tEsIS4ZK2pYZLVy0dWOa9rGo5sjxxedsK4xUFK4ZWBqw8uIq2Km3VT6vtV5eufr0mek1rgV7ByoLBtQFr6wtVCuWFfevc1+1dT1gvWd+1YfqGnRs+FYmKrhTbF5cVf9go3HjlG4dvyr+Z3JS0qavEuWTPZtJm6ebeLZ5bDpaql+aXDm4N2dq0Dd9WtO319kXbL5fNKNu7g7ZDuaO/PLi8ZafJzs07P1SkVPRU+lQ27tLdtWHX+G7R7ht7vPY07NXbW7z3/T7JvttVAVVN1WbVZftJ+7P3P66Jqun4lvttXa1ObXHtxwPSA/0HIw6217nU1R3SPVRSj9Yr60cOxx++/p3vdy0NNg1VjZzG4iNwRHnk6fcJ3/ceDTradox7rOEH0x92HWcdL2pCmvKaRptTmvtbYlu6T8w+0dbq3nr8R9sfD5w0PFl5SvNUyWna6YLTk2fyz4ydlZ19fi753GDborZ752PO32oPb++6EHTh0kX/i+c7vDvOXPK4dPKy2+UTV7hXmq86X23qdOo8/pPTT8e7nLuarrlca7nuer21e2b36RueN87d9L158Rb/1tWeOT3dvfN6b/fF9/XfFt1+cif9zsu72Xcn7q28T7xf9EDtQdlD3YfVP1v+3Njv3H9qwHeg89HcR/cGhYPP/pH1jw9DBY+Zj8uGDYbrnjg+OTniP3L96fynQ89kzyaeF/6i/suuFxYvfvjV69fO0ZjRoZfyl5O/bXyl/erA6xmv28bCxh6+yXgzMV70VvvtwXfcdx3vo98PT+R8IH8o/2j5sfVT0Kf7kxmTk/8EA5jz/GMzLdsAAAAgY0hSTQAAeiUAAICDAAD5/wAAgOkAAHUwAADqYAAAOpgAABdvkl/FRgAAAHlJREFUeNrcU1sNgDAQ6wgmcAM2MICGGlg1gJnNzWQcvwQGy1j4oUl/7tH0mpwzM7SgQyO+EZAUWh2MkkzSWhJwuRAlHYsJwEwyvs1gABDuzqoJcTw5qxaIJN0bgQRgIjnlmn1heSO5PE6Y2YXe+5Cr5+h++gs12AcAS6FS+7YOsj4AAAAASUVORK5CYII="),$.each(this.trChildren,function(e,t){$(t).css("display","none"),t.trCollapse()}))},$(t).click(function(){this.trChildrenVisible?this.trCollapse(!0):this.trExpand(!0)})}),i}return{depthFirst:u,makeTree:t,renderTree:l,attr2attr:n,treeTable:c,appendTreetable:function(e,t){(t=t||{}).idAttr=t.idAttr||"id",t.childrenAttr=t.childrenAttr||"children";var n=t.controls||[];t.mountPoint||(t.mountPoint=$("body")),t.depthFirst&&u(e,t.depthFirst,t.childrenAttr);var r=l(e,t.childrenAttr,t.idAttr,t.renderedAttr,t.renderer,t.tableAttributes);c(r),t.replaceContent&&t.mountPoint.html("");var i,o,a=t.initialExpandLevel?parseInt(t.initialExpandLevel):-1;if(a=Math.min(a,e[0].maxLevel),r.expandLevel(a),t.slider){var s=$('
    ');s.width("200px"),s.slider({min:1,max:e[0].maxLevel,range:"min",value:a,slide:function(e,t){r.expandLevel(t.value)}}),n=[s].concat(t.controls)}return 0"),$.each(i,function(e,t){o.append($('
    "+t+""+s+""+e+"
    "+n[e]+""+n[s]+""+t+"').append(t))}),$('').append(o))),t.mountPoint.append(r),r},jsTreeTable:"1.0",register:function(n){$.each(this,function(e,t){"register"!=e&&(n[e]=t)})}}}(); ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/gulpfile.js ================================================ const gulp = require('gulp'); const plugins = require('gulp-load-plugins')(); const open = require('open'); const app = { srcPath: 'app/', // 源代码 devPath: 'tmp/', // 开发打包 prdPath: 'dist/' // 生产打包 }; const JS_LIBS = [ 'node_modules/angular-ui-router/release/angular-ui-router.js', 'node_modules/oclazyload/dist/ocLazyLoad.min.js', 'node_modules/angular-loading-bar/build/loading-bar.min.js', 'node_modules/angular-bootstrap/ui-bootstrap-tpls.min.js', 'node_modules/moment/moment.js', 'node_modules/angular-date-time-input/src/dateTimeInput.js', 'node_modules/angularjs-bootstrap-datetimepicker/src/js/datetimepicker.js', 'node_modules/angular-table-resize/dist/angular-table-resize.min.js', 'node_modules/angular-clipboard/angular-clipboard.js', 'node_modules/selectize/dist/js/standalone/selectize.js', 'node_modules/angular-selectize2/dist/selectize.js', 'node_modules/bootstrap-switch/dist/js/bootstrap-switch.min.js', 'node_modules/ng-dialog/js/ngDialog.js', 'node_modules/angular-ui-notification/dist/angular-ui-notification.min.js', 'node_modules/angular-utils-pagination/dirPagination.js', 'app/scripts/libs/treeTable.js', ]; const CSS_APP = [ 'node_modules/angular-loading-bar/build/loading-bar.min.css', 'node_modules/bootstrap-switch/dist/css/bootstrap3/bootstrap-switch.css', 'node_modules/ng-dialog/css/ngDialog.min.css', 'node_modules/ng-dialog/css/ngDialog-theme-default.css', 'node_modules/angularjs-bootstrap-datetimepicker/src/css/datetimepicker.css', 'node_modules/angular-ui-notification/dist/angular-ui-notification.min.css', 'node_modules/angular-table-resize/dist/angular-table-resize.css', 'node_modules/selectize/dist/css/selectize.css', 'app/styles/page.css', 'app/styles/timeline.css', 'app/styles/main.css' ]; const JS_APP = [ 'app/scripts/app.js', 'app/scripts/filters/filters.js', 'app/scripts/services/auth_service.js', 'app/scripts/services/appservice.js', 'app/scripts/services/flow_service_v1.js', 'app/scripts/services/flow_service_v2.js', 'app/scripts/services/degradeservice.js', 'app/scripts/services/systemservice.js', 'app/scripts/services/machineservice.js', 'app/scripts/services/identityservice.js', 'app/scripts/services/metricservice.js', 'app/scripts/services/param_flow_service.js', 'app/scripts/services/authority_service.js', 'app/scripts/services/cluster_state_service.js', ]; gulp.task('lib', function () { gulp.src(JS_LIBS) .pipe(plugins.concat('app.vendor.js')) .pipe(gulp.dest(app.devPath + 'js')) .pipe(plugins.uglify()) .pipe(gulp.dest(app.prdPath + 'js')) .pipe(plugins.connect.reload()); }); /* * css任务 * 在src下创建style文件夹,里面存放less文件。 */ gulp.task('css', function () { gulp.src(CSS_APP) .pipe(plugins.concat('app.css')) .pipe(gulp.dest(app.devPath + 'css')) .pipe(plugins.cssmin()) .pipe(gulp.dest(app.prdPath + 'css')) .pipe(plugins.connect.reload()); }); /* * js任务 * 在src目录下创建script文件夹,里面存放所有的js文件 */ gulp.task('js', function () { gulp.src(JS_APP) .pipe(plugins.concat('app.js')) .pipe(gulp.dest(app.devPath + 'js')) .pipe(plugins.uglify()) .pipe(gulp.dest(app.prdPath + 'js')) .pipe(plugins.connect.reload()); }); /* * js任务 * 在src目录下创建script文件夹,里面存放所有的js文件 */ gulp.task('jshint', function () { gulp.src(JS_APP) .pipe(plugins.jshint()) .pipe(plugins.jshint.reporter()); }); // 每次发布的时候,可能需要把之前目录内的内容清除,避免旧的文件对新的容有所影响。 需要在每次发布前删除dist和build目录 gulp.task('clean', function () { gulp.src([app.devPath, app.prdPath]) .pipe(plugins.clean()); }); // 总任务 gulp.task('build', ['clean', 'jshint', 'lib', 'js', 'css']); // 服务 gulp.task('serve', ['build'], function () { plugins.connect.server({ //启动一个服务器 root: [app.devPath], // 服务器从哪个路径开始读取,默认从开发路径读取 livereload: true, // 自动刷新 port: 1234 }); // 打开浏览器 setTimeout(() => { open('http://localhost:8080/index_dev.htm') }, 200); // 监听 gulp.watch(app.srcPath + '**/*.js', ['js']); gulp.watch(app.srcPath + '**/*.css', ['css']); }); // 定义default任务 gulp.task('default', ['serve']); ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/index.htm ================================================ Sentinel Dashboard
    ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/index_dev.htm ================================================ Sentinel 控制台
    ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/license-stat.csv ================================================ Type,Package,License npm,angular,MIT License npm,angular-animate,MIT License npm,angular-bootstrap,MIT License npm,angular-clipboard,MIT License npm,angular-cookies,MIT License npm,angular-date-time-input,MIT License npm,angular-loading-bar,MIT License npm,angular-mocks,MIT License npm,angular-resource,MIT License npm,angular-route,MIT License npm,angular-selectize2,MIT License npm,angular-table-resize,MIT License npm,angular-touch,MIT License npm,angular-ui-notification,MIT License npm,angular-ui-router,MIT License npm,angular-utils-pagination,MIT License npm,angularjs-bootstrap-datetimepicker,MIT License npm,bootstrap-switch,Apache License 2.0 npm,bootstrap-tagsinput,MIT License npm,moment,MIT License npm,ng-dialog,MIT License npm,ng-tags-input,MIT License npm,oclazyload,MIT License npm,selectize,Apache License 2.0 lib,jsTreeTable,MIT License ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/main/webapp/resources/package.json ================================================ { "name": "sentinel-dashboard", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo no test case", "build": "gulp build", "start": "gulp" }, "author": "x-cold ", "license": "MIT", "dependencies": { "angular": "^1.4.8", "angular-animate": "^1.4.0", "angular-bootstrap": "^0.12.2", "angular-clipboard": "^1.6.2", "angular-cookies": "^1.4.0", "angular-date-time-input": "^1.2.1", "angular-loading-bar": "^0.9.0", "angular-mocks": "^1.4.0", "angular-resource": "^1.4.0", "angular-route": "^1.4.0", "angular-selectize2": "^v1.2.3", "angular-table-resize": "^2.0.1", "angular-touch": "^1.4.0", "angular-ui-notification": "^0.3.6", "angular-ui-router": "^1.0.18", "angular-utils-pagination": "^0.11.1", "angularjs-bootstrap-datetimepicker": "^1.1.4", "bootstrap-switch": "^3.3.4", "bootstrap-tagsinput": "~0.7.1", "moment": "^2.12.0", "ng-dialog": "^0.6.6", "ng-tags-input": "~3.0.0", "oclazyload": "^1.1.0", "selectize": "^0.12.1", "lodash": ">=4.17.11" }, "devDependencies": { "gulp": "^3.9.1", "gulp-clean": "^0.4.0", "gulp-concat": "^2.6.1", "gulp-connect": "^5.7.0", "gulp-csscomb": "^3.0.8", "gulp-cssmin": "^0.2.0", "gulp-jshint": "^2.1.0", "gulp-load-plugins": "^1.5.0", "gulp-serv": "0.0.1", "gulp-uglify": "^3.0.0", "jshint": "^2.10.2", "open": "0.0.5", "source-map": "^0.7.3" } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/test/java/com/alibaba/csp/sentinel/dashboard/config/DashboardConfigTest.java ================================================ /* * 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 static org.junit.Assert.assertEquals; import org.junit.Rule; import org.junit.Test; import org.junit.contrib.java.lang.system.EnvironmentVariables; public class DashboardConfigTest { @Rule public final EnvironmentVariables environmentVariables = new EnvironmentVariables(); @Test public void testGetConfigStr() { // clear cache DashboardConfig.clearCache(); // if not set, return null assertEquals(null, DashboardConfig.getConfigStr("a")); // test property System.setProperty("a", "111"); assertEquals("111", DashboardConfig.getConfigStr("a")); // test env environmentVariables.set("a", "222"); // return value in cache assertEquals("111", DashboardConfig.getConfigStr("a")); // clear cache and then test DashboardConfig.clearCache(); assertEquals("222", DashboardConfig.getConfigStr("a")); } @Test public void testGetConfigInt() { // clear cache DashboardConfig.clearCache(); // default value assertEquals(0, DashboardConfig.getConfigInt("t", 0, 10)); DashboardConfig.clearCache(); assertEquals(1, DashboardConfig.getConfigInt("t", 1, 10)); // property, wrong format System.setProperty("t", "asdf"); DashboardConfig.clearCache(); assertEquals(0, DashboardConfig.getConfigInt("t", 0, 10)); System.setProperty("t", ""); DashboardConfig.clearCache(); assertEquals(0, DashboardConfig.getConfigInt("t", 0, 10)); // min value System.setProperty("t", "2"); DashboardConfig.clearCache(); assertEquals(2, DashboardConfig.getConfigInt("t", 0, 1)); DashboardConfig.clearCache(); assertEquals(10, DashboardConfig.getConfigInt("t", 0, 10)); DashboardConfig.clearCache(); assertEquals(2, DashboardConfig.getConfigInt("t", 0, -1)); // env environmentVariables.set("t", "20"); DashboardConfig.clearCache(); assertEquals(20, DashboardConfig.getConfigInt("t", 0, 10)); // wrong format env var, but it will override property environmentVariables.set("t", "20dddd"); DashboardConfig.clearCache(); assertEquals(0, DashboardConfig.getConfigInt("t", 0, 10)); // clear env, it will take property environmentVariables.set("t", ""); DashboardConfig.clearCache(); assertEquals(10, DashboardConfig.getConfigInt("t", 0, 10)); DashboardConfig.clearCache(); assertEquals(2, DashboardConfig.getConfigInt("t", 0, 1)); // enable cache System.setProperty("t", "666"); DashboardConfig.clearCache(); assertEquals(666, DashboardConfig.getConfigInt("t", 0, 1)); System.setProperty("t", "777"); assertEquals(666, DashboardConfig.getConfigInt("t", 0, 1)); System.setProperty("t", "555"); assertEquals(666, DashboardConfig.getConfigInt("t", 0, 1)); } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/test/java/com/alibaba/csp/sentinel/dashboard/datasource/entity/SentinelVersionTest.java ================================================ package com.alibaba.csp.sentinel.dashboard.datasource.entity; import static org.junit.Assert.*; import org.junit.Test; public class SentinelVersionTest { @Test public void testEqual() { assertEquals(new SentinelVersion(1, 0, 0), new SentinelVersion(1, 0, 0)); assertNotEquals(new SentinelVersion(1, 0, 0), new SentinelVersion(1, 2, 3)); assertNotEquals(new SentinelVersion(1, 0, 0), new SentinelVersion(1, 0, 0, "")); assertEquals(new SentinelVersion(1, 0, 0, ""), new SentinelVersion(1, 0, 0, "")); assertNotEquals(new SentinelVersion(1, 0, 0, ""), new SentinelVersion(1, 0, 0, null)); assertEquals(new SentinelVersion(1, 0, 0, null), new SentinelVersion(1, 0, 0, null)); } @Test public void testGreater() { assertTrue(new SentinelVersion(2, 0, 0).greaterThan(new SentinelVersion(1, 0, 0))); assertTrue(new SentinelVersion(1, 1, 0).greaterThan(new SentinelVersion(1, 0, 0))); assertTrue(new SentinelVersion(1, 1, 2).greaterThan(new SentinelVersion(1, 1, 0))); assertTrue(new SentinelVersion(1, 1, 4).greaterThan(new SentinelVersion(1, 1, 3))); assertFalse(new SentinelVersion(1, 0, 0).greaterThan(new SentinelVersion(1, 0, 0))); assertFalse(new SentinelVersion(1, 0, 0).greaterThan(new SentinelVersion(1, 1, 0))); assertFalse(new SentinelVersion(1, 1, 3).greaterThan(new SentinelVersion(1, 1, 3))); assertFalse(new SentinelVersion(1, 1, 2).greaterThan(new SentinelVersion(1, 1, 3))); assertFalse(new SentinelVersion(1, 0, 0, "").greaterThan(new SentinelVersion(1, 0, 0))); assertTrue(new SentinelVersion(1, 0, 1).greaterThan(new SentinelVersion(1, 0, 0))); assertTrue(new SentinelVersion(1, 0, 1, "a").greaterThan(new SentinelVersion(1, 0, 0, "b"))); assertFalse(new SentinelVersion(1, 0, 0, "b").greaterThan(new SentinelVersion(1, 0, 0, "a"))); } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/test/java/com/alibaba/csp/sentinel/dashboard/discovery/AppInfoTest.java ================================================ /* * Copyright 1999-2019 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.discovery; import java.util.ConcurrentModificationException; import java.util.Set; import org.junit.Test; import com.alibaba.csp.sentinel.dashboard.config.DashboardConfig; import static org.junit.Assert.*; public class AppInfoTest { @Test public void testConcurrentGetMachines() throws Exception { AppInfo appInfo = new AppInfo("testApp"); appInfo.addMachine(genMachineInfo("hostName1", "10.18.129.91")); appInfo.addMachine(genMachineInfo("hostName2", "10.18.129.92")); Set machines = appInfo.getMachines(); new Thread(() -> { try { for (MachineInfo m : machines) { System.out.println(m); try { Thread.sleep(200); } catch (InterruptedException e) { } } } catch (ConcurrentModificationException e) { e.printStackTrace(); fail(); } }).start(); Thread.sleep(100); try { appInfo.addMachine(genMachineInfo("hostName3", "10.18.129.93")); } catch (ConcurrentModificationException e) { e.printStackTrace(); fail(); } Thread.sleep(1000); } private MachineInfo genMachineInfo(String hostName, String ip) { MachineInfo machine = new MachineInfo(); machine.setApp("testApp"); machine.setHostname(hostName); machine.setIp(ip); machine.setPort(8719); machine.setVersion(String.valueOf(System.currentTimeMillis())); return machine; } @Test public void addRemoveMachineTest() { AppInfo appInfo = new AppInfo("default"); assertEquals("default", appInfo.getApp()); assertEquals(0, appInfo.getMachines().size()); //add one { MachineInfo machineInfo = new MachineInfo(); machineInfo.setApp("default"); machineInfo.setHostname("bogon"); machineInfo.setIp("127.0.0.1"); machineInfo.setPort(3389); machineInfo.setLastHeartbeat(System.currentTimeMillis()); machineInfo.setHeartbeatVersion(1); machineInfo.setVersion("0.4.1"); appInfo.addMachine(machineInfo); } assertEquals(1, appInfo.getMachines().size()); //add duplicated one { MachineInfo machineInfo = new MachineInfo(); machineInfo.setApp("default"); machineInfo.setHostname("bogon"); machineInfo.setIp("127.0.0.1"); machineInfo.setPort(3389); machineInfo.setLastHeartbeat(System.currentTimeMillis()); machineInfo.setHeartbeatVersion(1); machineInfo.setVersion("0.4.2"); appInfo.addMachine(machineInfo); } assertEquals(1, appInfo.getMachines().size()); //add different one { MachineInfo machineInfo = new MachineInfo(); machineInfo.setApp("default"); machineInfo.setHostname("bogon"); machineInfo.setIp("127.0.0.1"); machineInfo.setPort(3390); machineInfo.setLastHeartbeat(System.currentTimeMillis()); machineInfo.setHeartbeatVersion(1); machineInfo.setVersion("0.4.3"); appInfo.addMachine(machineInfo); } assertEquals(2, appInfo.getMachines().size()); appInfo.removeMachine("127.0.0.1", 3389); assertEquals(1, appInfo.getMachines().size()); appInfo.removeMachine("127.0.0.1", 3390); assertEquals(0, appInfo.getMachines().size()); } @Test public void testHealthyAndDead() { System.setProperty(DashboardConfig.CONFIG_HIDE_APP_NO_MACHINE_MILLIS, "60000"); System.setProperty(DashboardConfig.CONFIG_REMOVE_APP_NO_MACHINE_MILLIS, "600000"); DashboardConfig.clearCache(); String appName = "default"; AppInfo appInfo = new AppInfo(); appInfo.setApp(appName); { MachineInfo machineInfo = MachineInfo.of(appName, "127.0.0.1", 8801); machineInfo.setHeartbeatVersion(1); machineInfo.setLastHeartbeat(System.currentTimeMillis()); appInfo.addMachine(machineInfo); } assertTrue(appInfo.isShown()); assertFalse(appInfo.isDead()); { MachineInfo machineInfo = MachineInfo.of(appName, "127.0.0.1", 8801); machineInfo.setHeartbeatVersion(1); machineInfo.setLastHeartbeat(System.currentTimeMillis() - 70000); appInfo.addMachine(machineInfo); } assertFalse(appInfo.isShown()); assertFalse(appInfo.isDead()); { MachineInfo machineInfo = MachineInfo.of(appName, "127.0.0.1", 8801); machineInfo.setHeartbeatVersion(1); machineInfo.setLastHeartbeat(System.currentTimeMillis() - 700000); appInfo.addMachine(machineInfo); } assertFalse(appInfo.isShown()); assertTrue(appInfo.isDead()); } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/test/java/com/alibaba/csp/sentinel/dashboard/discovery/MachineInfoTest.java ================================================ /* * 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.discovery; import static org.junit.Assert.*; import org.junit.Test; import com.alibaba.csp.sentinel.dashboard.config.DashboardConfig; /** * @author Jason Joo */ public class MachineInfoTest { @Test public void testHealthyAndDead() { System.setProperty(DashboardConfig.CONFIG_UNHEALTHY_MACHINE_MILLIS, "60000"); System.setProperty(DashboardConfig.CONFIG_AUTO_REMOVE_MACHINE_MILLIS, "600000"); DashboardConfig.clearCache(); MachineInfo machineInfo = new MachineInfo(); machineInfo.setHeartbeatVersion(1); machineInfo.setLastHeartbeat(System.currentTimeMillis() - 10000); assertTrue(machineInfo.isHealthy()); assertFalse(machineInfo.isDead()); machineInfo.setLastHeartbeat(System.currentTimeMillis() - 100000); assertFalse(machineInfo.isHealthy()); assertFalse(machineInfo.isDead()); machineInfo.setLastHeartbeat(System.currentTimeMillis() - 1000000); assertFalse(machineInfo.isHealthy()); assertTrue(machineInfo.isDead()); } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/test/java/com/alibaba/csp/sentinel/dashboard/repository/metric/InMemoryMetricsRepositoryTest.java ================================================ /* * Copyright 1999-2019 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.repository.metric; import com.alibaba.csp.sentinel.dashboard.datasource.entity.MetricEntity; import org.assertj.core.util.Lists; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.springframework.util.CollectionUtils; import java.util.ConcurrentModificationException; import java.util.Date; import java.util.List; import java.util.concurrent.*; import static org.junit.Assert.*; /** * Test cases for {@link InMemoryMetricsRepository}. * * @author Nick Tan */ public class InMemoryMetricsRepositoryTest { private static final String DEFAULT_APP = "default"; private static final String DEFAULT_EXPIRE_APP = "default_expire_app"; private static final String DEFAULT_RESOURCE = "test"; private static final long EXPIRE_TIME = 1000 * 60 * 5L; private InMemoryMetricsRepository inMemoryMetricsRepository; private ExecutorService executorService; @Before public void setUp() throws Exception { inMemoryMetricsRepository = new InMemoryMetricsRepository(); executorService = Executors.newFixedThreadPool(8); } @After public void tearDown() { executorService.shutdownNow(); } private void testSave() { for (int i = 0; i < 1000000; i++) { MetricEntity entry = new MetricEntity(); entry.setApp(DEFAULT_APP); entry.setResource(DEFAULT_RESOURCE); entry.setTimestamp(new Date(System.currentTimeMillis())); entry.setPassQps(1L); entry.setExceptionQps(1L); entry.setBlockQps(0L); entry.setSuccessQps(1L); inMemoryMetricsRepository.save(entry); } } @Test public void testExpireMetric() { long now = System.currentTimeMillis(); MetricEntity expireEntry = new MetricEntity(); expireEntry.setApp(DEFAULT_EXPIRE_APP); expireEntry.setResource(DEFAULT_RESOURCE); expireEntry.setTimestamp(new Date(now - EXPIRE_TIME - 10L)); expireEntry.setPassQps(1L); expireEntry.setExceptionQps(1L); expireEntry.setBlockQps(0L); expireEntry.setSuccessQps(1L); inMemoryMetricsRepository.save(expireEntry); MetricEntity entry = new MetricEntity(); entry.setApp(DEFAULT_EXPIRE_APP); entry.setResource(DEFAULT_RESOURCE); entry.setTimestamp(new Date(now)); entry.setPassQps(1L); entry.setExceptionQps(1L); entry.setBlockQps(0L); entry.setSuccessQps(1L); inMemoryMetricsRepository.save(entry); List list = inMemoryMetricsRepository.queryByAppAndResourceBetween( DEFAULT_EXPIRE_APP, DEFAULT_RESOURCE, now - 2 * EXPIRE_TIME, now + EXPIRE_TIME); assertFalse(CollectionUtils.isEmpty(list)); assertEquals(1, list.size()); } @Test public void testListResourcesOfApp() { // prepare basic test data testSave(); System.out.println( "[" + System.currentTimeMillis() + "] Basic test data ready in testListResourcesOfApp"); List futures = Lists.newArrayList(); // concurrent query resources of app final CyclicBarrier cyclicBarrier = new CyclicBarrier(8); for (int j = 0; j < 10000; j++) { futures.add( CompletableFuture.runAsync(() -> { try { cyclicBarrier.await(); inMemoryMetricsRepository.listResourcesOfApp(DEFAULT_APP); } catch (InterruptedException | BrokenBarrierException e) { e.printStackTrace(); } }, executorService) ); } // batch add metric entity for (int i = 0; i < 10000; i++) { MetricEntity entry = new MetricEntity(); entry.setApp(DEFAULT_APP); entry.setResource(DEFAULT_RESOURCE); entry.setTimestamp(new Date(System.currentTimeMillis() - EXPIRE_TIME - 1000L)); entry.setPassQps(1L); entry.setExceptionQps(1L); entry.setBlockQps(0L); entry.setSuccessQps(1L); inMemoryMetricsRepository.save(entry); } CompletableFuture all = CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])); try { all.get(10, TimeUnit.SECONDS); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.getCause().printStackTrace(); if (e.getCause() instanceof ConcurrentModificationException) { fail("concurrent error occurred"); } else { fail("unexpected exception"); } } catch (TimeoutException e) { fail("allOf future timeout"); } } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/test/java/com/alibaba/csp/sentinel/dashboard/rule/apollo/ApolloConfig.java ================================================ /* * 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.rule.apollo; import java.util.List; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity; import com.alibaba.csp.sentinel.datasource.Converter; import com.alibaba.fastjson.JSON; import com.ctrip.framework.apollo.openapi.client.ApolloOpenApiClient; /** * @author hantianwei@gmail.com * @since 1.5.0 */ @Configuration public class ApolloConfig { @Bean public Converter, String> flowRuleEntityEncoder() { return JSON::toJSONString; } @Bean public Converter> flowRuleEntityDecoder() { return s -> JSON.parseArray(s, FlowRuleEntity.class); } @Bean public ApolloOpenApiClient apolloOpenApiClient() { ApolloOpenApiClient client = ApolloOpenApiClient.newBuilder() .withPortalUrl("http://localhost:10034") .withToken("token") .build(); return client; } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/test/java/com/alibaba/csp/sentinel/dashboard/rule/apollo/ApolloConfigUtil.java ================================================ /* * 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.rule.apollo; /** * @author hantianwei@gmail.com * @since 1.5.0 */ public final class ApolloConfigUtil { public static final String FLOW_DATA_ID_POSTFIX = "-flow-rules"; private ApolloConfigUtil() { } public static String getFlowDataId(String appName) { return String.format("%s%s", appName, FLOW_DATA_ID_POSTFIX); } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/test/java/com/alibaba/csp/sentinel/dashboard/rule/apollo/FlowRuleApolloProvider.java ================================================ /* * 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.rule.apollo; import java.util.ArrayList; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity; import com.alibaba.csp.sentinel.dashboard.rule.DynamicRuleProvider; import com.alibaba.csp.sentinel.datasource.Converter; import com.alibaba.csp.sentinel.util.StringUtil; import com.ctrip.framework.apollo.openapi.client.ApolloOpenApiClient; import com.ctrip.framework.apollo.openapi.dto.OpenItemDTO; import com.ctrip.framework.apollo.openapi.dto.OpenNamespaceDTO; /** * @author hantianwei@gmail.com * @since 1.5.0 */ @Component("flowRuleApolloProvider") public class FlowRuleApolloProvider implements DynamicRuleProvider> { @Autowired private ApolloOpenApiClient apolloOpenApiClient; @Autowired private Converter> converter; @Override public List getRules(String appName) throws Exception { String appId = "appId"; String flowDataId = ApolloConfigUtil.getFlowDataId(appName); OpenNamespaceDTO openNamespaceDTO = apolloOpenApiClient.getNamespace(appId, "DEV", "default", "application"); String rules = openNamespaceDTO .getItems() .stream() .filter(p -> p.getKey().equals(flowDataId)) .map(OpenItemDTO::getValue) .findFirst() .orElse(""); if (StringUtil.isEmpty(rules)) { return new ArrayList<>(); } return converter.convert(rules); } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/test/java/com/alibaba/csp/sentinel/dashboard/rule/apollo/FlowRuleApolloPublisher.java ================================================ /* * 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.rule.apollo; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity; import com.alibaba.csp.sentinel.dashboard.rule.DynamicRulePublisher; import com.alibaba.csp.sentinel.datasource.Converter; import com.alibaba.csp.sentinel.util.AssertUtil; import com.ctrip.framework.apollo.openapi.client.ApolloOpenApiClient; import com.ctrip.framework.apollo.openapi.dto.NamespaceReleaseDTO; import com.ctrip.framework.apollo.openapi.dto.OpenItemDTO; /** * @author hantianwei@gmail.com * @since 1.5.0 */ @Component("flowRuleApolloPublisher") public class FlowRuleApolloPublisher implements DynamicRulePublisher> { @Autowired private ApolloOpenApiClient apolloOpenApiClient; @Autowired private Converter, String> converter; @Override public void publish(String app, List rules) throws Exception { AssertUtil.notEmpty(app, "app name cannot be empty"); if (rules == null) { return; } // Increase the configuration String appId = "appId"; String flowDataId = ApolloConfigUtil.getFlowDataId(app); OpenItemDTO openItemDTO = new OpenItemDTO(); openItemDTO.setKey(flowDataId); openItemDTO.setValue(converter.convert(rules)); openItemDTO.setComment("Program auto-join"); openItemDTO.setDataChangeCreatedBy("some-operator"); apolloOpenApiClient.createOrUpdateItem(appId, "DEV", "default", "application", openItemDTO); // Release configuration NamespaceReleaseDTO namespaceReleaseDTO = new NamespaceReleaseDTO(); namespaceReleaseDTO.setEmergencyPublish(true); namespaceReleaseDTO.setReleaseComment("Modify or add configurations"); namespaceReleaseDTO.setReleasedBy("some-operator"); namespaceReleaseDTO.setReleaseTitle("Modify or add configurations"); apolloOpenApiClient.publishNamespace(appId, "DEV", "default", "application", namespaceReleaseDTO); } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/test/java/com/alibaba/csp/sentinel/dashboard/rule/nacos/FlowRuleNacosProvider.java ================================================ /* * 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.rule.nacos; import java.util.ArrayList; import java.util.List; import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity; import com.alibaba.csp.sentinel.dashboard.rule.DynamicRuleProvider; import com.alibaba.csp.sentinel.datasource.Converter; import com.alibaba.csp.sentinel.util.StringUtil; import com.alibaba.nacos.api.config.ConfigService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; /** * @author Eric Zhao * @since 1.4.0 */ @Component("flowRuleNacosProvider") public class FlowRuleNacosProvider implements DynamicRuleProvider> { @Autowired private ConfigService configService; @Autowired private Converter> converter; @Override public List getRules(String appName) throws Exception { String rules = configService.getConfig(appName + NacosConfigUtil.FLOW_DATA_ID_POSTFIX, NacosConfigUtil.GROUP_ID, 3000); if (StringUtil.isEmpty(rules)) { return new ArrayList<>(); } return converter.convert(rules); } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/test/java/com/alibaba/csp/sentinel/dashboard/rule/nacos/FlowRuleNacosPublisher.java ================================================ /* * 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.rule.nacos; import java.util.List; import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity; import com.alibaba.csp.sentinel.dashboard.rule.DynamicRulePublisher; import com.alibaba.csp.sentinel.datasource.Converter; import com.alibaba.csp.sentinel.util.AssertUtil; import com.alibaba.nacos.api.config.ConfigService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; /** * @author Eric Zhao * @since 1.4.0 */ @Component("flowRuleNacosPublisher") public class FlowRuleNacosPublisher implements DynamicRulePublisher> { @Autowired private ConfigService configService; @Autowired private Converter, String> converter; @Override public void publish(String app, List rules) throws Exception { AssertUtil.notEmpty(app, "app name cannot be empty"); if (rules == null) { return; } configService.publishConfig(app + NacosConfigUtil.FLOW_DATA_ID_POSTFIX, NacosConfigUtil.GROUP_ID, converter.convert(rules)); } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/test/java/com/alibaba/csp/sentinel/dashboard/rule/nacos/NacosConfig.java ================================================ /* * 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.rule.nacos; import java.util.List; import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity; import com.alibaba.csp.sentinel.datasource.Converter; import com.alibaba.fastjson.JSON; import com.alibaba.nacos.api.config.ConfigFactory; import com.alibaba.nacos.api.config.ConfigService; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * @author Eric Zhao * @since 1.4.0 */ @Configuration public class NacosConfig { @Bean public Converter, String> flowRuleEntityEncoder() { return JSON::toJSONString; } @Bean public Converter> flowRuleEntityDecoder() { return s -> JSON.parseArray(s, FlowRuleEntity.class); } @Bean public ConfigService nacosConfigService() throws Exception { return ConfigFactory.createConfigService("localhost"); } } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/test/java/com/alibaba/csp/sentinel/dashboard/rule/nacos/NacosConfigUtil.java ================================================ /* * 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.rule.nacos; /** * @author Eric Zhao * @since 1.4.0 */ public final class NacosConfigUtil { public static final String GROUP_ID = "SENTINEL_GROUP"; public static final String FLOW_DATA_ID_POSTFIX = "-flow-rules"; public static final String PARAM_FLOW_DATA_ID_POSTFIX = "-param-rules"; public static final String CLUSTER_MAP_DATA_ID_POSTFIX = "-cluster-map"; /** * cc for `cluster-client` */ public static final String CLIENT_CONFIG_DATA_ID_POSTFIX = "-cc-config"; /** * cs for `cluster-server` */ public static final String SERVER_TRANSPORT_CONFIG_DATA_ID_POSTFIX = "-cs-transport-config"; public static final String SERVER_FLOW_CONFIG_DATA_ID_POSTFIX = "-cs-flow-config"; public static final String SERVER_NAMESPACE_SET_DATA_ID_POSTFIX = "-cs-namespace-set"; private NacosConfigUtil() {} } ================================================ FILE: 4-Finchley/alibaba-sentinel-dashboard-nacos/src/test/java/com/alibaba/csp/sentinel/dashboard/util/VersionUtilsTest.java ================================================ package com.alibaba.csp.sentinel.dashboard.util; import static org.junit.Assert.*; import java.util.Optional; import org.junit.Test; import com.alibaba.csp.sentinel.dashboard.datasource.entity.SentinelVersion; public class VersionUtilsTest { @Test public void test() { Optional version = VersionUtils.parseVersion("1.2.3"); assertTrue(version.isPresent()); assertEquals(1, version.get().getMajorVersion()); assertEquals(2, version.get().getMinorVersion()); assertEquals(3, version.get().getFixVersion()); assertNull(version.get().getPostfix()); version = VersionUtils.parseVersion("1.2"); assertTrue(version.isPresent()); assertEquals(1, version.get().getMajorVersion()); assertEquals(2, version.get().getMinorVersion()); assertEquals(0, version.get().getFixVersion()); assertNull(version.get().getPostfix()); version = VersionUtils.parseVersion("1."); assertTrue(version.isPresent()); assertEquals(1, version.get().getMajorVersion()); assertEquals(0, version.get().getMinorVersion()); assertEquals(0, version.get().getFixVersion()); assertNull(version.get().getPostfix()); version = VersionUtils.parseVersion("1.2."); assertTrue(version.isPresent()); assertEquals(1, version.get().getMajorVersion()); assertEquals(2, version.get().getMinorVersion()); assertEquals(0, version.get().getFixVersion()); assertNull(version.get().getPostfix()); version = VersionUtils.parseVersion("1.2.3."); assertTrue(version.isPresent()); assertEquals(1, version.get().getMajorVersion()); assertEquals(2, version.get().getMinorVersion()); assertEquals(3, version.get().getFixVersion()); assertNull(version.get().getPostfix()); version = VersionUtils.parseVersion("1.2.3.4"); assertTrue(version.isPresent()); assertEquals(1, version.get().getMajorVersion()); assertEquals(2, version.get().getMinorVersion()); assertEquals(3, version.get().getFixVersion()); assertNull(version.get().getPostfix()); version = VersionUtils.parseVersion("1"); assertTrue(version.isPresent()); assertEquals(1, version.get().getMajorVersion()); assertEquals(0, version.get().getMinorVersion()); assertEquals(0, version.get().getFixVersion()); assertNull(version.get().getPostfix()); version = VersionUtils.parseVersion("1.2.3-"); assertTrue(version.isPresent()); assertEquals(1, version.get().getMajorVersion()); assertEquals(2, version.get().getMinorVersion()); assertEquals(3, version.get().getFixVersion()); assertNull(version.get().getPostfix()); version = VersionUtils.parseVersion("-"); assertFalse(version.isPresent()); version = VersionUtils.parseVersion("-t"); assertFalse(version.isPresent()); version = VersionUtils.parseVersion(""); assertFalse(version.isPresent()); version = VersionUtils.parseVersion(null); assertFalse(version.isPresent()); version = VersionUtils.parseVersion("1.2.3-SNAPSHOTS"); assertTrue(version.isPresent()); assertEquals(1, version.get().getMajorVersion()); assertEquals(2, version.get().getMinorVersion()); assertEquals(3, version.get().getFixVersion()); assertEquals("SNAPSHOTS", version.get().getPostfix()); } } ================================================ FILE: 4-Finchley/alibaba-sentinel-datasource-apollo/pom.xml ================================================ 4.0.0 com.didispace alibaba-sentinel-datasource-apollo 0.0.1-SNAPSHOT jar org.springframework.boot spring-boot-starter-parent 2.0.5.RELEASE UTF-8 UTF-8 1.8 org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-alibaba-sentinel com.alibaba.csp sentinel-datasource-apollo 1.5.2 org.projectlombok lombok 1.18.2 true org.springframework.boot spring-boot-starter-test test org.springframework.cloud spring-cloud-dependencies Finchley.SR1 pom import org.springframework.cloud spring-cloud-alibaba-dependencies 0.2.2.RELEASE pom import org.springframework.boot spring-boot-maven-plugin ================================================ FILE: 4-Finchley/alibaba-sentinel-datasource-apollo/src/main/java/com/didispace/alibaba/sentinel/TestApplication.java ================================================ package com.didispace.alibaba.sentinel; import com.ctrip.framework.apollo.spring.annotation.EnableApolloConfig; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @EnableApolloConfig @SpringBootApplication public class TestApplication { public static void main(String[] args) { SpringApplication.run(TestApplication.class, args); } @Slf4j @RestController static class TestController { @GetMapping("/hello") public String hello() { return "didispace.com"; } } } ================================================ FILE: 4-Finchley/alibaba-sentinel-datasource-apollo/src/main/resources/apollo-env.properties ================================================ local.meta=http://localhost:8080 dev.meta=http://localhost:8080 ================================================ FILE: 4-Finchley/alibaba-sentinel-datasource-apollo/src/main/resources/application.properties ================================================ spring.application.name=sentinel-datasource-apollo server.port=8002 # apollo config app.id=${spring.application.name} # sentinel dashboard spring.cloud.sentinel.transport.dashboard=localhost:8080 # sentinel datasource apollo spring.cloud.sentinel.datasource.ds.apollo.namespaceName=application spring.cloud.sentinel.datasource.ds.apollo.flowRulesKey=sentinel.flowRules spring.cloud.sentinel.datasource.ds.apollo.rule-type=flow ================================================ FILE: 4-Finchley/alibaba-sentinel-datasource-nacos/pom.xml ================================================ 4.0.0 com.didispace alibaba-sentinel-datasource-nacos 0.0.1-SNAPSHOT jar org.springframework.boot spring-boot-starter-parent 2.0.5.RELEASE UTF-8 UTF-8 1.8 org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-alibaba-sentinel com.alibaba.csp sentinel-datasource-nacos 1.5.2 org.projectlombok lombok 1.18.2 true org.springframework.boot spring-boot-starter-test test org.springframework.cloud spring-cloud-dependencies Finchley.SR1 pom import org.springframework.cloud spring-cloud-alibaba-dependencies 0.2.2.RELEASE pom import org.springframework.boot spring-boot-maven-plugin ================================================ FILE: 4-Finchley/alibaba-sentinel-datasource-nacos/src/main/java/com/didispace/alibaba/sentinel/TestApplication.java ================================================ package com.didispace.alibaba.sentinel; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @SpringBootApplication public class TestApplication { public static void main(String[] args) { SpringApplication.run(TestApplication.class, args); } @Slf4j @RestController static class TestController { @GetMapping("/hello") public String hello() { return "didispace.com"; } } } ================================================ FILE: 4-Finchley/alibaba-sentinel-datasource-nacos/src/main/resources/application.properties ================================================ spring.application.name=alibaba-sentinel-datasource-nacos server.port=8003 # sentinel dashboard spring.cloud.sentinel.transport.dashboard=localhost:8080 # sentinel datasource nacos :http://blog.didispace.com/spring-cloud-alibaba-sentinel-2-1/ spring.cloud.sentinel.datasource.ds.nacos.server-addr=localhost:8848 spring.cloud.sentinel.datasource.ds.nacos.groupId=DEFAULT_GROUP spring.cloud.sentinel.datasource.ds.nacos.dataId=${spring.application.name}-sentinel spring.cloud.sentinel.datasource.ds.nacos.rule-type=flow ================================================ FILE: 4-Finchley/alibaba-sentinel-rate-limiting/pom.xml ================================================ 4.0.0 com.didispace alibaba-sentinel-rate-limiting 0.0.1-SNAPSHOT jar org.springframework.boot spring-boot-starter-parent 2.0.5.RELEASE UTF-8 UTF-8 1.8 org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-alibaba-sentinel org.projectlombok lombok 1.18.2 true org.springframework.boot spring-boot-starter-test test org.springframework.cloud spring-cloud-dependencies Finchley.SR1 pom import org.springframework.cloud spring-cloud-alibaba-dependencies 0.2.2.RELEASE pom import org.springframework.boot spring-boot-maven-plugin ================================================ FILE: 4-Finchley/alibaba-sentinel-rate-limiting/src/main/java/com/didispace/alibaba/sentinel/TestApplication.java ================================================ package com.didispace.alibaba.sentinel; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @SpringBootApplication public class TestApplication { public static void main(String[] args) { SpringApplication.run(TestApplication.class, args); } @Slf4j @RestController static class TestController { @GetMapping("/hello") public String hello() { return "didispace.com"; } } } ================================================ FILE: 4-Finchley/alibaba-sentinel-rate-limiting/src/main/resources/application.properties ================================================ spring.application.name=alibaba-sentinel-rate-limiting server.port=8001 # sentinel dashboard spring.cloud.sentinel.transport.dashboard=localhost:8080 ================================================ FILE: 4-Finchley/pom.xml ================================================ 4.0.0 com.didispace spring-cloud-finchley pom 1.0 alibaba-nacos-discovery-server alibaba-nacos-discovery-client-common alibaba-nacos-config-client alibaba-nacos-discovery-client-resttemplate alibaba-nacos-discovery-client-webclient alibaba-nacos-discovery-client-feign alibaba-sentinel-rate-limiting alibaba-sentinel-datasource-nacos alibaba-sentinel-datasource-apollo alibaba-sentinel-dashboard-apollo alibaba-sentinel-dashboard-nacos alibaba-sentinel-annotation alibaba-dubbo-api alibaba-dubbo-server alibaba-dubbo-client stream-consumer-self stream-exception-handler-1 stream-exception-handler-2 stream-exception-handler-3 stream-exception-handler-4 stream-content-route stream-delayed-message ================================================ FILE: 4-Finchley/stream-consumer-self/pom.xml ================================================ 4.0.0 com.didispace stream-consumer-self 0.0.1-SNAPSHOT jar org.springframework.boot spring-boot-starter-parent 2.0.5.RELEASE UTF-8 UTF-8 1.8 org.projectlombok lombok 1.18.2 org.springframework.boot spring-boot-starter-test test org.springframework.cloud spring-cloud-starter-stream-rabbit org.springframework.boot spring-boot-starter-actuator org.springframework.cloud spring-cloud-dependencies Finchley.SR1 pom import org.springframework.boot spring-boot-maven-plugin ================================================ FILE: 4-Finchley/stream-consumer-self/src/main/java/com/didispace/stream/TestApplication.java ================================================ package com.didispace.stream; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.stream.annotation.EnableBinding; @EnableBinding(TestTopic.class) @SpringBootApplication public class TestApplication { public static void main(String[] args) { SpringApplication.run(TestApplication.class, args); } } ================================================ FILE: 4-Finchley/stream-consumer-self/src/main/java/com/didispace/stream/TestController.java ================================================ package com.didispace.stream; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.integration.support.MessageBuilder; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @Slf4j @RestController public class TestController { @Autowired private TestTopic testTopic; @GetMapping("/sendMessage") public String messageWithMQ(@RequestParam String message) { testTopic.output().send(MessageBuilder.withPayload(message).build()); return "ok"; } } ================================================ FILE: 4-Finchley/stream-consumer-self/src/main/java/com/didispace/stream/TestListener.java ================================================ package com.didispace.stream; import lombok.extern.slf4j.Slf4j; import org.springframework.cloud.stream.annotation.StreamListener; import org.springframework.stereotype.Component; @Slf4j @Component public class TestListener { @StreamListener(TestTopic.INPUT) public void receive(String payload) { log.info("Received: " + payload); } } ================================================ FILE: 4-Finchley/stream-consumer-self/src/main/java/com/didispace/stream/TestTopic.java ================================================ package com.didispace.stream; import org.springframework.cloud.stream.annotation.Input; import org.springframework.cloud.stream.annotation.Output; import org.springframework.messaging.MessageChannel; import org.springframework.messaging.SubscribableChannel; public interface TestTopic { String OUTPUT = "example-topic-output"; String INPUT = "example-topic-input"; @Output(OUTPUT) MessageChannel output(); @Input(INPUT) SubscribableChannel input(); } ================================================ FILE: 4-Finchley/stream-consumer-self/src/main/resources/application.properties ================================================ spring.application.name=stream-consumer-self server.port=8080 spring.cloud.stream.bindings.example-topic-input.destination=aaa-topic spring.cloud.stream.bindings.example-topic-output.destination=aaa-topic ================================================ FILE: 4-Finchley/stream-content-route/pom.xml ================================================ 4.0.0 com.didispace stream-content-route 0.0.1-SNAPSHOT jar org.springframework.boot spring-boot-starter-parent 2.0.5.RELEASE UTF-8 UTF-8 1.8 org.projectlombok lombok 1.18.2 org.springframework.boot spring-boot-starter-test test org.springframework.cloud spring-cloud-starter-stream-rabbit org.springframework.boot spring-boot-starter-actuator org.springframework.cloud spring-cloud-dependencies Finchley.SR1 pom import org.springframework.boot spring-boot-maven-plugin ================================================ FILE: 4-Finchley/stream-content-route/src/main/java/com/didispace/stream/TestApplication.java ================================================ package com.didispace.stream; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.stream.annotation.EnableBinding; import org.springframework.cloud.stream.annotation.Input; import org.springframework.cloud.stream.annotation.Output; import org.springframework.cloud.stream.annotation.StreamListener; import org.springframework.integration.support.MessageBuilder; import org.springframework.messaging.MessageChannel; import org.springframework.messaging.SubscribableChannel; import org.springframework.messaging.handler.annotation.Header; import org.springframework.stereotype.Component; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @EnableBinding(TestApplication.TestTopic.class) @SpringBootApplication public class TestApplication { public static void main(String[] args) { SpringApplication.run(TestApplication.class, args); } @RestController static class TestController { @Autowired private TestTopic testTopic; /** * 消息生产接口 * * @param message * @return */ @GetMapping("/sendMessage") public String messageWithMQ(@RequestParam String message) { testTopic.output().send(MessageBuilder.withPayload(message).setHeader("version", "1.0").build()); testTopic.output().send(MessageBuilder.withPayload(message).setHeader("version", "2.0").build()); return "ok"; } } /** * 消息消费逻辑 */ @Slf4j @Component static class TestListener { @StreamListener(value = TestTopic.INPUT, condition = "headers['version']=='1.0'") public void receiveV1(String payload, @Header("version") String version) { log.info("Received v1 : " + payload + ", " + version); } @StreamListener(value = TestTopic.INPUT, condition = "headers['version']=='2.0'") public void receiveV2(String payload, @Header("version") String version) { log.info("Received v2 : " + payload + ", " + version); } } interface TestTopic { String OUTPUT = "example-topic-output"; String INPUT = "example-topic-input"; @Output(OUTPUT) MessageChannel output(); @Input(INPUT) SubscribableChannel input(); } } ================================================ FILE: 4-Finchley/stream-content-route/src/main/resources/application.properties ================================================ spring.application.name=stream-content-route server.port=8080 spring.cloud.stream.bindings.example-topic-input.destination=test-topic spring.cloud.stream.bindings.example-topic-input.group=stream-content-route spring.cloud.stream.bindings.example-topic-output.destination=test-topic ================================================ FILE: 4-Finchley/stream-delayed-message/pom.xml ================================================ 4.0.0 com.didispace stream-delayed-message 0.0.1-SNAPSHOT jar org.springframework.boot spring-boot-starter-parent 2.0.5.RELEASE UTF-8 UTF-8 1.8 org.projectlombok lombok 1.18.2 org.springframework.boot spring-boot-starter-test test org.springframework.cloud spring-cloud-starter-stream-rabbit org.springframework.boot spring-boot-starter-actuator org.springframework.cloud spring-cloud-dependencies Finchley.SR1 pom import org.springframework.boot spring-boot-maven-plugin ================================================ FILE: 4-Finchley/stream-delayed-message/src/main/java/com/didispace/stream/TestApplication.java ================================================ package com.didispace.stream; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.stream.annotation.EnableBinding; import org.springframework.cloud.stream.annotation.Input; import org.springframework.cloud.stream.annotation.Output; import org.springframework.cloud.stream.annotation.StreamListener; import org.springframework.integration.support.MessageBuilder; import org.springframework.messaging.MessageChannel; import org.springframework.messaging.SubscribableChannel; import org.springframework.stereotype.Component; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @EnableBinding(TestApplication.TestTopic.class) @SpringBootApplication public class TestApplication { public static void main(String[] args) { SpringApplication.run(TestApplication.class, args); } @Slf4j @RestController static class TestController { @Autowired private TestTopic testTopic; /** * 消息生产接口 * * @param message * @return */ @GetMapping("/sendMessage") public String messageWithMQ(@RequestParam String message) { log.info("Send: " + message); testTopic.output().send(MessageBuilder.withPayload(message).setHeader("x-delay", 5000).build()); return "ok"; } } /** * 消息消费逻辑 */ @Slf4j @Component static class TestListener { @StreamListener(TestTopic.INPUT) public void receive(String payload) { log.info("Received: " + payload); } } interface TestTopic { String OUTPUT = "example-topic-output"; String INPUT = "example-topic-input"; @Output(OUTPUT) MessageChannel output(); @Input(INPUT) SubscribableChannel input(); } } ================================================ FILE: 4-Finchley/stream-delayed-message/src/main/resources/application.properties ================================================ spring.application.name=stream-delayed-message server.port=8080 spring.cloud.stream.bindings.example-topic-input.destination=delay-topic spring.cloud.stream.bindings.example-topic-input.group=test spring.cloud.stream.rabbit.bindings.example-topic-input.consumer.delayed-exchange=true spring.cloud.stream.bindings.example-topic-output.destination=delay-topic spring.cloud.stream.rabbit.bindings.example-topic-output.producer.delayed-exchange=true ================================================ FILE: 4-Finchley/stream-exception-handler-1/pom.xml ================================================ 4.0.0 com.didispace stream-exception-handler-1 0.0.1-SNAPSHOT jar org.springframework.boot spring-boot-starter-parent 2.0.5.RELEASE UTF-8 UTF-8 1.8 org.projectlombok lombok 1.18.2 org.springframework.boot spring-boot-starter-test test org.springframework.cloud spring-cloud-starter-stream-rabbit org.springframework.boot spring-boot-starter-actuator org.springframework.cloud spring-cloud-dependencies Finchley.SR1 pom import org.springframework.boot spring-boot-maven-plugin ================================================ FILE: 4-Finchley/stream-exception-handler-1/src/main/java/com/didispace/stream/TestApplication.java ================================================ package com.didispace.stream; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.stream.annotation.EnableBinding; import org.springframework.cloud.stream.annotation.Input; import org.springframework.cloud.stream.annotation.Output; import org.springframework.cloud.stream.annotation.StreamListener; import org.springframework.integration.support.MessageBuilder; import org.springframework.messaging.MessageChannel; import org.springframework.messaging.SubscribableChannel; import org.springframework.stereotype.Component; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @EnableBinding(TestApplication.TestTopic.class) @SpringBootApplication public class TestApplication { public static void main(String[] args) { SpringApplication.run(TestApplication.class, args); } @RestController static class TestController { @Autowired private TestTopic testTopic; /** * 消息生产接口 * * @param message * @return */ @GetMapping("/sendMessage") public String messageWithMQ(@RequestParam String message) { testTopic.output().send(MessageBuilder.withPayload(message).build()); return "ok"; } } /** * 消息消费逻辑 */ @Slf4j @Component static class TestListener { int counter = 1; @StreamListener(TestTopic.INPUT) public void receive(String payload) { log.info("Received: " + payload + ", " + counter); throw new RuntimeException("Message consumer failed!"); // 计数,模拟重试过程中成功消费 // if (counter == 3) { // counter = 1; // return; // } else { // counter++; // throw new RuntimeException("Message consumer failed!"); // } } } interface TestTopic { String OUTPUT = "example-topic-output"; String INPUT = "example-topic-input"; @Output(OUTPUT) MessageChannel output(); @Input(INPUT) SubscribableChannel input(); } } ================================================ FILE: 4-Finchley/stream-exception-handler-1/src/main/resources/application.properties ================================================ spring.application.name=stream-exception-handler-1 server.port=8080 spring.cloud.stream.bindings.example-topic-input.destination=test-topic spring.cloud.stream.bindings.example-topic-input.consumer.max-attempts=1 spring.cloud.stream.bindings.example-topic-output.destination=test-topic ================================================ FILE: 4-Finchley/stream-exception-handler-2/pom.xml ================================================ 4.0.0 com.didispace stream-exception-handler-2 0.0.1-SNAPSHOT jar org.springframework.boot spring-boot-starter-parent 2.0.5.RELEASE UTF-8 UTF-8 1.8 org.projectlombok lombok 1.18.2 org.springframework.boot spring-boot-starter-test test org.springframework.cloud spring-cloud-starter-stream-rabbit org.springframework.boot spring-boot-starter-actuator org.springframework.cloud spring-cloud-dependencies Finchley.SR1 pom import org.springframework.boot spring-boot-maven-plugin ================================================ FILE: 4-Finchley/stream-exception-handler-2/src/main/java/com/didispace/stream/TestApplication.java ================================================ package com.didispace.stream; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.stream.annotation.EnableBinding; import org.springframework.cloud.stream.annotation.Input; import org.springframework.cloud.stream.annotation.Output; import org.springframework.cloud.stream.annotation.StreamListener; import org.springframework.integration.annotation.ServiceActivator; import org.springframework.integration.support.MessageBuilder; import org.springframework.messaging.Message; import org.springframework.messaging.MessageChannel; import org.springframework.messaging.SubscribableChannel; import org.springframework.stereotype.Component; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @EnableBinding(TestApplication.TestTopic.class) @SpringBootApplication public class TestApplication { public static void main(String[] args) { SpringApplication.run(TestApplication.class, args); } @RestController static class TestController { @Autowired private TestTopic testTopic; /** * 消息生产接口 * * @param message * @return */ @GetMapping("/sendMessage") public String messageWithMQ(@RequestParam String message) { testTopic.output().send(MessageBuilder.withPayload(message).build()); return "ok"; } } /** * 消息消费逻辑 */ @Slf4j @Component static class TestListener { @StreamListener(TestTopic.INPUT) public void receive(String payload) { log.info("Received payload : " + payload); throw new RuntimeException("Message consumer failed!"); } /** * 消息消费失败的降级处理逻辑 * * @param message */ @ServiceActivator(inputChannel = "test-topic.stream-exception-handler.errors") public void error(Message message) { log.info("Message consumer failed, call fallback!"); } } interface TestTopic { String OUTPUT = "example-topic-output"; String INPUT = "example-topic-input"; @Output(OUTPUT) MessageChannel output(); @Input(INPUT) SubscribableChannel input(); } } ================================================ FILE: 4-Finchley/stream-exception-handler-2/src/main/resources/application.properties ================================================ spring.application.name=stream-exception-handler-2 server.port=8080 spring.cloud.stream.bindings.example-topic-input.destination=test-topic spring.cloud.stream.bindings.example-topic-input.group=stream-exception-handler spring.cloud.stream.bindings.example-topic-input.consumer.max-attempts=1 spring.cloud.stream.bindings.example-topic-output.destination=test-topic ================================================ FILE: 4-Finchley/stream-exception-handler-3/pom.xml ================================================ 4.0.0 com.didispace stream-exception-handler-3 0.0.1-SNAPSHOT jar org.springframework.boot spring-boot-starter-parent 2.0.5.RELEASE UTF-8 UTF-8 1.8 org.projectlombok lombok 1.18.2 org.springframework.boot spring-boot-starter-test test org.springframework.cloud spring-cloud-starter-stream-rabbit org.springframework.boot spring-boot-starter-actuator org.springframework.cloud spring-cloud-dependencies Finchley.SR1 pom import org.springframework.boot spring-boot-maven-plugin ================================================ FILE: 4-Finchley/stream-exception-handler-3/src/main/java/com/didispace/stream/TestApplication.java ================================================ package com.didispace.stream; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.stream.annotation.EnableBinding; import org.springframework.cloud.stream.annotation.Input; import org.springframework.cloud.stream.annotation.Output; import org.springframework.cloud.stream.annotation.StreamListener; import org.springframework.integration.support.MessageBuilder; import org.springframework.messaging.MessageChannel; import org.springframework.messaging.SubscribableChannel; import org.springframework.stereotype.Component; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @EnableBinding(TestApplication.TestTopic.class) @SpringBootApplication public class TestApplication { public static void main(String[] args) { SpringApplication.run(TestApplication.class, args); } @RestController static class TestController { @Autowired private TestTopic testTopic; /** * 消息生产接口 * * @param message * @return */ @GetMapping("/sendMessage") public String messageWithMQ(@RequestParam String message) { testTopic.output().send(MessageBuilder.withPayload(message).build()); return "ok"; } } /** * 消息消费逻辑 */ @Slf4j @Component static class TestListener { @StreamListener(TestTopic.INPUT) public void receive(String payload) { log.info("Received payload : " + payload); throw new RuntimeException("Message consumer failed!"); } } interface TestTopic { String OUTPUT = "example-topic-output"; String INPUT = "example-topic-input"; @Output(OUTPUT) MessageChannel output(); @Input(INPUT) SubscribableChannel input(); } } ================================================ FILE: 4-Finchley/stream-exception-handler-3/src/main/resources/application.properties ================================================ spring.application.name=stream-exception-handler-3 server.port=8080 spring.cloud.stream.bindings.example-topic-input.destination=test-topic spring.cloud.stream.bindings.example-topic-input.group=stream-exception-handler spring.cloud.stream.bindings.example-topic-input.consumer.max-attempts=1 spring.cloud.stream.rabbit.bindings.example-topic-input.consumer.auto-bind-dlq=true spring.cloud.stream.rabbit.bindings.example-topic-input.consumer.republish-to-dlq=true #spring.cloud.stream.rabbit.bindings.example-topic-input.consumer.dlq-ttl=10000 spring.cloud.stream.bindings.example-topic-output.destination=test-topic ================================================ FILE: 4-Finchley/stream-exception-handler-4/pom.xml ================================================ 4.0.0 com.didispace stream-exception-handler-4 0.0.1-SNAPSHOT jar org.springframework.boot spring-boot-starter-parent 2.0.5.RELEASE UTF-8 UTF-8 1.8 org.projectlombok lombok 1.18.2 org.springframework.boot spring-boot-starter-test test org.springframework.cloud spring-cloud-starter-stream-rabbit org.springframework.boot spring-boot-starter-actuator org.springframework.cloud spring-cloud-dependencies Finchley.SR1 pom import org.springframework.boot spring-boot-maven-plugin ================================================ FILE: 4-Finchley/stream-exception-handler-4/src/main/java/com/didispace/stream/TestApplication.java ================================================ package com.didispace.stream; import lombok.extern.slf4j.Slf4j; import org.springframework.amqp.AmqpRejectAndDontRequeueException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.stream.annotation.EnableBinding; import org.springframework.cloud.stream.annotation.Input; import org.springframework.cloud.stream.annotation.Output; import org.springframework.cloud.stream.annotation.StreamListener; import org.springframework.integration.support.MessageBuilder; import org.springframework.messaging.MessageChannel; import org.springframework.messaging.SubscribableChannel; import org.springframework.stereotype.Component; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @EnableBinding(TestApplication.TestTopic.class) @SpringBootApplication public class TestApplication { public static void main(String[] args) { SpringApplication.run(TestApplication.class, args); } @RestController static class TestController { @Autowired private TestTopic testTopic; /** * 消息生产接口 * * @param message * @return */ @GetMapping("/sendMessage") public String messageWithMQ(@RequestParam String message) { testTopic.output().send(MessageBuilder.withPayload(message).build()); return "ok"; } } /** * 消息消费逻辑 */ @Slf4j @Component static class TestListener { private int count = 1; @StreamListener(TestTopic.INPUT) public void receive(String payload) { log.info("Received payload : " + payload + ", " + count); throw new RuntimeException("Message consumer failed!"); // 进入DLQ的逻辑 // if (count == 3) { // count = 1; // throw new AmqpRejectAndDontRequeueException("tried 3 times failed, send to dlq!"); // } else { // count ++; // throw new RuntimeException("Message consumer failed!"); // } } } interface TestTopic { String OUTPUT = "example-topic-output"; String INPUT = "example-topic-input"; @Output(OUTPUT) MessageChannel output(); @Input(INPUT) SubscribableChannel input(); } } ================================================ FILE: 4-Finchley/stream-exception-handler-4/src/main/resources/application.properties ================================================ spring.application.name=stream-exception-handler-4 server.port=8080 spring.cloud.stream.bindings.example-topic-input.destination=test-topic spring.cloud.stream.bindings.example-topic-input.group=stream-exception-handler spring.cloud.stream.bindings.example-topic-input.consumer.max-attempts=1 spring.cloud.stream.rabbit.bindings.example-topic-input.consumer.auto-bind-dlq=true spring.cloud.stream.rabbit.bindings.example-topic-input.consumer.requeue-rejected=true spring.cloud.stream.bindings.example-topic-output.destination=test-topic ================================================ FILE: README.md ================================================ # Spring Cloud教程 本项目内容为Spring Cloud教程的程序样例。如您觉得该项目对您有用,欢迎点击右上方的**Star**按钮,给予支持!! - 公益调试 Eureka:http://eureka.didispace.com - 公益调试 Nacos:http://blog.didispace.com/open-nacos-server-1-0-0/ ## 特别赞助商
    > 如果您也想赞助支持并出现在上表中的话,可以通过邮件联系我:`didi@didispace.com` ## 教程列表 ### 《Spring Cloud构建微服务架构》系列博文 #### Finchley版 **本系列主要补充之前版本新增或是变动的主要内容,基础使用依然可以参考Dalston版教程** - [Spring Cloud Finchley版中Consul多实例注册的问题处理](http://blog.didispace.com/Spring-Cloud-Finchley-Consul-InstanceId/) ##### Spring Cloud Aliabab专题 - [Spring Cloud Alibaba与Spring Boot、Spring Cloud之间不得不说的版本关系](http://blog.didispace.com/spring-cloud-alibaba-version/) - [说说我为什么看好Spring Cloud Alibaba](http://blog.didispace.com/spring-cloud-alibaba-significance/) - [Spring Cloud Alibaba到底坑不坑?](http://blog.didispace.com/bo-kengdie-spring-cloud-alibaba/) *注册中心与配置中心:Nacos* - [Spring Cloud Alibaba基础教程:使用Nacos实现服务注册与发现](http://blog.didispace.com/spring-cloud-alibaba-1/) - [Spring Cloud Alibaba基础教程:Nacos 生产级版本 0.8.0](http://blog.didispace.com/spring-cloud-alibaba-nacos-1/) - [Spring Cloud Alibaba基础教程:支持的几种服务消费方式(RestTemplate、WebClient、Feign)](http://blog.didispace.com/spring-cloud-alibaba-2/) - [Spring Cloud Alibaba基础教程:使用Nacos作为配置中心](http://blog.didispace.com/spring-cloud-alibaba-3/) - [Spring Cloud Alibaba基础教程:Nacos配置的加载规则详解](http://blog.didispace.com/spring-cloud-alibaba-nacos-config-1/) - [Spring Cloud Alibaba基础教程:Nacos配置的多环境管理](http://blog.didispace.com/spring-cloud-alibaba-nacos-config-2/) - [Spring Cloud Alibaba基础教程:Nacos配置的多文件加载与共享配置](http://blog.didispace.com/spring-cloud-alibaba-nacos-config-3/) - [Spring Cloud Alibaba基础教程:Nacos的数据持久化](http://blog.didispace.com/spring-cloud-alibaba-4/) - [Spring Cloud Alibaba基础教程:Nacos的集群部署](http://blog.didispace.com/spring-cloud-alibaba-5/) *分布式流量防卫兵:Sentinel* - [Spring Cloud Alibaba基础教程:使用Sentinel实现接口限流](http://blog.didispace.com/spring-cloud-alibaba-sentinel-1/) - [Spring Cloud Alibaba基础教程:Sentinel使用Nacos存储规则](http://blog.didispace.com/spring-cloud-alibaba-sentinel-2-1/) - [Spring Cloud Alibaba基础教程:Sentinel使用Apollo存储规则](http://blog.didispace.com/spring-cloud-alibaba-sentinel-2-2/) - [Spring Cloud Alibaba基础教程:Sentinel Dashboard中修改规则同步到Apollo](http://blog.didispace.com/spring-cloud-alibaba-sentinel-2-3/) - [Spring Cloud Alibaba基础教程:Sentinel Dashboard中修改规则同步到Nacos](http://blog.didispace.com/spring-cloud-alibaba-sentinel-2-4/) - [Spring Cloud Alibaba基础教程:@SentinelResource注解使用详解](http://blog.didispace.com/spring-cloud-alibaba-sentinel-2-5/) *国内使用最多的RPC框架整合:Dubbo* - [Spring Cloud Alibaba基础教程:与Dubbo的完美融合](http://blog.didispace.com/spring-cloud-alibaba-dubbo-1/) ##### Spring Cloud Stream专题补充 - [Spring Cloud Stream如何消费自己生产的消息](http://blog.didispace.com/spring-cloud-starter-finchley-7-1) - [Spring Cloud Stream同一通道根据消息内容分发不同的消费逻辑](http://blog.didispace.com/spring-cloud-starter-finchley-7-6) - [Spring Cloud Stream使用延迟消息实现定时任务(RabbitMQ)](http://blog.didispace.com/spring-cloud-starter-finchley-7-7) - [Spring Cloud Stream消费失败后的处理策略(一):自动重试](http://blog.didispace.com/spring-cloud-starter-finchley-7-2) - [Spring Cloud Stream消费失败后的处理策略(二):自定义错误处理逻辑](http://blog.didispace.com/spring-cloud-starter-finchley-7-3) - [Spring Cloud Stream消费失败后的处理策略(三):使用DLQ队列(RabbitMQ)](http://blog.didispace.com/spring-cloud-starter-finchley-7-4) - [Spring Cloud Stream消费失败后的处理策略(四):重新入队(RabbitMQ)](http://blog.didispace.com/spring-cloud-starter-finchley-7-5) #### Edgware版 > 本系列主要是对Dalston版的补充,包含Edgware版的主要新增或变动的内容,对于Spring Cloud的基础使用依然建议参考Dalston版教程 - [分布式配置中心(数据库存储)](http://blog.didispace.com/spring-cloud-starter-edgware-3-1) #### Dalston版 - [服务注册与发现(Eureka、Consul)](http://blog.didispace.com/spring-cloud-starter-dalston-1/) - [服务消费者(基础)](http://blog.didispace.com/spring-cloud-starter-dalston-2-1/) - [服务消费者(Ribbon)](http://blog.didispace.com/spring-cloud-starter-dalston-2-2/) - [服务消费者(Feign)](http://blog.didispace.com/spring-cloud-starter-dalston-2-3/) - [服务消费者(Feign)传文件](http://blog.didispace.com/spring-cloud-starter-dalston-2-4/) - [分布式配置中心](http://blog.didispace.com/spring-cloud-starter-dalston-3) - [服务容错保护(Hystrix服务降级)](http://blog.didispace.com/spring-cloud-starter-dalston-4-1) - [服务容错保护(Hystrix依赖隔离)](http://blog.didispace.com/spring-cloud-starter-dalston-4-2) - [服务容错保护(Hystrix断路器)](http://blog.didispace.com/spring-cloud-starter-dalston-4-3) - [Hystrix监控面板](http://blog.didispace.com/spring-cloud-starter-dalston-5-1/) - [Hystrix监控数据聚合](http://blog.didispace.com/spring-cloud-starter-dalston-5-2/) - [服务网关(基础)](http://blog.didispace.com/spring-cloud-starter-dalston-6-1/) - [服务网关(路由配置)](http://blog.didispace.com/spring-cloud-starter-dalston-6-2/) - [服务网关(过滤器)](http://blog.didispace.com/spring-cloud-starter-dalston-6-3/) - [服务网关(API文档汇总)](http://blog.didispace.com/Spring-Cloud-Zuul-use-Swagger-API-doc/) - [消息驱动的微服务(入门)](http://blog.didispace.com/spring-cloud-starter-dalston-7-1/) - [消息驱动的微服务(核心概念)](http://blog.didispace.com/spring-cloud-starter-dalston-7-2/) - [消息驱动的微服务(消费组)](http://blog.didispace.com/spring-cloud-starter-dalston-7-3/) - [消息驱动的微服务(消费组案例:解决消息重复消费)](http://blog.didispace.com/spring-cloud-starter-dalston-7-5/) - [消息驱动的微服务(消息分区)](http://blog.didispace.com/spring-cloud-starter-dalston-7-4/) - [分布式服务跟踪(入门)](http://blog.didispace.com/spring-cloud-starter-dalston-8-1/) - [分布式服务跟踪(跟踪原理)](http://blog.didispace.com/spring-cloud-starter-dalston-8-2/) - [分布式服务跟踪(整合logstash)](http://blog.didispace.com/spring-cloud-starter-dalston-8-3/) - [分布式服务跟踪(整合zipkin)](http://blog.didispace.com/spring-cloud-starter-dalston-8-4/) - [分布式服务跟踪(收集原理)](http://blog.didispace.com/spring-cloud-starter-dalston-8-5/) - [分布式服务跟踪(抽样收集)](http://blog.didispace.com/spring-cloud-starter-dalston-8-6/) #### Brixton版 - 1-Brixton版教程示例/chapter1-1-1:[Spring Cloud构建微服务架构(一)服务注册与发现](http://blog.didispace.com/springcloud1/) - 1-Brixton版教程示例/chapter1-1-2:[Spring Cloud构建微服务架构(二)服务消费者](http://blog.didispace.com/springcloud2/) - 1-Brixton版教程示例/chapter1-1-3:[Spring Cloud构建微服务架构(三)断路器](http://blog.didispace.com/springcloud3/) - 1-Brixton版教程示例/chapter1-1-4:[Spring Cloud构建微服务架构(四)分布式配置中心](http://blog.didispace.com/springcloud4/) - 1-Brixton版教程示例/chapter1-1-8:[Spring Cloud构建微服务架构(四)分布式配置中心(续)](http://blog.didispace.com/springcloud4-2/) - 1-Brixton版教程示例/chapter1-1-5:[Spring Cloud构建微服务架构(五)服务网关](http://blog.didispace.com/springcloud5/) - 1-Brixton版教程示例/chapter1-1-6:[Spring Cloud构建微服务架构(六)高可用服务注册中心](http://blog.didispace.com/springcloud6/) - 1-Brixton版教程示例/chapter1-1-7:[Spring Cloud构建微服务架构(七)消息总线(Rabbit)](http://blog.didispace.com/springcloud7/) - 1-Brixton版教程示例/chapter1-1-7:[Spring Cloud构建微服务架构(七)消息总线(Kafka)](http://blog.didispace.com/springcloud7-2/) ### 《Spring Cloud源码分析》系列博文 - [Spring Cloud源码分析(一)Eureka](http://blog.didispace.com/springcloud-sourcecode-eureka/) - [Spring Cloud源码分析(二)Ribbon](http://blog.didispace.com/springcloud-sourcecode-ribbon/) - [Spring Cloud源码分析(二)Ribbon](http://blog.didispace.com/springcloud-sourcecode-ribbon/) - [Spring Cloud源码分析(四)Zuul:核心过滤器](http://blog.didispace.com/spring-cloud-source-zuul/) - 未完待续 ### 《Spring Cloud实战小贴士》系列博文 - [Spring Cloud实战小贴士:版本依赖关系](http://blog.didispace.com/spring-cloud-tips-1/) - [Spring Cloud实战小贴士:随机端口](http://blog.didispace.com/spring-cloud-tips-2/) - [Spring Cloud实战小贴士:健康检查](http://blog.didispace.com/spring-cloud-tips-3/) - [Spring Cloud实战小贴士:Zuul处理Cookie和重定向](http://blog.didispace.com/spring-cloud-zuul-cookie-redirect/) - [Spring Cloud实战小贴士:Zuul统一异常处理(一)](http://blog.didispace.com/spring-cloud-zuul-exception/) - [Spring Cloud实战小贴士:Zuul统一异常处理(二)](http://blog.didispace.com/spring-cloud-zuul-exception-2/) - [Spring Cloud实战小贴士:Zuul统一异常处理(三)【Dalston版】](http://blog.didispace.com/spring-cloud-zuul-exception-3/) - [Spring Cloud实战小贴士:Turbine如何聚合设置了context-path的Hystrix数据](http://blog.didispace.com/spring-cloud-tips-4/) - [Spring Cloud实战小贴士:Feign的继承特性(伪RPC模式)](http://blog.didispace.com/spring-cloud-tips-feign-rpc/) - [Spring Cloud实战小贴士:Ribbon的饥饿加载(eager-load)模式](http://blog.didispace.com/spring-cloud-tips-ribbon-eager/) - [Spring Cloud实战小贴士:Zuul的饥饿加载(eager-load)使用](http://blog.didispace.com/spring-cloud-tips-zuul-eager/) #### 其他文章 - [使用Intellij中的Spring Initializr来快速构建Spring Boot/Cloud工程](http://blog.didispace.com/spring-initializr-in-intellij/) - [为Spring Cloud Ribbon配置请求重试(Camden.SR2+)](http://blog.didispace.com/spring-cloud-ribbon-failed-retry/) - [Consul注销实例时候的问题](http://blog.didispace.com/consul-deregister/) - [使用Spring Boot Actuator、Jolokia和Grafana实现准实时监控](http://blog.didispace.com/spring-boot-jolokia-grafana-monitor/) - [Netflix Zuul与Nginx的性能对比](http://blog.didispace.com/zuul-vs-nginx-performance/) - [基于Consul的分布式锁实现](http://blog.didispace.com/spring-cloud-consul-lock-and-semphore/) - [基于Consul的分布式信号量实现](http://blog.didispace.com/spring-cloud-consul-lock-and-semphore-2/) ## 我的公众号 ![](http://git.oschina.net/uploads/images/2017/0105/082137_85109d07_437188.jpeg "在这里输入图片标题") ## 推荐我的书 ![](https://git.oschina.net/uploads/images/2017/0416/233656_dd3bce94_437188.png "在这里输入图片标题") ## 其他推荐 - [我的博客](http://blog.didispace.com):分享平时学习和实践过的技术内容 - [知识星球](https://t.xiaomiquan.com/zfEiY3v):聊聊技术人的斜杠生活 - [GitHub](https://github.com/dyc87112/SpringCloud-Learning):Star支持一下呗 - [Gitee](https://gitee.com/didispace/SpringCloud-Learning):Star支持一下呗 - [Spring问答社区](http://www.spring4all.com/):如果您有什么问题,可以去这里发帖 - [Spring Boot基础教程](http://blog.didispace.com/Spring-Boot%E5%9F%BA%E7%A1%80%E6%95%99%E7%A8%8B/):全网Star最多的免费Spring Boot基础教程 - [Spring Cloud基础教程](http://blog.didispace.com/Spring-Cloud%E5%9F%BA%E7%A1%80%E6%95%99%E7%A8%8B/):全网最早最全的免费Spring Cloud基础教程 - [微服务架构专题](http://blog.didispace.com/micro-serivces-arch/) ================================================ FILE: README_zh.md ================================================ # Spring Cloud教程 本项目内容为Spring Cloud教程的程序样例。如您觉得该项目对您有用,欢迎点击右上方的**Star**按钮,给予支持!! - 公益调试 Eureka:http://eureka.didispace.com - 公益调试 Nacos:http://blog.didispace.com/open-nacos-server-1-0-0/ ## 特别赞助商
    > 如果您也想赞助支持并出现在上表中的话,可以通过邮件联系我:`didi@didispace.com` ## 教程列表 ### 《Spring Cloud构建微服务架构》系列博文 #### Finchley版 **本系列主要补充之前版本新增或是变动的主要内容,基础使用依然可以参考Dalston版教程** - [Spring Cloud Finchley版中Consul多实例注册的问题处理](http://blog.didispace.com/Spring-Cloud-Finchley-Consul-InstanceId/) ##### Spring Cloud Aliabab专题 - [Spring Cloud Alibaba与Spring Boot、Spring Cloud之间不得不说的版本关系](http://blog.didispace.com/spring-cloud-alibaba-version/) - [说说我为什么看好Spring Cloud Alibaba](http://blog.didispace.com/spring-cloud-alibaba-significance/) - [Spring Cloud Alibaba到底坑不坑?](http://blog.didispace.com/bo-kengdie-spring-cloud-alibaba/) *注册中心与配置中心:Nacos* - [Spring Cloud Alibaba基础教程:使用Nacos实现服务注册与发现](http://blog.didispace.com/spring-cloud-alibaba-1/) - [Spring Cloud Alibaba基础教程:Nacos 生产级版本 0.8.0](http://blog.didispace.com/spring-cloud-alibaba-nacos-1/) - [Spring Cloud Alibaba基础教程:支持的几种服务消费方式(RestTemplate、WebClient、Feign)](http://blog.didispace.com/spring-cloud-alibaba-2/) - [Spring Cloud Alibaba基础教程:使用Nacos作为配置中心](http://blog.didispace.com/spring-cloud-alibaba-3/) - [Spring Cloud Alibaba基础教程:Nacos配置的加载规则详解](http://blog.didispace.com/spring-cloud-alibaba-nacos-config-1/) - [Spring Cloud Alibaba基础教程:Nacos配置的多环境管理](http://blog.didispace.com/spring-cloud-alibaba-nacos-config-2/) - [Spring Cloud Alibaba基础教程:Nacos配置的多文件加载与共享配置](http://blog.didispace.com/spring-cloud-alibaba-nacos-config-3/) - [Spring Cloud Alibaba基础教程:Nacos的数据持久化](http://blog.didispace.com/spring-cloud-alibaba-4/) - [Spring Cloud Alibaba基础教程:Nacos的集群部署](http://blog.didispace.com/spring-cloud-alibaba-5/) *分布式流量防卫兵:Sentinel* - [Spring Cloud Alibaba基础教程:使用Sentinel实现接口限流](http://blog.didispace.com/spring-cloud-alibaba-sentinel-1/) - [Spring Cloud Alibaba基础教程:Sentinel使用Nacos存储规则](http://blog.didispace.com/spring-cloud-alibaba-sentinel-2-1/) - [Spring Cloud Alibaba基础教程:Sentinel使用Apollo存储规则](http://blog.didispace.com/spring-cloud-alibaba-sentinel-2-2/) - [Spring Cloud Alibaba基础教程:Sentinel Dashboard中修改规则同步到Apollo](http://blog.didispace.com/spring-cloud-alibaba-sentinel-2-3/) - [Spring Cloud Alibaba基础教程:Sentinel Dashboard中修改规则同步到Nacos](http://blog.didispace.com/spring-cloud-alibaba-sentinel-2-4/) - [Spring Cloud Alibaba基础教程:@SentinelResource注解使用详解](http://blog.didispace.com/spring-cloud-alibaba-sentinel-2-5/) *国内使用最多的RPC框架整合:Dubbo* - [Spring Cloud Alibaba基础教程:与Dubbo的完美融合](http://blog.didispace.com/spring-cloud-alibaba-dubbo-1/) ##### Spring Cloud Stream专题补充 - [Spring Cloud Stream如何消费自己生产的消息](http://blog.didispace.com/spring-cloud-starter-finchley-7-1) - [Spring Cloud Stream同一通道根据消息内容分发不同的消费逻辑](http://blog.didispace.com/spring-cloud-starter-finchley-7-6) - [Spring Cloud Stream使用延迟消息实现定时任务(RabbitMQ)](http://blog.didispace.com/spring-cloud-starter-finchley-7-7) - [Spring Cloud Stream消费失败后的处理策略(一):自动重试](http://blog.didispace.com/spring-cloud-starter-finchley-7-2) - [Spring Cloud Stream消费失败后的处理策略(二):自定义错误处理逻辑](http://blog.didispace.com/spring-cloud-starter-finchley-7-3) - [Spring Cloud Stream消费失败后的处理策略(三):使用DLQ队列(RabbitMQ)](http://blog.didispace.com/spring-cloud-starter-finchley-7-4) - [Spring Cloud Stream消费失败后的处理策略(四):重新入队(RabbitMQ)](http://blog.didispace.com/spring-cloud-starter-finchley-7-5) #### Edgware版 > 本系列主要是对Dalston版的补充,包含Edgware版的主要新增或变动的内容,对于Spring Cloud的基础使用依然建议参考Dalston版教程 - [分布式配置中心(数据库存储)](http://blog.didispace.com/spring-cloud-starter-edgware-3-1) #### Dalston版 - [服务注册与发现(Eureka、Consul)](http://blog.didispace.com/spring-cloud-starter-dalston-1/) - [服务消费者(基础)](http://blog.didispace.com/spring-cloud-starter-dalston-2-1/) - [服务消费者(Ribbon)](http://blog.didispace.com/spring-cloud-starter-dalston-2-2/) - [服务消费者(Feign)](http://blog.didispace.com/spring-cloud-starter-dalston-2-3/) - [服务消费者(Feign)传文件](http://blog.didispace.com/spring-cloud-starter-dalston-2-4/) - [分布式配置中心](http://blog.didispace.com/spring-cloud-starter-dalston-3) - [服务容错保护(Hystrix服务降级)](http://blog.didispace.com/spring-cloud-starter-dalston-4-1) - [服务容错保护(Hystrix依赖隔离)](http://blog.didispace.com/spring-cloud-starter-dalston-4-2) - [服务容错保护(Hystrix断路器)](http://blog.didispace.com/spring-cloud-starter-dalston-4-3) - [Hystrix监控面板](http://blog.didispace.com/spring-cloud-starter-dalston-5-1/) - [Hystrix监控数据聚合](http://blog.didispace.com/spring-cloud-starter-dalston-5-2/) - [服务网关(基础)](http://blog.didispace.com/spring-cloud-starter-dalston-6-1/) - [服务网关(路由配置)](http://blog.didispace.com/spring-cloud-starter-dalston-6-2/) - [服务网关(过滤器)](http://blog.didispace.com/spring-cloud-starter-dalston-6-3/) - [服务网关(API文档汇总)](http://blog.didispace.com/Spring-Cloud-Zuul-use-Swagger-API-doc/) - [消息驱动的微服务(入门)](http://blog.didispace.com/spring-cloud-starter-dalston-7-1/) - [消息驱动的微服务(核心概念)](http://blog.didispace.com/spring-cloud-starter-dalston-7-2/) - [消息驱动的微服务(消费组)](http://blog.didispace.com/spring-cloud-starter-dalston-7-3/) - [消息驱动的微服务(消费组案例:解决消息重复消费)](http://blog.didispace.com/spring-cloud-starter-dalston-7-5/) - [消息驱动的微服务(消息分区)](http://blog.didispace.com/spring-cloud-starter-dalston-7-4/) - [分布式服务跟踪(入门)](http://blog.didispace.com/spring-cloud-starter-dalston-8-1/) - [分布式服务跟踪(跟踪原理)](http://blog.didispace.com/spring-cloud-starter-dalston-8-2/) - [分布式服务跟踪(整合logstash)](http://blog.didispace.com/spring-cloud-starter-dalston-8-3/) - [分布式服务跟踪(整合zipkin)](http://blog.didispace.com/spring-cloud-starter-dalston-8-4/) - [分布式服务跟踪(收集原理)](http://blog.didispace.com/spring-cloud-starter-dalston-8-5/) - [分布式服务跟踪(抽样收集)](http://blog.didispace.com/spring-cloud-starter-dalston-8-6/) #### Brixton版 - 1-Brixton版教程示例/chapter1-1-1:[Spring Cloud构建微服务架构(一)服务注册与发现](http://blog.didispace.com/springcloud1/) - 1-Brixton版教程示例/chapter1-1-2:[Spring Cloud构建微服务架构(二)服务消费者](http://blog.didispace.com/springcloud2/) - 1-Brixton版教程示例/chapter1-1-3:[Spring Cloud构建微服务架构(三)断路器](http://blog.didispace.com/springcloud3/) - 1-Brixton版教程示例/chapter1-1-4:[Spring Cloud构建微服务架构(四)分布式配置中心](http://blog.didispace.com/springcloud4/) - 1-Brixton版教程示例/chapter1-1-8:[Spring Cloud构建微服务架构(四)分布式配置中心(续)](http://blog.didispace.com/springcloud4-2/) - 1-Brixton版教程示例/chapter1-1-5:[Spring Cloud构建微服务架构(五)服务网关](http://blog.didispace.com/springcloud5/) - 1-Brixton版教程示例/chapter1-1-6:[Spring Cloud构建微服务架构(六)高可用服务注册中心](http://blog.didispace.com/springcloud6/) - 1-Brixton版教程示例/chapter1-1-7:[Spring Cloud构建微服务架构(七)消息总线(Rabbit)](http://blog.didispace.com/springcloud7/) - 1-Brixton版教程示例/chapter1-1-7:[Spring Cloud构建微服务架构(七)消息总线(Kafka)](http://blog.didispace.com/springcloud7-2/) ### 《Spring Cloud源码分析》系列博文 - [Spring Cloud源码分析(一)Eureka](http://blog.didispace.com/springcloud-sourcecode-eureka/) - [Spring Cloud源码分析(二)Ribbon](http://blog.didispace.com/springcloud-sourcecode-ribbon/) - [Spring Cloud源码分析(二)Ribbon](http://blog.didispace.com/springcloud-sourcecode-ribbon/) - [Spring Cloud源码分析(四)Zuul:核心过滤器](http://blog.didispace.com/spring-cloud-source-zuul/) - 未完待续 ### 《Spring Cloud实战小贴士》系列博文 - [Spring Cloud实战小贴士:版本依赖关系](http://blog.didispace.com/spring-cloud-tips-1/) - [Spring Cloud实战小贴士:随机端口](http://blog.didispace.com/spring-cloud-tips-2/) - [Spring Cloud实战小贴士:健康检查](http://blog.didispace.com/spring-cloud-tips-3/) - [Spring Cloud实战小贴士:Zuul处理Cookie和重定向](http://blog.didispace.com/spring-cloud-zuul-cookie-redirect/) - [Spring Cloud实战小贴士:Zuul统一异常处理(一)](http://blog.didispace.com/spring-cloud-zuul-exception/) - [Spring Cloud实战小贴士:Zuul统一异常处理(二)](http://blog.didispace.com/spring-cloud-zuul-exception-2/) - [Spring Cloud实战小贴士:Zuul统一异常处理(三)【Dalston版】](http://blog.didispace.com/spring-cloud-zuul-exception-3/) - [Spring Cloud实战小贴士:Turbine如何聚合设置了context-path的Hystrix数据](http://blog.didispace.com/spring-cloud-tips-4/) - [Spring Cloud实战小贴士:Feign的继承特性(伪RPC模式)](http://blog.didispace.com/spring-cloud-tips-feign-rpc/) - [Spring Cloud实战小贴士:Ribbon的饥饿加载(eager-load)模式](http://blog.didispace.com/spring-cloud-tips-ribbon-eager/) - [Spring Cloud实战小贴士:Zuul的饥饿加载(eager-load)使用](http://blog.didispace.com/spring-cloud-tips-zuul-eager/) #### 其他文章 - [使用Intellij中的Spring Initializr来快速构建Spring Boot/Cloud工程](http://blog.didispace.com/spring-initializr-in-intellij/) - [为Spring Cloud Ribbon配置请求重试(Camden.SR2+)](http://blog.didispace.com/spring-cloud-ribbon-failed-retry/) - [Consul注销实例时候的问题](http://blog.didispace.com/consul-deregister/) - [使用Spring Boot Actuator、Jolokia和Grafana实现准实时监控](http://blog.didispace.com/spring-boot-jolokia-grafana-monitor/) - [Netflix Zuul与Nginx的性能对比](http://blog.didispace.com/zuul-vs-nginx-performance/) - [基于Consul的分布式锁实现](http://blog.didispace.com/spring-cloud-consul-lock-and-semphore/) - [基于Consul的分布式信号量实现](http://blog.didispace.com/spring-cloud-consul-lock-and-semphore-2/) ## 我的公众号 ![](http://git.oschina.net/uploads/images/2017/0105/082137_85109d07_437188.jpeg "在这里输入图片标题") ## 推荐我的书 ![](https://git.oschina.net/uploads/images/2017/0416/233656_dd3bce94_437188.png "在这里输入图片标题") ## 其他推荐 - [我的博客](http://blog.didispace.com):分享平时学习和实践过的技术内容 - [知识星球](https://t.xiaomiquan.com/zfEiY3v):聊聊技术人的斜杠生活 - [GitHub](https://github.com/dyc87112/SpringCloud-Learning):Star支持一下呗 - [Gitee](https://gitee.com/didispace/SpringCloud-Learning):Star支持一下呗 - [Spring问答社区](http://www.spring4all.com/):如果您有什么问题,可以去这里发帖 - [Spring Boot基础教程](http://blog.didispace.com/Spring-Boot%E5%9F%BA%E7%A1%80%E6%95%99%E7%A8%8B/):全网Star最多的免费Spring Boot基础教程 - [Spring Cloud基础教程](http://blog.didispace.com/Spring-Cloud%E5%9F%BA%E7%A1%80%E6%95%99%E7%A8%8B/):全网最早最全的免费Spring Cloud基础教程 - [微服务架构专题](http://blog.didispace.com/micro-serivces-arch/)