├── .gitignore ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── build.bat ├── build.sh ├── cert_file ├── ca.crt ├── ca.key ├── ca.srl ├── cert_file.go ├── client.crt ├── client.csr ├── client.key ├── server.crt ├── server.csr └── server.key ├── conf └── dev │ ├── base.toml │ ├── mysql_map.toml │ ├── plugin_config.toml │ ├── proxy.toml │ └── redis_map.toml ├── control.sh ├── dashboard_controller ├── admin.go ├── admin_login.go ├── app.go ├── dashboard.go ├── oauth.go └── service.go ├── dashboard_middleware ├── recovery.go ├── request_log.go ├── response.go ├── session_auth.go └── translation.go ├── dashboard_router ├── httpserver.go └── route.go ├── debug.sh ├── dist ├── favicon.ico ├── index.html └── static │ ├── css │ ├── app.baa39a92.css │ ├── chunk-1c7e619f.0be79fdd.css │ ├── chunk-294e231b.d3b4ac8e.css │ ├── chunk-2f5ea260.7777da69.css │ ├── chunk-3f238847.a2fda0b3.css │ ├── chunk-3f3d4b77.fe722f73.css │ ├── chunk-4ce61457.41f5667e.css │ ├── chunk-61ae4e7a.93d95231.css │ ├── chunk-6525d41e.b1d9f1ec.css │ ├── chunk-68b61de5.fb787c4e.css │ ├── chunk-6f6c0d4e.c5f2d082.css │ ├── chunk-80699a70.c0f8cc73.css │ ├── chunk-bcebd8aa.78a33c26.css │ ├── chunk-db410d48.50024827.css │ ├── chunk-e9dba01a.b0fc6a10.css │ └── chunk-libs.3dfb7769.css │ ├── fonts │ ├── element-icons.535877f5.woff │ └── element-icons.732389de.ttf │ ├── img │ ├── 401.089007e7.gif │ ├── 404.a57b6f31.png │ ├── 404_cloud.0f4bc32b.png │ ├── double-arrow-left.56942267.svg │ └── logo.e3a14490.svg │ └── js │ ├── app.0f90f977.js │ ├── chunk-1c7e619f.4dad2980.js │ ├── chunk-294e231b.9c9d3bca.js │ ├── chunk-2d230fe7.f069ed37.js │ ├── chunk-2f5ea260.34edaec3.js │ ├── chunk-3f238847.dde743ce.js │ ├── chunk-3f3d4b77.f6066ce5.js │ ├── chunk-4ce61457.350a970f.js │ ├── chunk-57e15452.3a533841.js │ ├── chunk-61ae4e7a.9dab1bdd.js │ ├── chunk-6525d41e.3357aec3.js │ ├── chunk-68b61de5.e515d0f3.js │ ├── chunk-6f6c0d4e.3a34421a.js │ ├── chunk-80699a70.d1cca3f7.js │ ├── chunk-88ad1d84.14359848.js │ ├── chunk-bcebd8aa.852867b4.js │ ├── chunk-db410d48.75429a29.js │ ├── chunk-e9dba01a.4f29b4df.js │ ├── chunk-elementUI.1485574a.js │ └── chunk-libs.16cb3d3b.js ├── dist_backup ├── favicon.ico ├── index.html └── static │ ├── css │ ├── app.0eba9b7d.css │ ├── chunk-294e231b.d3b4ac8e.css │ ├── chunk-2f5ea260.7777da69.css │ ├── chunk-335e7249.6d24dacd.css │ ├── chunk-3f238847.a2fda0b3.css │ ├── chunk-5c8c1f18.25617263.css │ ├── chunk-61ae4e7a.93d95231.css │ ├── chunk-6525d41e.b1d9f1ec.css │ ├── chunk-68b61de5.fb787c4e.css │ ├── chunk-6f6c0d4e.c5f2d082.css │ ├── chunk-7be427f9.9fb4309b.css │ ├── chunk-80699a70.c0f8cc73.css │ ├── chunk-bcebd8aa.78a33c26.css │ ├── chunk-e8029a4a.5c0a5547.css │ ├── chunk-e9dba01a.b0fc6a10.css │ └── chunk-libs.3dfb7769.css │ ├── fonts │ ├── element-icons.535877f5.woff │ └── element-icons.732389de.ttf │ ├── img │ ├── 401.089007e7.gif │ ├── 404.a57b6f31.png │ ├── 404_cloud.0f4bc32b.png │ ├── double-arrow-left.56942267.svg │ └── logo.e3a14490.svg │ └── js │ ├── app.ded26861.js │ ├── chunk-294e231b.9c9d3bca.js │ ├── chunk-2d230fe7.f069ed37.js │ ├── chunk-2f5ea260.34edaec3.js │ ├── chunk-335e7249.8363c846.js │ ├── chunk-3f238847.dde743ce.js │ ├── chunk-57e15452.3a533841.js │ ├── chunk-5c8c1f18.3f0435f6.js │ ├── chunk-61ae4e7a.9dab1bdd.js │ ├── chunk-6525d41e.3357aec3.js │ ├── chunk-68b61de5.e515d0f3.js │ ├── chunk-6f6c0d4e.3a34421a.js │ ├── chunk-7be427f9.449b823a.js │ ├── chunk-80699a70.d1cca3f7.js │ ├── chunk-88ad1d84.14359848.js │ ├── chunk-bcebd8aa.852867b4.js │ ├── chunk-e8029a4a.619eb279.js │ ├── chunk-e9dba01a.4f29b4df.js │ ├── chunk-elementUI.1485574a.js │ └── chunk-libs.16cb3d3b.js ├── docker_build.sh ├── dockerfile-dashboard ├── dockerfile-server ├── docs ├── docs.go ├── swagger.json └── swagger.yaml ├── favicon.ico ├── gatekeeper.sql ├── go.mod ├── go.sum ├── golang_common ├── lib │ ├── cmd.go │ ├── conf.go │ ├── func.go │ ├── log.go │ ├── mysql.go │ ├── redis.go │ └── request.go ├── trace │ ├── trace.go │ └── trace_test.go └── zerolog │ ├── .gitignore │ ├── CNAME │ ├── LICENSE │ ├── _config.yml │ ├── access_write.go │ ├── array.go │ ├── array_json.go │ ├── array_json_test.go │ ├── array_test.go │ ├── benchmark_json_test.go │ ├── binary_json_test.go │ ├── binary_test.go │ ├── cmd │ └── lint │ │ ├── lint.go │ │ └── readme.md │ ├── console.go │ ├── console_test.go │ ├── context.go │ ├── ctx.go │ ├── ctx_test.go │ ├── ddlog │ ├── ddlog.go │ ├── init.go │ └── log.go │ ├── diode │ ├── diode.go │ ├── diode_example_test.go │ ├── diode_test.go │ └── internal │ │ └── diodes │ │ ├── README │ │ ├── many_to_one.go │ │ ├── one_to_one.go │ │ ├── poller.go │ │ └── waiter.go │ ├── elevate │ └── cr.yml │ ├── encoder.go │ ├── encoder_cbor.go │ ├── encoder_console.go │ ├── encoder_json.go │ ├── event.go │ ├── event_json.go │ ├── fields.go │ ├── file_write.go │ ├── globals.go │ ├── hlog │ ├── hlog.go │ ├── hlog_example_test.go │ └── hlog_test.go │ ├── hook.go │ ├── hook_test.go │ ├── internal │ ├── cbor │ │ ├── README.md │ │ ├── base.go │ │ ├── cbor.go │ │ ├── decode_stream.go │ │ ├── decoder_test.go │ │ ├── examples │ │ │ ├── genLog.go │ │ │ └── makefile │ │ ├── string.go │ │ ├── string_test.go │ │ ├── time.go │ │ ├── time_test.go │ │ ├── types.go │ │ └── types_test.go │ ├── console │ │ ├── base.go │ │ ├── bytes.go │ │ ├── string.go │ │ ├── time.go │ │ └── types.go │ ├── json │ │ ├── base.go │ │ ├── bytes.go │ │ ├── bytes_test.go │ │ ├── string.go │ │ ├── string_test.go │ │ ├── time.go │ │ ├── types.go │ │ └── types_test.go │ └── ordered │ │ └── ordered.go │ ├── journald │ ├── journald.go │ └── journald_test.go │ ├── log.go │ ├── log │ ├── log.go │ └── log_example_test.go │ ├── log_example_test.go │ ├── log_json_example_test.go │ ├── log_json_test.go │ ├── log_test.go │ ├── pretty.png │ ├── sampler.go │ ├── sampler_test.go │ ├── stdout_write.go │ ├── syslog.go │ ├── syslog_json_test.go │ ├── syslog_test.go │ ├── utils.go │ ├── wfwriter.go │ ├── writer.go │ ├── writer_json_test.go │ └── writer_test.go ├── grpc_proxy_middleware ├── grpc_black_list.go ├── grpc_flow_count.go ├── grpc_flow_limit.go ├── grpc_jwt_auth_token.go ├── grpc_jwt_flow_count.go ├── grpc_jwt_flow_limit.go ├── grpc_metadata_transfer.go ├── grpc_test.go └── grpc_white_list.go ├── grpc_proxy_router └── grpcserver.go ├── handler ├── app_count_handler.go ├── app_manager_handler.go ├── distributed_count_handler.go ├── distributed_limit_handler.go ├── limit_handler.go ├── load_balancer_handler.go ├── service_count_handler.go ├── service_manager_handler.go └── transporter_handler.go ├── http_proxy_middleware ├── http_access_mode.go ├── http_black_list.go ├── http_flow_count.go ├── http_flow_limit.go ├── http_header_transfer.go ├── http_jwt_auth_token.go ├── http_jwt_flow_count.go ├── http_jwt_flow_limit.go ├── http_recovery.go ├── http_request_log.go ├── http_reverse_proxy.go ├── http_strip_uri.go ├── http_translation.go ├── http_url_rewrite.go └── http_white_list.go ├── http_proxy_router ├── httpserver.go └── route.go ├── install ├── build.bat ├── build.sh ├── check │ ├── conf.go │ ├── gateway.go │ ├── mysql.go │ ├── path.go │ └── redis.go ├── go.mod ├── go.sum ├── install.sh ├── main.go ├── template │ ├── conf.go │ └── mysql.go └── tool │ ├── log.go │ ├── reader.go │ ├── system.go │ └── zip.go ├── k8s_dashboard.yaml ├── k8s_server.yaml ├── load_balance ├── consistent_hash_strategy.go ├── consistent_hash_strategy_test.go ├── loadbalance_check_config.go ├── loadbalance_config.go ├── loadbalance_config_test.go ├── loadbalance_strategy.go ├── random_strategy.go ├── random_strategy_test.go ├── round_robin_strategy.go ├── round_robin_strategy_test.go ├── weight_round_strategy.go └── weight_round_strategy_test.go ├── main.go ├── model ├── admin_dto.go ├── admin_login_dto.go ├── admin_model.go ├── app_dto.go ├── app_model.go ├── dashboard_dto.go ├── oauth_dto.go ├── plugin_config.go ├── service_detail_model.go ├── service_dto.go └── service_info_model.go ├── public ├── const.go ├── http_response.go ├── jwt.go ├── log.go ├── params.go ├── redis.go └── util.go ├── reverse_proxy ├── grcp_reverse_proxy.go ├── http_reverse_proxy.go └── tcp_reverse_proxy.go ├── setup.sh ├── tcp_proxy_middleware ├── tcp_black_list.go ├── tcp_flow_count.go ├── tcp_flow_limit.go ├── tcp_slice_router.go └── tcp_white_list.go ├── tcp_proxy_router └── tcpserver.go ├── tcp_server ├── tcp_conn.go └── tcp_server.go └── test_suites ├── README.md ├── SqlHandler └── service_info_model.go ├── cert_file ├── ca.crt ├── ca.key ├── ca.srl ├── cert_file.go ├── client.crt ├── client.csr ├── client.key ├── server.crt ├── server.csr └── server.key ├── conf ├── base.toml ├── mysql_map.toml ├── plugin_config.toml ├── proxy.toml └── redis_map.toml ├── gatekeeper_test.sql ├── goconvey.sh ├── goconvey_shell.sh ├── run_test.go ├── test_case.go ├── test_httpserver.sh ├── test_rpcserver.sh ├── testhttp ├── main │ └── main.go └── test_server.go └── testrpc ├── main └── main.go ├── thrift_gen.thrift ├── thriftclient └── main.go ├── thriftgen ├── thrift_gen-consts.go └── thrift_gen.go └── thriftserver └── server.go /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .idea/ 3 | logs/ 4 | bin/ 5 | build/ 6 | onekeyupdate.sh -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contribution Guideline 2 | 3 | Thanks for considering to contribute this project. All issues and pull requests are highly appreciated. 4 | 5 | ## Pull Requests 6 | 7 | Before sending pull request to this project, please read and follow guidelines below. 8 | 9 | 1. Branch: We only accept pull request on `master` branch. 10 | 2. Coding style: Follow the coding style used in Go. 11 | 3. Commit message: Use English and be aware of your spell. 12 | 4. Test: Make sure to test your code. 13 | 14 | Add device mode, API version, related log, screenshots and other related information in your pull request if possible. 15 | 16 | NOTE: We assume all your contribution can be licensed under the [Apache License 2.0](LICENSE). 17 | 18 | ## Issues 19 | 20 | We love clearly described issues. :) 21 | 22 | Following information can help us to resolve the issue faster. 23 | 24 | * Device mode and hardware information. 25 | * API version. 26 | * Logs. 27 | * Screenshots. 28 | * Steps to reproduce the issue. 29 | -------------------------------------------------------------------------------- /build.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | color 0d 3 | 4 | echo ================================= 5 | echo ==========Build Linux ====== 6 | echo ================================= 7 | SET CGO_ENABLED=0 8 | SET GOOS=linux 9 | SET GOARCH=amd64 10 | echo now the CGO_ENABLED: 11 | go env CGO_ENABLED 12 | echo now the GOOS: 13 | go env GOOS 14 | echo now the GOARCH: 15 | go env GOARCH 16 | go build -o bin/gatekeeper_linux main.go 17 | 18 | echo ================================= 19 | echo ==========Build Mac ====== 20 | echo ================================= 21 | SET CGO_ENABLED=0 22 | SET GOOS=darwin 23 | SET GOARCH=amd64 24 | echo now the CGO_ENABLED: 25 | go env CGO_ENABLED 26 | echo now the GOOS: 27 | go env GOOS 28 | echo now the GOARCH: 29 | go env GOARCH 30 | go build -o bin/gatekeeper_mac main.go 31 | 32 | echo ================================= 33 | echo ==========Build Windows ====== 34 | echo ================================= 35 | SET CGO_ENABLED=1 36 | SET GOOS=windows 37 | SET GOARCH=amd64 38 | echo now the CGO_ENABLED: 39 | go env CGO_ENABLED 40 | echo now the GOOS: 41 | go env GOOS 42 | echo now the GOARCH: 43 | go env GOARCH 44 | go build -o bin/gatekeeper_windows.exe main.go 45 | 46 | 47 | -------------------------------------------------------------------------------- /cert_file/ca.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDATCCAemgAwIBAgIJAKCCwAOLbkgiMA0GCSqGSIb3DQEBCwUAMBcxFTATBgNV 3 | BAMMDGV4YW1wbGUxLmNvbTAeFw0yMDAzMzExNTQzMzZaFw0zMzEyMDgxNTQzMzZa 4 | MBcxFTATBgNVBAMMDGV4YW1wbGUxLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEP 5 | ADCCAQoCggEBAK0bl7z0oPQYOj2XSiFljYifyzReev77cmYfGg+GQF3WZqIbYppD 6 | ajMi88gOidfcUDZGQI/tPgO8tB5M5TetP/q+apSu6VK7fMpeia417VBvL9T05yCK 7 | R9ylqDzg+he4DFxdruIu8Q4MhhFNoPSw4DGoNYjQw76UFu1x2h4X01MFm9sJ3hhG 8 | LmcTUb27p3EEbi2s/8TZVsTIuBF6GeJrNxDMBRRvgOm4/NCSWQSVPGGOdYJroQJG 9 | FKxSHb+VjAh1GfSD4np7lTgfMGlLvhTt1meynEUYorV15xnoyeD077NM0OX2JG0Z 10 | 1HbBUqVd2EO4WFX3kXZDGBB8C3wPg2zYtCUCAwEAAaNQME4wHQYDVR0OBBYEFMOA 11 | FTyjXBBcvO0Z5nY34ITA/vwZMB8GA1UdIwQYMBaAFMOAFTyjXBBcvO0Z5nY34ITA 12 | /vwZMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAG/kIiQalUfXPT4/ 13 | QQAMk6msDETY9wEDwnTIsHyXQlZiKxroIgplDHuhlVjRww2Fa0JHqXuRpIJN9UR1 14 | mLZubn3oCMk/hWkgfb714f3/YFho4vyjzfMQ8rwCwrD/r3ZnlMSj39a7bTA7YKW4 15 | Hni4fooM/ny//c+xXeif64yTkmsYCqxSwtVZLvCRJMxfrbgo3f7ILn5rTB7hsUfG 16 | tcwu4g8bNkFnXrVGS1OLBNd53jDDSmPs1pdx9jekR3o8l7Sr5BPo3C/CPOiXdfjJ 17 | Tqm+I2wo6pr/SKbZsslxajGBXjSwhjgkjYRBCLu7wxCJsbxHnl5ORs6O7eoINpcM 18 | e6JTUfY= 19 | -----END CERTIFICATE----- 20 | -------------------------------------------------------------------------------- /cert_file/ca.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEowIBAAKCAQEArRuXvPSg9Bg6PZdKIWWNiJ/LNF56/vtyZh8aD4ZAXdZmohti 3 | mkNqMyLzyA6J19xQNkZAj+0+A7y0HkzlN60/+r5qlK7pUrt8yl6JrjXtUG8v1PTn 4 | IIpH3KWoPOD6F7gMXF2u4i7xDgyGEU2g9LDgMag1iNDDvpQW7XHaHhfTUwWb2wne 5 | GEYuZxNRvbuncQRuLaz/xNlWxMi4EXoZ4ms3EMwFFG+A6bj80JJZBJU8YY51gmuh 6 | AkYUrFIdv5WMCHUZ9IPienuVOB8waUu+FO3WZ7KcRRiitXXnGejJ4PTvs0zQ5fYk 7 | bRnUdsFSpV3YQ7hYVfeRdkMYEHwLfA+DbNi0JQIDAQABAoIBABSNcx2PGdEBU2De 8 | poGCkiRHfJPSJ95AMlvnk2uGb/v0KalTgQh5upEptDHrb/g+AxP+kUnFTBibffMf 9 | BBPxMOhvVS4j+jXFZtKMGOKjKnLjUJWDIjHd2RxcWrYnq+nHA0iwIsRd+GbHm99Q 10 | DS0gFu4uX3TH/IWTBYnZe6EZCRERJmoL3T8oIqW9V8M3lszoQ8UormPAXIH3g+k2 11 | 3L3Dy/Eo8IxoFmsYiCWf8ec16lCyOTS9zLvdNGK26SGU7J/w7x7fhqlZ6zLS97lH 12 | KMgVQtoZWz7S9NrrMRpdDbdgz/KVnm465o7E+X2PuwjJsyG3gCjrsNgq4wJgfZzF 13 | QJmE48ECgYEA1lrLUg2hebSaBHbnYKphpEd9jGA1FMDl6S93kwZvQKUhqEBlK5gv 14 | Ty+4R+/Pvc0qtAj+bLwrL8fFwQ0l3sXtpGbpt1jPVO87ui5ikP0Orfc300asjtp2 15 | JViYgaHHW4phWmlqtQGswaXsDFx608a/OrFw3IwlKDUzGtOEb+b6u/ECgYEAzr1U 16 | 6OtgDHKD6YlPUwgSujENadZ0BFk6wW91DjEldb9eXycgYenWKg2vJZskylRpvzSQ 17 | uKTtQLAxj7WxCymrtrPpA4XvWbFG3p2mSYLt1XO63UE0MJ1f5FDBW/Co4TM7YMbs 18 | Eknqapwm4ahOtDrSbb3uL95yMJBBRkpJjxKIv3UCgYAxxVkqTzHsIWwVl0o4HreX 19 | PmY/XuNUU0nO8A+Sms7gMrdy6qjTC34Io+rlASC6UFYXAXOZ6cMZUAhxv8zIQirg 20 | nmisArn5Xab/nt+SDMAI0rsqmmFctgrytvSKPPceIS5joNB/AMmNGSqK4DpAzAgA 21 | 58xt2TiTcm7QTsaUeQxE8QKBgQCPFk1hVB9LHischLOJRUoo4fBls07i/5sB7JF4 22 | vB0wLL41X1AzVHOs8YGqpoFFJD14X/pWQZgPsKLs0xTxI+s77bM1hAqP6nmhdD12 23 | HY9cr9fCcPGdQB7xV88sQhmwnBPZvHQBiHUdSmxCvImUhi3EVLM5IF2qLP1wl9Pn 24 | mS1aTQKBgDkTOc3Sod1Zj42MBvJQO8uDAjwx/CJDKEh1v2v1roOt0m2H/peJJ9HY 25 | SXB/fk3oXnUyKn23jl9HOwEgArHGTxwnorH7VVOjN8PA5VyPnZzbyRCbo4P24mNT 26 | 5GJjUyHXtAKfYn/vL4XneNo8htRmgO/buDA9Mz03LGFZjHn+/sB7 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /cert_file/ca.srl: -------------------------------------------------------------------------------- 1 | EB10040B98B12C16 2 | -------------------------------------------------------------------------------- /cert_file/cert_file.go: -------------------------------------------------------------------------------- 1 | package cert_file 2 | 3 | import ( 4 | "path/filepath" 5 | "runtime" 6 | ) 7 | 8 | // basepath is the root directory of this package. 9 | var basepath string 10 | 11 | func init() { 12 | _, currentFile, _, _ := runtime.Caller(0) 13 | basepath = filepath.Dir(currentFile) 14 | } 15 | 16 | func Path(rel string) string { 17 | if filepath.IsAbs(rel) { 18 | return rel 19 | } 20 | 21 | return filepath.Join(basepath, rel) 22 | } 23 | -------------------------------------------------------------------------------- /cert_file/client.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICqjCCAZICCQDrEAQLmLEsFTANBgkqhkiG9w0BAQsFADAXMRUwEwYDVQQDDAxl 3 | eGFtcGxlMS5jb20wHhcNMjAwMzI5MTMxNTQyWhcNMzMxMjA2MTMxNTQyWjAXMRUw 4 | EwYDVQQDDAxleGFtcGxlMS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK 5 | AoIBAQDmMVbGx9gdqb3l+WcQAVht5YvIfPhrUx8/PjZ8XUFwqIt8s4JJcmCoYrpi 6 | QwYjckajLNEfeovieFPlx1EpDbMa9C3QFeuqCI453w2InAEiCU6VGKqftSy/OQM0 7 | 19fNOX7ec9uB+64AjifSkyjr3GnmJjSHZS8XczKZk4XZV8vA1oUwMvW7shbDL8ar 8 | Qw7MEALFzCFgz7Ff5/Hfpm0gHY0A2ychUTn/w6zB/fTTV5+kYtZNnmFP5zPBdOVX 9 | soNYRY3GXGRAvoJy3o9tzPWetL/0Uzjmc1HU+kHVp2vVhyfsuZldHxgTY9KRA22m 10 | /5jdVPJX5Z4UBEaW5+hrhh8T6BQJAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAETU 11 | I5Tkek8W+I86arMmswXTm+/CsJdpoXAm3xuhXoUQo7QfZI0lQ/vbsSMc26GUn8ho 12 | NLMJvBv31QYIq39dQypFKAloA9mml91jUwMno9L0nsEyYMXcFaUvs2YE5v2RZghN 13 | j86FBGOt0Ou3UDGhvzbQUvIS53gurCavDCFSHVuFxKeLhyVRCoESWClRX426mVv7 14 | lPfHXfAIOs0yZxvxRA/fVokGfqhmTjPIeLPuDTxoR4uVqV/kRJ7tb6mrNAGVhDZJ 15 | BzrhvZ8DbF56sImetftbkMdzIumddy2H3emd8syEf+zy8LLMfXnLS9QXkFD2+RRJ 16 | EaC4UfgFTEcvooUO6/s= 17 | -----END CERTIFICATE----- 18 | -------------------------------------------------------------------------------- /cert_file/client.csr: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE REQUEST----- 2 | MIICXDCCAUQCAQAwFzEVMBMGA1UEAwwMZXhhbXBsZTEuY29tMIIBIjANBgkqhkiG 3 | 9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5jFWxsfYHam95flnEAFYbeWLyHz4a1MfPz42 4 | fF1BcKiLfLOCSXJgqGK6YkMGI3JGoyzRH3qL4nhT5cdRKQ2zGvQt0BXrqgiOOd8N 5 | iJwBIglOlRiqn7UsvzkDNNfXzTl+3nPbgfuuAI4n0pMo69xp5iY0h2UvF3MymZOF 6 | 2VfLwNaFMDL1u7IWwy/Gq0MOzBACxcwhYM+xX+fx36ZtIB2NANsnIVE5/8Oswf30 7 | 01efpGLWTZ5hT+czwXTlV7KDWEWNxlxkQL6Cct6Pbcz1nrS/9FM45nNR1PpB1adr 8 | 1Ycn7LmZXR8YE2PSkQNtpv+Y3VTyV+WeFARGlufoa4YfE+gUCQIDAQABoAAwDQYJ 9 | KoZIhvcNAQELBQADggEBALEp4ePq57a+XRa7GSS6WUMX5HibHNSqy1VrA5h8LdTP 10 | zFv8ZnasuCgWh3Z7w7j8TeBgygO4wwldbafdXS234rzYas5wXlVFW7tY4jX/jCDy 11 | 7ftypbYGnKCMScQ34FKRB2WOZvWEdMYN5i8EyKaBy0Bitc+kZ+yCGSNH57QptOgR 12 | oRMK/r6Z+TGucF6EpLhxgOknUCitGme9FP96A6gs3lLrPQ1yOYE456ubTzq6svVo 13 | 5viGzSZwADzGzBNQ0GDr7Fv9boLEKyK+wCB+8us0PDVDid9ryIokjxT+Ddnv19MS 14 | fEL9GdJTyQCu/JWWynQgh1t5uF4vyUgi6wlU8Ermjnw= 15 | -----END CERTIFICATE REQUEST----- 16 | -------------------------------------------------------------------------------- /cert_file/client.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEpQIBAAKCAQEA5jFWxsfYHam95flnEAFYbeWLyHz4a1MfPz42fF1BcKiLfLOC 3 | SXJgqGK6YkMGI3JGoyzRH3qL4nhT5cdRKQ2zGvQt0BXrqgiOOd8NiJwBIglOlRiq 4 | n7UsvzkDNNfXzTl+3nPbgfuuAI4n0pMo69xp5iY0h2UvF3MymZOF2VfLwNaFMDL1 5 | u7IWwy/Gq0MOzBACxcwhYM+xX+fx36ZtIB2NANsnIVE5/8Oswf3001efpGLWTZ5h 6 | T+czwXTlV7KDWEWNxlxkQL6Cct6Pbcz1nrS/9FM45nNR1PpB1adr1Ycn7LmZXR8Y 7 | E2PSkQNtpv+Y3VTyV+WeFARGlufoa4YfE+gUCQIDAQABAoIBAQDh1I/EjnTIjSl/ 8 | QBHLUvaVQjjDpU71w+OI4RkFI1w7ES9fVKDCO5L2P32JPyX7inYgSayUDF7F/LSa 9 | XdOSyhznmZyEY60C8EfQILMfTaWS5byRa1ShQFY4987cfdD6RqjfxpwYRnirlMse 10 | vD7OPjwqjVrFZhAwGlUO8/rBgm+jqzUWL4aGoGuH2M/6XP6rUN42GFrLxQnshxpq 11 | CiGTnyB1nYYdQKSEWSBoEthQKNXR/MyTQDHt05EpAu8TQFGi/oXytFzu6LlVlQVB 12 | OdfDAXDcLqF1VyEndjdCNp8i8jWVdvNuHCm/dw2v286HADq8ZnaspG6ghxvegHSF 13 | CPWlAvdxAoGBAPnd2UWFpo95EdvsDo2MKkgJRmqyjx2kkUPjClgZgz6sfrjufV71 14 | Axonj8SCM4QmnjvZN8tdjKc7N1xYOShDAlIUkuAKNw/uxyzuj98Fi5SwEfCrCowT 15 | MlJEliRFcRhSDKJdUwhz+gX3f/SFHLUOT8S2VR3kssesklznB0k9R6CtAoGBAOvX 16 | 3EmZd4vDijDLmZ5hz5fmsI9/LYrDSzd5+D0nYHrt0l81Yn69T4TNU6pau3Qv6ZET 17 | 5PyDiuDSTi2XLV3TPgeGqimU6E3MzH2ZX69Sqz74JNSrOj2xmiLTHpjFLx2HgYBo 18 | 2r/4qaX3UbeLosU+OAgTZz7tAUu8oKaVNWAMp8BNAoGBAMrF+CoMUWRDq3OfORwz 19 | 8KgvJr82EyDKSb0fBmkCo1j8YYawvHuQOKlEC888npQTRvxoxne6fofEbaP6UMBU 20 | yRBVc/BvOcorS+Yx3/3soR7UWGrIU7HjbkYHNRVQaXyEMY6eT+EfRXsMJQIJ+IcS 21 | izenhKHS1Cdo7AqKzoXoPjWtAoGBAOY6toBewZX90GRsDU3Q/B+0gv3CUEoa9c11 22 | UwEX9JZsnaZ17e39jqf87UTrB/2XXDJVMn/TRfrNMqZM42upcO9V5b1r8Q8p8yJV 23 | XFRBpox1HzdA5D0fqHGAmWjXbXQtOR2KvqkS7UHoWqyMMtzMLVSs5GhIY7B0tnuI 24 | Qy+9ivEtAoGAdvwxqztV2UTTszPhBZZ5rWhMB1W4DAW4SkqVtChsaVgjPO+w1RCz 25 | sRauu3pewdyhseOouDvGB7nRdozfusr8gE1qimj7T+bvBAz67jfLdjIB/EWt73UM 26 | rypIzF6D7FQMx1FTD1VDBqE3dmkDbTj6A3vrcv9FrqqS+IirNlbwdbk= 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /cert_file/server.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICqjCCAZICCQDrEAQLmLEsFjANBgkqhkiG9w0BAQsFADAXMRUwEwYDVQQDDAxl 3 | eGFtcGxlMS5jb20wHhcNMjAwMzMxMTU0NTA5WhcNMzMxMjA4MTU0NTA5WjAXMRUw 4 | EwYDVQQDDAxleGFtcGxlMS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK 5 | AoIBAQDMfuI0VxqOmpgFa65BMa6pv5rYYLLAVuQeGNPbl19sZeSNlfR0Zupk2jmg 6 | MTK4F6R77bCpv3505txFxFxn8vFVJcOSDwpZCCl0iTI9jZoYugYh2Mk2bslHCxV0 7 | k/yzVhCCIvxeKFb3/XSUSu4YZ76D9PmqSxGUqKUqXv5lgI3QWDMO3/CwWP6I0SWv 8 | tyxB63mM3UfjfHw301syvJuPxWfAMdCHnxiAa8RlLcoKMawMQUyZqOP5dHmL565V 9 | izHDbrB+B2D2gznoazV95rW7adlpf9cIsQI30ExksL+FnDcmlsChpjooHLJGF8Wg 10 | XZNU1RTfp9oOpA7/K0ul5D9BYGgfAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAAXt 11 | FToCcyVURZXKNDKJEw8qHMJshwygtHnnxrZtioGuy2T767PAMWoi7yrv4WSRGnWn 12 | lhM6nx3iUBDRJ7UNEbn3bOQJm5kAVGTb+/dB7rm7slwsQ6Es2Tl+cnr9ySruIs0s 13 | jPRr48ufD3C1275d7KhDXXT2l9un+w5G8a55EfXt5qN5tQByR5jNNsSkYnBhtrbk 14 | VvjSpTm4n98qI3PfMd4JyAJjkxm/qvgeCS6XZS7LdTwf5LTOaEg/+0BZ0KoQC0uu 15 | C35qUJv3hUEXm3bUz/HD2CrFloskD+jcnLcqIWjQ79uFfgJVH4fH+Tl1P+/+N0zp 16 | TEGij1GvoSfrnq30qpc= 17 | -----END CERTIFICATE----- 18 | -------------------------------------------------------------------------------- /cert_file/server.csr: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE REQUEST----- 2 | MIICXDCCAUQCAQAwFzEVMBMGA1UEAwwMZXhhbXBsZTEuY29tMIIBIjANBgkqhkiG 3 | 9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzH7iNFcajpqYBWuuQTGuqb+a2GCywFbkHhjT 4 | 25dfbGXkjZX0dGbqZNo5oDEyuBeke+2wqb9+dObcRcRcZ/LxVSXDkg8KWQgpdIky 5 | PY2aGLoGIdjJNm7JRwsVdJP8s1YQgiL8XihW9/10lEruGGe+g/T5qksRlKilKl7+ 6 | ZYCN0FgzDt/wsFj+iNElr7csQet5jN1H43x8N9NbMrybj8VnwDHQh58YgGvEZS3K 7 | CjGsDEFMmajj+XR5i+euVYsxw26wfgdg9oM56Gs1fea1u2nZaX/XCLECN9BMZLC/ 8 | hZw3JpbAoaY6KByyRhfFoF2TVNUU36faDqQO/ytLpeQ/QWBoHwIDAQABoAAwDQYJ 9 | KoZIhvcNAQELBQADggEBAGL7N5UnX0K1i0OH+dJmTR2o14qFoub/eb3vXFZmLdmS 10 | KIR3eHBfnQPLwD6cOaoQiOZgBXOLjbV3lXyogDETUtRxBx+8nb4AX63oPyiHCu3M 11 | htJlxvB1tLZRvWePqypKNZ8hJy8c9c71ZvZggj7uzTT/yupjbs/u1s64NSGtEjio 12 | jXhlxOcClqn0NGIzVyyByVKTFCmOqNKBz5ohqCN78DnxRwzB8SOMLT9gzzXemVXg 13 | vOtctJo55KG6D35Ra5I6FVU68851W8o0FQy9Il+2GrbL0Lw8NpEbbGnfbD65fkKZ 14 | 33xk9GCEWmCYtL7XkCV1cvRWgKGrmoBK9LsScbH0O2A= 15 | -----END CERTIFICATE REQUEST----- 16 | -------------------------------------------------------------------------------- /cert_file/server.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEowIBAAKCAQEAzH7iNFcajpqYBWuuQTGuqb+a2GCywFbkHhjT25dfbGXkjZX0 3 | dGbqZNo5oDEyuBeke+2wqb9+dObcRcRcZ/LxVSXDkg8KWQgpdIkyPY2aGLoGIdjJ 4 | Nm7JRwsVdJP8s1YQgiL8XihW9/10lEruGGe+g/T5qksRlKilKl7+ZYCN0FgzDt/w 5 | sFj+iNElr7csQet5jN1H43x8N9NbMrybj8VnwDHQh58YgGvEZS3KCjGsDEFMmajj 6 | +XR5i+euVYsxw26wfgdg9oM56Gs1fea1u2nZaX/XCLECN9BMZLC/hZw3JpbAoaY6 7 | KByyRhfFoF2TVNUU36faDqQO/ytLpeQ/QWBoHwIDAQABAoIBABMQqUoz7Lfq1c17 8 | ko1lcmFFCcyuhzvDXhUoP2gznqPehAZnOpk3lxa7+a9jptTe72jWaigJQGLpuxOO 9 | EQdn8PP9R1RwrohKaIMC33o5n2o6vaOeMHQws/c5o1BxE5gsp/FaDalBnYoVSS8i 10 | JTyFP4/R4QztXsA7UNq4bINODuqjg8O6Fc7AQhXLOe7It8rQxbuNKF0Pps2RwzEL 11 | yTS1Ehi3Rujhb/uBVEnzYZluhylYUZJ2sE7SijNxHGp9sHe6l/YkKC1GLf+7SxvA 12 | 3vWC4SU/REnCbfpUKkm8eV2276frvvY9IJSgWaINLOnRrFKfYTEvuuIBm5Bav5Sl 13 | 1htTUBECgYEA6L+bwhLy5RD38xjuAIt4a1E7KIU5cDA/a8DUM5rTN3IZqlkbprRl 14 | wSe9MzsudFQd33qzyJXalhvWevrJiycOc3pZ92KDd5cnsh5a+JQkOO68evxyrPpx 15 | +d7+K31fu0qBBxfO26RPMGLOL/7CXfVBOeJZAWH0lO+uI5n8Aphv2TsCgYEA4Oy6 16 | MBaRDWYyM5gZbJdWSk1G9HR3rV8fQgjz9aMQksaJdV0Bm1IgqzMQEuDFr43tK89s 17 | UV/2W4ULl5esKJq9RBs4KsERIrARXgooU2Kw3A3PRzkg7mOS68qL/ESOtxF9F+UC 18 | PSyH8ehdfN6WAdNlxloIXsLJjkHWfJ9/ivYkHm0CgYA2T8gL8JoHg/8oFhArxl/y 19 | QwFYAkaV/FxAS7340M8q6BA/JQ4Dx6LbAOfwlYXQlXRnGt3rF7TrRFG3XuA6/YEs 20 | x0dJKA7fkEEuGlFGImOVeXg4BsLHxKVmFngfM+Fr9gXH3vFhZaUo+FV+86btf/aZ 21 | iE0WuoH1YzyyiBM7k9C90wKBgQDFYDcKl+L15SZMjD5TQoJgdWu8fK/AneZqJj0e 22 | 4tdaVYquSM1uJSWx1f9W8ZPIOD1V4pFk31bqfNftURWsFA3eRByHuCB3VhYHddZp 23 | RgN5N00bbRBu4UY+T+GDoA20rE4ft8C9OeSZ7ZSMTS9Jrt5yrvMFZN1GTpQPjE95 24 | /AE6CQKBgDEmZCoZt8vgzqWjsylzuQpx2gWnZT+5rXi3gOGZmzvqdZdOloZqxgVs 25 | EdCP2ZMcwcXecsraoEjdLMCjRVlgw1cCd+ag4yX93FQTcqClx8oqSBSpcoAab7xo 26 | COBb/9wUo4Dl7JztpX3UJacEf0QQpMUcfUAvs+w3rDkRlu0QeLz/ 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /conf/dev/base.toml: -------------------------------------------------------------------------------- 1 | # This is base config 2 | [base] 3 | debug_mode="release" 4 | time_location="Asia/Chongqing" 5 | ser_name="gatekeeper" 6 | 7 | [http] 8 | addr =":8880" # 监听地址, default ":8700" 9 | read_timeout = 10 # 建立连接到读取请求内容超时时间 10 | write_timeout = 10 # 读取内容到响应的超时时间 11 | max_header_bytes = 20 # 最大的header大小,二进制位长度 12 | allow_ip = [ # 白名单ip列表 13 | "127.0.0.1", 14 | "192.168.1.1" 15 | ] 16 | 17 | [pprof] 18 | 19 | [log] 20 | on = true #日志开关,是否开启 21 | level = "info" #日志级别,只支持小写 22 | file_prefix = "didi" #文件名前缀, 默认会加后缀.log 或者.log.wf 23 | file_dir = "logs" #生成文件目录 24 | auto_clear = false #是否自动清理日志 25 | clear_hours = 3 #保留日志 n 个小时的 26 | clear_step = 3 #清理时间从当前时间前推 n 个小时算起 27 | separate = false #文件是否分离,分离文件以.wf结尾 28 | disable_link = false #是否启动link 29 | 30 | [cluster] 31 | cluster_ip="127.0.0.1" 32 | cluster_port="8080" 33 | cluster_ssl_port="4433" 34 | 35 | [swagger] 36 | title="gatekeeper swagger API" 37 | desc="This is a sample server celler server." 38 | host="" 39 | base_path="" -------------------------------------------------------------------------------- /conf/dev/mysql_map.toml: -------------------------------------------------------------------------------- 1 | # this is mysql config 2 | [list] 3 | [list.default] 4 | driver_name = "mysql" 5 | data_source_name = "root:123456@tcp(127.0.0.1:3306)/gatekeeper?charset=utf8&parseTime=true&loc=Asia%2FChongqing" 6 | max_open_conn = 20 7 | max_idle_conn = 10 8 | max_conn_life_time = 100 -------------------------------------------------------------------------------- /conf/dev/proxy.toml: -------------------------------------------------------------------------------- 1 | # This is base config 2 | 3 | [base] 4 | debug_mode="release" 5 | time_location="Asia/Chongqing" 6 | 7 | [http] 8 | addr =":8080" # 监听地址, default ":8700" 9 | read_timeout = 10 # 读取超时时长 10 | write_timeout = 10 # 写入超时时长 11 | max_header_bytes = 20 # 最大的header大小,二进制位长度 12 | 13 | [https] 14 | addr =":4433" # 监听地址, default ":8700" 15 | read_timeout = 10 # 读取超时时长 16 | write_timeout = 10 # 写入超时时长 17 | max_header_bytes = 20 # 最大的header大小,二进制位长度 -------------------------------------------------------------------------------- /conf/dev/redis_map.toml: -------------------------------------------------------------------------------- 1 | [list] 2 | [list.default] 3 | proxy_list = ["127.0.0.1:6379"] 4 | conn_timeout = 500 5 | password = "" 6 | db = 0 7 | read_timeout = 1000 8 | write_timeout = 1000 9 | max_active = 200 10 | max_idle = 500 -------------------------------------------------------------------------------- /control.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ############################################# 3 | ## main 4 | ## 以托管方式, 启动服务 5 | ## control.sh脚本, 必须实现start方法 6 | ############################################# 7 | 8 | set -e 9 | workspace=$(cd $(dirname $0) && pwd -P) 10 | cd $workspace 11 | app=gatekeeper 12 | cmd_run=" run -c conf/dev/ -p" 13 | 14 | function check_go_version() { 15 | use_go_version="1120" 16 | go_version=`go version |grep -Eo '([0-9])\.([0-9]{1,2})\.([0-9]*)' |awk -F '.' '{print $1 $2 $3}'` 17 | if [ ! -n "$go_version" ] || [ "$go_version" -le "$use_go_version" ];then 18 | echo "go version < 1.12.0 pleace upgreate" 19 | echo "Installation package download address : https://golang.org/dl/ or https://golang.google.cn/dl/" 20 | echo "Installation tutorial address : https://www.runoob.com/go/go-environment.html" 21 | exit -1 22 | fi 23 | } 24 | 25 | function run() { 26 | panel_type=$1 27 | cmd_run="${cmd_run} ${panel_type}" 28 | 29 | # bin run gatekeeper 30 | if [ -f "${app}" ];then 31 | echo "run bin gatekeeper" 32 | chmod 755 ${app} 33 | eval ./${app}${cmd_run} 34 | return 35 | fi 36 | 37 | # go run gatekeeper 38 | if [ -f "main.go" ];then 39 | check_go_version 40 | echo "go run main.go ${cmd_run}" 41 | eval go run main.go ${cmd_run} 42 | return 43 | fi 44 | echo "not found run file" 45 | } 46 | 47 | function help() { 48 | echo -e "Control command manager" 49 | echo -e "Usage:" 50 | echo -e "\t[command]" 51 | echo -e "\n" 52 | echo -e "Available Commands:" 53 | echo -e "\tstart_proxy \t start gatekeeper proxy" 54 | echo -e "\tstart_control \t start gatekeeper control" 55 | echo -e "\tstart_both \t start gatekeeper control && proxy" 56 | echo -e "\n" 57 | echo -e "Flags:" 58 | echo -e "\t-h,\t--help\thelp for this command" 59 | } 60 | 61 | action=$1 62 | case $action in 63 | -h|--help) 64 | help 65 | ;; 66 | "start_proxy" ) 67 | run proxy 68 | ;; 69 | "start_control" ) 70 | run control 71 | ;; 72 | "start_both" ) 73 | run both 74 | ;; 75 | * ) 76 | help 77 | ;; 78 | esac 79 | -------------------------------------------------------------------------------- /dashboard_middleware/recovery.go: -------------------------------------------------------------------------------- 1 | package dashboard_middleware 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "github.com/didi/gatekeeper/golang_common/lib" 7 | "github.com/didi/gatekeeper/golang_common/trace" 8 | "github.com/didi/gatekeeper/public" 9 | "github.com/gin-gonic/gin" 10 | "runtime/debug" 11 | ) 12 | 13 | func RecoveryMiddleware() gin.HandlerFunc { 14 | return func(c *gin.Context) { 15 | defer func() { 16 | if err := recover(); err != nil { 17 | if lib.ConfBase.Log.On { 18 | public.GinLogWarning(c, trace.DLTagUndefined, map[string]interface{}{ 19 | "error": fmt.Sprint(err), 20 | "stack": string(debug.Stack()), 21 | }) 22 | } 23 | if lib.ConfBase.Base.DebugMode != "debug" { 24 | ResponseError(c, 500, errors.New("内部错误")) 25 | return 26 | } else { 27 | ResponseError(c, 500, errors.New(fmt.Sprint(err))) 28 | return 29 | } 30 | } 31 | }() 32 | c.Next() 33 | } 34 | } -------------------------------------------------------------------------------- /dashboard_middleware/response.go: -------------------------------------------------------------------------------- 1 | package dashboard_middleware 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "github.com/didi/gatekeeper/golang_common/lib" 7 | "github.com/didi/gatekeeper/public" 8 | "github.com/gin-gonic/gin" 9 | "strings" 10 | ) 11 | 12 | type ResponseCode int 13 | 14 | //1000以下为通用码,1000以上为用户自定义码 15 | const ( 16 | SuccessCode ResponseCode = iota 17 | UndefErrorCode 18 | ValidErrorCode 19 | InternalErrorCode 20 | InvalidRequestErrorCode ResponseCode = 401 21 | CustomizeCode ResponseCode = 1000 22 | ) 23 | 24 | type Response struct { 25 | ErrorCode ResponseCode `json:"errno"` 26 | ErrorMsg string `json:"errmsg"` 27 | Data interface{} `json:"data"` 28 | TraceId interface{} `json:"trace_id"` 29 | Stack interface{} `json:"stack"` 30 | } 31 | 32 | func ResponseError(c *gin.Context, code ResponseCode, err error) { 33 | traceContext := public.GetGinTraceContext(c) 34 | stack := "" 35 | if c.Query("is_debug") == "1" || lib.GetConfEnv() == "dev" { 36 | stack = strings.Replace(fmt.Sprintf("%+v", err), err.Error()+"\n", "", -1) 37 | } 38 | 39 | resp := &Response{ErrorCode: code, ErrorMsg: err.Error(), Data: "", TraceId: traceContext.TraceId, Stack: stack} 40 | c.JSON(200, resp) 41 | response, _ := json.Marshal(resp) 42 | c.Set("response", string(response)) 43 | c.AbortWithError(200, err) 44 | } 45 | 46 | func ResponseSuccess(c *gin.Context, data interface{}) { 47 | traceContext := public.GetGinTraceContext(c) 48 | resp := &Response{ErrorCode: SuccessCode, ErrorMsg: "", Data: data, TraceId: traceContext.TraceId} 49 | c.JSON(200, resp) 50 | response, _ := json.Marshal(resp) 51 | c.Set("response", string(response)) 52 | } 53 | -------------------------------------------------------------------------------- /dashboard_middleware/session_auth.go: -------------------------------------------------------------------------------- 1 | package dashboard_middleware 2 | 3 | import ( 4 | "errors" 5 | "github.com/didi/gatekeeper/public" 6 | "github.com/gin-gonic/contrib/sessions" 7 | "github.com/gin-gonic/gin" 8 | ) 9 | 10 | func SessionAuthMiddleware() gin.HandlerFunc { 11 | return func(c *gin.Context) { 12 | session := sessions.Default(c) 13 | if adminInfo, ok := session.Get(public.AdminSessionInfoKey).(string); !ok || adminInfo == "" { 14 | ResponseError(c, InternalErrorCode, errors.New("user not login")) 15 | c.Abort() 16 | return 17 | } 18 | c.Next() 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /dashboard_router/httpserver.go: -------------------------------------------------------------------------------- 1 | package dashboard_router 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "github.com/didi/gatekeeper/golang_common/lib" 7 | "github.com/didi/gatekeeper/golang_common/zerolog/log" 8 | "github.com/gin-gonic/gin" 9 | "net/http" 10 | "time" 11 | ) 12 | 13 | var ( 14 | HttpSrvHandler *http.Server 15 | ) 16 | 17 | func HttpServerRun() { 18 | gin.SetMode(lib.GetStringConf("base.base.debug_mode")) 19 | r := InitRouter() 20 | HttpSrvHandler = &http.Server{ 21 | Addr: lib.GetStringConf("base.http.addr"), 22 | Handler: r, 23 | ReadTimeout: time.Duration(lib.GetIntConf("base.http.read_timeout")) * time.Second, 24 | WriteTimeout: time.Duration(lib.GetIntConf("base.http.write_timeout")) * time.Second, 25 | MaxHeaderBytes: 1 << uint(lib.GetIntConf("base.http.max_header_bytes")), 26 | } 27 | go func() { 28 | log.Info().Msg(lib.Purple(fmt.Sprintf("start HTTP control service [http://127.0.0.1%s/dist/]", lib.GetStringConf("base.http.addr")))) 29 | if err := HttpSrvHandler.ListenAndServe(); err != nil { 30 | log.Error().Msg(lib.Purple(fmt.Sprintf("failed to start HTTP service service [%s] %v", lib.GetStringConf("base.http.addr"), err))) 31 | } 32 | }() 33 | } 34 | 35 | func HttpServerStop() { 36 | ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) 37 | defer cancel() 38 | if err := HttpSrvHandler.Shutdown(ctx); err != nil { 39 | log.Error().Msg(lib.Purple(fmt.Sprintf("HttpServerStop err:%v", err))) 40 | } 41 | log.Error().Msg(lib.Purple(fmt.Sprintf("stop HTTP control service [%s]", lib.GetStringConf("base.http.addr")))) 42 | } 43 | -------------------------------------------------------------------------------- /debug.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -x 3 | export GO111MODULE=on 4 | export GOPROXY=https://goproxy.cn 5 | go build -o bin/gatekeeper 6 | rm -rf ./logs/* 7 | ps aux | grep gatekeeper | grep -v 'grep' | awk '{print $2}' | xargs kill 8 | 9 | action=$1 10 | case $action in 11 | "control" ) 12 | ./bin/gatekeeper -c ./conf/dev/ -p $action 13 | ;; 14 | "proxy" ) 15 | ./bin/gatekeeper -c ./conf/dev/ -p $action 16 | ;; 17 | * ) 18 | echo "unknown command" 19 | exit 1 20 | ;; 21 | esac -------------------------------------------------------------------------------- /dist/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/didi/GateKeeper/3c5211ab37040f47c5c9bd236adb06e8c69127d3/dist/favicon.ico -------------------------------------------------------------------------------- /dist/static/css/chunk-1c7e619f.0be79fdd.css: -------------------------------------------------------------------------------- 1 | .waves-ripple{position:absolute;border-radius:100%;background-color:rgba(0,0,0,.15);background-clip:padding-box;pointer-events:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-transform:scale(0);transform:scale(0);opacity:1}.waves-ripple.z-active{opacity:0;-webkit-transform:scale(2);transform:scale(2);-webkit-transition:opacity 1.2s ease-out,-webkit-transform .6s ease-out;transition:opacity 1.2s ease-out,-webkit-transform .6s ease-out;transition:opacity 1.2s ease-out,transform .6s ease-out;transition:opacity 1.2s ease-out,transform .6s ease-out,-webkit-transform .6s ease-out}.pagination_container[data-v-07260ece]{background:#fff;padding:32px 16px}.pagination_container.hidden[data-v-07260ece]{display:none}.app-container .filter-container[data-v-4c48a17c]{display:-webkit-box;display:-ms-flexbox;display:flex;padding:20px;-webkit-box-shadow:0 2px 6px 0 rgba(26,27,31,.12);box-shadow:0 2px 6px 0 rgba(26,27,31,.12);border-radius:5px;background-color:#fff;margin-bottom:6px}.app-container .filter-container-input[data-v-4c48a17c]{width:20rem;border-radius:4px}.app-container .filter-container-btn[data-v-4c48a17c]{width:141px;background:#3d6aff;border-radius:4px;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.app-container-table[data-v-4c48a17c]{padding:20px;-webkit-box-shadow:0 2px 6px 0 rgba(26,27,31,.12);box-shadow:0 2px 6px 0 rgba(26,27,31,.12);border-radius:5px;background-color:#fff}.app-container-table .btn[data-v-4c48a17c]{font-family:PingFangSC-Regular;font-size:14px;color:#3d6aff;text-align:left;line-height:22px;font-weight:400}[data-v-4c48a17c] .filter-container-input .el-input-group__append{background-color:#fff;width:40px} -------------------------------------------------------------------------------- /dist/static/css/chunk-294e231b.d3b4ac8e.css: -------------------------------------------------------------------------------- 1 | .mixin-components-container[data-v-8bcdbc8a]{background-color:#f0f2f5;padding:30px;min-height:calc(100vh - 84px)}.component-item[data-v-8bcdbc8a]{min-height:100px}.el-select .el-input[data-v-8bcdbc8a]{width:130px}.input-with-select .el-input-group__prepend[data-v-8bcdbc8a]{background-color:#fff} -------------------------------------------------------------------------------- /dist/static/css/chunk-3f238847.a2fda0b3.css: -------------------------------------------------------------------------------- 1 | .errPage-container[data-v-61343d02]{width:800px;max-width:100%;margin:100px auto}.errPage-container .pan-back-btn[data-v-61343d02]{background:#008489;color:#fff;border:none!important}.errPage-container .pan-gif[data-v-61343d02]{margin:0 auto;display:block}.errPage-container .pan-img[data-v-61343d02]{display:block;margin:0 auto;width:100%}.errPage-container .text-jumbo[data-v-61343d02]{font-size:60px;font-weight:700;color:#484848}.errPage-container .list-unstyled[data-v-61343d02]{font-size:14px}.errPage-container .list-unstyled li[data-v-61343d02]{padding-bottom:5px}.errPage-container .list-unstyled a[data-v-61343d02]{color:#008489;text-decoration:none}.errPage-container .list-unstyled a[data-v-61343d02]:hover{text-decoration:underline} -------------------------------------------------------------------------------- /dist/static/css/chunk-3f3d4b77.fe722f73.css: -------------------------------------------------------------------------------- 1 | .waves-ripple{position:absolute;border-radius:100%;background-color:rgba(0,0,0,.15);background-clip:padding-box;pointer-events:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-transform:scale(0);transform:scale(0);opacity:1}.waves-ripple.z-active{opacity:0;-webkit-transform:scale(2);transform:scale(2);-webkit-transition:opacity 1.2s ease-out,-webkit-transform .6s ease-out;transition:opacity 1.2s ease-out,-webkit-transform .6s ease-out;transition:opacity 1.2s ease-out,transform .6s ease-out;transition:opacity 1.2s ease-out,transform .6s ease-out,-webkit-transform .6s ease-out}.pagination_container[data-v-07260ece]{background:#fff;padding:32px 16px}.pagination_container.hidden[data-v-07260ece]{display:none}.app-container .filter-container[data-v-ef1b8ba4]{display:-webkit-box;display:-ms-flexbox;display:flex;padding:20px;-webkit-box-shadow:0 2px 6px 0 rgba(26,27,31,.12);box-shadow:0 2px 6px 0 rgba(26,27,31,.12);border-radius:5px;background-color:#fff;margin-bottom:6px}.app-container .filter-container-input[data-v-ef1b8ba4]{width:20rem;border-radius:4px}.app-container .filter-container-btn[data-v-ef1b8ba4]{width:141px;background:#3d6aff;border-radius:4px;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.app-container-table[data-v-ef1b8ba4]{padding:20px;-webkit-box-shadow:0 2px 6px 0 rgba(26,27,31,.12);box-shadow:0 2px 6px 0 rgba(26,27,31,.12);border-radius:5px;background-color:#fff}.app-container-table .btn[data-v-ef1b8ba4]{font-family:PingFangSC-Regular;font-size:14px;color:#3d6aff;text-align:left;line-height:22px;font-weight:400}[data-v-ef1b8ba4] .filter-container-input .el-input-group__append{background-color:#fff;width:40px} -------------------------------------------------------------------------------- /dist/static/css/chunk-4ce61457.41f5667e.css: -------------------------------------------------------------------------------- 1 | .mixin-components-container[data-v-6a0bc019]{background-color:#f0f2f5;padding:30px;min-height:calc(100vh - 84px)}.component-item[data-v-6a0bc019]{min-height:100px}.card[data-v-6a0bc019]{margin-top:20px}.bottom[data-v-6a0bc019]{background:#fff;display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-pack:distribute;justify-content:space-around;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-shadow:0 2px 12px 0 rgb(0 0 0/10%);box-shadow:0 2px 12px 0 rgb(0 0 0/10%);margin-top:10px;padding:30px 0} -------------------------------------------------------------------------------- /dist/static/css/chunk-61ae4e7a.93d95231.css: -------------------------------------------------------------------------------- 1 | @supports (-webkit-mask:none) and (not (cater-color:#fff)){.login-container .el-input input{color:#fff}}.login-container .el-input{display:inline-block;height:47px;width:85%}.login-container .el-input input{background:transparent;border:0;-webkit-appearance:none;border-radius:0;padding:12px 5px 12px 15px;color:#fff;height:47px;caret-color:#fff}.login-container .el-input input:-webkit-autofill{-webkit-box-shadow:0 0 0 1000px #283443 inset!important;box-shadow:inset 0 0 0 1000px #283443!important;-webkit-text-fill-color:#fff!important}.login-container .el-form-item{border:1px solid hsla(0,0%,100%,.1);background:rgba(0,0,0,.1);border-radius:5px;color:#454545}.login-container[data-v-11647fdd]{min-height:100%;width:100%;background-color:#2d3a4b;overflow:hidden}.login-container .login-form[data-v-11647fdd]{position:relative;width:520px;max-width:100%;padding:160px 35px 0;margin:0 auto;overflow:hidden}.login-container .tips[data-v-11647fdd]{font-size:14px;color:#fff;margin-bottom:10px}.login-container .tips span[data-v-11647fdd]:first-of-type{margin-right:16px}.login-container .svg-container[data-v-11647fdd]{padding:6px 5px 6px 15px;color:#889aa4;vertical-align:middle;width:30px;display:inline-block}.login-container .title-container[data-v-11647fdd]{position:relative}.login-container .title-container .title[data-v-11647fdd]{font-size:26px;color:#eee;margin:0 auto 40px auto;text-align:center;font-weight:700}.login-container .show-pwd[data-v-11647fdd]{position:absolute;right:10px;top:7px;font-size:16px;color:#889aa4;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.login-container .thirdparty-button[data-v-11647fdd]{position:absolute;right:0;bottom:6px}@media only screen and (max-width:470px){.login-container .thirdparty-button[data-v-11647fdd]{display:none}} -------------------------------------------------------------------------------- /dist/static/css/chunk-6525d41e.b1d9f1ec.css: -------------------------------------------------------------------------------- 1 | .mixin-components-container[data-v-19bb35ad]{background-color:#f0f2f5;padding:30px;min-height:calc(100vh - 84px)}.component-item[data-v-19bb35ad]{min-height:100px}.el-select .el-input[data-v-19bb35ad]{width:130px}.input-with-select .el-input-group__prepend[data-v-19bb35ad]{background-color:#fff} -------------------------------------------------------------------------------- /dist/static/css/chunk-68b61de5.fb787c4e.css: -------------------------------------------------------------------------------- 1 | .mixin-components-container[data-v-e17f2ec0]{background-color:#f0f2f5;padding:30px;min-height:calc(100vh - 84px)}.component-item[data-v-e17f2ec0]{min-height:100px}.card[data-v-e17f2ec0]{margin-top:20px}.bottom[data-v-e17f2ec0]{background:#fff;display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-pack:distribute;justify-content:space-around;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-shadow:0 2px 12px 0 rgb(0 0 0/10%);box-shadow:0 2px 12px 0 rgb(0 0 0/10%);margin-top:10px;padding:30px 0} -------------------------------------------------------------------------------- /dist/static/css/chunk-6f6c0d4e.c5f2d082.css: -------------------------------------------------------------------------------- 1 | .chart-container[data-v-33d2dd29]{position:relative;width:100%;height:calc(100vh - 84px)} -------------------------------------------------------------------------------- /dist/static/css/chunk-80699a70.c0f8cc73.css: -------------------------------------------------------------------------------- 1 | .panel-group[data-v-ec27f13c]{margin-top:18px}.panel-group .card-panel-col[data-v-ec27f13c]{margin-bottom:32px}.panel-group .card-panel[data-v-ec27f13c]{height:108px;font-size:12px;position:relative;overflow:hidden;color:#666;background:#fff;-webkit-box-shadow:4px 4px 40px rgba(0,0,0,.05);box-shadow:4px 4px 40px rgba(0,0,0,.05);border-color:rgba(0,0,0,.05)}.panel-group .card-panel .icon-people[data-v-ec27f13c]{color:#40c9c6}.panel-group .card-panel .icon-message[data-v-ec27f13c]{color:#36a3f7}.panel-group .card-panel .icon-money[data-v-ec27f13c]{color:#f4516c}.panel-group .card-panel .icon-shopping[data-v-ec27f13c]{color:#34bfa3}.panel-group .card-panel .card-panel-icon-wrapper[data-v-ec27f13c]{float:left;margin:14px 0 0 14px;padding:16px;-webkit-transition:all .38s ease-out;transition:all .38s ease-out;border-radius:6px}.panel-group .card-panel .card-panel-icon[data-v-ec27f13c]{float:left;font-size:48px}.panel-group .card-panel .card-panel-description[data-v-ec27f13c]{float:right;font-weight:700;margin:26px;margin-left:0}.panel-group .card-panel .card-panel-description .card-panel-text[data-v-ec27f13c]{line-height:18px;color:rgba(0,0,0,.45);font-size:16px;margin-bottom:12px}.panel-group .card-panel .card-panel-description .card-panel-num[data-v-ec27f13c]{font-size:20px}@media (max-width:550px){.card-panel-description[data-v-ec27f13c]{display:none}.card-panel-icon-wrapper[data-v-ec27f13c]{float:none!important;width:100%;height:100%;margin:0!important}.card-panel-icon-wrapper .svg-icon[data-v-ec27f13c]{display:block;margin:14px auto!important;float:none!important}}.dashboard-editor-container[data-v-4ac7101a]{padding:32px;position:relative}.dashboard-editor-container .github-corner[data-v-4ac7101a]{position:absolute;top:0;border:0;right:0}.dashboard-editor-container .chart-wrapper[data-v-4ac7101a]{background:#fff;padding:16px 16px 0;margin-bottom:32px}@media (max-width:1024px){.chart-wrapper[data-v-4ac7101a]{padding:8px}} -------------------------------------------------------------------------------- /dist/static/css/chunk-bcebd8aa.78a33c26.css: -------------------------------------------------------------------------------- 1 | .chart-container[data-v-da4d48fa]{position:relative;width:100%;height:calc(100vh - 84px)} -------------------------------------------------------------------------------- /dist/static/css/chunk-db410d48.50024827.css: -------------------------------------------------------------------------------- 1 | .wrapper-class[data-v-a6a3aeee]{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;margin-top:15px}.wrapper-class .el-icon-question[data-v-a6a3aeee]{color:#36a3f7} -------------------------------------------------------------------------------- /dist/static/css/chunk-e9dba01a.b0fc6a10.css: -------------------------------------------------------------------------------- 1 | .mixin-components-container[data-v-550e716b]{background-color:#f0f2f5;padding:30px;min-height:calc(100vh - 84px)}.component-item[data-v-550e716b]{min-height:100px}.el-select .el-input[data-v-550e716b]{width:130px}.input-with-select .el-input-group__prepend[data-v-550e716b]{background-color:#fff} -------------------------------------------------------------------------------- /dist/static/fonts/element-icons.535877f5.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/didi/GateKeeper/3c5211ab37040f47c5c9bd236adb06e8c69127d3/dist/static/fonts/element-icons.535877f5.woff -------------------------------------------------------------------------------- /dist/static/fonts/element-icons.732389de.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/didi/GateKeeper/3c5211ab37040f47c5c9bd236adb06e8c69127d3/dist/static/fonts/element-icons.732389de.ttf -------------------------------------------------------------------------------- /dist/static/img/401.089007e7.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/didi/GateKeeper/3c5211ab37040f47c5c9bd236adb06e8c69127d3/dist/static/img/401.089007e7.gif -------------------------------------------------------------------------------- /dist/static/img/404.a57b6f31.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/didi/GateKeeper/3c5211ab37040f47c5c9bd236adb06e8c69127d3/dist/static/img/404.a57b6f31.png -------------------------------------------------------------------------------- /dist/static/img/404_cloud.0f4bc32b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/didi/GateKeeper/3c5211ab37040f47c5c9bd236adb06e8c69127d3/dist/static/img/404_cloud.0f4bc32b.png -------------------------------------------------------------------------------- /dist/static/img/double-arrow-left.56942267.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /dist/static/img/logo.e3a14490.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | logo 4 | 5 | 6 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /dist/static/js/chunk-2d230fe7.f069ed37.js: -------------------------------------------------------------------------------- 1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-2d230fe7"],{ef3c:function(e,r,n){"use strict";n.r(r);n("8dee");var t,u,a={created:function(){var e=this.$route,r=e.params,n=e.query,t=r.path;this.$router.replace({path:"/"+t,query:n})},render:function(e){return e()}},c=a,o=n("cba8"),p=Object(o["a"])(c,t,u,!1,null,null,null);r["default"]=p.exports}}]); -------------------------------------------------------------------------------- /dist/static/js/chunk-2f5ea260.34edaec3.js: -------------------------------------------------------------------------------- 1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-2f5ea260"],{"1db4":function(t,s,a){"use strict";a.r(s);var c=function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"wscn-http404-container"},[a("div",{staticClass:"wscn-http404"},[t._m(0),a("div",{staticClass:"bullshit"},[a("div",{staticClass:"bullshit__oops"},[t._v("OOPS!")]),t._m(1),a("div",{staticClass:"bullshit__headline"},[t._v(t._s(t.message))]),a("div",{staticClass:"bullshit__info"},[t._v(" Please check that the URL you entered is correct, or click the button below to return to the homepage. ")]),a("a",{staticClass:"bullshit__return-home",attrs:{href:""}},[t._v("Back to home")])])])])},e=[function(){var t=this,s=t.$createElement,c=t._self._c||s;return c("div",{staticClass:"pic-404"},[c("img",{staticClass:"pic-404__parent",attrs:{src:a("a36b"),alt:"404"}}),c("img",{staticClass:"pic-404__child left",attrs:{src:a("26fc"),alt:"404"}}),c("img",{staticClass:"pic-404__child mid",attrs:{src:a("26fc"),alt:"404"}}),c("img",{staticClass:"pic-404__child right",attrs:{src:a("26fc"),alt:"404"}})])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"bullshit__info"},[t._v(" All rights reserved "),a("a",{staticStyle:{color:"#20a0ff"},attrs:{href:"https://wallstreetcn.com",target:"_blank"}},[t._v(" wallstreetcn ")])])}],i={name:"Page404",computed:{message:function(){return"The webmaster said that you can not enter this page..."}}},l=i,n=(a("26c2"),a("cba8")),r=Object(n["a"])(l,c,e,!1,null,"c4f16cd2",null);s["default"]=r.exports},"26c2":function(t,s,a){"use strict";a("ff95")},"26fc":function(t,s,a){t.exports=a.p+"static/img/404_cloud.0f4bc32b.png"},a36b:function(t,s,a){t.exports=a.p+"static/img/404.a57b6f31.png"},ff95:function(t,s,a){}}]); -------------------------------------------------------------------------------- /dist/static/js/chunk-3f238847.dde743ce.js: -------------------------------------------------------------------------------- 1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-3f238847"],{"24e2":function(t,a,i){"use strict";i.r(a);var e=function(){var t=this,a=t.$createElement,i=t._self._c||a;return i("div",{staticClass:"errPage-container"},[i("el-button",{staticClass:"pan-back-btn",attrs:{icon:"el-icon-arrow-left"},on:{click:t.back}},[t._v(" 返回 ")]),i("el-row",[i("el-col",{attrs:{span:12}},[i("h1",{staticClass:"text-jumbo text-ginormous"},[t._v(" Oops! ")]),t._v(" gif来源 "),i("a",{attrs:{href:"https://zh.airbnb.com/",target:"_blank"}},[t._v("airbnb")]),t._v(" 页面 "),i("h2",[t._v("你没有权限去该页面")]),i("h6",[t._v("如有不满请联系你领导")]),i("ul",{staticClass:"list-unstyled"},[i("li",[t._v("或者你可以去:")]),i("li",{staticClass:"link-type"},[i("router-link",{attrs:{to:"/dashboard"}},[t._v(" 回首页 ")])],1),i("li",{staticClass:"link-type"},[i("a",{attrs:{href:"https://www.taobao.com/"}},[t._v("随便看看")])]),i("li",[i("a",{attrs:{href:"#"},on:{click:function(a){a.preventDefault(),t.dialogVisible=!0}}},[t._v("点我看图")])])])]),i("el-col",{attrs:{span:12}},[i("img",{attrs:{src:t.errGif,width:"313",height:"428",alt:"Girl has dropped her ice cream."}})])],1),i("el-dialog",{attrs:{visible:t.dialogVisible,title:"随便看"},on:{"update:visible":function(a){t.dialogVisible=a}}},[i("img",{staticClass:"pan-img",attrs:{src:t.ewizardClap}})])],1)},s=[],r=i("cc6c"),c=i.n(r),l={name:"Page401",data:function(){return{errGif:c.a+"?"+ +new Date,ewizardClap:"https://wpimg.wallstcn.com/007ef517-bafd-4066-aae4-6883632d9646",dialogVisible:!1}},methods:{back:function(){this.$route.query.noGoBack?this.$router.push({path:"/dashboard"}):this.$router.go(-1)}}},n=l,o=(i("663b"),i("cba8")),u=Object(o["a"])(n,e,s,!1,null,"61343d02",null);a["default"]=u.exports},"663b":function(t,a,i){"use strict";i("ec97")},cc6c:function(t,a,i){t.exports=i.p+"static/img/401.089007e7.gif"},ec97:function(t,a,i){}}]); -------------------------------------------------------------------------------- /dist/static/js/chunk-57e15452.3a533841.js: -------------------------------------------------------------------------------- 1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-57e15452"],{"69b0":function(n,t){n.exports=Object.is||function(n,t){return n===t?0!==n||1/n===1/t:n!=n&&t!=t}},b829:function(n,t,e){"use strict";e.r(t);e("d91d");var r,i,o={name:"AuthRedirect",created:function(){var n=window.location.search.slice(1);window.localStorage&&(window.localStorage.setItem("x-admin-oauth-code",n),window.close())},render:function(n){return n()}},a=o,c=e("cba8"),u=Object(c["a"])(a,r,i,!1,null,null,null);t["default"]=u.exports},d91d:function(n,t,e){"use strict";var r=e("a86f"),i=e("69b0"),o=e("f417");e("c46f")("search",1,(function(n,t,e,a){return[function(e){var r=n(this),i=void 0==e?void 0:e[t];return void 0!==i?i.call(e,r):new RegExp(e)[t](String(r))},function(n){var t=a(e,n,this);if(t.done)return t.value;var c=r(n),u=String(this),d=c.lastIndex;i(d,0)||(c.lastIndex=0);var l=o(c,u);return i(c.lastIndex,d)||(c.lastIndex=d),null===l?-1:l.index}]}))}}]); -------------------------------------------------------------------------------- /dist_backup/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/didi/GateKeeper/3c5211ab37040f47c5c9bd236adb06e8c69127d3/dist_backup/favicon.ico -------------------------------------------------------------------------------- /dist_backup/static/css/chunk-294e231b.d3b4ac8e.css: -------------------------------------------------------------------------------- 1 | .mixin-components-container[data-v-8bcdbc8a]{background-color:#f0f2f5;padding:30px;min-height:calc(100vh - 84px)}.component-item[data-v-8bcdbc8a]{min-height:100px}.el-select .el-input[data-v-8bcdbc8a]{width:130px}.input-with-select .el-input-group__prepend[data-v-8bcdbc8a]{background-color:#fff} -------------------------------------------------------------------------------- /dist_backup/static/css/chunk-335e7249.6d24dacd.css: -------------------------------------------------------------------------------- 1 | .waves-ripple{position:absolute;border-radius:100%;background-color:rgba(0,0,0,.15);background-clip:padding-box;pointer-events:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-transform:scale(0);transform:scale(0);opacity:1}.waves-ripple.z-active{opacity:0;-webkit-transform:scale(2);transform:scale(2);-webkit-transition:opacity 1.2s ease-out,-webkit-transform .6s ease-out;transition:opacity 1.2s ease-out,-webkit-transform .6s ease-out;transition:opacity 1.2s ease-out,transform .6s ease-out;transition:opacity 1.2s ease-out,transform .6s ease-out,-webkit-transform .6s ease-out}.pagination-container[data-v-6af373ef]{background:#fff;padding:32px 16px}.pagination-container.hidden[data-v-6af373ef]{display:none} -------------------------------------------------------------------------------- /dist_backup/static/css/chunk-3f238847.a2fda0b3.css: -------------------------------------------------------------------------------- 1 | .errPage-container[data-v-61343d02]{width:800px;max-width:100%;margin:100px auto}.errPage-container .pan-back-btn[data-v-61343d02]{background:#008489;color:#fff;border:none!important}.errPage-container .pan-gif[data-v-61343d02]{margin:0 auto;display:block}.errPage-container .pan-img[data-v-61343d02]{display:block;margin:0 auto;width:100%}.errPage-container .text-jumbo[data-v-61343d02]{font-size:60px;font-weight:700;color:#484848}.errPage-container .list-unstyled[data-v-61343d02]{font-size:14px}.errPage-container .list-unstyled li[data-v-61343d02]{padding-bottom:5px}.errPage-container .list-unstyled a[data-v-61343d02]{color:#008489;text-decoration:none}.errPage-container .list-unstyled a[data-v-61343d02]:hover{text-decoration:underline} -------------------------------------------------------------------------------- /dist_backup/static/css/chunk-5c8c1f18.25617263.css: -------------------------------------------------------------------------------- 1 | .mixin-components-container[data-v-686a0906]{background-color:#f0f2f5;padding:30px;min-height:calc(100vh - 84px)}.component-item[data-v-686a0906]{min-height:100px}.card[data-v-686a0906]{margin-top:20px}.bottom[data-v-686a0906]{background:#fff;display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-pack:distribute;justify-content:space-around;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-shadow:0 2px 12px 0 rgb(0 0 0/10%);box-shadow:0 2px 12px 0 rgb(0 0 0/10%);margin-top:10px;padding:30px 0} -------------------------------------------------------------------------------- /dist_backup/static/css/chunk-61ae4e7a.93d95231.css: -------------------------------------------------------------------------------- 1 | @supports (-webkit-mask:none) and (not (cater-color:#fff)){.login-container .el-input input{color:#fff}}.login-container .el-input{display:inline-block;height:47px;width:85%}.login-container .el-input input{background:transparent;border:0;-webkit-appearance:none;border-radius:0;padding:12px 5px 12px 15px;color:#fff;height:47px;caret-color:#fff}.login-container .el-input input:-webkit-autofill{-webkit-box-shadow:0 0 0 1000px #283443 inset!important;box-shadow:inset 0 0 0 1000px #283443!important;-webkit-text-fill-color:#fff!important}.login-container .el-form-item{border:1px solid hsla(0,0%,100%,.1);background:rgba(0,0,0,.1);border-radius:5px;color:#454545}.login-container[data-v-11647fdd]{min-height:100%;width:100%;background-color:#2d3a4b;overflow:hidden}.login-container .login-form[data-v-11647fdd]{position:relative;width:520px;max-width:100%;padding:160px 35px 0;margin:0 auto;overflow:hidden}.login-container .tips[data-v-11647fdd]{font-size:14px;color:#fff;margin-bottom:10px}.login-container .tips span[data-v-11647fdd]:first-of-type{margin-right:16px}.login-container .svg-container[data-v-11647fdd]{padding:6px 5px 6px 15px;color:#889aa4;vertical-align:middle;width:30px;display:inline-block}.login-container .title-container[data-v-11647fdd]{position:relative}.login-container .title-container .title[data-v-11647fdd]{font-size:26px;color:#eee;margin:0 auto 40px auto;text-align:center;font-weight:700}.login-container .show-pwd[data-v-11647fdd]{position:absolute;right:10px;top:7px;font-size:16px;color:#889aa4;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.login-container .thirdparty-button[data-v-11647fdd]{position:absolute;right:0;bottom:6px}@media only screen and (max-width:470px){.login-container .thirdparty-button[data-v-11647fdd]{display:none}} -------------------------------------------------------------------------------- /dist_backup/static/css/chunk-6525d41e.b1d9f1ec.css: -------------------------------------------------------------------------------- 1 | .mixin-components-container[data-v-19bb35ad]{background-color:#f0f2f5;padding:30px;min-height:calc(100vh - 84px)}.component-item[data-v-19bb35ad]{min-height:100px}.el-select .el-input[data-v-19bb35ad]{width:130px}.input-with-select .el-input-group__prepend[data-v-19bb35ad]{background-color:#fff} -------------------------------------------------------------------------------- /dist_backup/static/css/chunk-68b61de5.fb787c4e.css: -------------------------------------------------------------------------------- 1 | .mixin-components-container[data-v-e17f2ec0]{background-color:#f0f2f5;padding:30px;min-height:calc(100vh - 84px)}.component-item[data-v-e17f2ec0]{min-height:100px}.card[data-v-e17f2ec0]{margin-top:20px}.bottom[data-v-e17f2ec0]{background:#fff;display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-pack:distribute;justify-content:space-around;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-shadow:0 2px 12px 0 rgb(0 0 0/10%);box-shadow:0 2px 12px 0 rgb(0 0 0/10%);margin-top:10px;padding:30px 0} -------------------------------------------------------------------------------- /dist_backup/static/css/chunk-6f6c0d4e.c5f2d082.css: -------------------------------------------------------------------------------- 1 | .chart-container[data-v-33d2dd29]{position:relative;width:100%;height:calc(100vh - 84px)} -------------------------------------------------------------------------------- /dist_backup/static/css/chunk-7be427f9.9fb4309b.css: -------------------------------------------------------------------------------- 1 | .waves-ripple{position:absolute;border-radius:100%;background-color:rgba(0,0,0,.15);background-clip:padding-box;pointer-events:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-transform:scale(0);transform:scale(0);opacity:1}.waves-ripple.z-active{opacity:0;-webkit-transform:scale(2);transform:scale(2);-webkit-transition:opacity 1.2s ease-out,-webkit-transform .6s ease-out;transition:opacity 1.2s ease-out,-webkit-transform .6s ease-out;transition:opacity 1.2s ease-out,transform .6s ease-out;transition:opacity 1.2s ease-out,transform .6s ease-out,-webkit-transform .6s ease-out}.pagination-container[data-v-6af373ef]{background:#fff;padding:32px 16px}.pagination-container.hidden[data-v-6af373ef]{display:none}.app-container .filter-container[data-v-4c48a17c]{display:-webkit-box;display:-ms-flexbox;display:flex;padding:20px;-webkit-box-shadow:0 2px 6px 0 rgba(26,27,31,.12);box-shadow:0 2px 6px 0 rgba(26,27,31,.12);border-radius:5px;background-color:#fff;margin-bottom:6px}.app-container .filter-container-input[data-v-4c48a17c]{width:20rem;border-radius:4px}.app-container .filter-container-btn[data-v-4c48a17c]{width:141px;background:#3d6aff;border-radius:4px;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.app-container-table[data-v-4c48a17c]{padding:20px;-webkit-box-shadow:0 2px 6px 0 rgba(26,27,31,.12);box-shadow:0 2px 6px 0 rgba(26,27,31,.12);border-radius:5px;background-color:#fff}.app-container-table .btn[data-v-4c48a17c]{font-family:PingFangSC-Regular;font-size:14px;color:#3d6aff;text-align:left;line-height:22px;font-weight:400}[data-v-4c48a17c] .filter-container-input .el-input-group__append{background-color:#fff;width:40px} -------------------------------------------------------------------------------- /dist_backup/static/css/chunk-80699a70.c0f8cc73.css: -------------------------------------------------------------------------------- 1 | .panel-group[data-v-ec27f13c]{margin-top:18px}.panel-group .card-panel-col[data-v-ec27f13c]{margin-bottom:32px}.panel-group .card-panel[data-v-ec27f13c]{height:108px;font-size:12px;position:relative;overflow:hidden;color:#666;background:#fff;-webkit-box-shadow:4px 4px 40px rgba(0,0,0,.05);box-shadow:4px 4px 40px rgba(0,0,0,.05);border-color:rgba(0,0,0,.05)}.panel-group .card-panel .icon-people[data-v-ec27f13c]{color:#40c9c6}.panel-group .card-panel .icon-message[data-v-ec27f13c]{color:#36a3f7}.panel-group .card-panel .icon-money[data-v-ec27f13c]{color:#f4516c}.panel-group .card-panel .icon-shopping[data-v-ec27f13c]{color:#34bfa3}.panel-group .card-panel .card-panel-icon-wrapper[data-v-ec27f13c]{float:left;margin:14px 0 0 14px;padding:16px;-webkit-transition:all .38s ease-out;transition:all .38s ease-out;border-radius:6px}.panel-group .card-panel .card-panel-icon[data-v-ec27f13c]{float:left;font-size:48px}.panel-group .card-panel .card-panel-description[data-v-ec27f13c]{float:right;font-weight:700;margin:26px;margin-left:0}.panel-group .card-panel .card-panel-description .card-panel-text[data-v-ec27f13c]{line-height:18px;color:rgba(0,0,0,.45);font-size:16px;margin-bottom:12px}.panel-group .card-panel .card-panel-description .card-panel-num[data-v-ec27f13c]{font-size:20px}@media (max-width:550px){.card-panel-description[data-v-ec27f13c]{display:none}.card-panel-icon-wrapper[data-v-ec27f13c]{float:none!important;width:100%;height:100%;margin:0!important}.card-panel-icon-wrapper .svg-icon[data-v-ec27f13c]{display:block;margin:14px auto!important;float:none!important}}.dashboard-editor-container[data-v-4ac7101a]{padding:32px;position:relative}.dashboard-editor-container .github-corner[data-v-4ac7101a]{position:absolute;top:0;border:0;right:0}.dashboard-editor-container .chart-wrapper[data-v-4ac7101a]{background:#fff;padding:16px 16px 0;margin-bottom:32px}@media (max-width:1024px){.chart-wrapper[data-v-4ac7101a]{padding:8px}} -------------------------------------------------------------------------------- /dist_backup/static/css/chunk-bcebd8aa.78a33c26.css: -------------------------------------------------------------------------------- 1 | .chart-container[data-v-da4d48fa]{position:relative;width:100%;height:calc(100vh - 84px)} -------------------------------------------------------------------------------- /dist_backup/static/css/chunk-e8029a4a.5c0a5547.css: -------------------------------------------------------------------------------- 1 | .wrapper-class[data-v-f006624a]{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;margin-top:15px}.wrapper-class .el-icon-question[data-v-f006624a]{color:#36a3f7} -------------------------------------------------------------------------------- /dist_backup/static/css/chunk-e9dba01a.b0fc6a10.css: -------------------------------------------------------------------------------- 1 | .mixin-components-container[data-v-550e716b]{background-color:#f0f2f5;padding:30px;min-height:calc(100vh - 84px)}.component-item[data-v-550e716b]{min-height:100px}.el-select .el-input[data-v-550e716b]{width:130px}.input-with-select .el-input-group__prepend[data-v-550e716b]{background-color:#fff} -------------------------------------------------------------------------------- /dist_backup/static/fonts/element-icons.535877f5.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/didi/GateKeeper/3c5211ab37040f47c5c9bd236adb06e8c69127d3/dist_backup/static/fonts/element-icons.535877f5.woff -------------------------------------------------------------------------------- /dist_backup/static/fonts/element-icons.732389de.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/didi/GateKeeper/3c5211ab37040f47c5c9bd236adb06e8c69127d3/dist_backup/static/fonts/element-icons.732389de.ttf -------------------------------------------------------------------------------- /dist_backup/static/img/401.089007e7.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/didi/GateKeeper/3c5211ab37040f47c5c9bd236adb06e8c69127d3/dist_backup/static/img/401.089007e7.gif -------------------------------------------------------------------------------- /dist_backup/static/img/404.a57b6f31.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/didi/GateKeeper/3c5211ab37040f47c5c9bd236adb06e8c69127d3/dist_backup/static/img/404.a57b6f31.png -------------------------------------------------------------------------------- /dist_backup/static/img/404_cloud.0f4bc32b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/didi/GateKeeper/3c5211ab37040f47c5c9bd236adb06e8c69127d3/dist_backup/static/img/404_cloud.0f4bc32b.png -------------------------------------------------------------------------------- /dist_backup/static/img/double-arrow-left.56942267.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /dist_backup/static/img/logo.e3a14490.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | logo 4 | 5 | 6 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /dist_backup/static/js/chunk-2d230fe7.f069ed37.js: -------------------------------------------------------------------------------- 1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-2d230fe7"],{ef3c:function(e,r,n){"use strict";n.r(r);n("8dee");var t,u,a={created:function(){var e=this.$route,r=e.params,n=e.query,t=r.path;this.$router.replace({path:"/"+t,query:n})},render:function(e){return e()}},c=a,o=n("cba8"),p=Object(o["a"])(c,t,u,!1,null,null,null);r["default"]=p.exports}}]); -------------------------------------------------------------------------------- /dist_backup/static/js/chunk-2f5ea260.34edaec3.js: -------------------------------------------------------------------------------- 1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-2f5ea260"],{"1db4":function(t,s,a){"use strict";a.r(s);var c=function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"wscn-http404-container"},[a("div",{staticClass:"wscn-http404"},[t._m(0),a("div",{staticClass:"bullshit"},[a("div",{staticClass:"bullshit__oops"},[t._v("OOPS!")]),t._m(1),a("div",{staticClass:"bullshit__headline"},[t._v(t._s(t.message))]),a("div",{staticClass:"bullshit__info"},[t._v(" Please check that the URL you entered is correct, or click the button below to return to the homepage. ")]),a("a",{staticClass:"bullshit__return-home",attrs:{href:""}},[t._v("Back to home")])])])])},e=[function(){var t=this,s=t.$createElement,c=t._self._c||s;return c("div",{staticClass:"pic-404"},[c("img",{staticClass:"pic-404__parent",attrs:{src:a("a36b"),alt:"404"}}),c("img",{staticClass:"pic-404__child left",attrs:{src:a("26fc"),alt:"404"}}),c("img",{staticClass:"pic-404__child mid",attrs:{src:a("26fc"),alt:"404"}}),c("img",{staticClass:"pic-404__child right",attrs:{src:a("26fc"),alt:"404"}})])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"bullshit__info"},[t._v(" All rights reserved "),a("a",{staticStyle:{color:"#20a0ff"},attrs:{href:"https://wallstreetcn.com",target:"_blank"}},[t._v(" wallstreetcn ")])])}],i={name:"Page404",computed:{message:function(){return"The webmaster said that you can not enter this page..."}}},l=i,n=(a("26c2"),a("cba8")),r=Object(n["a"])(l,c,e,!1,null,"c4f16cd2",null);s["default"]=r.exports},"26c2":function(t,s,a){"use strict";a("ff95")},"26fc":function(t,s,a){t.exports=a.p+"static/img/404_cloud.0f4bc32b.png"},a36b:function(t,s,a){t.exports=a.p+"static/img/404.a57b6f31.png"},ff95:function(t,s,a){}}]); -------------------------------------------------------------------------------- /dist_backup/static/js/chunk-3f238847.dde743ce.js: -------------------------------------------------------------------------------- 1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-3f238847"],{"24e2":function(t,a,i){"use strict";i.r(a);var e=function(){var t=this,a=t.$createElement,i=t._self._c||a;return i("div",{staticClass:"errPage-container"},[i("el-button",{staticClass:"pan-back-btn",attrs:{icon:"el-icon-arrow-left"},on:{click:t.back}},[t._v(" 返回 ")]),i("el-row",[i("el-col",{attrs:{span:12}},[i("h1",{staticClass:"text-jumbo text-ginormous"},[t._v(" Oops! ")]),t._v(" gif来源 "),i("a",{attrs:{href:"https://zh.airbnb.com/",target:"_blank"}},[t._v("airbnb")]),t._v(" 页面 "),i("h2",[t._v("你没有权限去该页面")]),i("h6",[t._v("如有不满请联系你领导")]),i("ul",{staticClass:"list-unstyled"},[i("li",[t._v("或者你可以去:")]),i("li",{staticClass:"link-type"},[i("router-link",{attrs:{to:"/dashboard"}},[t._v(" 回首页 ")])],1),i("li",{staticClass:"link-type"},[i("a",{attrs:{href:"https://www.taobao.com/"}},[t._v("随便看看")])]),i("li",[i("a",{attrs:{href:"#"},on:{click:function(a){a.preventDefault(),t.dialogVisible=!0}}},[t._v("点我看图")])])])]),i("el-col",{attrs:{span:12}},[i("img",{attrs:{src:t.errGif,width:"313",height:"428",alt:"Girl has dropped her ice cream."}})])],1),i("el-dialog",{attrs:{visible:t.dialogVisible,title:"随便看"},on:{"update:visible":function(a){t.dialogVisible=a}}},[i("img",{staticClass:"pan-img",attrs:{src:t.ewizardClap}})])],1)},s=[],r=i("cc6c"),c=i.n(r),l={name:"Page401",data:function(){return{errGif:c.a+"?"+ +new Date,ewizardClap:"https://wpimg.wallstcn.com/007ef517-bafd-4066-aae4-6883632d9646",dialogVisible:!1}},methods:{back:function(){this.$route.query.noGoBack?this.$router.push({path:"/dashboard"}):this.$router.go(-1)}}},n=l,o=(i("663b"),i("cba8")),u=Object(o["a"])(n,e,s,!1,null,"61343d02",null);a["default"]=u.exports},"663b":function(t,a,i){"use strict";i("ec97")},cc6c:function(t,a,i){t.exports=i.p+"static/img/401.089007e7.gif"},ec97:function(t,a,i){}}]); -------------------------------------------------------------------------------- /dist_backup/static/js/chunk-57e15452.3a533841.js: -------------------------------------------------------------------------------- 1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-57e15452"],{"69b0":function(n,t){n.exports=Object.is||function(n,t){return n===t?0!==n||1/n===1/t:n!=n&&t!=t}},b829:function(n,t,e){"use strict";e.r(t);e("d91d");var r,i,o={name:"AuthRedirect",created:function(){var n=window.location.search.slice(1);window.localStorage&&(window.localStorage.setItem("x-admin-oauth-code",n),window.close())},render:function(n){return n()}},a=o,c=e("cba8"),u=Object(c["a"])(a,r,i,!1,null,null,null);t["default"]=u.exports},d91d:function(n,t,e){"use strict";var r=e("a86f"),i=e("69b0"),o=e("f417");e("c46f")("search",1,(function(n,t,e,a){return[function(e){var r=n(this),i=void 0==e?void 0:e[t];return void 0!==i?i.call(e,r):new RegExp(e)[t](String(r))},function(n){var t=a(e,n,this);if(t.done)return t.value;var c=r(n),u=String(this),d=c.lastIndex;i(d,0)||(c.lastIndex=0);var l=o(c,u);return i(c.lastIndex,d)||(c.lastIndex=d),null===l?-1:l.index}]}))}}]); -------------------------------------------------------------------------------- /docker_build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | export GO111MODULE=auto && export GOPROXY=https://goproxy.cn && go mod tidy 3 | GOOS=linux GOARCH=amd64 go build -o ./bin/gatekeeper 4 | docker build -f dockerfile-dashboard -t go-gateteway-dashboard . 5 | docker build -f dockerfile-server -t go-gateteway-server . -------------------------------------------------------------------------------- /dockerfile-dashboard: -------------------------------------------------------------------------------- 1 | FROM golang 2 | 3 | WORKDIR /go/src/app 4 | COPY . . 5 | #原始方式:直接镜像内打包编译 6 | #RUN export GO111MODULE=auto && export GOPROXY=https://goproxy.cn && go mod tidy 7 | #RUN go build -o ./bin/gatekeeper 8 | 9 | CMD ./bin/gatekeeper -c ./conf/dev/ -p control -------------------------------------------------------------------------------- /dockerfile-server: -------------------------------------------------------------------------------- 1 | FROM golang 2 | 3 | WORKDIR /go/src/app 4 | COPY . . 5 | 6 | #原始方式:直接镜像内打包编译 7 | #RUN export GO111MODULE=auto && export GOPROXY=https://goproxy.cn && go mod tidy 8 | #RUN go build -o gatekeeper 9 | 10 | CMD ./bin/gatekeeper -c ./conf/dev/ -p control -------------------------------------------------------------------------------- /favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/didi/GateKeeper/3c5211ab37040f47c5c9bd236adb06e8c69127d3/favicon.ico -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/didi/gatekeeper 2 | 3 | go 1.16 4 | 5 | require ( 6 | git.apache.org/thrift.git v0.13.0 7 | github.com/BurntSushi/toml v0.3.1 8 | github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 9 | github.com/bitly/go-simplejson v0.5.0 10 | github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 // indirect 11 | github.com/boj/redistore v0.0.0-20180917114910-cd5dcc76aeff // indirect 12 | github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf 13 | github.com/denisenkom/go-mssqldb v0.0.0-20200620013148-b91950f658ec // indirect 14 | github.com/dgrijalva/jwt-go v3.2.0+incompatible 15 | github.com/e421083458/gorm v1.0.1 16 | github.com/e421083458/grpc-proxy v0.2.0 17 | github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 // indirect 18 | github.com/garyburd/redigo v1.6.0 19 | github.com/gin-gonic/contrib v0.0.0-20191209060500-d6e26eeaa607 20 | github.com/gin-gonic/gin v1.7.0 21 | github.com/go-playground/locales v0.13.0 22 | github.com/go-playground/universal-translator v0.17.0 23 | github.com/go-sql-driver/mysql v1.5.0 // indirect 24 | github.com/gorilla/sessions v1.1.3 // indirect 25 | github.com/jinzhu/inflection v1.0.0 // indirect 26 | github.com/jinzhu/now v1.1.1 // indirect 27 | github.com/lib/pq v1.7.1 // indirect 28 | github.com/mattn/go-sqlite3 v1.14.0 // indirect 29 | github.com/mwitkow/grpc-proxy v0.0.0-20181017164139-0f1106ef9c76 // indirect 30 | github.com/pkg/errors v0.9.1 31 | github.com/rs/xid v1.3.0 32 | github.com/smartystreets/goconvey v1.6.4 33 | github.com/spf13/cobra v1.2.1 34 | github.com/spf13/viper v1.8.1 35 | github.com/swaggo/files v0.0.0-20190704085106-630677cd5c14 36 | github.com/swaggo/gin-swagger v1.2.0 37 | github.com/swaggo/swag v1.6.5 38 | github.com/zenazn/goji v1.0.1 39 | golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1 40 | golang.org/x/tools v0.1.5 41 | google.golang.org/grpc v1.38.0 42 | gopkg.in/go-playground/validator.v9 v9.29.0 43 | ) 44 | -------------------------------------------------------------------------------- /golang_common/lib/cmd.go: -------------------------------------------------------------------------------- 1 | package lib 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "github.com/pkg/errors" 6 | "github.com/spf13/cobra" 7 | ) 8 | 9 | var confPath string 10 | var panelType string 11 | var cmdRun = &cobra.Command{ 12 | Use: "run", 13 | Short: "Run a gatekeeper application", 14 | Long: `Run a gatekeeper application by parameter`, 15 | Args: func(cmd *cobra.Command, args []string) error { 16 | panelType = cmd.Flag("panel_type").Value.String() 17 | confPath = cmd.Flag("conf_path").Value.String() 18 | if ok, _ := PathExists(confPath); !ok { 19 | return errors.New("conf_path is not a real dir") 20 | } 21 | if !InArrayString(panelType, []string{"proxy", "control", "both"}) { 22 | return errors.New("panel_type error,choose one from both/proxy/control") 23 | } 24 | return nil 25 | }, 26 | Run: func(cmd *cobra.Command, args []string) { 27 | }, 28 | } 29 | 30 | func CmdExecute() error { 31 | cmdRun.Flags().StringVarP(&panelType, "panel_type", "p", "", "set panel type like 'both/proxy/control'") 32 | cmdRun.Flags().StringVarP(&confPath, "conf_path", "c", "", "set configuration path like './conf/dev/'") 33 | cmdRun.MarkFlagRequired("panel_type") 34 | cmdRun.MarkFlagRequired("conf_path") 35 | var rootCmd = &cobra.Command{ 36 | Use: "", 37 | Short: "Gatekeeper command manager", 38 | CompletionOptions: cobra.CompletionOptions{DisableDefaultCmd: true, DisableNoDescFlag: true, DisableDescriptions: true}, 39 | } 40 | rootCmd.AddCommand(cmdRun) 41 | gin.SetMode(gin.ReleaseMode) 42 | return rootCmd.Execute() 43 | } 44 | 45 | func GetCmdConfPath() string { 46 | return confPath 47 | } 48 | 49 | func GetCmdPanelType() string { 50 | return panelType 51 | } 52 | 53 | func SetCmdConfPath(path string) { 54 | confPath = path 55 | } 56 | 57 | func SetCmdPanelType(paneltype string) { 58 | panelType = paneltype 59 | } 60 | -------------------------------------------------------------------------------- /golang_common/lib/func.go: -------------------------------------------------------------------------------- 1 | package lib 2 | 3 | import ( 4 | "crypto/md5" 5 | "encoding/hex" 6 | "net" 7 | "os" 8 | ) 9 | 10 | func GetMd5Hash(text string) string { 11 | hasher := md5.New() 12 | hasher.Write([]byte(text)) 13 | return hex.EncodeToString(hasher.Sum(nil)) 14 | } 15 | 16 | func Encode(data string) (string, error) { 17 | h := md5.New() 18 | _, err := h.Write([]byte(data)) 19 | if err != nil { 20 | return "", err 21 | } 22 | return hex.EncodeToString(h.Sum(nil)), nil 23 | } 24 | 25 | func GetLocalIPs() (ips []net.IP) { 26 | interfaceAddr, err := net.InterfaceAddrs() 27 | if err != nil { 28 | return nil 29 | } 30 | for _, address := range interfaceAddr { 31 | ipNet, isValidIpNet := address.(*net.IPNet) 32 | if isValidIpNet && !ipNet.IP.IsLoopback() { 33 | if ipNet.IP.To4() != nil { 34 | ips = append(ips, ipNet.IP) 35 | } 36 | } 37 | } 38 | return ips 39 | } 40 | 41 | func InArrayString(s string, arr []string) bool { 42 | for _, i := range arr { 43 | if i == s { 44 | return true 45 | } 46 | } 47 | return false 48 | } 49 | 50 | func Substr(str string, start int64, end int64) string { 51 | length := int64(len(str)) 52 | if start < 0 || start > length { 53 | return "" 54 | } 55 | if end < 0 { 56 | return "" 57 | } 58 | if end > length { 59 | end = length 60 | } 61 | return string(str[start:end]) 62 | } 63 | 64 | func PathExists(path string) (bool, error) { 65 | _, err := os.Stat(path) 66 | if err == nil { 67 | return true, nil 68 | } 69 | if os.IsNotExist(err) { 70 | return false, nil 71 | } 72 | return false, err 73 | } -------------------------------------------------------------------------------- /golang_common/trace/trace_test.go: -------------------------------------------------------------------------------- 1 | package trace 2 | 3 | import ( 4 | "context" 5 | "net/http/httptest" 6 | "reflect" 7 | "testing" 8 | ) 9 | 10 | func TestGetTraceId(t *testing.T) { 11 | req := httptest.NewRequest("POST", "http://www.test.com", nil) 12 | tracer := New(req) 13 | traceid := tracer.GetTraceId(req) 14 | if len(traceid) <= 0 { 15 | t.Failed() 16 | } 17 | t.Log(traceid) 18 | } 19 | 20 | func TestIsTraceSampleEnabled(t *testing.T) { 21 | req := httptest.NewRequest("POST", "http://www.test.com", nil) 22 | tracer := New(req) 23 | hit := tracer.IsTraceSampleEnabled() 24 | t.Log(hit) 25 | } 26 | 27 | func BenchmarkGetTraceId(b *testing.B) { 28 | req := httptest.NewRequest("POST", "http://www.test.com", nil) 29 | for i := 0; i < b.N; i++ { 30 | tracer := New(req) 31 | tracer.GetTraceId(req) 32 | } 33 | } 34 | 35 | func BenchmarkGetLocalIP(b *testing.B) { 36 | for i := 0; i < b.N; i++ { 37 | getLocalIP() 38 | } 39 | } 40 | 41 | func TestGetCtxTrace(t *testing.T) { 42 | type args struct { 43 | ctx context.Context 44 | } 45 | tests := []struct { 46 | name string 47 | args args 48 | want *Trace 49 | want1 bool 50 | }{ 51 | // TODO: Add test cases. 52 | } 53 | for _, tt := range tests { 54 | t.Run(tt.name, func(t *testing.T) { 55 | got, got1 := GetCtxTrace(tt.args.ctx) 56 | if !reflect.DeepEqual(got, tt.want) { 57 | t.Errorf("GetCtxTrace() got = %v, want %v", got, tt.want) 58 | } 59 | if got1 != tt.want1 { 60 | t.Errorf("GetCtxTrace() got1 = %v, want %v", got1, tt.want1) 61 | } 62 | }) 63 | } 64 | } 65 | 66 | func TestNewWithMap(t *testing.T) { 67 | type args struct { 68 | m map[string]string 69 | } 70 | tests := []struct { 71 | name string 72 | args args 73 | }{ 74 | // TODO: Add test cases. 75 | { 76 | name: "test1", 77 | args: args { 78 | m: map[string]string{ 79 | "didi-header-rid":"1234", 80 | DIDI_HEADER_SPANID:"3435", 81 | }, 82 | }, 83 | }, 84 | } 85 | for _, tt := range tests { 86 | t.Run(tt.name, func(t *testing.T) { 87 | if gotTrace := NewWithMap(tt.args.m); gotTrace != nil { 88 | t.Logf("NewWithMap() = %v", gotTrace) 89 | } 90 | }) 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /golang_common/zerolog/.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | tmp 10 | 11 | # Architecture specific extensions/prefixes 12 | *.[568vq] 13 | [568vq].out 14 | 15 | *.cgo1.go 16 | *.cgo2.c 17 | _cgo_defun.c 18 | _cgo_gotypes.go 19 | _cgo_export.* 20 | 21 | _testmain.go 22 | 23 | *.exe 24 | *.test 25 | *.prof 26 | .* 27 | !/.gitignore -------------------------------------------------------------------------------- /golang_common/zerolog/CNAME: -------------------------------------------------------------------------------- 1 | zerolog.io -------------------------------------------------------------------------------- /golang_common/zerolog/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Olivier Poitrey 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /golang_common/zerolog/_config.yml: -------------------------------------------------------------------------------- 1 | remote_theme: rs/gh-readme 2 | -------------------------------------------------------------------------------- /golang_common/zerolog/array_json_test.go: -------------------------------------------------------------------------------- 1 | // +build json_log 2 | 3 | package zerolog 4 | 5 | import ( 6 | "net" 7 | "testing" 8 | "time" 9 | ) 10 | 11 | func TestArray(t *testing.T) { 12 | a := Arr(). 13 | Bool(true). 14 | Int(1). 15 | Int8(2). 16 | Int16(3). 17 | Int32(4). 18 | Int64(5). 19 | Uint(6). 20 | Uint8(7). 21 | Uint16(8). 22 | Uint32(9). 23 | Uint64(10). 24 | Float32(11.98122). 25 | Float64(12.987654321). 26 | Str("a"). 27 | Bytes([]byte("b")). 28 | Hex([]byte{0x1f}). 29 | Time(time.Time{}). 30 | IPAddr(net.IP{192, 168, 0, 10}). 31 | Dur(0) 32 | want := `[true,1,2,3,4,5,6,7,8,9,10,11.98122,12.987654321,"a","b","1f","0001-01-01T00:00:00Z","192.168.0.10",0]` 33 | if got := decodeObjectToStr(a.write([]byte{})); got != want { 34 | t.Errorf("Array.write()\ngot: %s\nwant: %s", got, want) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /golang_common/zerolog/array_test.go: -------------------------------------------------------------------------------- 1 | package zerolog 2 | 3 | import ( 4 | "net" 5 | "testing" 6 | "time" 7 | ) 8 | 9 | func TestArray(t *testing.T) { 10 | a := Arr(). 11 | Bool(true). 12 | Int(1). 13 | Int8(2). 14 | Int16(3). 15 | Int32(4). 16 | Int64(5). 17 | Uint(6). 18 | Uint8(7). 19 | Uint16(8). 20 | Uint32(9). 21 | Uint64(10). 22 | Float32(11.98122). 23 | Float64(12.987654321). 24 | Str("a"). 25 | Bytes([]byte("b")). 26 | Hex([]byte{0x1f}). 27 | Time(time.Time{}). 28 | IPAddr(net.IP{192, 168, 0, 10}). 29 | Dur(0) 30 | want := `[true,1,2,3,4,5,6,7,8,9,10,11.98122,12.987654321,a,b,1f,0001-01-01T00:00:00Z,192.168.0.10,0]` 31 | if got := decodeObjectToStr(a.write([]byte{})); got != want { 32 | t.Errorf("Array.write()\ngot: %s\nwant: %s", got, want) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /golang_common/zerolog/ctx.go: -------------------------------------------------------------------------------- 1 | package zerolog 2 | 3 | import ( 4 | "context" 5 | ) 6 | 7 | var disabledLogger *Logger 8 | 9 | func init() { 10 | l := Nop() 11 | disabledLogger = &l 12 | } 13 | 14 | type ctxKey struct{} 15 | 16 | // WithContext returns a copy of ctx with l associated. If an instance of Logger 17 | // is already in the context, the context is not updated. 18 | // 19 | // For instance, to add a field to an existing logger in the context, use this 20 | // notation: 21 | // 22 | // ctx := r.Context() 23 | // l := zerolog.Ctx(ctx) 24 | // l.UpdateContext(func(c Context) Context { 25 | // return c.Str("bar", "baz") 26 | // }) 27 | func (l *Logger) WithContext(ctx context.Context) context.Context { 28 | if lp, ok := ctx.Value(ctxKey{}).(*Logger); ok { 29 | if lp == l { 30 | // Do not store same logger. 31 | return ctx 32 | } 33 | } else if l.level == Disabled { 34 | // Do not store disabled logger. 35 | return ctx 36 | } 37 | return context.WithValue(ctx, ctxKey{}, l) 38 | } 39 | 40 | // Ctx returns the Logger associated with the ctx. If no logger 41 | // is associated, a disabled logger is returned. 42 | func Ctx(ctx context.Context) *Logger { 43 | if l, ok := ctx.Value(ctxKey{}).(*Logger); ok { 44 | return l 45 | } 46 | return disabledLogger 47 | } 48 | -------------------------------------------------------------------------------- /golang_common/zerolog/ctx_test.go: -------------------------------------------------------------------------------- 1 | package zerolog 2 | 3 | import ( 4 | "context" 5 | "io/ioutil" 6 | "reflect" 7 | "testing" 8 | ) 9 | 10 | func TestCtx(t *testing.T) { 11 | log := New(ioutil.Discard) 12 | ctx := log.WithContext(context.Background()) 13 | log2 := Ctx(ctx) 14 | if !reflect.DeepEqual(log, *log2) { 15 | t.Error("Ctx did not return the expected logger") 16 | } 17 | 18 | // update 19 | log = log.Level(InfoLevel) 20 | ctx = log.WithContext(ctx) 21 | log2 = Ctx(ctx) 22 | if !reflect.DeepEqual(log, *log2) { 23 | t.Error("Ctx did not return the expected logger") 24 | } 25 | 26 | log2 = Ctx(context.Background()) 27 | if log2 != disabledLogger { 28 | t.Error("Ctx did not return the expected logger") 29 | } 30 | } 31 | 32 | func TestCtxDisabled(t *testing.T) { 33 | dl := New(ioutil.Discard).Level(Disabled) 34 | ctx := dl.WithContext(context.Background()) 35 | if ctx != context.Background() { 36 | t.Error("WithContext stored a disabled logger") 37 | } 38 | 39 | l := New(ioutil.Discard).With().Str("foo", "bar").Logger() 40 | ctx = l.WithContext(ctx) 41 | if Ctx(ctx) != &l { 42 | t.Error("WithContext did not store logger") 43 | } 44 | 45 | l.UpdateContext(func(c Context) Context { 46 | return c.Str("bar", "baz") 47 | }) 48 | ctx = l.WithContext(ctx) 49 | if Ctx(ctx) != &l { 50 | t.Error("WithContext did not store updated logger") 51 | } 52 | 53 | l = l.Level(DebugLevel) 54 | ctx = l.WithContext(ctx) 55 | if Ctx(ctx) != &l { 56 | t.Error("WithContext did not store copied logger") 57 | } 58 | 59 | ctx = dl.WithContext(ctx) 60 | if Ctx(ctx) != &dl { 61 | t.Error("WithContext did not overide logger with a disabled logger") 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /golang_common/zerolog/ddlog/log.go: -------------------------------------------------------------------------------- 1 | package ddlog 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/didi/gatekeeper/golang_common/zerolog" 7 | ) 8 | 9 | var ( 10 | //Logger is defualt log ,and print to stdout, if want format context, use RegisterContextFormat 11 | Logger *DiLogHandle 12 | ) 13 | 14 | func init() { 15 | diLogger := zerolog.New(zerolog.NewStdoutWriter()) 16 | diLogger = diLogger.Level(zerolog.DebugLevel) 17 | Logger = &DiLogHandle{Logger: diLogger} 18 | } 19 | 20 | func Debugf(ctx context.Context, tag string, format string, args ...interface{}) { 21 | Logger.Debugf(ctx, tag, format, args...) 22 | } 23 | 24 | func Debug(args ...interface{}) { 25 | Logger.Debug(args...) 26 | } 27 | 28 | func Infof(ctx context.Context, tag string, format string, args ...interface{}) { 29 | Logger.Infof(ctx, tag, format, args...) 30 | } 31 | 32 | func Info(args ...interface{}) { 33 | Logger.Info(args...) 34 | } 35 | 36 | func Warnf(ctx context.Context, tag string, format string, args ...interface{}) { 37 | Logger.Warnf(ctx, tag, format, args...) 38 | } 39 | 40 | func Warn(args ...interface{}) { 41 | Logger.Warn(args...) 42 | } 43 | 44 | func Errorf(ctx context.Context, tag string, format string, args ...interface{}) { 45 | Logger.Errorf(ctx, tag, format, args...) 46 | } 47 | 48 | func Error(args ...interface{}) { 49 | Logger.Error(args...) 50 | } 51 | 52 | func Fatalf(ctx context.Context, tag string, format string, args ...interface{}) { 53 | Logger.Fatalf(ctx, tag, format, args...) 54 | } 55 | 56 | func Fatal(args ...interface{}) { 57 | Logger.Fatal(args...) 58 | } 59 | -------------------------------------------------------------------------------- /golang_common/zerolog/diode/diode_example_test.go: -------------------------------------------------------------------------------- 1 | // +build !binary_log 2 | 3 | package diode_test 4 | 5 | import ( 6 | "fmt" 7 | "os" 8 | 9 | "github.com/didi/gatekeeper/golang_common/zerolog" 10 | "github.com/didi/gatekeeper/golang_common/zerolog/diode" 11 | ) 12 | 13 | func ExampleNewWriter() { 14 | w := diode.NewWriter(os.Stdout, 1000, 0, func(missed int) { 15 | fmt.Printf("Dropped %d messages\n", missed) 16 | }) 17 | log := zerolog.New(w) 18 | log.Print("test") 19 | 20 | w.Close() 21 | 22 | // Output: {"level":"debug","message":"test"} 23 | } 24 | -------------------------------------------------------------------------------- /golang_common/zerolog/diode/diode_test.go: -------------------------------------------------------------------------------- 1 | package diode_test 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "io/ioutil" 7 | "log" 8 | "os" 9 | "testing" 10 | "time" 11 | 12 | "github.com/didi/gatekeeper/golang_common/zerolog" 13 | "github.com/didi/gatekeeper/golang_common/zerolog/diode" 14 | "github.com/didi/gatekeeper/golang_common/zerolog/internal/cbor" 15 | ) 16 | 17 | func TestNewWriter(t *testing.T) { 18 | buf := bytes.Buffer{} 19 | w := diode.NewWriter(&buf, 1000, 0, func(missed int) { 20 | fmt.Printf("Dropped %d messages\n", missed) 21 | }) 22 | log := zerolog.New(w) 23 | log.Print("test") 24 | 25 | w.Close() 26 | want := "{\"level\":\"debug\",\"message\":\"test\"}\n" 27 | got := cbor.DecodeIfBinaryToString(buf.Bytes()) 28 | if got != want { 29 | t.Errorf("Diode New Writer Test failed. got:%s, want:%s!", got, want) 30 | } 31 | } 32 | 33 | func Benchmark(b *testing.B) { 34 | log.SetOutput(ioutil.Discard) 35 | defer log.SetOutput(os.Stderr) 36 | benchs := map[string]time.Duration{ 37 | "Waiter": 0, 38 | "Pooler": 10 * time.Millisecond, 39 | } 40 | for name, interval := range benchs { 41 | b.Run(name, func(b *testing.B) { 42 | w := diode.NewWriter(ioutil.Discard, 100000, interval, nil) 43 | log := zerolog.New(w) 44 | defer w.Close() 45 | 46 | b.SetParallelism(1000) 47 | b.RunParallel(func(pb *testing.PB) { 48 | for pb.Next() { 49 | log.Print("test") 50 | } 51 | }) 52 | }) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /golang_common/zerolog/diode/internal/diodes/README: -------------------------------------------------------------------------------- 1 | Copied from https://github.com/cloudfoundry/go-diodes to avoid test dependencies. 2 | -------------------------------------------------------------------------------- /golang_common/zerolog/diode/internal/diodes/poller.go: -------------------------------------------------------------------------------- 1 | package diodes 2 | 3 | import ( 4 | "context" 5 | "time" 6 | ) 7 | 8 | // Diode is any implementation of a diode. 9 | type Diode interface { 10 | Set(GenericDataType) 11 | TryNext() (GenericDataType, bool) 12 | } 13 | 14 | // Poller will poll a diode until a value is available. 15 | type Poller struct { 16 | Diode 17 | interval time.Duration 18 | ctx context.Context 19 | } 20 | 21 | // PollerConfigOption can be used to setup the poller. 22 | type PollerConfigOption func(*Poller) 23 | 24 | // WithPollingInterval sets the interval at which the diode is queried 25 | // for new data. The default is 10ms. 26 | func WithPollingInterval(interval time.Duration) PollerConfigOption { 27 | return PollerConfigOption(func(c *Poller) { 28 | c.interval = interval 29 | }) 30 | } 31 | 32 | // WithPollingContext sets the context to cancel any retrieval (Next()). It 33 | // will not change any results for adding data (Set()). Default is 34 | // context.Background(). 35 | func WithPollingContext(ctx context.Context) PollerConfigOption { 36 | return PollerConfigOption(func(c *Poller) { 37 | c.ctx = ctx 38 | }) 39 | } 40 | 41 | // NewPoller returns a new Poller that wraps the given diode. 42 | func NewPoller(d Diode, opts ...PollerConfigOption) *Poller { 43 | p := &Poller{ 44 | Diode: d, 45 | interval: 10 * time.Millisecond, 46 | ctx: context.Background(), 47 | } 48 | 49 | for _, o := range opts { 50 | o(p) 51 | } 52 | 53 | return p 54 | } 55 | 56 | // Next polls the diode until data is available or until the context is done. 57 | // If the context is done, then nil will be returned. 58 | func (p *Poller) Next() GenericDataType { 59 | for { 60 | data, ok := p.Diode.TryNext() 61 | if !ok { 62 | if p.isDone() { 63 | return nil 64 | } 65 | 66 | time.Sleep(p.interval) 67 | continue 68 | } 69 | return data 70 | } 71 | } 72 | 73 | func (p *Poller) isDone() bool { 74 | select { 75 | case <-p.ctx.Done(): 76 | return true 77 | default: 78 | return false 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /golang_common/zerolog/diode/internal/diodes/waiter.go: -------------------------------------------------------------------------------- 1 | package diodes 2 | 3 | import ( 4 | "context" 5 | "sync" 6 | ) 7 | 8 | // Waiter will use a conditional mutex to alert the reader to when data is 9 | // available. 10 | type Waiter struct { 11 | Diode 12 | mu sync.Mutex 13 | c *sync.Cond 14 | ctx context.Context 15 | } 16 | 17 | // WaiterConfigOption can be used to setup the waiter. 18 | type WaiterConfigOption func(*Waiter) 19 | 20 | // WithWaiterContext sets the context to cancel any retrieval (Next()). It 21 | // will not change any results for adding data (Set()). Default is 22 | // context.Background(). 23 | func WithWaiterContext(ctx context.Context) WaiterConfigOption { 24 | return WaiterConfigOption(func(c *Waiter) { 25 | c.ctx = ctx 26 | }) 27 | } 28 | 29 | // NewWaiter returns a new Waiter that wraps the given diode. 30 | func NewWaiter(d Diode, opts ...WaiterConfigOption) *Waiter { 31 | w := new(Waiter) 32 | w.Diode = d 33 | w.c = sync.NewCond(&w.mu) 34 | w.ctx = context.Background() 35 | 36 | for _, opt := range opts { 37 | opt(w) 38 | } 39 | 40 | go func() { 41 | <-w.ctx.Done() 42 | w.c.Broadcast() 43 | }() 44 | 45 | return w 46 | } 47 | 48 | // Set invokes the wrapped diode's Set with the given data and uses Broadcast 49 | // to wake up any readers. 50 | func (w *Waiter) Set(data GenericDataType) { 51 | w.Diode.Set(data) 52 | w.c.Broadcast() 53 | } 54 | 55 | // Next returns the next data point on the wrapped diode. If there is not any 56 | // new data, it will Wait for set to be called or the context to be done. 57 | // If the context is done, then nil will be returned. 58 | func (w *Waiter) Next() GenericDataType { 59 | w.mu.Lock() 60 | defer w.mu.Unlock() 61 | 62 | for { 63 | data, ok := w.Diode.TryNext() 64 | if !ok { 65 | if w.isDone() { 66 | return nil 67 | } 68 | 69 | w.c.Wait() 70 | continue 71 | } 72 | return data 73 | } 74 | } 75 | 76 | func (w *Waiter) isDone() bool { 77 | select { 78 | case <-w.ctx.Done(): 79 | return true 80 | default: 81 | return false 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /golang_common/zerolog/elevate/cr.yml: -------------------------------------------------------------------------------- 1 | reviewers: weijingwu,shenjun 2 | owners: weijingwu,shenjun 3 | strategy: default 4 | lint: 5 | force: false 6 | language: 7 | go: 8 | standard: golint 9 | exclude: 10 | - (^vendor/) 11 | 12 | -------------------------------------------------------------------------------- /golang_common/zerolog/encoder_cbor.go: -------------------------------------------------------------------------------- 1 | // +build binary_log 2 | 3 | package zerolog 4 | 5 | // This file contains bindings to do binary encoding. 6 | 7 | import ( 8 | "github.com/didi/gatekeeper/golang_common/zerolog/internal/cbor" 9 | ) 10 | 11 | var ( 12 | _ encoder = (*cbor.Encoder)(nil) 13 | 14 | enc = cbor.Encoder{} 15 | ) 16 | 17 | func appendJSON(dst []byte, j []byte) []byte { 18 | return cbor.AppendEmbeddedJSON(dst, j) 19 | } 20 | 21 | // decodeIfBinaryToString - converts a binary formatted log msg to a 22 | // JSON formatted String Log message. 23 | func decodeIfBinaryToString(in []byte) string { 24 | return cbor.DecodeIfBinaryToString(in) 25 | } 26 | 27 | func decodeObjectToStr(in []byte) string { 28 | return cbor.DecodeObjectToStr(in) 29 | } 30 | 31 | // decodeIfBinaryToBytes - converts a binary formatted log msg to a 32 | // JSON formatted Bytes Log message. 33 | func decodeIfBinaryToBytes(in []byte) []byte { 34 | return cbor.DecodeIfBinaryToBytes(in) 35 | } 36 | -------------------------------------------------------------------------------- /golang_common/zerolog/encoder_console.go: -------------------------------------------------------------------------------- 1 | package zerolog 2 | 3 | // encoder_json.go file contains bindings to generate 4 | // JSON encoded byte stream. 5 | 6 | import ( 7 | "os" 8 | 9 | "github.com/didi/gatekeeper/golang_common/zerolog/internal/console" 10 | ) 11 | 12 | var ( 13 | cwd, _ = os.Getwd() 14 | _ encoder = (*console.Encoder)(nil) 15 | 16 | enc = console.Encoder{} 17 | ) 18 | 19 | func appendJSON(dst []byte, j []byte) []byte { 20 | return append(dst, j...) 21 | } 22 | 23 | func decodeIfBinaryToString(in []byte) string { 24 | return string(in) 25 | } 26 | 27 | func decodeObjectToStr(in []byte) string { 28 | return string(in) 29 | } 30 | 31 | func decodeIfBinaryToBytes(in []byte) []byte { 32 | return in 33 | } 34 | -------------------------------------------------------------------------------- /golang_common/zerolog/encoder_json.go: -------------------------------------------------------------------------------- 1 | // +build json_log 2 | 3 | package zerolog 4 | 5 | // encoder_json.go file contains bindings to generate 6 | // JSON encoded byte stream. 7 | 8 | import ( 9 | "github.com/didi/gatekeeper/golang_common/zerolog/internal/json" 10 | ) 11 | 12 | var ( 13 | _ encoder = (*json.Encoder)(nil) 14 | 15 | enc = json.Encoder{} 16 | ) 17 | 18 | func appendJSON(dst []byte, j []byte) []byte { 19 | return append(dst, j...) 20 | } 21 | 22 | func decodeIfBinaryToString(in []byte) string { 23 | return string(in) 24 | } 25 | 26 | func decodeObjectToStr(in []byte) string { 27 | return string(in) 28 | } 29 | 30 | func decodeIfBinaryToBytes(in []byte) []byte { 31 | return in 32 | } 33 | -------------------------------------------------------------------------------- /golang_common/zerolog/hook.go: -------------------------------------------------------------------------------- 1 | package zerolog 2 | 3 | // Hook defines an interface to a log hook. 4 | type Hook interface { 5 | // Run runs the hook with the event. 6 | Run(e *Event, level Level, message string) 7 | } 8 | 9 | // HookFunc is an adaptor to allow the use of an ordinary function 10 | // as a Hook. 11 | type HookFunc func(e *Event, level Level, message string) 12 | 13 | // Run implements the Hook interface. 14 | func (h HookFunc) Run(e *Event, level Level, message string) { 15 | h(e, level, message) 16 | } 17 | 18 | // LevelHook applies a different hook for each level. 19 | type LevelHook struct { 20 | NoLevelHook, DebugHook, InfoHook, WarnHook, ErrorHook, FatalHook, PanicHook Hook 21 | } 22 | 23 | // Run implements the Hook interface. 24 | func (h LevelHook) Run(e *Event, level Level, message string) { 25 | switch level { 26 | case DebugLevel: 27 | if h.DebugHook != nil { 28 | h.DebugHook.Run(e, level, message) 29 | } 30 | case InfoLevel: 31 | if h.InfoHook != nil { 32 | h.InfoHook.Run(e, level, message) 33 | } 34 | case WarnLevel: 35 | if h.WarnHook != nil { 36 | h.WarnHook.Run(e, level, message) 37 | } 38 | case ErrorLevel: 39 | if h.ErrorHook != nil { 40 | h.ErrorHook.Run(e, level, message) 41 | } 42 | case FatalLevel: 43 | if h.FatalHook != nil { 44 | h.FatalHook.Run(e, level, message) 45 | } 46 | case PanicLevel: 47 | if h.PanicHook != nil { 48 | h.PanicHook.Run(e, level, message) 49 | } 50 | case NoLevel: 51 | if h.NoLevelHook != nil { 52 | h.NoLevelHook.Run(e, level, message) 53 | } 54 | } 55 | } 56 | 57 | // NewLevelHook returns a new LevelHook. 58 | func NewLevelHook() LevelHook { 59 | return LevelHook{} 60 | } 61 | -------------------------------------------------------------------------------- /golang_common/zerolog/internal/cbor/base.go: -------------------------------------------------------------------------------- 1 | package cbor 2 | 3 | type Encoder struct{} 4 | 5 | // AppendKey adds a key (string) to the binary encoded log message 6 | func (e Encoder) AppendKey(dst []byte, key string) []byte { 7 | if len(dst) < 1 { 8 | dst = e.AppendBeginMarker(dst) 9 | } 10 | return e.AppendString(dst, key) 11 | } -------------------------------------------------------------------------------- /golang_common/zerolog/internal/cbor/examples/genLog.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "compress/zlib" 5 | "flag" 6 | "io" 7 | "log" 8 | "os" 9 | "time" 10 | 11 | "github.com/didi/gatekeeper/golang_common/zerolog" 12 | ) 13 | 14 | func writeLog(fname string, count int, useCompress bool) { 15 | opFile := os.Stdout 16 | if fname != "" { 17 | fil, _ := os.Create(fname) 18 | opFile = fil 19 | defer func() { 20 | if err := fil.Close(); err != nil { 21 | log.Fatal(err) 22 | } 23 | }() 24 | } 25 | 26 | var f io.WriteCloser = opFile 27 | if useCompress { 28 | f = zlib.NewWriter(f) 29 | defer func() { 30 | if err := f.Close(); err != nil { 31 | log.Fatal(err) 32 | } 33 | }() 34 | 35 | } 36 | 37 | zerolog.TimestampFunc = func() time.Time { return time.Now().Round(time.Second) } 38 | log := zerolog.New(f).With(). 39 | Timestamp(). 40 | Logger() 41 | for i := 0; i < count; i++ { 42 | log.Error(). 43 | Int("Fault", 41650+i).Msg("Some Message") 44 | } 45 | } 46 | 47 | func main() { 48 | outFile := flag.String("out", "", "Output File to which logs will be written to (WILL overwrite if already present).") 49 | numLogs := flag.Int("num", 10, "Number of log messages to generate.") 50 | doCompress := flag.Bool("compress", false, "Enable inline compressed writer") 51 | 52 | flag.Parse() 53 | 54 | writeLog(*outFile, *numLogs, *doCompress) 55 | } 56 | -------------------------------------------------------------------------------- /golang_common/zerolog/internal/cbor/examples/makefile: -------------------------------------------------------------------------------- 1 | all: genLogJSON genLogCBOR 2 | 3 | genLogJSON: genLog.go 4 | go build -o genLogJSON genLog.go 5 | 6 | genLogCBOR: genLog.go 7 | go build -tags binary_log -o genLogCBOR genLog.go 8 | 9 | clean: 10 | rm -f genLogJSON genLogCBOR 11 | -------------------------------------------------------------------------------- /golang_common/zerolog/internal/cbor/string.go: -------------------------------------------------------------------------------- 1 | package cbor 2 | 3 | // AppendStrings encodes and adds an array of strings to the dst byte array. 4 | func (e Encoder) AppendStrings(dst []byte, vals []string) []byte { 5 | major := majorTypeArray 6 | l := len(vals) 7 | if l <= additionalMax { 8 | lb := byte(l) 9 | dst = append(dst, byte(major|lb)) 10 | } else { 11 | dst = appendCborTypePrefix(dst, major, uint64(l)) 12 | } 13 | for _, v := range vals { 14 | dst = e.AppendString(dst, v) 15 | } 16 | return dst 17 | } 18 | 19 | // AppendString encodes and adds a string to the dst byte array. 20 | func (Encoder) AppendString(dst []byte, s string) []byte { 21 | major := majorTypeUtf8String 22 | 23 | l := len(s) 24 | if l <= additionalMax { 25 | lb := byte(l) 26 | dst = append(dst, byte(major|lb)) 27 | } else { 28 | dst = appendCborTypePrefix(dst, majorTypeUtf8String, uint64(l)) 29 | } 30 | return append(dst, s...) 31 | } 32 | 33 | // AppendBytes encodes and adds an array of bytes to the dst byte array. 34 | func (Encoder) AppendBytes(dst, s []byte) []byte { 35 | major := majorTypeByteString 36 | 37 | l := len(s) 38 | if l <= additionalMax { 39 | lb := byte(l) 40 | dst = append(dst, byte(major|lb)) 41 | } else { 42 | dst = appendCborTypePrefix(dst, major, uint64(l)) 43 | } 44 | return append(dst, s...) 45 | } 46 | 47 | // AppendEmbeddedJSON adds a tag and embeds input JSON as such. 48 | func AppendEmbeddedJSON(dst, s []byte) []byte { 49 | major := majorTypeTags 50 | minor := additionalTypeEmbeddedJSON 51 | 52 | // Append the TAG to indicate this is Embedded JSON. 53 | dst = append(dst, byte(major|additionalTypeIntUint16)) 54 | dst = append(dst, byte(minor>>8)) 55 | dst = append(dst, byte(minor&0xff)) 56 | 57 | // Append the JSON Object as Byte String. 58 | major = majorTypeByteString 59 | 60 | l := len(s) 61 | if l <= additionalMax { 62 | lb := byte(l) 63 | dst = append(dst, byte(major|lb)) 64 | } else { 65 | dst = appendCborTypePrefix(dst, major, uint64(l)) 66 | } 67 | return append(dst, s...) 68 | } 69 | -------------------------------------------------------------------------------- /golang_common/zerolog/internal/console/base.go: -------------------------------------------------------------------------------- 1 | package console 2 | 3 | type Encoder struct{} 4 | 5 | // AppendKey appends a new key to the output JSON. 6 | func (e Encoder) AppendKey(dst []byte, key string) []byte { 7 | 8 | if len(dst) > 1 { 9 | dst = append(dst, []byte("||")...) 10 | } 11 | if key != "" { 12 | dst = e.AppendString(dst, key+"=") 13 | } 14 | 15 | return dst 16 | } 17 | -------------------------------------------------------------------------------- /golang_common/zerolog/internal/json/base.go: -------------------------------------------------------------------------------- 1 | package json 2 | 3 | type Encoder struct{} 4 | 5 | // AppendKey appends a new key to the output JSON. 6 | func (e Encoder) AppendKey(dst []byte, key string) []byte { 7 | if len(dst) > 1 && dst[len(dst)-1] != '{' { 8 | dst = append(dst, ',') 9 | } 10 | dst = e.AppendString(dst, key) 11 | return append(dst, ':') 12 | } -------------------------------------------------------------------------------- /golang_common/zerolog/journald/journald_test.go: -------------------------------------------------------------------------------- 1 | // +build !windows 2 | 3 | package journald_test 4 | 5 | import "github.com/didi/gatekeeper/golang_common/zerolog" 6 | import "github.com/didi/gatekeeper/golang_common/zerolog/journald" 7 | 8 | func ExampleNewJournalDWriter() { 9 | log := zerolog.New(journald.NewJournalDWriter()) 10 | log.Info().Str("foo", "bar").Uint64("small", 123).Float64("float", 3.14).Uint64("big", 1152921504606846976).Msg("Journal Test") 11 | // Output: 12 | } 13 | 14 | /* 15 | 16 | There is no automated way to verify the output - since the output is sent 17 | to journald process and method to retrieve is journalctl. Will find a way 18 | to automate the process and fix this test. 19 | 20 | $ journalctl -o verbose -f 21 | 22 | Thu 2018-04-26 22:30:20.768136 PDT [s=3284d695bde946e4b5017c77a399237f;i=329f0;b=98c0dca0debc4b98a5b9534e910e7dd6;m=7a702e35dd4;t=56acdccd2ed0a;x=4690034cf0348614] 23 | PRIORITY=6 24 | _AUDIT_LOGINUID=1000 25 | _BOOT_ID=98c0dca0debc4b98a5b9534e910e7dd6 26 | _MACHINE_ID=926ed67eb4744580948de70fb474975e 27 | _HOSTNAME=sprint 28 | _UID=1000 29 | _GID=1000 30 | _CAP_EFFECTIVE=0 31 | _SYSTEMD_SLICE=-.slice 32 | _TRANSPORT=journal 33 | _SYSTEMD_CGROUP=/ 34 | _AUDIT_SESSION=2945 35 | MESSAGE=Journal Test 36 | FOO=bar 37 | BIG=1152921504606846976 38 | _COMM=journald.test 39 | SMALL=123 40 | FLOAT=3.14 41 | JSON={"level":"info","foo":"bar","small":123,"float":3.14,"big":1152921504606846976,"message":"Journal Test"} 42 | _PID=27103 43 | _SOURCE_REALTIME_TIMESTAMP=1524807020768136 44 | */ 45 | -------------------------------------------------------------------------------- /golang_common/zerolog/pretty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/didi/GateKeeper/3c5211ab37040f47c5c9bd236adb06e8c69127d3/golang_common/zerolog/pretty.png -------------------------------------------------------------------------------- /golang_common/zerolog/sampler_test.go: -------------------------------------------------------------------------------- 1 | // +build binary_log 2 | 3 | package zerolog 4 | 5 | import ( 6 | "testing" 7 | "time" 8 | ) 9 | 10 | var samplers = []struct { 11 | name string 12 | sampler func() Sampler 13 | total int 14 | wantMin int 15 | wantMax int 16 | }{ 17 | { 18 | "BasicSampler_1", 19 | func() Sampler { 20 | return &BasicSampler{N: 1} 21 | }, 22 | 100, 100, 100, 23 | }, 24 | { 25 | "BasicSampler_5", 26 | func() Sampler { 27 | return &BasicSampler{N: 5} 28 | }, 29 | 100, 20, 20, 30 | }, 31 | { 32 | "RandomSampler", 33 | func() Sampler { 34 | return RandomSampler(5) 35 | }, 36 | 100, 10, 30, 37 | }, 38 | { 39 | "BurstSampler", 40 | func() Sampler { 41 | return &BurstSampler{Burst: 20, Period: time.Second} 42 | }, 43 | 100, 20, 20, 44 | }, 45 | { 46 | "BurstSamplerNext", 47 | func() Sampler { 48 | return &BurstSampler{Burst: 20, Period: time.Second, NextSampler: &BasicSampler{N: 5}} 49 | }, 50 | 120, 40, 40, 51 | }, 52 | } 53 | 54 | func TestSamplers(t *testing.T) { 55 | for i := range samplers { 56 | s := samplers[i] 57 | t.Run(s.name, func(t *testing.T) { 58 | sampler := s.sampler() 59 | got := 0 60 | for t := s.total; t > 0; t-- { 61 | if sampler.Sample(0) { 62 | got++ 63 | } 64 | } 65 | if got < s.wantMin || got > s.wantMax { 66 | t.Errorf("%s.Sample(0) == true %d on %d, want [%d, %d]", s.name, got, s.total, s.wantMin, s.wantMax) 67 | } 68 | }) 69 | } 70 | } 71 | 72 | func BenchmarkSamplers(b *testing.B) { 73 | for i := range samplers { 74 | s := samplers[i] 75 | b.Run(s.name, func(b *testing.B) { 76 | sampler := s.sampler() 77 | b.RunParallel(func(pb *testing.PB) { 78 | for pb.Next() { 79 | sampler.Sample(0) 80 | } 81 | }) 82 | }) 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /golang_common/zerolog/stdout_write.go: -------------------------------------------------------------------------------- 1 | package zerolog 2 | 3 | import ( 4 | "os" 5 | ) 6 | 7 | type StdoutWriter struct { 8 | AccessWriter 9 | } 10 | 11 | // NewStdoutWriter will write to stdout. 12 | func NewStdoutWriter(options ...Option) *StdoutWriter { 13 | w := AccessWriter{} 14 | 15 | for _, opt := range options { 16 | opt(&w) 17 | } 18 | 19 | w.FileOut = os.Stdout 20 | return &StdoutWriter{w} 21 | } 22 | 23 | func (w *StdoutWriter) WriteLevel(l Level, p []byte) (n int, err error) { 24 | 25 | return w.Write(p) 26 | } 27 | -------------------------------------------------------------------------------- /golang_common/zerolog/syslog.go: -------------------------------------------------------------------------------- 1 | // +build !windows 2 | // +build !binary_log 3 | 4 | package zerolog 5 | 6 | import ( 7 | "io" 8 | ) 9 | 10 | // SyslogWriter is an interface matching a syslog.Writer struct. 11 | type SyslogWriter interface { 12 | io.Writer 13 | Debug(m string) error 14 | Info(m string) error 15 | Warning(m string) error 16 | Err(m string) error 17 | Emerg(m string) error 18 | Crit(m string) error 19 | } 20 | 21 | type syslogWriter struct { 22 | w SyslogWriter 23 | } 24 | 25 | // SyslogLevelWriter wraps a SyslogWriter and call the right syslog level 26 | // method matching the zerolog level. 27 | func SyslogLevelWriter(w SyslogWriter) LevelWriter { 28 | return syslogWriter{w} 29 | } 30 | 31 | func (sw syslogWriter) Write(p []byte) (n int, err error) { 32 | return sw.w.Write(p) 33 | } 34 | 35 | // WriteLevel implements LevelWriter interface. 36 | func (sw syslogWriter) WriteLevel(level Level, p []byte) (n int, err error) { 37 | switch level { 38 | case DebugLevel: 39 | err = sw.w.Debug(string(p)) 40 | case InfoLevel: 41 | err = sw.w.Info(string(p)) 42 | case WarnLevel: 43 | err = sw.w.Warning(string(p)) 44 | case ErrorLevel: 45 | err = sw.w.Err(string(p)) 46 | case FatalLevel: 47 | err = sw.w.Emerg(string(p)) 48 | case PanicLevel: 49 | err = sw.w.Crit(string(p)) 50 | case NoLevel: 51 | err = sw.w.Info(string(p)) 52 | default: 53 | panic("invalid level") 54 | } 55 | n = len(p) 56 | return 57 | } 58 | 59 | func (sw syslogWriter) FormatCaller(i string) string { 60 | return defaultFormatCaller(i) 61 | } 62 | 63 | func (sw syslogWriter) FormatTimestamp() string { 64 | return defaultFormatTimestamp() 65 | } 66 | 67 | func (sw syslogWriter) FormatMessage(i string) string { 68 | return defaultFormatMessage(i) 69 | } 70 | 71 | func (sw syslogWriter) FormatLevel(i string) string { 72 | return defaultFormatLevel(i) 73 | } 74 | -------------------------------------------------------------------------------- /golang_common/zerolog/syslog_json_test.go: -------------------------------------------------------------------------------- 1 | // +build !binary_log 2 | // +build !windows 3 | // +build json_log 4 | 5 | package zerolog 6 | 7 | import "testing" 8 | import "reflect" 9 | 10 | type syslogEvent struct { 11 | level string 12 | msg string 13 | } 14 | type syslogTestWriter struct { 15 | events []syslogEvent 16 | } 17 | 18 | func (w *syslogTestWriter) Write(p []byte) (int, error) { 19 | return 0, nil 20 | } 21 | func (w *syslogTestWriter) Debug(m string) error { 22 | w.events = append(w.events, syslogEvent{"Debug", m}) 23 | return nil 24 | } 25 | func (w *syslogTestWriter) Info(m string) error { 26 | w.events = append(w.events, syslogEvent{"Info", m}) 27 | return nil 28 | } 29 | func (w *syslogTestWriter) Warning(m string) error { 30 | w.events = append(w.events, syslogEvent{"Warning", m}) 31 | return nil 32 | } 33 | func (w *syslogTestWriter) Err(m string) error { 34 | w.events = append(w.events, syslogEvent{"Err", m}) 35 | return nil 36 | } 37 | func (w *syslogTestWriter) Emerg(m string) error { 38 | w.events = append(w.events, syslogEvent{"Emerg", m}) 39 | return nil 40 | } 41 | func (w *syslogTestWriter) Crit(m string) error { 42 | w.events = append(w.events, syslogEvent{"Crit", m}) 43 | return nil 44 | } 45 | 46 | func TestSyslogWriter(t *testing.T) { 47 | sw := &syslogTestWriter{} 48 | log := New(SyslogLevelWriter(sw)) 49 | log.Debug().Msg("debug") 50 | log.Info().Msg("info") 51 | log.Warn().Msg("warn") 52 | log.Error().Msg("error") 53 | log.Log().Msg("nolevel") 54 | want := []syslogEvent{ 55 | {"Debug", `{"level":"debug","message":"debug"}` + "\n"}, 56 | {"Info", `{"level":"info","message":"info"}` + "\n"}, 57 | {"Warning", `{"level":"warn","message":"warn"}` + "\n"}, 58 | {"Err", `{"level":"error","message":"error"}` + "\n"}, 59 | {"Info", `{"message":"nolevel"}` + "\n"}, 60 | } 61 | if got := sw.events; !reflect.DeepEqual(got, want) { 62 | t.Errorf("Invalid syslog message routing: want %v, got %v", want, got) 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /golang_common/zerolog/syslog_test.go: -------------------------------------------------------------------------------- 1 | // +build !binary_log 2 | // +build !windows 3 | 4 | package zerolog 5 | 6 | import "testing" 7 | import "reflect" 8 | 9 | type syslogEvent struct { 10 | level string 11 | msg string 12 | } 13 | type syslogTestWriter struct { 14 | events []syslogEvent 15 | } 16 | 17 | func (w *syslogTestWriter) Write(p []byte) (int, error) { 18 | return 0, nil 19 | } 20 | func (w *syslogTestWriter) Debug(m string) error { 21 | w.events = append(w.events, syslogEvent{"Debug", m}) 22 | return nil 23 | } 24 | func (w *syslogTestWriter) Info(m string) error { 25 | w.events = append(w.events, syslogEvent{"Info", m}) 26 | return nil 27 | } 28 | func (w *syslogTestWriter) Warning(m string) error { 29 | w.events = append(w.events, syslogEvent{"Warning", m}) 30 | return nil 31 | } 32 | func (w *syslogTestWriter) Err(m string) error { 33 | w.events = append(w.events, syslogEvent{"Err", m}) 34 | return nil 35 | } 36 | func (w *syslogTestWriter) Emerg(m string) error { 37 | w.events = append(w.events, syslogEvent{"Emerg", m}) 38 | return nil 39 | } 40 | func (w *syslogTestWriter) Crit(m string) error { 41 | w.events = append(w.events, syslogEvent{"Crit", m}) 42 | return nil 43 | } 44 | 45 | func TestSyslogWriter(t *testing.T) { 46 | sw := &syslogTestWriter{} 47 | log := New(SyslogLevelWriter(sw)) 48 | log.Debug().Msg("debug") 49 | log.Info().Msg("info") 50 | log.Warn().Msg("warn") 51 | log.Error().Msg("error") 52 | log.Log().Msg("nolevel") 53 | want := []syslogEvent{ 54 | {"Debug", `[DEBUG]||debug` + "\n"}, 55 | {"Info", `[INFO]||info` + "\n"}, 56 | {"Warning", `[WARNING]||warn` + "\n"}, 57 | {"Err", `[ERROR]||error` + "\n"}, 58 | {"Info", `` + "\n"}, 59 | } 60 | if got := sw.events; !reflect.DeepEqual(got, want) { 61 | t.Errorf("Invalid syslog message routing: want %v, got %v", want, got) 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /golang_common/zerolog/utils.go: -------------------------------------------------------------------------------- 1 | package zerolog 2 | 3 | import ( 4 | "io/ioutil" 5 | "path/filepath" 6 | "strconv" 7 | "strings" 8 | "time" 9 | ) 10 | 11 | // GetFilesByDir ... 12 | func getFilesByDir(dir string) ([]string, error) { 13 | var fileList []string 14 | fs, err := ioutil.ReadDir(dir) 15 | if err != nil { 16 | return nil, err 17 | } 18 | for _, f := range fs { 19 | if f.IsDir() { 20 | continue 21 | } 22 | path := filepath.Join(dir, f.Name()) 23 | fileList = append(fileList, path) 24 | } 25 | 26 | return fileList, nil 27 | } 28 | 29 | func getExpiredFilesByDir(dir string, beginTime int64, filePrefix string) ([]string, error) { 30 | var fileList []string 31 | fs, err := ioutil.ReadDir(dir) 32 | if err != nil { 33 | return nil, err 34 | } 35 | for _, f := range fs { 36 | if f.IsDir() { 37 | continue 38 | } 39 | filename := f.Name() 40 | if !strings.HasPrefix(filename, filePrefix) { 41 | continue 42 | } 43 | idx := strings.LastIndex(filename, ".") 44 | ts := filename[idx+1:] 45 | if len(ts) == 10 { 46 | dateStr := ts[0:8] 47 | hourStr := ts[8:] 48 | date, err := time.Parse("20060102", dateStr) 49 | if err != nil { 50 | return []string{}, err 51 | } 52 | reviseDate := date.Local() 53 | offset, err := strconv.ParseInt(hourStr, 10, 64) 54 | if err != nil { 55 | return []string{}, err 56 | } 57 | timeUnix := reviseDate.Unix() - int64(reviseDate.Hour())*3600 + offset*3600 58 | if timeUnix <= beginTime { 59 | path := filepath.Join(dir, filename) 60 | fileList = append(fileList, path) 61 | } 62 | } 63 | } 64 | 65 | return fileList, nil 66 | } 67 | -------------------------------------------------------------------------------- /golang_common/zerolog/writer_json_test.go: -------------------------------------------------------------------------------- 1 | // +build !binary_log 2 | // +build !windows 3 | // +build json_log 4 | 5 | package zerolog 6 | 7 | import ( 8 | "reflect" 9 | "testing" 10 | ) 11 | 12 | func TestMultiSyslogWriter(t *testing.T) { 13 | sw := &syslogTestWriter{} 14 | log := New(MultiLevelWriter(SyslogLevelWriter(sw))) 15 | log.Debug().Msg("debug") 16 | log.Info().Msg("info") 17 | log.Warn().Msg("warn") 18 | log.Error().Msg("error") 19 | log.Log().Msg("nolevel") 20 | want := []syslogEvent{ 21 | {"Debug", `{"level":"debug","message":"debug"}` + "\n"}, 22 | {"Info", `{"level":"info","message":"info"}` + "\n"}, 23 | {"Warning", `{"level":"warn","message":"warn"}` + "\n"}, 24 | {"Err", `{"level":"error","message":"error"}` + "\n"}, 25 | {"Info", `{"message":"nolevel"}` + "\n"}, 26 | } 27 | if got := sw.events; !reflect.DeepEqual(got, want) { 28 | t.Errorf("Invalid syslog message routing: want %v, got %v", want, got) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /golang_common/zerolog/writer_test.go: -------------------------------------------------------------------------------- 1 | // +build !binary_log 2 | // +build !windows 3 | 4 | package zerolog 5 | 6 | import ( 7 | "reflect" 8 | "testing" 9 | ) 10 | 11 | func TestMultiSyslogWriter(t *testing.T) { 12 | sw := &syslogTestWriter{} 13 | log := New(MultiLevelWriter(SyslogLevelWriter(sw))) 14 | log.Debug().Msg("debug") 15 | log.Info().Msg("info") 16 | log.Warn().Msg("warn") 17 | log.Error().Msg("error") 18 | log.Log().Msg("nolevel") 19 | want := []syslogEvent{ 20 | {"Debug", `[DEBUG]||debug` + "\n"}, 21 | {"Info", `[INFO]||info` + "\n"}, 22 | {"Warning", `[WARNING]||warn` + "\n"}, 23 | {"Err", `[ERROR]||error` + "\n"}, 24 | {"Info", `` + "\n"}, 25 | } 26 | if got := sw.events; !reflect.DeepEqual(got, want) { 27 | t.Errorf("Invalid syslog message routing: want %v, got %v", want, got) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /grpc_proxy_middleware/grpc_black_list.go: -------------------------------------------------------------------------------- 1 | package grpc_proxy_middleware 2 | 3 | import ( 4 | "fmt" 5 | "github.com/didi/gatekeeper/model" 6 | "github.com/didi/gatekeeper/public" 7 | "github.com/pkg/errors" 8 | "google.golang.org/grpc" 9 | "google.golang.org/grpc/peer" 10 | "log" 11 | "strings" 12 | ) 13 | 14 | func GrpcBlackListMiddleware(serviceDetail *model.ServiceDetail) func(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error { 15 | return func(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error { 16 | whiteListStr := serviceDetail.PluginConf.GetPath("grpc_whiteblacklist", "ip_white_list").MustString() 17 | blackListStr := serviceDetail.PluginConf.GetPath("grpc_whiteblacklist", "ip_black_list").MustString() 18 | 19 | peerCtx, ok := peer.FromContext(ss.Context()) 20 | if !ok { 21 | return errors.New("peer not found with context") 22 | } 23 | peerAddr := peerCtx.Addr.String() 24 | addrPos := strings.LastIndex(peerAddr, ":") 25 | clientIP := peerAddr[0:addrPos] 26 | 27 | if whiteListStr == "" && blackListStr != "" { 28 | if public.InIPSliceStr(clientIP, blackListStr) { 29 | return errors.New(fmt.Sprintf("%s in black ip list", clientIP)) 30 | } 31 | } 32 | if err := handler(srv, ss); err != nil { 33 | log.Printf("RPC failed with error %v\n", err) 34 | return err 35 | } 36 | return nil 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /grpc_proxy_middleware/grpc_flow_count.go: -------------------------------------------------------------------------------- 1 | package grpc_proxy_middleware 2 | 3 | import ( 4 | "github.com/didi/gatekeeper/model" 5 | "github.com/didi/gatekeeper/handler" 6 | "github.com/didi/gatekeeper/public" 7 | "google.golang.org/grpc" 8 | "log" 9 | ) 10 | 11 | func GrpcFlowCountMiddleware(serviceDetail *model.ServiceDetail) func(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error{ 12 | return func(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, sHandler grpc.StreamHandler) error{ 13 | totalCounter, err := handler.ServiceCounterHandler.GetCounter(public.FlowTotal) 14 | if err != nil { 15 | return err 16 | } 17 | totalCounter.Increase() 18 | serviceCounter, err := handler.ServiceCounterHandler.GetCounter(public.FlowServicePrefix + serviceDetail.Info.ServiceName) 19 | if err != nil { 20 | return err 21 | } 22 | serviceCounter.Increase() 23 | 24 | if err := sHandler(srv, ss);err != nil { 25 | log.Printf("GrpcFlowCountMiddleware failed with error %v\n", err) 26 | return err 27 | } 28 | return nil 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /grpc_proxy_middleware/grpc_jwt_auth_token.go: -------------------------------------------------------------------------------- 1 | package grpc_proxy_middleware 2 | 3 | import ( 4 | "github.com/didi/gatekeeper/handler" 5 | "github.com/didi/gatekeeper/model" 6 | "github.com/didi/gatekeeper/public" 7 | "github.com/pkg/errors" 8 | "google.golang.org/grpc" 9 | "google.golang.org/grpc/metadata" 10 | "log" 11 | "strings" 12 | ) 13 | 14 | //jwt auth token 15 | func GrpcJwtAuthTokenMiddleware(serviceDetail *model.ServiceDetail) func(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error { 16 | return func(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, grpcHandler grpc.StreamHandler) error { 17 | md, ok := metadata.FromIncomingContext(ss.Context()) 18 | if !ok { 19 | return errors.New("miss metadata from context") 20 | } 21 | if serviceDetail.Info.AuthType != "jwt_auth" { 22 | if err := grpcHandler(srv, ss); err != nil { 23 | log.Printf("GrpcJwtAuthTokenMiddleware failed with error %v\n", err) 24 | return err 25 | } 26 | return nil 27 | } 28 | 29 | authToken := "" 30 | auths := md.Get("authorization") 31 | if len(auths) > 0 { 32 | authToken = auths[0] 33 | } 34 | appMatched := false 35 | claims, err := public.JwtDecode(strings.ReplaceAll(authToken, "Bearer ", "")) 36 | if err != nil { 37 | return errors.WithMessage(err, "JwtDecode") 38 | } 39 | appList := handler.AppManagerHandler.GetAppList() 40 | for _, appInfo := range appList { 41 | if appInfo.AppID == claims.Issuer { 42 | md.Set("app", public.Obj2Json(appInfo)) 43 | appMatched = true 44 | break 45 | } 46 | } 47 | if !appMatched { 48 | return errors.New("not match valid app") 49 | } 50 | if err := grpcHandler(srv, ss); err != nil { 51 | log.Printf("GrpcJwtAuthTokenMiddleware failed with error %v\n", err) 52 | return err 53 | } 54 | return nil 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /grpc_proxy_middleware/grpc_jwt_flow_count.go: -------------------------------------------------------------------------------- 1 | package grpc_proxy_middleware 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "github.com/didi/gatekeeper/handler" 7 | "github.com/didi/gatekeeper/model" 8 | "github.com/didi/gatekeeper/public" 9 | "github.com/pkg/errors" 10 | "google.golang.org/grpc" 11 | "google.golang.org/grpc/metadata" 12 | "log" 13 | ) 14 | 15 | func GrpcJwtFlowCountMiddleware(serviceDetail *model.ServiceDetail) func(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error{ 16 | return func(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, grpcHandler grpc.StreamHandler) error{ 17 | md, ok := metadata.FromIncomingContext(ss.Context()) 18 | if !ok { 19 | return errors.New("miss metadata from context") 20 | } 21 | if serviceDetail.Info.AuthType != "jwt_auth" { 22 | if err := grpcHandler(srv, ss); err != nil { 23 | log.Printf("GrpcJwtAuthTokenMiddleware failed with error %v\n", err) 24 | return err 25 | } 26 | return nil 27 | } 28 | 29 | appInfos := md.Get("app") 30 | if len(appInfos)==0 { 31 | if err := grpcHandler(srv, ss);err != nil { 32 | log.Printf("RPC failed with error %v\n", err) 33 | return err 34 | } 35 | return nil 36 | } 37 | appInfo := &model.App{} 38 | if err:=json.Unmarshal([]byte(appInfos[0]),appInfo);err!=nil{ 39 | return err 40 | } 41 | appCounter, err := handler.AppCounterHandler.GetCounter(public.FlowAppPrefix + appInfo.AppID) 42 | if err != nil { 43 | return err 44 | } 45 | appCounter.Increase() 46 | if appInfo.Qpd>0 && appCounter.TotalCount>appInfo.Qpd{ 47 | return errors.New(fmt.Sprintf("租户日请求量限流 limit:%v current:%v",appInfo.Qpd,appCounter.TotalCount)) 48 | } 49 | if err := grpcHandler(srv, ss);err != nil { 50 | log.Printf("RPC failed with error %v\n", err) 51 | return err 52 | } 53 | return nil 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /grpc_proxy_middleware/grpc_metadata_transfer.go: -------------------------------------------------------------------------------- 1 | package grpc_proxy_middleware 2 | 3 | import ( 4 | "github.com/didi/gatekeeper/model" 5 | "github.com/pkg/errors" 6 | "google.golang.org/grpc" 7 | "google.golang.org/grpc/metadata" 8 | "log" 9 | "strings" 10 | ) 11 | 12 | func GrpcMetadataTransferMiddleware(serviceDetail *model.ServiceDetail) func(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error { 13 | return func(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error { 14 | md, ok := metadata.FromIncomingContext(ss.Context()) 15 | if !ok { 16 | return errors.New("miss metadata from context") 17 | } 18 | transferRule := serviceDetail.PluginConf.GetPath("metadata_transfer","transfer_rule").MustString() 19 | for _, item := range strings.Split(transferRule, ",") { 20 | items := strings.Split(item, " ") 21 | if len(items) != 3 { 22 | continue 23 | } 24 | if items[0] == "add" || items[0] == "edit" { 25 | md.Set(items[1], items[2]) 26 | } 27 | if items[0] == "del" { 28 | delete(md, items[1]) 29 | } 30 | } 31 | if err := ss.SetHeader(md); err != nil { 32 | return errors.WithMessage(err, "SetHeader") 33 | } 34 | if err := handler(srv, ss); err != nil { 35 | log.Printf("RPC failed with error %v\n", err) 36 | return err 37 | } 38 | return nil 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /grpc_proxy_middleware/grpc_test.go: -------------------------------------------------------------------------------- 1 | package grpc_proxy_middleware 2 | 3 | import ( 4 | "google.golang.org/grpc" 5 | "log" 6 | ) 7 | 8 | //流式RPC拦截器 9 | func GrpcAuthStreamInterceptor(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error { 10 | err := handler(srv, ss) 11 | if err != nil { 12 | log.Printf("RPC failed with error %v\n", err) 13 | } 14 | return err 15 | } 16 | -------------------------------------------------------------------------------- /grpc_proxy_middleware/grpc_white_list.go: -------------------------------------------------------------------------------- 1 | package grpc_proxy_middleware 2 | 3 | import ( 4 | "fmt" 5 | "github.com/didi/gatekeeper/model" 6 | "github.com/didi/gatekeeper/public" 7 | "github.com/pkg/errors" 8 | "google.golang.org/grpc" 9 | "google.golang.org/grpc/peer" 10 | "log" 11 | "strings" 12 | ) 13 | 14 | //匹配接入方式 基于请求信息 15 | func GrpcWhiteListMiddleware(serviceDetail *model.ServiceDetail) func(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error { 16 | return func(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error { 17 | peerCtx, ok := peer.FromContext(ss.Context()) 18 | if !ok { 19 | return errors.New("peer not found with context") 20 | } 21 | peerAddr := peerCtx.Addr.String() 22 | addrPos := strings.LastIndex(peerAddr, ":") 23 | clientIP := peerAddr[0:addrPos] 24 | 25 | whiteListStr := serviceDetail.PluginConf.GetPath("grpc_whiteblacklist", "ip_white_list").MustString() 26 | if whiteListStr != "" { 27 | if !public.InIPSliceStr(clientIP, whiteListStr) { 28 | return errors.New(fmt.Sprintf("%s not in white ip list", clientIP)) 29 | } 30 | } 31 | if err := handler(srv, ss); err != nil { 32 | log.Printf("RPC failed with error %v\n", err) 33 | return err 34 | } 35 | return nil 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /handler/app_count_handler.go: -------------------------------------------------------------------------------- 1 | package handler 2 | 3 | import ( 4 | "github.com/didi/gatekeeper/public" 5 | "log" 6 | "sync" 7 | ) 8 | 9 | var AppCounterHandler *FlowAppCounter 10 | 11 | type FlowAppCounter struct { 12 | FlowCounter 13 | } 14 | 15 | func NewFlowAppCounter() *FlowAppCounter { 16 | return &FlowAppCounter{ 17 | FlowCounter{ 18 | RedisFlowCountMap: map[string]*DistributedCountService{}, 19 | Locker: sync.RWMutex{}, 20 | }, 21 | } 22 | } 23 | 24 | func (counter *FlowAppCounter) Update(e *AppEvent) { 25 | log.Printf("FlowAppCounter.Update\n") 26 | for _, app := range e.AddApp { 27 | counter.GetCounter(public.FlowAppPrefix + app.AppID) 28 | } 29 | for _, item := range counter.RedisFlowCountMap { 30 | for _, app := range e.DeleteApp { 31 | if item.Name == public.FlowAppPrefix+app.AppID { 32 | item.Close() 33 | delete(counter.RedisFlowCountMap, item.Name) 34 | } 35 | } 36 | } 37 | } 38 | 39 | func init() { 40 | AppCounterHandler = NewFlowAppCounter() 41 | } 42 | -------------------------------------------------------------------------------- /handler/service_count_handler.go: -------------------------------------------------------------------------------- 1 | package handler 2 | 3 | import ( 4 | "github.com/didi/gatekeeper/public" 5 | "sync" 6 | "time" 7 | ) 8 | 9 | var ServiceCounterHandler *FlowCounter 10 | 11 | type FlowCounter struct { 12 | RedisFlowCountMap map[string]*DistributedCountService 13 | Locker sync.RWMutex 14 | } 15 | 16 | func NewFlowCounter() *FlowCounter { 17 | return &FlowCounter{ 18 | RedisFlowCountMap: map[string]*DistributedCountService{}, 19 | Locker: sync.RWMutex{}, 20 | } 21 | } 22 | 23 | func init() { 24 | ServiceCounterHandler = NewFlowCounter() 25 | ServiceManagerHandler.Regist(ServiceCounterHandler) 26 | } 27 | 28 | func (counter *FlowCounter) Update(e *ServiceEvent) { 29 | for _, service := range e.AddService { 30 | counter.GetCounter(public.FlowServicePrefix + service.Info.ServiceName) 31 | } 32 | for _, item := range counter.RedisFlowCountMap { 33 | for _, service := range e.DeleteService { 34 | if item.Name == public.FlowServicePrefix+service.Info.ServiceName { 35 | item.Close() 36 | delete(counter.RedisFlowCountMap, item.Name) 37 | } 38 | } 39 | } 40 | } 41 | 42 | func (counter *FlowCounter) GetCounter(name string) (*DistributedCountService, error) { 43 | counter.Locker.Lock() 44 | defer counter.Locker.Unlock() 45 | if item, ok := counter.RedisFlowCountMap[name]; ok { 46 | return item, nil 47 | } 48 | newCounter := NewDistributedCountService(name, 1*time.Second) 49 | counter.RedisFlowCountMap[name] = newCounter 50 | return newCounter, nil 51 | } 52 | -------------------------------------------------------------------------------- /http_proxy_middleware/http_access_mode.go: -------------------------------------------------------------------------------- 1 | package http_proxy_middleware 2 | 3 | import ( 4 | "github.com/didi/gatekeeper/handler" 5 | "github.com/didi/gatekeeper/public" 6 | "github.com/gin-gonic/gin" 7 | ) 8 | 9 | //匹配接入方式 基于请求信息 10 | func HTTPAccessModeMiddleware() gin.HandlerFunc { 11 | return func(c *gin.Context) { 12 | service, err := handler.ServiceManagerHandler.HTTPAccessMode(c) 13 | if err != nil { 14 | public.ResponseError(c, 1001, err) 15 | c.Abort() 16 | return 17 | } 18 | c.Set(public.ServiceDetailContextKey, service) 19 | c.Next() 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /http_proxy_middleware/http_black_list.go: -------------------------------------------------------------------------------- 1 | package http_proxy_middleware 2 | 3 | import ( 4 | "fmt" 5 | "github.com/didi/gatekeeper/model" 6 | "github.com/didi/gatekeeper/public" 7 | "github.com/gin-gonic/gin" 8 | "github.com/pkg/errors" 9 | ) 10 | 11 | //匹配接入方式 基于请求信息 12 | func HTTPBlackListMiddleware() gin.HandlerFunc { 13 | return func(c *gin.Context) { 14 | serviceDetail, err := model.GetServiceDetailFromGinContext(c) 15 | if err != nil { 16 | public.ResponseError(c, 2001, err) 17 | c.Abort() 18 | return 19 | } 20 | 21 | whiteListStr := serviceDetail.PluginConf.GetPath("http_whiteblacklist", "ip_white_list").MustString() 22 | blackListStr := serviceDetail.PluginConf.GetPath("http_whiteblacklist", "ip_black_list").MustString() 23 | if whiteListStr == "" && public.InIPSliceStr(c.ClientIP(), blackListStr) { 24 | public.ResponseError(c, 3001, errors.New(fmt.Sprintf("%s in black ip list", c.ClientIP()))) 25 | c.Abort() 26 | return 27 | } 28 | 29 | c.Next() 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /http_proxy_middleware/http_flow_count.go: -------------------------------------------------------------------------------- 1 | package http_proxy_middleware 2 | 3 | import ( 4 | "bytes" 5 | "github.com/didi/gatekeeper/handler" 6 | "github.com/didi/gatekeeper/model" 7 | "github.com/didi/gatekeeper/public" 8 | "github.com/gin-gonic/gin" 9 | ) 10 | 11 | func HTTPFlowCountMiddleware() gin.HandlerFunc { 12 | return func(c *gin.Context) { 13 | serviceDetail,err:= model.GetServiceDetailFromGinContext(c) 14 | if err!=nil{ 15 | public.ResponseError(c, 2001, err) 16 | c.Abort() 17 | return 18 | } 19 | totalCounter, err := handler.ServiceCounterHandler.GetCounter(public.FlowTotal) 20 | if err != nil { 21 | public.ResponseError(c, 4001, err) 22 | c.Abort() 23 | return 24 | } 25 | totalCounter.Increase() 26 | 27 | sCounterBuffer := bytes.NewBufferString(public.FlowServicePrefix) 28 | sCounterBuffer.WriteString(serviceDetail.Info.ServiceName) 29 | serviceCounter, err := handler.ServiceCounterHandler.GetCounter(sCounterBuffer.String()) 30 | if err != nil { 31 | public.ResponseError(c, 4001, err) 32 | c.Abort() 33 | return 34 | } 35 | serviceCounter.Increase() 36 | c.Next() 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /http_proxy_middleware/http_header_transfer.go: -------------------------------------------------------------------------------- 1 | package http_proxy_middleware 2 | 3 | import ( 4 | "github.com/didi/gatekeeper/model" 5 | "github.com/didi/gatekeeper/public" 6 | "github.com/gin-gonic/gin" 7 | "strings" 8 | ) 9 | 10 | func HTTPHeaderTransferMiddleware() gin.HandlerFunc { 11 | return func(c *gin.Context) { 12 | serviceDetail, err := model.GetServiceDetailFromGinContext(c) 13 | if err != nil { 14 | public.ResponseError(c, 2001, err) 15 | c.Abort() 16 | return 17 | } 18 | transferRule := serviceDetail.PluginConf.GetPath("header_transfer", "header_transfer_rule").MustString() 19 | for _, item := range strings.Split(transferRule, "\n") { 20 | if item == "" { 21 | continue 22 | } 23 | items := strings.Split(item, " ") 24 | if len(items) == 3 { 25 | if items[0] == "add" { 26 | c.Request.Header.Add(items[1], items[2]) 27 | } 28 | if items[0] == "edit" { 29 | c.Request.Header.Set(items[1], items[2]) 30 | } 31 | continue 32 | } 33 | if len(items) == 2 && items[0] == "del" { 34 | c.Request.Header.Del(items[1]) 35 | } 36 | } 37 | c.Next() 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /http_proxy_middleware/http_jwt_auth_token.go: -------------------------------------------------------------------------------- 1 | package http_proxy_middleware 2 | 3 | import ( 4 | "github.com/didi/gatekeeper/handler" 5 | "github.com/didi/gatekeeper/model" 6 | "github.com/didi/gatekeeper/public" 7 | "github.com/gin-gonic/gin" 8 | "github.com/pkg/errors" 9 | "strings" 10 | ) 11 | 12 | func HTTPJwtAuthTokenMiddleware() gin.HandlerFunc { 13 | return func(c *gin.Context) { 14 | serviceDetail, err := model.GetServiceDetailFromGinContext(c) 15 | if err != nil { 16 | public.ResponseError(c, 2001, err) 17 | c.Abort() 18 | return 19 | } 20 | if serviceDetail.Info.AuthType != "jwt_auth" { 21 | c.Next() 22 | return 23 | } 24 | appMatched := false 25 | claims, err := public.JwtDecode(strings.ReplaceAll(c.GetHeader("Authorization"), "Bearer ", "")) 26 | if err != nil { 27 | public.ResponseError(c, 2002, err) 28 | c.Abort() 29 | return 30 | } 31 | appList := handler.AppManagerHandler.GetAppList() 32 | for _, appInfo := range appList { 33 | if appInfo.AppID == claims.Issuer { 34 | c.Set("app", appInfo) 35 | appMatched = true 36 | break 37 | } 38 | } 39 | if !appMatched { 40 | public.ResponseError(c, 2003, errors.New("not match valid app")) 41 | c.Abort() 42 | return 43 | } 44 | c.Next() 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /http_proxy_middleware/http_jwt_flow_count.go: -------------------------------------------------------------------------------- 1 | package http_proxy_middleware 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "github.com/didi/gatekeeper/handler" 7 | "github.com/didi/gatekeeper/model" 8 | "github.com/didi/gatekeeper/public" 9 | "github.com/gin-gonic/gin" 10 | "errors" 11 | ) 12 | 13 | func HTTPJwtFlowCountMiddleware() gin.HandlerFunc { 14 | return func(c *gin.Context) { 15 | serviceDetail, err := model.GetServiceDetailFromGinContext(c) 16 | if err != nil { 17 | public.ResponseError(c, 2001, err) 18 | c.Abort() 19 | return 20 | } 21 | if serviceDetail.Info.AuthType != "jwt_auth" { 22 | c.Next() 23 | return 24 | } 25 | 26 | appInterface, ok := c.Get("app") 27 | if !ok { 28 | c.Next() 29 | return 30 | } 31 | appInfo := appInterface.(*model.App) 32 | countBuffer := bytes.NewBufferString(public.FlowAppPrefix) 33 | countBuffer.WriteString(appInfo.AppID) 34 | appCounter, err := handler.ServiceCounterHandler.GetCounter(countBuffer.String()) 35 | if err != nil { 36 | public.ResponseError(c, 2002, err) 37 | c.Abort() 38 | return 39 | } 40 | appCounter.Increase() 41 | if appInfo.Qpd > 0 && appCounter.TotalCount > appInfo.Qpd { 42 | public.ResponseError(c, 2003, errors.New(fmt.Sprintf("租户日请求量限流 limit:%v current:%v", appInfo.Qpd, appCounter.TotalCount))) 43 | c.Abort() 44 | return 45 | } 46 | c.Next() 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /http_proxy_middleware/http_jwt_flow_limit.go: -------------------------------------------------------------------------------- 1 | package http_proxy_middleware 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "github.com/didi/gatekeeper/handler" 7 | "github.com/didi/gatekeeper/model" 8 | "github.com/didi/gatekeeper/public" 9 | "github.com/gin-gonic/gin" 10 | "errors" 11 | ) 12 | 13 | func HTTPJwtFlowLimitMiddleware() gin.HandlerFunc { 14 | return func(c *gin.Context) { 15 | serviceDetail, err := model.GetServiceDetailFromGinContext(c) 16 | if err != nil { 17 | public.ResponseError(c, 2001, err) 18 | c.Abort() 19 | return 20 | } 21 | if serviceDetail.Info.AuthType != "jwt_auth" { 22 | c.Next() 23 | return 24 | } 25 | 26 | appInterface, ok := c.Get("app") 27 | if !ok { 28 | c.Next() 29 | return 30 | } 31 | appInfo := appInterface.(*model.App) 32 | if appInfo.Qps > 0 { 33 | cLimiterBuffer := bytes.NewBufferString(public.FlowAppPrefix) 34 | cLimiterBuffer.WriteString(appInfo.AppID) 35 | cLimiterBuffer.WriteString("_") 36 | //cLimiterBuffer.WriteString(c.ClientIP()) 37 | clientLimiter, err := handler.FlowLimiterHandler.GetLimiter(cLimiterBuffer.String(), float64(appInfo.Qps), 0, true) 38 | if err != nil { 39 | public.ResponseError(c, 5001, err) 40 | c.Abort() 41 | return 42 | } 43 | if !clientLimiter.Allow() { 44 | public.ResponseError(c, 5002, errors.New(fmt.Sprintf("%v flow limit %v", c.ClientIP(), appInfo.Qps), )) 45 | c.Abort() 46 | return 47 | } 48 | } 49 | c.Next() 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /http_proxy_middleware/http_recovery.go: -------------------------------------------------------------------------------- 1 | package http_proxy_middleware 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "github.com/didi/gatekeeper/golang_common/lib" 7 | "github.com/didi/gatekeeper/golang_common/trace" 8 | "github.com/didi/gatekeeper/public" 9 | "github.com/gin-gonic/gin" 10 | "runtime/debug" 11 | ) 12 | 13 | func HTTPRecoveryMiddleware() gin.HandlerFunc { 14 | return func(c *gin.Context) { 15 | defer func() { 16 | if err := recover(); err != nil { 17 | if lib.ConfBase.Log.On { 18 | public.GinLogWarning(c, trace.DLTagUndefined, map[string]interface{}{ 19 | "error": fmt.Sprint(err), 20 | "stack": string(debug.Stack()), 21 | }) 22 | } 23 | public.ResponseError(c, 500, errors.New(fmt.Sprint(err)+"||"+string(debug.Stack()))) 24 | return 25 | } 26 | }() 27 | c.Next() 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /http_proxy_middleware/http_request_log.go: -------------------------------------------------------------------------------- 1 | package http_proxy_middleware 2 | 3 | import ( 4 | "github.com/didi/gatekeeper/golang_common/lib" 5 | "github.com/didi/gatekeeper/golang_common/trace" 6 | "github.com/gin-gonic/gin" 7 | "time" 8 | ) 9 | 10 | func HTTPRequestLogger() gin.HandlerFunc { 11 | return func(c *gin.Context) { 12 | if !lib.ConfBase.Log.On { 13 | c.Next() 14 | return 15 | } 16 | t := time.Now() 17 | c.Request = c.Request.WithContext(trace.SetCtxTrace(c.Request.Context(), trace.New(c.Request))) 18 | lib.ZLog.Infof(c.Request.Context(), trace.DLTagRequestIn, "") 19 | c.Next() 20 | lib.ZLog.Infof(c.Request.Context(), trace.DLTagRequestOut, "proc_time=%f||status=%v", time.Since(t).Seconds(), c.Writer.Status()) 21 | if traceCtx, ok := trace.GetCtxTrace(c.Request.Context()); ok { 22 | trace.PutTrace(traceCtx) 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /http_proxy_middleware/http_reverse_proxy.go: -------------------------------------------------------------------------------- 1 | package http_proxy_middleware 2 | 3 | import ( 4 | "github.com/didi/gatekeeper/handler" 5 | "github.com/didi/gatekeeper/model" 6 | "github.com/didi/gatekeeper/public" 7 | "github.com/didi/gatekeeper/reverse_proxy" 8 | "github.com/gin-gonic/gin" 9 | ) 10 | 11 | func HTTPReverseProxyMiddleware() gin.HandlerFunc { 12 | return func(c *gin.Context) { 13 | serviceDetail, err := model.GetServiceDetailFromGinContext(c) 14 | if err != nil { 15 | public.ResponseError(c, 2001, err) 16 | c.Abort() 17 | return 18 | } 19 | lb, err := handler.LoadBalancerHandler.GetLoadBalancer(serviceDetail) 20 | if err != nil { 21 | public.ResponseError(c, 2002, err) 22 | c.Abort() 23 | return 24 | } 25 | trans, err := handler.TransportorHandler.GetTrans(serviceDetail) 26 | if err != nil { 27 | public.ResponseError(c, 2003, err) 28 | c.Abort() 29 | return 30 | } 31 | proxy := reverse_proxy.NewLoadBalanceReverseProxy(c, lb, trans) 32 | proxy.ServeHTTP(c.Writer, c.Request) 33 | c.Abort() 34 | return 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /http_proxy_middleware/http_strip_uri.go: -------------------------------------------------------------------------------- 1 | package http_proxy_middleware 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/didi/gatekeeper/model" 7 | "github.com/didi/gatekeeper/public" 8 | "github.com/gin-gonic/gin" 9 | ) 10 | 11 | func HTTPStripUriMiddleware() gin.HandlerFunc { 12 | return func(c *gin.Context) { 13 | serviceDetail, err := model.GetServiceDetailFromGinContext(c) 14 | if err != nil { 15 | public.ResponseError(c, 2001, err) 16 | c.Abort() 17 | return 18 | } 19 | if serviceDetail.Info.HttpStripPrefix == 1 { 20 | strArr := strings.Split(serviceDetail.Info.HTTPPaths,"\n") 21 | tmpStr := c.Request.URL.Path 22 | for _, replaceStr := range strArr { 23 | c.Request.URL.Path = strings.Replace(c.Request.URL.Path, replaceStr, "", 1) 24 | if c.Request.URL.Path != tmpStr { 25 | break 26 | } 27 | } 28 | 29 | } 30 | c.Next() 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /http_proxy_middleware/http_url_rewrite.go: -------------------------------------------------------------------------------- 1 | package http_proxy_middleware 2 | 3 | import ( 4 | "github.com/didi/gatekeeper/model" 5 | "github.com/didi/gatekeeper/public" 6 | "github.com/gin-gonic/gin" 7 | "regexp" 8 | "strings" 9 | ) 10 | 11 | func HTTPUrlRewriteMiddleware() gin.HandlerFunc { 12 | return func(c *gin.Context) { 13 | serviceDetail, err := model.GetServiceDetailFromGinContext(c) 14 | if err != nil { 15 | public.ResponseError(c, 2001, err) 16 | c.Abort() 17 | return 18 | } 19 | rewriteUrl := serviceDetail.PluginConf.GetPath("url_rewrite", "rewrite_rule").MustString() 20 | if rewriteUrl == "" { 21 | c.Next() 22 | return 23 | } 24 | for _, item := range strings.Split(rewriteUrl, ",") { 25 | items := strings.Split(item, " ") 26 | if len(items) != 2 { 27 | continue 28 | } 29 | regexp, err := regexp.Compile(items[0]) 30 | if err != nil { 31 | continue 32 | } 33 | replacePath := regexp.ReplaceAll([]byte(c.Request.URL.Path), []byte(items[1])) 34 | c.Request.URL.Path = string(replacePath) 35 | } 36 | c.Next() 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /http_proxy_middleware/http_white_list.go: -------------------------------------------------------------------------------- 1 | package http_proxy_middleware 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "github.com/didi/gatekeeper/model" 7 | "github.com/didi/gatekeeper/public" 8 | "github.com/gin-gonic/gin" 9 | ) 10 | 11 | func HTTPWhiteListMiddleware() gin.HandlerFunc { 12 | return func(c *gin.Context) { 13 | serviceDetail, err := model.GetServiceDetailFromGinContext(c) 14 | if err != nil { 15 | public.ResponseError(c, 2001, err) 16 | c.Abort() 17 | return 18 | } 19 | 20 | ipWhiteListString := serviceDetail.PluginConf.GetPath("http_whiteblacklist", "ip_white_list").MustString() 21 | if ipWhiteListString != "" { 22 | if !public.InIPSliceStr(c.ClientIP(), ipWhiteListString) { 23 | public.ResponseError(c, 3001, errors.New(fmt.Sprintf("%s not in white ip list", c.ClientIP()))) 24 | c.Abort() 25 | return 26 | } 27 | } 28 | 29 | 30 | urlWhiteUrlString := serviceDetail.PluginConf.GetPath("http_whiteblacklist", "url_white_list").MustString() 31 | if urlWhiteUrlString != "" { 32 | if !public.InURLSliceStr(c.Request.URL.Path, urlWhiteUrlString) { 33 | public.ResponseError(c, 3001, errors.New(fmt.Sprintf("%s not in white url list", c.Request.URL.Path))) 34 | c.Abort() 35 | return 36 | } 37 | } 38 | 39 | c.Set("ip_white_list", ipWhiteListString) 40 | c.Set("url_white_list", urlWhiteUrlString) 41 | c.Next() 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /http_proxy_router/route.go: -------------------------------------------------------------------------------- 1 | package http_proxy_router 2 | 3 | import ( 4 | "github.com/didi/gatekeeper/dashboard_controller" 5 | "github.com/didi/gatekeeper/http_proxy_middleware" 6 | "github.com/gin-gonic/gin" 7 | ) 8 | 9 | func InitRouter(middlewares ...gin.HandlerFunc) *gin.Engine { 10 | router := gin.New() 11 | router.Use(middlewares...) 12 | router.GET("/ping", func(c *gin.Context) { 13 | c.JSON(200, gin.H{ 14 | "message": "pong", 15 | }) 16 | }) 17 | oauth := router.Group("/oauth") 18 | oauth.Use(http_proxy_middleware.TranslationMiddleware()) 19 | { 20 | dashboard_controller.OAuthRegister(oauth) 21 | } 22 | router.Use( 23 | http_proxy_middleware.HTTPAccessModeMiddleware(), 24 | http_proxy_middleware.HTTPFlowCountMiddleware(), 25 | http_proxy_middleware.HTTPFlowLimitMiddleware(), 26 | http_proxy_middleware.HTTPWhiteListMiddleware(), 27 | http_proxy_middleware.HTTPBlackListMiddleware(), 28 | http_proxy_middleware.HTTPJwtAuthTokenMiddleware(), 29 | http_proxy_middleware.HTTPJwtFlowCountMiddleware(), 30 | http_proxy_middleware.HTTPJwtFlowLimitMiddleware(), 31 | http_proxy_middleware.HTTPHeaderTransferMiddleware(), 32 | http_proxy_middleware.HTTPStripUriMiddleware(), 33 | http_proxy_middleware.HTTPUrlRewriteMiddleware(), 34 | http_proxy_middleware.HTTPReverseProxyMiddleware()) 35 | return router 36 | } 37 | -------------------------------------------------------------------------------- /install/build.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | color 0d 3 | 4 | echo ================================= 5 | echo ==========Build Linux ====== 6 | echo ================================= 7 | SET CGO_ENABLED=0 8 | SET GOOS=linux 9 | SET GOARCH=amd64 10 | echo now the CGO_ENABLED: 11 | go env CGO_ENABLED 12 | echo now the GOOS: 13 | go env GOOS 14 | echo now the GOARCH: 15 | go env GOARCH 16 | go build -o bin/install_linux main.go 17 | 18 | echo ================================= 19 | echo ==========Build Mac ====== 20 | echo ================================= 21 | SET CGO_ENABLED=0 22 | SET GOOS=darwin 23 | SET GOARCH=amd64 24 | echo now the CGO_ENABLED: 25 | go env CGO_ENABLED 26 | echo now the GOOS: 27 | go env GOOS 28 | echo now the GOARCH: 29 | go env GOARCH 30 | go build -o bin/install_mac main.go 31 | 32 | echo ================================= 33 | echo ==========Build Windows ====== 34 | echo ================================= 35 | SET CGO_ENABLED=1 36 | SET GOOS=windows 37 | SET GOARCH=amd64 38 | echo now the CGO_ENABLED: 39 | go env CGO_ENABLED 40 | echo now the GOOS: 41 | go env GOOS 42 | echo now the GOARCH: 43 | go env GOARCH 44 | go build -o bin/install_windows.exe main.go 45 | 46 | 47 | -------------------------------------------------------------------------------- /install/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | function buildLinux() { 4 | echo ================================= 5 | echo ==========Build Linux ====== 6 | echo ================================= 7 | CGO_ENABLED=0 8 | GOOS=linux 9 | GOARCH=amd64 10 | echo now the CGO_ENABLED: 11 | go env CGO_ENABLED 12 | echo now the GOOS: 13 | go env GOOS 14 | echo now the GOARCH: 15 | go env GOARCH 16 | go build -o bin/install_linux main.go 17 | } 18 | 19 | 20 | function buildMac() { 21 | echo ================================= 22 | echo ==========Build Mac ====== 23 | echo ================================= 24 | CGO_ENABLED=0 25 | GOOS=darwin 26 | GOARCH=amd64 27 | echo now the CGO_ENABLED: 28 | go env CGO_ENABLED 29 | echo now the GOOS: 30 | go env GOOS 31 | echo now the GOARCH: 32 | go env GOARCH 33 | go build -o bin/install_mac main.go 34 | } 35 | 36 | function buildWindows() { 37 | echo ================================= 38 | echo ==========Build Windows ====== 39 | echo ================================= 40 | CGO_ENABLED=1 41 | GOOS=windows 42 | GOARCH=amd64 43 | echo now the CGO_ENABLED: 44 | go env CGO_ENABLED 45 | echo now the GOOS: 46 | go env GOOS 47 | echo now the GOARCH: 48 | go env GOARCH 49 | go build -o bin/install_windows.exe main.go 50 | } 51 | 52 | 53 | 54 | buildLinux 55 | buildMac 56 | buildWindows 57 | -------------------------------------------------------------------------------- /install/check/conf.go: -------------------------------------------------------------------------------- 1 | package check 2 | 3 | import ( 4 | "fmt" 5 | "gatekeeper/install/template" 6 | "gatekeeper/install/tool" 7 | "io/ioutil" 8 | "strings" 9 | ) 10 | 11 | var ( 12 | ConfPath = GateKeeperPath + "/conf/dev/" 13 | ) 14 | 15 | func InitConf() error { 16 | 17 | tool.LogInfo.Println("init conf start") 18 | 19 | err := initBase() 20 | if err != nil { 21 | return err 22 | } 23 | 24 | 25 | err = initRedis() 26 | if err != nil { 27 | return err 28 | } 29 | 30 | err = initMysql() 31 | if err != nil { 32 | return err 33 | } 34 | tool.LogInfo.Println("init conf end") 35 | return nil 36 | } 37 | 38 | 39 | func initBase() error { 40 | tool.LogInfo.Println("init base conf") 41 | 42 | fileName := ConfPath + "base.toml" 43 | redisClient := RedisClient.Host + ":" + RedisClient.Port 44 | baseConf := strings.Replace(template.BaseConf, "#REDIS_CLIENT", redisClient, 1) 45 | baseConf = strings.Replace(baseConf, "#REDIS_PWD", RedisClient.Pwd, 1) 46 | err := ioutil.WriteFile(fileName, []byte(baseConf), 0666); if err != nil{ 47 | return err 48 | } 49 | return nil 50 | 51 | } 52 | 53 | 54 | func initRedis() error { 55 | tool.LogInfo.Println("init redis conf") 56 | 57 | fileName := ConfPath + "redis_map.toml" 58 | redisClient := RedisClient.Host + ":" + RedisClient.Port 59 | redisConf := strings.Replace(template.RedisConf, "#REDIS_CLIENT", redisClient, 1) 60 | redisConf = strings.Replace(redisConf, "#REDIS_PWD", RedisClient.Pwd, 1) 61 | err := ioutil.WriteFile(fileName, []byte(redisConf), 0666); if err != nil{ 62 | return err 63 | } 64 | return nil 65 | 66 | } 67 | 68 | 69 | func initMysql() error { 70 | tool.LogInfo.Println("init mysql conf") 71 | 72 | fileName := ConfPath + "mysql_map.toml" 73 | mysqlClient := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8&parseTime=true&loc=Local", 74 | MysqlClient.User, 75 | MysqlClient.Pwd, 76 | MysqlClient.Host, 77 | MysqlClient.Port, 78 | MysqlClient.Database) 79 | mysqlConf := strings.Replace(template.MysqlConf, "#MYSQL_CLIENT", mysqlClient, 1) 80 | err := ioutil.WriteFile(fileName, []byte(mysqlConf), 0666); if err != nil{ 81 | return err 82 | } 83 | return nil 84 | } 85 | 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /install/check/gateway.go: -------------------------------------------------------------------------------- 1 | package check 2 | 3 | import ( 4 | "fmt" 5 | "gatekeeper/install/tool" 6 | ) 7 | 8 | 9 | var ( 10 | GateKeeperPath string = tool.GateKeeperPath 11 | CmdRun string = "cd %s && %s run main.go run -c %s/conf/dev/ -p control" 12 | ) 13 | 14 | 15 | func RunGateKeeper() error{ 16 | boolRunGatekeeper, err := tool.Confirm("run gatekeeper?", 2) 17 | if err != nil{ 18 | return err 19 | } 20 | CmdRun := fmt.Sprintf(tool.CmdRun + "&&" + CmdRun, GateKeeperPath, GoPath, GateKeeperPath) 21 | if boolRunGatekeeper { 22 | tool.LogInfo.Println(CmdRun) 23 | err := tool.RunCmd(CmdRun) 24 | if err != nil{ 25 | return err 26 | } 27 | } 28 | return nil 29 | } -------------------------------------------------------------------------------- /install/check/path.go: -------------------------------------------------------------------------------- 1 | package check 2 | 3 | import ( 4 | "errors" 5 | "gatekeeper/install/tool" 6 | "regexp" 7 | "strconv" 8 | "strings" 9 | ) 10 | 11 | var ( 12 | UseVersion = "1.12.0" 13 | GoPath string = "go" 14 | ) 15 | 16 | func InitGo() error{ 17 | err := checkVersion() 18 | if err != nil{ 19 | tool.LogWarning.Println(err) 20 | err = enterGoPath() 21 | if err != nil{ 22 | return err 23 | } 24 | 25 | err = checkVersion() 26 | if err != nil{ 27 | return err 28 | } 29 | } 30 | 31 | return nil 32 | } 33 | 34 | 35 | func checkVersion() error{ 36 | 37 | str, err := tool.Cmd(GoPath + " version") 38 | if err != nil{ 39 | return errors.New("go command not found") 40 | } 41 | versionPre := regexp.MustCompile(`[0-9]\.[0-9]{1,2}\.[0-9]*`) 42 | match := versionPre.FindString(string(str)) 43 | goVersion, _ := strconv.Atoi(strings.Replace(match, ".", "", -1)) 44 | useVersion, _ := strconv.Atoi(strings.Replace(UseVersion, ".", "", -1)) 45 | if goVersion < useVersion { 46 | return errors.New("gatekeeper use go version must be >= 1.12.0 please check") 47 | } 48 | return nil 49 | } 50 | 51 | 52 | func enterGoPath() error{ 53 | goPath, err := tool.Input("please enter go path (/use/bin/go):", "") 54 | if err != nil{ 55 | return err 56 | } 57 | if goPath == ""{ 58 | return errors.New("no go use") 59 | } 60 | GoPath = goPath 61 | return nil 62 | } -------------------------------------------------------------------------------- /install/check/redis.go: -------------------------------------------------------------------------------- 1 | package check 2 | 3 | import ( 4 | "fmt" 5 | "gatekeeper/install/tool" 6 | "github.com/go-redis/redis" 7 | ) 8 | 9 | 10 | type Redis struct{ 11 | Host string 12 | Port string 13 | Pwd string 14 | } 15 | 16 | 17 | var ( 18 | RedisClient Redis 19 | ) 20 | 21 | 22 | func InitRedis() error{ 23 | host, err := tool.Input("please enter redis host (default:127.0.0.1)", "127.0.0.1") 24 | if err != nil{ 25 | return err 26 | } 27 | 28 | port, err := tool.Input("please enter redis port (default:6379)", "6379") 29 | if err != nil{ 30 | return err 31 | } 32 | 33 | pwd, err := tool.Input("please enter redis pwd (default:null)", "") 34 | if err != nil{ 35 | return err 36 | } 37 | 38 | redisClient := Redis{ 39 | Host: host, 40 | Port: port, 41 | Pwd: pwd, 42 | } 43 | RedisClient = redisClient 44 | tool.LogInfo.Println(fmt.Sprintf("redis connect info host:[%s] port:[%s] pwd:[%s]", host, port, pwd)) 45 | err = redisClient.Init();if err !=nil{ 46 | tool.LogError.Println(err) 47 | return err 48 | } 49 | return nil 50 | } 51 | 52 | func (r *Redis) Init() error{ 53 | client := redis.NewClient(&redis.Options{ 54 | Addr: fmt.Sprintf("%s:%s", r.Host, r.Port), 55 | Password: r.Pwd, 56 | DB: 0, 57 | }) 58 | _, err := client.Ping().Result() 59 | if err != nil { 60 | tool.LogWarning.Println(err) 61 | return InitRedis() 62 | } 63 | tool.LogInfo.Println("connect redis success") 64 | return nil 65 | } -------------------------------------------------------------------------------- /install/go.mod: -------------------------------------------------------------------------------- 1 | module gatekeeper/install 2 | 3 | go 1.15 4 | 5 | require ( 6 | github.com/go-redis/redis v6.15.9+incompatible 7 | github.com/go-sql-driver/mysql v1.6.0 8 | github.com/pkg/errors v0.8.0 9 | ) 10 | -------------------------------------------------------------------------------- /install/go.sum: -------------------------------------------------------------------------------- 1 | github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= 2 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 3 | github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY= 4 | github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= 5 | github.com/didi/gatekeeper v0.1.1 h1:v3pahFkgiGQgnBk9Qz5/vxfskiP38m2z9D2VEnmZt5Q= 6 | github.com/go-redis/redis v6.15.9+incompatible h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGKFlFgcHWWmHQjg= 7 | github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= 8 | github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= 9 | github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= 10 | github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw= 11 | github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 12 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 13 | github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= 14 | github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 15 | github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= 16 | github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= 17 | github.com/urfave/cli v1.22.5 h1:lNq9sAHXK2qfdI8W+GRItjCEkI+2oR4d+MEHy1CKXoU= 18 | github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= 19 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 20 | gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= 21 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 22 | -------------------------------------------------------------------------------- /install/install.sh: -------------------------------------------------------------------------------- 1 | #! bin/bash 2 | 3 | 4 | # check go version 5 | 6 | function checkGoVerion() { 7 | useGoVersion="1120" 8 | goVersion=`go version |grep -Eo '([0-9])\.([0-9]{1,2})\.([0-9]*)' |awk -F '.' '{print $1 $2 $3}'` 9 | if [ ! -n "$goVersion" ] || [ "$goVersion" -le "$useGoVersion" ];then 10 | echo "go version < 1.12.0 pleace upgreate" 11 | echo "Installation package download address : https://golang.org/dl/ or https://golang.google.cn/dl/" 12 | echo "Installation tutorial address : https://www.runoob.com/go/go-environment.html" 13 | exit -1 14 | else 15 | initInstall 16 | fi 17 | } 18 | 19 | 20 | function initInstall() { 21 | export GO111MODULE=on && export GOPROXY=https://goproxy.cn 22 | go run main.go 23 | } 24 | 25 | 26 | checkGoVerion 27 | -------------------------------------------------------------------------------- /install/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "gatekeeper/install/check" 5 | "gatekeeper/install/tool" 6 | "os" 7 | ) 8 | 9 | func main() { 10 | var ( 11 | err error 12 | ) 13 | 14 | tool.InitSystem() 15 | 16 | err = check.InitRedis(); if err != nil{ 17 | tool.LogError.Println("connect redis error") 18 | tool.LogError.Println(err) 19 | os.Exit(-1) 20 | } 21 | 22 | err = check.InitDb(); if err != nil{ 23 | tool.LogError.Println(err) 24 | os.Exit(-1) 25 | } 26 | 27 | err = check.InitConf(); if err != nil{ 28 | tool.LogError.Println(err) 29 | os.Exit(-1) 30 | } 31 | 32 | //err = check.InitGo(); if err != nil{ 33 | // tool.LogWarning.Println(err) 34 | // os.Exit(-1) 35 | //} 36 | // 37 | //err = check.RunGateKeeper(); if err != nil{ 38 | // tool.LogError.Println(err) 39 | // os.Exit(-1) 40 | //} 41 | 42 | } 43 | 44 | -------------------------------------------------------------------------------- /install/template/mysql.go: -------------------------------------------------------------------------------- 1 | package template 2 | 3 | import ( 4 | "bufio" 5 | "gatekeeper/install/tool" 6 | "os" 7 | "regexp" 8 | "strings" 9 | ) 10 | 11 | var( 12 | TableSql map[string]string 13 | Tables map[string][]string 14 | ) 15 | 16 | func InitSql() error { 17 | TableSql = make(map[string]string) 18 | Tables = make(map[string][]string) 19 | err := getCreateSql() 20 | if err != nil{ 21 | return err 22 | } 23 | for tableName, sql := range TableSql { 24 | Tables[tableName] = strings.Split(sql, ";") 25 | } 26 | return nil 27 | } 28 | 29 | 30 | func getCreateSql() error{ 31 | sqlFilePath := tool.GateKeeperPath + "/gatekeeper.sql" 32 | tool.LogInfo.Println("sql file path :" + sqlFilePath) 33 | tableName := "" 34 | tablePre := regexp.MustCompile(`gateway_[a-z_]*`) 35 | f, err := os.Open(sqlFilePath) 36 | if err != nil{ 37 | return err 38 | } 39 | defer f.Close() 40 | r := bufio.NewReader(f) 41 | for { 42 | lineSql, err := tool.ReadLine(r) 43 | if strings.Index(lineSql, "/*") < 0 && strings.Index(lineSql, "--") < 0 && lineSql != "" { 44 | if strings.Index(lineSql, "DROP TABLE IF EXISTS") >= 0 { 45 | tableName = tablePre.FindString(lineSql) 46 | } 47 | TableSql[tableName] += lineSql 48 | } 49 | if err != nil { 50 | break 51 | } 52 | } 53 | return nil 54 | } -------------------------------------------------------------------------------- /install/tool/log.go: -------------------------------------------------------------------------------- 1 | package tool 2 | 3 | import ( 4 | "log" 5 | "os" 6 | ) 7 | 8 | var ( 9 | LogInfo *log.Logger 10 | LogError *log.Logger 11 | LogWarning *log.Logger 12 | ) 13 | 14 | func init() { 15 | LogInfo = log.New(os.Stdout, "[INFO] ", log.Ldate|log.Ltime) 16 | LogError = log.New(os.Stderr, "[ERROR] ", log.Ldate|log.Ltime) 17 | LogWarning = log.New(os.Stderr, "[WARNING] ", log.Ldate|log.Ltime) 18 | } 19 | -------------------------------------------------------------------------------- /install/tool/reader.go: -------------------------------------------------------------------------------- 1 | package tool 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "github.com/pkg/errors" 7 | "os" 8 | "strings" 9 | ) 10 | 11 | 12 | func NewReader() *bufio.Reader { 13 | reader := bufio.NewReader(os.Stdin) 14 | return reader 15 | } 16 | 17 | 18 | func ReadStdin(reader *bufio.Reader, describe string)(string, error) { 19 | fmt.Print(describe) 20 | readString, err := reader.ReadString('\n') 21 | if err != nil { 22 | return "", err 23 | } 24 | readString = strings.Replace(readString, "\n", "", 1) 25 | return readString, nil 26 | } 27 | 28 | 29 | func Input(describe string, defaultString string) (string, error) { 30 | reader := NewReader() 31 | readStdin, err := ReadStdin(reader, describe) 32 | if err != nil { 33 | LogError.Println(err) 34 | LogError.Println("read input error") 35 | return "", err 36 | } 37 | readStdin = strings.Trim(readStdin, "\r") 38 | if readStdin == "" { 39 | readStdin = defaultString 40 | } 41 | fmt.Println(readStdin) 42 | return readStdin, nil 43 | } 44 | 45 | 46 | func Confirm(describe string, retry int) (bool, error) { 47 | describe += " please enter (y/n):" 48 | 49 | i := 1 50 | for i <= retry { 51 | isConfirm, err := confirm(describe) 52 | if err != nil{ 53 | LogError.Println(err) 54 | LogError.Println("read input error") 55 | i++ 56 | continue 57 | } 58 | return isConfirm, nil 59 | } 60 | return false, nil 61 | } 62 | 63 | 64 | func confirm(describe string) (bool, error){ 65 | reader := NewReader() 66 | isConfirm, err := ReadStdin(reader, describe) 67 | if err != nil{ 68 | return false, err 69 | } 70 | isConfirm = strings.ToLower(strings.Trim(isConfirm, "\r")) 71 | if isConfirm == "n" || isConfirm == "no"{ 72 | return false, nil 73 | } 74 | if isConfirm == "y" || isConfirm == "yes"{ 75 | return true, nil 76 | } 77 | return false, errors.New("please enter (y/n)") 78 | } 79 | 80 | func ReadLine(r *bufio.Reader) (string, error) { 81 | line, isprefix, err := r.ReadLine() 82 | for isprefix && err == nil { 83 | var bs []byte 84 | bs, isprefix, err = r.ReadLine() 85 | line = append(line, bs...) 86 | } 87 | return string(line), err 88 | } -------------------------------------------------------------------------------- /install/tool/system.go: -------------------------------------------------------------------------------- 1 | package tool 2 | 3 | import ( 4 | "os" 5 | "os/exec" 6 | "runtime" 7 | "strings" 8 | ) 9 | 10 | var ( 11 | SystemType string 12 | CommandType string 13 | CommandArgs string 14 | CmdRun string 15 | GateKeeperPath string = gatekeeperPath() 16 | ) 17 | 18 | 19 | func InitSystem() { 20 | SystemType = runtime.GOOS 21 | if SystemType == "windows"{ 22 | CommandType = "cmd" 23 | CommandArgs = "/C" 24 | CmdRun = "SET GO111MODULE=on&& SET GOPROXY=https://goproxy.cn" 25 | } else { 26 | CommandType = "sh" 27 | CommandArgs = "-c" 28 | CmdRun = "export GO111MODULE=on && export GOPROXY=https://goproxy.cn" 29 | } 30 | } 31 | 32 | func Cmd(command string) (string, error){ 33 | LogInfo.Println(command) 34 | cmd := exec.Command(CommandType, CommandArgs, command) 35 | str, err := cmd.Output() 36 | LogInfo.Println(string(str)) 37 | return string(str), err 38 | } 39 | 40 | 41 | func RunCmd(command string) error{ 42 | cmd := exec.Command(CommandType, CommandArgs, command) 43 | // 命令的错误输出和标准输出都连接到同一个管道 44 | stdout, err := cmd.StdoutPipe() 45 | cmd.Stderr = cmd.Stdout 46 | 47 | if err != nil { 48 | return err 49 | } 50 | if err = cmd.Start(); err != nil { 51 | return err 52 | } 53 | 54 | // 从管道中实时获取输出并打印到终端 55 | for { 56 | tmp := make([]byte, 1024) 57 | _, err := stdout.Read(tmp) 58 | LogInfo.Println(string(tmp)) 59 | if err != nil { 60 | break 61 | } 62 | } 63 | 64 | if err = cmd.Wait(); err != nil { 65 | return err 66 | } 67 | return nil 68 | } 69 | 70 | 71 | func GetCurrentPath() string{ 72 | path, _ := os.Getwd() 73 | return strings.Replace(path, "\\", "/", -1) 74 | } 75 | 76 | 77 | func gatekeeperPath() string{ 78 | path := GetCurrentPath() 79 | pathArr := strings.Split(path, "/") 80 | index := len(pathArr) 81 | pathArr = pathArr[0:index-1] 82 | path = strings.Join(pathArr, "/") 83 | return path 84 | } -------------------------------------------------------------------------------- /install/tool/zip.go: -------------------------------------------------------------------------------- 1 | package tool 2 | 3 | import ( 4 | "archive/zip" 5 | "io" 6 | "os" 7 | "path/filepath" 8 | ) 9 | 10 | 11 | func Zip(srcFile string, destZip string) error { 12 | zipfile, err := os.Create(destZip) 13 | if err != nil { 14 | return err 15 | } 16 | defer zipfile.Close() 17 | 18 | archive := zip.NewWriter(zipfile) 19 | defer archive.Close() 20 | 21 | filepath.Walk(srcFile, func(path string, info os.FileInfo, err error) error { 22 | if err != nil { 23 | return err 24 | } 25 | 26 | header, err := zip.FileInfoHeader(info) 27 | if err != nil { 28 | return err 29 | } 30 | 31 | header.Name = path 32 | if info.IsDir() { 33 | header.Name += "/" 34 | } else { 35 | header.Method = zip.Deflate 36 | } 37 | 38 | writer, err := archive.CreateHeader(header) 39 | if err != nil { 40 | return err 41 | } 42 | 43 | if ! info.IsDir() { 44 | file, err := os.Open(path) 45 | if err != nil { 46 | return err 47 | } 48 | defer file.Close() 49 | _, err = io.Copy(writer, file) 50 | } 51 | return err 52 | }) 53 | 54 | return err 55 | } 56 | 57 | 58 | func Unzip(zipFile string, destDir string) error { 59 | zipReader, err := zip.OpenReader(zipFile) 60 | if err != nil { 61 | return err 62 | } 63 | defer zipReader.Close() 64 | 65 | for _, f := range zipReader.File { 66 | fpath := filepath.Join(destDir, f.Name) 67 | if f.FileInfo().IsDir() { 68 | os.MkdirAll(fpath, os.ModePerm) 69 | } else { 70 | if err = os.MkdirAll(filepath.Dir(fpath), os.ModePerm); err != nil { 71 | return err 72 | } 73 | 74 | inFile, err := f.Open() 75 | if err != nil { 76 | return err 77 | } 78 | defer inFile.Close() 79 | 80 | outFile, err := os.OpenFile(fpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode()) 81 | if err != nil { 82 | return err 83 | } 84 | defer outFile.Close() 85 | 86 | _, err = io.Copy(outFile, inFile) 87 | if err != nil { 88 | return err 89 | } 90 | } 91 | } 92 | return nil 93 | } 94 | 95 | -------------------------------------------------------------------------------- /k8s_dashboard.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: go-gateteway-dashboard 5 | spec: 6 | replicas: 1 7 | selector: 8 | matchLabels: 9 | name: go-gateteway-dashboard 10 | template: 11 | metadata: 12 | labels: 13 | name: go-gateteway-dashboard 14 | spec: 15 | containers: 16 | - name: go-gateteway-dashboard 17 | image: go-gateteway-dashboard 18 | imagePullPolicy: Never 19 | ports: 20 | - containerPort: 8880 21 | --- 22 | apiVersion: v1 23 | kind: Service 24 | metadata: 25 | name: go-gateteway-dashboard 26 | spec: 27 | ports: 28 | - port: 8880 29 | name: "dashboard" 30 | targetPort: 8880 31 | protocol: TCP 32 | nodePort: 30088 33 | type: NodePort 34 | selector: 35 | name: go-gateteway-dashboard -------------------------------------------------------------------------------- /k8s_server.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: go-gateteway-server 5 | spec: 6 | replicas: 3 7 | selector: 8 | matchLabels: 9 | name: go-gateteway-server 10 | template: 11 | metadata: 12 | labels: 13 | name: go-gateteway-server 14 | spec: 15 | containers: 16 | - name: go-gateteway-server 17 | image: go-gateteway-server:latest 18 | imagePullPolicy: Never 19 | ports: 20 | - containerPort: 8080 21 | - containerPort: 4433 22 | --- 23 | apiVersion: v1 24 | kind: Service 25 | metadata: 26 | name: go-gateteway-server 27 | spec: 28 | ports: 29 | - port: 8080 30 | name: "http" 31 | targetPort: 8080 32 | protocol: TCP 33 | nodePort: 30080 34 | - port: 4433 35 | name: "https" 36 | targetPort: 4433 37 | protocol: TCP 38 | nodePort: 30443 39 | type: NodePort 40 | selector: 41 | name: go-gateteway-server -------------------------------------------------------------------------------- /load_balance/consistent_hash_strategy_test.go: -------------------------------------------------------------------------------- 1 | package load_balance 2 | -------------------------------------------------------------------------------- /load_balance/loadbalance_config.go: -------------------------------------------------------------------------------- 1 | package load_balance 2 | 3 | import "github.com/didi/gatekeeper/model" 4 | 5 | type LoadBalanceConf interface { 6 | Attach(o Observer) 7 | GetConf() []string 8 | WatchConf() 9 | UpdateConf(conf []string) 10 | CloseWatch() 11 | } 12 | 13 | type Observer interface { 14 | Update() 15 | } 16 | 17 | type CheckConfigHandler func(service *model.ServiceDetail) (LoadBalanceConf, error) 18 | 19 | var CheckConfigHandlerMap map[string]CheckConfigHandler 20 | 21 | func RegisterCheckConfigHandler(lbtype string, conf CheckConfigHandler) { 22 | if CheckConfigHandlerMap == nil { 23 | CheckConfigHandlerMap = map[string]CheckConfigHandler{} 24 | } 25 | CheckConfigHandlerMap[lbtype] = conf 26 | } 27 | 28 | func GetCheckConfigHandler(lbtype string) CheckConfigHandler { 29 | if CheckConfigHandlerMap == nil { 30 | return nil 31 | } 32 | handler, ok := CheckConfigHandlerMap[lbtype] 33 | if !ok { 34 | return CheckConfigHandlerMap["upstream_config"] 35 | } 36 | return handler 37 | } 38 | -------------------------------------------------------------------------------- /load_balance/loadbalance_config_test.go: -------------------------------------------------------------------------------- 1 | package load_balance 2 | -------------------------------------------------------------------------------- /load_balance/loadbalance_strategy.go: -------------------------------------------------------------------------------- 1 | package load_balance 2 | 3 | import ( 4 | "strings" 5 | ) 6 | 7 | type LoadBalanceStrategy interface { 8 | Add(...string) error 9 | RemoveAll() error 10 | GetAll() ([]string, error) 11 | Get(string) (string, error) 12 | } 13 | 14 | type LoadBalance struct { 15 | strategy LoadBalanceStrategy 16 | conf LoadBalanceConf 17 | } 18 | 19 | func (r *LoadBalance) Add(params ...string) error { 20 | r.strategy.Add(params...) 21 | return nil 22 | } 23 | 24 | func (r *LoadBalance) Get(params string) (string, error) { 25 | return r.strategy.Get(params) 26 | } 27 | 28 | func (r *LoadBalance) GetAll() ([]string, error) { 29 | return r.strategy.GetAll() 30 | } 31 | 32 | func (r *LoadBalance) Update() { 33 | r.strategy.RemoveAll() 34 | for _, ip := range r.conf.GetConf() { 35 | r.strategy.Add(strings.Split(ip, ",")...) 36 | } 37 | } 38 | 39 | func (r *LoadBalance) Close() { 40 | r.conf.CloseWatch() 41 | } 42 | 43 | func NewLoadBalance(strategy LoadBalanceStrategy, conf LoadBalanceConf) *LoadBalance { 44 | return &LoadBalance{ 45 | strategy: strategy, 46 | conf: conf, 47 | } 48 | } 49 | 50 | func LoadBanlanceFactorWithStrategy(strategy LoadBalanceStrategy, conf LoadBalanceConf) *LoadBalance { 51 | lb := NewLoadBalance(strategy, conf) 52 | conf.Attach(lb) 53 | lb.Update() 54 | return lb 55 | } 56 | 57 | type LoadBalanceStrategyHandler func() LoadBalanceStrategy 58 | 59 | var LoadBalanceStrategyHandlerMap map[string]LoadBalanceStrategyHandler 60 | 61 | func RegisterLoadBalanceStrategyHandler(name string, handler LoadBalanceStrategyHandler) { 62 | if LoadBalanceStrategyHandlerMap == nil { 63 | LoadBalanceStrategyHandlerMap = map[string]LoadBalanceStrategyHandler{} 64 | } 65 | LoadBalanceStrategyHandlerMap[name] = handler 66 | } 67 | 68 | func GetLoadBalanceStrategy(name string) LoadBalanceStrategy { 69 | if LoadBalanceStrategyHandlerMap == nil { 70 | return nil 71 | } 72 | handler := LoadBalanceStrategyHandlerMap[name] 73 | return handler() 74 | } 75 | -------------------------------------------------------------------------------- /load_balance/random_strategy.go: -------------------------------------------------------------------------------- 1 | package load_balance 2 | 3 | import ( 4 | "errors" 5 | "math/rand" 6 | ) 7 | 8 | type RandomStrategy struct { 9 | curIndex int 10 | rss []string 11 | } 12 | 13 | func (r *RandomStrategy) Add(params ...string) error { 14 | if len(params) == 0 { 15 | return errors.New("param len 1 at least") 16 | } 17 | addr := params[0] 18 | r.rss = append(r.rss, addr) 19 | return nil 20 | } 21 | 22 | func (r *RandomStrategy) Next() string { 23 | if len(r.rss) == 0 { 24 | return "" 25 | } 26 | r.curIndex = rand.Intn(len(r.rss)) 27 | return r.rss[r.curIndex] 28 | } 29 | 30 | func (r *RandomStrategy) Get(key string) (string, error) { 31 | return r.Next(), nil 32 | } 33 | 34 | func (r *RandomStrategy) GetAll() ([]string, error) { 35 | return r.rss, nil 36 | } 37 | 38 | func (r *RandomStrategy) RemoveAll() error { 39 | r.rss = []string{} 40 | return nil 41 | } 42 | 43 | func init() { 44 | RegisterLoadBalanceStrategyHandler("random", func() LoadBalanceStrategy { 45 | return &RandomStrategy{} 46 | }) 47 | } 48 | -------------------------------------------------------------------------------- /load_balance/random_strategy_test.go: -------------------------------------------------------------------------------- 1 | package load_balance 2 | -------------------------------------------------------------------------------- /load_balance/round_robin_strategy.go: -------------------------------------------------------------------------------- 1 | package load_balance 2 | 3 | import ( 4 | "errors" 5 | ) 6 | 7 | type RoundRobinStrategy struct { 8 | curIndex int 9 | rss []string 10 | } 11 | 12 | func (r *RoundRobinStrategy) Add(params ...string) error { 13 | if len(params) == 0 { 14 | return errors.New("param len 1 at least") 15 | } 16 | addr := params[0] 17 | r.rss = append(r.rss, addr) 18 | return nil 19 | } 20 | 21 | func (r *RoundRobinStrategy) Next() string { 22 | if len(r.rss) == 0 { 23 | return "" 24 | } 25 | lens := len(r.rss) //5 26 | if r.curIndex >= lens { 27 | r.curIndex = 0 28 | } 29 | curAddr := r.rss[r.curIndex] 30 | r.curIndex = (r.curIndex + 1) % lens 31 | return curAddr 32 | } 33 | 34 | func (r *RoundRobinStrategy) Get(key string) (string, error) { 35 | return r.Next(), nil 36 | } 37 | 38 | func (r *RoundRobinStrategy) GetAll() ([]string, error) { 39 | return r.rss, nil 40 | } 41 | 42 | func (r *RoundRobinStrategy) RemoveAll() error { 43 | r.rss = []string{} 44 | return nil 45 | } 46 | 47 | func init() { 48 | RegisterLoadBalanceStrategyHandler("round", func() LoadBalanceStrategy { 49 | return &RoundRobinStrategy{} 50 | }) 51 | } 52 | -------------------------------------------------------------------------------- /load_balance/round_robin_strategy_test.go: -------------------------------------------------------------------------------- 1 | package load_balance 2 | 3 | import "testing" 4 | 5 | func TestRoundRobinStrategy_Add(t *testing.T) { 6 | type fields struct { 7 | curIndex int 8 | rss []string 9 | } 10 | type args struct { 11 | params []string 12 | } 13 | tests := []struct { 14 | name string 15 | fields fields 16 | args args 17 | wantErr bool 18 | }{ 19 | // TODO: Add test cases. 20 | } 21 | for _, tt := range tests { 22 | t.Run(tt.name, func(t *testing.T) { 23 | r := &RoundRobinStrategy{ 24 | curIndex: tt.fields.curIndex, 25 | rss: tt.fields.rss, 26 | } 27 | if err := r.Add(tt.args.params...); (err != nil) != tt.wantErr { 28 | t.Errorf("Add() error = %v, wantErr %v", err, tt.wantErr) 29 | } 30 | }) 31 | } 32 | } -------------------------------------------------------------------------------- /load_balance/weight_round_strategy.go: -------------------------------------------------------------------------------- 1 | package load_balance 2 | 3 | import ( 4 | "errors" 5 | "strconv" 6 | ) 7 | 8 | type WeightRoundRobinStrategy struct { 9 | curIndex int 10 | rss []*WeightNode 11 | rsw []int 12 | conf LoadBalanceConf 13 | } 14 | 15 | type WeightNode struct { 16 | addr string 17 | weight int //权重值 18 | currentWeight int //节点当前权重 19 | effectiveWeight int //有效权重 20 | } 21 | 22 | func (r *WeightRoundRobinStrategy) Add(params ...string) error { 23 | if len(params) != 2 { 24 | return errors.New("param len need 2") 25 | } 26 | parInt, err := strconv.ParseInt(params[1], 10, 64) 27 | if err != nil { 28 | return err 29 | } 30 | node := &WeightNode{addr: params[0], weight: int(parInt)} 31 | node.effectiveWeight = node.weight 32 | r.rss = append(r.rss, node) 33 | return nil 34 | } 35 | 36 | func (r *WeightRoundRobinStrategy) Next() string { 37 | total := 0 38 | var best *WeightNode 39 | for i := 0; i < len(r.rss); i++ { 40 | w := r.rss[i] 41 | total += w.effectiveWeight 42 | w.currentWeight += w.effectiveWeight 43 | if w.effectiveWeight < w.weight { 44 | w.effectiveWeight++ 45 | } 46 | if best == nil || w.currentWeight > best.currentWeight { 47 | best = w 48 | } 49 | } 50 | if best == nil { 51 | return "" 52 | } 53 | best.currentWeight -= total 54 | return best.addr 55 | } 56 | 57 | func (r *WeightRoundRobinStrategy) Get(key string) (string, error) { 58 | return r.Next(), nil 59 | } 60 | 61 | func (r *WeightRoundRobinStrategy) GetAll() ([]string, error) { 62 | iplist := []string{} 63 | for _, item := range r.rss { 64 | iplist = append(iplist, item.addr) 65 | } 66 | return iplist, nil 67 | } 68 | 69 | func (r *WeightRoundRobinStrategy) RemoveAll() error { 70 | r.rss = []*WeightNode{} 71 | r.rsw = []int{} 72 | return nil 73 | } 74 | 75 | func init() { 76 | RegisterLoadBalanceStrategyHandler("weight_round", func() LoadBalanceStrategy { 77 | return &WeightRoundRobinStrategy{} 78 | }) 79 | } 80 | -------------------------------------------------------------------------------- /load_balance/weight_round_strategy_test.go: -------------------------------------------------------------------------------- 1 | package load_balance 2 | -------------------------------------------------------------------------------- /model/admin_dto.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "github.com/didi/gatekeeper/public" 5 | "github.com/gin-gonic/gin" 6 | "time" 7 | ) 8 | 9 | type AdminInfoOutput struct { 10 | ID int `json:"id"` 11 | Name string `json:"name"` 12 | LoginTime time.Time `json:"login_time"` 13 | Avatar string `json:"avatar"` 14 | Introduction string `json:"introduction"` 15 | Roles []string `json:"roles"` 16 | } 17 | 18 | type ChangePwdInput struct { 19 | Password string `json:"password" form:"password" comment:"密码" example:"123456" validate:"required"` //密码 20 | } 21 | 22 | func (param *ChangePwdInput) BindValidParam(c *gin.Context) error { 23 | return public.DefaultGetValidParams(c, param) 24 | } 25 | -------------------------------------------------------------------------------- /model/admin_login_dto.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "github.com/didi/gatekeeper/public" 5 | "github.com/gin-gonic/gin" 6 | "time" 7 | ) 8 | 9 | type AdminSessionInfo struct { 10 | ID int `json:"id"` 11 | UserName string `json:"user_name"` 12 | LoginTime time.Time `json:"login_time"` 13 | } 14 | 15 | type AdminLoginInput struct { 16 | UserName string `json:"username" form:"username" comment:"管理员用户名" example:"admin" validate:"required,valid_username"` //管理员用户名 17 | Password string `json:"password" form:"password" comment:"密码" example:"123456" validate:"required"` //密码 18 | } 19 | 20 | func (param *AdminLoginInput) BindValidParam(c *gin.Context) error { 21 | return public.DefaultGetValidParams(c, param) 22 | } 23 | 24 | type AdminLoginOutput struct { 25 | Token string `json:"token" form:"token" comment:"token" example:"token" validate:""` //token 26 | } 27 | -------------------------------------------------------------------------------- /model/admin_model.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "github.com/didi/gatekeeper/public" 5 | "github.com/e421083458/gorm" 6 | "github.com/gin-gonic/gin" 7 | "github.com/pkg/errors" 8 | "time" 9 | ) 10 | 11 | type Admin struct { 12 | Id int `json:"id" gorm:"primary_key" description:"自增主键"` 13 | UserName string `json:"user_name" gorm:"column:user_name" description:"管理员用户名"` 14 | Salt string `json:"salt" gorm:"column:salt" description:"盐"` 15 | Password string `json:"password" gorm:"column:password" description:"密码"` 16 | UpdatedAt time.Time `json:"update_at" gorm:"column:update_at" description:"更新时间"` 17 | CreatedAt time.Time `json:"create_at" gorm:"column:create_at" description:"创建时间"` 18 | IsDelete int `json:"is_delete" gorm:"column:is_delete" description:"是否删除"` 19 | } 20 | 21 | func (t *Admin) TableName() string { 22 | return "gateway_admin" 23 | } 24 | 25 | func (t *Admin) LoginCheck(c *gin.Context, tx *gorm.DB, param *AdminLoginInput) (*Admin, error) { 26 | adminInfo, err := t.Find(c, tx, (&Admin{UserName: param.UserName, IsDelete: 0})) 27 | if err != nil { 28 | return nil, errors.New("用户信息不存在") 29 | } 30 | saltPassword := public.GenSaltPassword(adminInfo.Salt, param.Password) 31 | if adminInfo.Password != saltPassword { 32 | return nil, errors.New("密码错误,请重新输入") 33 | } 34 | return adminInfo, nil 35 | } 36 | 37 | func (t *Admin) Find(c *gin.Context, tx *gorm.DB, search *Admin) (*Admin, error) { 38 | out := &Admin{} 39 | err := tx.SetCtx(public.GetGinTraceContext(c)).Where(search).Find(out).Error 40 | if err != nil { 41 | return nil, err 42 | } 43 | return out, nil 44 | } 45 | 46 | func (t *Admin) Save(c *gin.Context, tx *gorm.DB) error { 47 | return tx.SetCtx(public.GetGinTraceContext(c)).Save(t).Error 48 | } 49 | -------------------------------------------------------------------------------- /model/dashboard_dto.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | type PanelGroupDataOutput struct { 4 | ServiceNum int64 `json:"serviceNum"` 5 | AppNum int64 `json:"appNum"` 6 | CurrentQPS int64 `json:"currentQps"` 7 | TodayRequestNum int64 `json:"todayRequestNum"` 8 | } 9 | 10 | type DashServiceStatItemOutput struct { 11 | Name string `json:"name"` 12 | LoadType int `json:"load_type"` 13 | Value int64 `json:"value"` 14 | } 15 | 16 | type DashServiceStatOutput struct { 17 | Legend []string `json:"legend"` 18 | Data []DashServiceStatItemOutput `json:"data"` 19 | } -------------------------------------------------------------------------------- /model/oauth_dto.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "github.com/didi/gatekeeper/public" 5 | "github.com/gin-gonic/gin" 6 | ) 7 | 8 | type TokensInput struct { 9 | GrantType string `json:"grant_type" form:"grant_type" comment:"授权类型" example:"client_credentials" validate:"required"` //授权类型 10 | Scope string `json:"scope" form:"scope" comment:"权限范围" example:"read_write" validate:"required"` //权限范围 11 | } 12 | 13 | func (param *TokensInput) BindValidParam(c *gin.Context) error { 14 | return public.DefaultGetValidParams(c, param) 15 | } 16 | 17 | type TokensOutput struct { 18 | AccessToken string `json:"access_token" form:"access_token"` //access_token 19 | ExpiresIn int `json:"expires_in" form:"expires_in"` //expires_in 20 | TokenType string `json:"token_type" form:"token_type"` //token_type 21 | Scope string `json:"scope" form:"scope"` //scope 22 | } -------------------------------------------------------------------------------- /model/service_detail_model.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "errors" 5 | "regexp" 6 | "strings" 7 | 8 | "github.com/bitly/go-simplejson" 9 | "github.com/didi/gatekeeper/public" 10 | "github.com/gin-gonic/gin" 11 | ) 12 | 13 | type ServiceDetail struct { 14 | Info *ServiceInfo `json:"info" description:"基本信息"` 15 | PluginConf *simplejson.Json `json:"plugin_conf" description:"plugin_conf"` 16 | } 17 | 18 | type UpstreamConfig struct { 19 | Schema string 20 | IpList []string 21 | IpWeight map[string]string 22 | } 23 | 24 | func GetUpstreamConfigFromString(upstreamList string) (*UpstreamConfig, error) { 25 | config := &UpstreamConfig{} 26 | if upstreamList == "" { 27 | return config, nil 28 | } 29 | tmpLine := strings.Split(upstreamList, "\n") 30 | ipList := []string{} 31 | ipConf := map[string]string{} 32 | for _, tmp := range tmpLine { 33 | r, _ := regexp.Compile("^(.*://)(.*?)\\s(.*?)$") 34 | submatch := r.FindStringSubmatch(tmp) 35 | if len(submatch) != 4 { 36 | return nil, errors.New("upstream_list format error") 37 | } 38 | config.Schema = submatch[1] 39 | ipList = append(ipList, submatch[2]) 40 | ipConf[submatch[2]] = submatch[3] 41 | } 42 | config.IpList = ipList 43 | config.IpWeight = ipConf 44 | return config, nil 45 | } 46 | 47 | func GetServiceDetailFromGinContext(c *gin.Context) (*ServiceDetail, error) { 48 | serverInterface, ok := c.Get(public.ServiceDetailContextKey) 49 | if !ok { 50 | return nil, errors.New("service not found") 51 | } 52 | return serverInterface.(*ServiceDetail), nil 53 | } 54 | -------------------------------------------------------------------------------- /public/const.go: -------------------------------------------------------------------------------- 1 | package public 2 | 3 | const ( 4 | ValidatorKey = "ValidatorKey" 5 | TranslatorKey = "TranslatorKey" 6 | AdminSessionInfoKey = "AdminSessionInfoKey" 7 | 8 | LoadTypeHTTP = 0 9 | LoadTypeTCP = 1 10 | LoadTypeGRPC = 2 11 | 12 | HTTPRuleTypePrefixURL = 0 13 | HTTPRuleTypeDomain = 1 14 | 15 | //flow_counter_store 16 | RedisFlowDayKey = "flow_day_count" 17 | RedisFlowHourKey = "flow_hour_count" 18 | 19 | //flow_limit && http_flow_limit function app&server's name or prefix 20 | FlowTotal = "flow_total" 21 | FlowServicePrefix = "flow_service_" 22 | FlowAppPrefix = "flow_app_" 23 | 24 | //distributed limter prefix 25 | DistributedLimiterPrefix = "dist_limiter_" 26 | 27 | JwtSignKey = "my_sign_key" 28 | JwtExpires = 60 * 60 29 | 30 | //middleware context key 31 | ServiceDetailContextKey = "service" 32 | ) 33 | 34 | var ( 35 | LoadTypeMap = map[int]string{ 36 | LoadTypeHTTP: "HTTP", 37 | LoadTypeTCP: "TCP", 38 | LoadTypeGRPC: "GRPC", 39 | } 40 | ) 41 | -------------------------------------------------------------------------------- /public/http_response.go: -------------------------------------------------------------------------------- 1 | package public 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "github.com/gin-gonic/gin" 7 | "strings" 8 | ) 9 | 10 | type ResponseCode int 11 | 12 | //1000以下为通用码,1000以上为用户自定义码 13 | const ( 14 | SuccessCode ResponseCode = iota 15 | UndefErrorCode 16 | ValidErrorCode 17 | InternalErrorCode 18 | InvalidRequestErrorCode ResponseCode = 401 19 | CustomizeCode ResponseCode = 1000 20 | GROUPALL_SAVE_FLOWERROR ResponseCode = 2001 21 | ) 22 | 23 | type Response struct { 24 | ErrorCode ResponseCode `json:"errno"` 25 | ErrorMsg string `json:"errmsg"` 26 | Data interface{} `json:"data"` 27 | TraceId interface{} `json:"trace_id"` 28 | Stack interface{} `json:"stack"` 29 | } 30 | 31 | func ResponseError(c *gin.Context, code ResponseCode, err error) { 32 | traceContext := GetGinTraceContext(c) 33 | errMsg := fmt.Sprintf("%+v", err) 34 | 35 | straceMsg := "" 36 | tmpStack := strings.Split(errMsg, "||") 37 | if len(tmpStack) == 2 { 38 | errMsg = tmpStack[0] 39 | straceMsg = tmpStack[1] 40 | } 41 | strackList := strings.Split(straceMsg, "\n") 42 | for i, t := range strackList { 43 | t = strings.Replace(t, "\t", " ", -1) 44 | strackList[i] = t 45 | } 46 | resp := &Response{ErrorCode: code, ErrorMsg: errMsg, Data: "", TraceId: traceContext.TraceId, Stack: strackList} 47 | c.JSON(200, resp) 48 | response, _ := json.Marshal(resp) 49 | c.Set("response", string(response)) 50 | c.AbortWithError(200, err) 51 | } 52 | 53 | func ResponseSuccess(c *gin.Context, data interface{}) { 54 | traceContext := GetGinTraceContext(c) 55 | resp := &Response{ErrorCode: SuccessCode, ErrorMsg: "", Data: data, TraceId: traceContext.TraceId} 56 | c.JSON(200, resp) 57 | response, _ := json.Marshal(resp) 58 | c.Set("response", string(response)) 59 | } 60 | -------------------------------------------------------------------------------- /public/jwt.go: -------------------------------------------------------------------------------- 1 | package public 2 | 3 | import ( 4 | "errors" 5 | "github.com/dgrijalva/jwt-go" 6 | ) 7 | 8 | func JwtDecode(tokenString string) (*jwt.StandardClaims, error) { 9 | token, err := jwt.ParseWithClaims(tokenString, &jwt.StandardClaims{}, func(token *jwt.Token) (interface{}, error) { 10 | return []byte(JwtSignKey), nil 11 | }) 12 | if err != nil { 13 | return nil, err 14 | } 15 | if claims, ok := token.Claims.(*jwt.StandardClaims); ok { 16 | return claims, nil 17 | } else { 18 | return nil, errors.New("token is not jwt.StandardClaims") 19 | } 20 | } 21 | 22 | func JwtEncode(claims jwt.StandardClaims) (string, error) { 23 | mySigningKey := []byte(JwtSignKey) 24 | token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) 25 | return token.SignedString(mySigningKey) 26 | } 27 | -------------------------------------------------------------------------------- /public/log.go: -------------------------------------------------------------------------------- 1 | package public 2 | 3 | import ( 4 | "github.com/didi/gatekeeper/golang_common/lib" 5 | "github.com/didi/gatekeeper/golang_common/trace" 6 | "github.com/gin-gonic/gin" 7 | ) 8 | 9 | func GinLogNotice(c *gin.Context, dltag string, m map[string]interface{}) { 10 | traceContext := GetGinTraceContext(c) 11 | lib.Log.TagInfo(traceContext, dltag, m) 12 | } 13 | 14 | func GinLogWarning(c *gin.Context, dltag string, m map[string]interface{}) { 15 | traceContext := GetGinTraceContext(c) 16 | lib.Log.TagError(traceContext, dltag, m) 17 | } 18 | 19 | func GetGinTraceContext(c *gin.Context) *trace.Trace { 20 | traceContext, exists := trace.GetCtxTrace(c.Request.Context()) 21 | if exists { 22 | return traceContext 23 | } 24 | return trace.New(c.Request) 25 | } -------------------------------------------------------------------------------- /public/redis.go: -------------------------------------------------------------------------------- 1 | package public 2 | 3 | import ( 4 | "github.com/didi/gatekeeper/golang_common/lib" 5 | "github.com/garyburd/redigo/redis" 6 | ) 7 | 8 | func RedisConfPipline(pip ...func(c redis.Conn)) error { 9 | c, err := lib.RedisConnFactory("default") 10 | if err != nil { 11 | return err 12 | } 13 | defer c.Close() 14 | for _, f := range pip { 15 | f(c) 16 | } 17 | c.Flush() 18 | return nil 19 | } 20 | 21 | func RedisConfDo(commandName string, args ...interface{}) (interface{}, error) { 22 | c, err := lib.RedisConnFactory("default") 23 | if err != nil { 24 | return nil, err 25 | } 26 | defer c.Close() 27 | return c.Do(commandName, args...) 28 | } 29 | -------------------------------------------------------------------------------- /reverse_proxy/grcp_reverse_proxy.go: -------------------------------------------------------------------------------- 1 | package reverse_proxy 2 | 3 | import ( 4 | "context" 5 | "github.com/didi/gatekeeper/load_balance" 6 | "github.com/e421083458/grpc-proxy/proxy" 7 | "google.golang.org/grpc" 8 | "google.golang.org/grpc/metadata" 9 | "log" 10 | ) 11 | 12 | func NewGrpcLoadBalanceHandler(lb *load_balance.LoadBalance) grpc.StreamHandler { 13 | return func() grpc.StreamHandler { 14 | nextAddr, err := lb.Get("") 15 | if err != nil { 16 | log.Fatal("get next addr fail") 17 | } 18 | director := func(ctx context.Context, fullMethodName string) (context.Context, *grpc.ClientConn, error) { 19 | c, err := grpc.DialContext(ctx, nextAddr, grpc.WithCodec(proxy.Codec()), grpc.WithInsecure()) 20 | md, _ := metadata.FromIncomingContext(ctx) 21 | outCtx, _ := context.WithCancel(ctx) 22 | outCtx = metadata.NewOutgoingContext(outCtx, md.Copy()) 23 | return outCtx, c, err 24 | } 25 | return proxy.TransparentHandler(director) 26 | }() 27 | } 28 | -------------------------------------------------------------------------------- /reverse_proxy/http_reverse_proxy.go: -------------------------------------------------------------------------------- 1 | package reverse_proxy 2 | 3 | import ( 4 | "bytes" 5 | "github.com/didi/gatekeeper/dashboard_middleware" 6 | "github.com/didi/gatekeeper/load_balance" 7 | "github.com/gin-gonic/gin" 8 | "net/http" 9 | "net/http/httputil" 10 | "net/url" 11 | "strings" 12 | ) 13 | 14 | func NewLoadBalanceReverseProxy(c *gin.Context, lb *load_balance.LoadBalance, trans *http.Transport) *httputil.ReverseProxy { 15 | director := func(req *http.Request) { 16 | nextAddr, err := lb.Get(req.URL.String()) 17 | if err != nil || nextAddr == "" { 18 | panic("get next addr fail") 19 | } 20 | target, err := url.Parse(nextAddr) 21 | if err != nil { 22 | panic(err) 23 | } 24 | targetQuery := target.RawQuery 25 | req.URL.Scheme = target.Scheme 26 | req.URL.Host = target.Host 27 | req.URL.Path = singleJoiningSlash(target.Path, req.URL.Path) 28 | req.Host = target.Host 29 | if targetQuery == "" || req.URL.RawQuery == "" { 30 | buffer := bytes.NewBufferString(targetQuery) 31 | buffer.WriteString(req.URL.RawQuery) 32 | req.URL.RawQuery = buffer.String() 33 | } else { 34 | buffer := bytes.NewBufferString(targetQuery) 35 | buffer.WriteString("&") 36 | buffer.WriteString(req.URL.RawQuery) 37 | req.URL.RawQuery = buffer.String() 38 | } 39 | } 40 | errFunc := func(w http.ResponseWriter, r *http.Request, err error) { 41 | dashboard_middleware.ResponseError(c, 999, err) 42 | } 43 | return &httputil.ReverseProxy{Director: director, Transport: trans, ErrorHandler: errFunc} 44 | } 45 | 46 | func singleJoiningSlash(a, b string) string { 47 | aslash := strings.HasSuffix(a, "/") 48 | bslash := strings.HasPrefix(b, "/") 49 | switch { 50 | case aslash && bslash: 51 | buffer := bytes.NewBufferString(a) 52 | buffer.WriteString(b[1:]) 53 | return buffer.String() 54 | case !aslash && !bslash: 55 | buffer := bytes.NewBufferString(a) 56 | buffer.WriteString("/") 57 | buffer.WriteString(b) 58 | return buffer.String() 59 | } 60 | buffer := bytes.NewBufferString(a) 61 | buffer.WriteString(b) 62 | return buffer.String() 63 | } 64 | -------------------------------------------------------------------------------- /tcp_proxy_middleware/tcp_black_list.go: -------------------------------------------------------------------------------- 1 | package tcp_proxy_middleware 2 | 3 | import ( 4 | "fmt" 5 | "github.com/didi/gatekeeper/public" 6 | "strings" 7 | ) 8 | 9 | //匹配接入方式 基于请求信息 10 | func TCPBlackListMiddleware() func(c *TcpSliceRouterContext) { 11 | return func(c *TcpSliceRouterContext) { 12 | serviceDetail, err := c.GetServiceDetail() 13 | if err != nil { 14 | c.conn.Write([]byte(err.Error())) 15 | c.Abort() 16 | return 17 | } 18 | whiteListStr := serviceDetail.PluginConf.GetPath("tcp_whiteblacklist", "ip_white_list").MustString() 19 | blackListStr := serviceDetail.PluginConf.GetPath("tcp_whiteblacklist", "ip_black_list").MustString() 20 | if blackListStr == "" { 21 | c.Next() 22 | return 23 | } 24 | 25 | splits := strings.Split(c.conn.RemoteAddr().String(), ":") 26 | clientIP := "" 27 | if len(splits) == 2 { 28 | clientIP = splits[0] 29 | } 30 | 31 | if whiteListStr == "" && blackListStr != "" { 32 | if public.InIPSliceStr(clientIP, blackListStr) { 33 | c.conn.Write([]byte(fmt.Sprintf("%s in black ip list", clientIP))) 34 | c.Abort() 35 | return 36 | } 37 | } 38 | c.Next() 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /tcp_proxy_middleware/tcp_flow_count.go: -------------------------------------------------------------------------------- 1 | package tcp_proxy_middleware 2 | 3 | import ( 4 | "github.com/didi/gatekeeper/handler" 5 | "github.com/didi/gatekeeper/public" 6 | ) 7 | 8 | func TCPFlowCountMiddleware() func(c *TcpSliceRouterContext) { 9 | return func(c *TcpSliceRouterContext) { 10 | serviceDetail,err := c.GetServiceDetail() 11 | if err != nil { 12 | c.conn.Write([]byte(err.Error())) 13 | c.Abort() 14 | return 15 | } 16 | totalCounter, err := handler.ServiceCounterHandler.GetCounter(public.FlowTotal) 17 | if err != nil { 18 | c.conn.Write([]byte(err.Error())) 19 | c.Abort() 20 | return 21 | } 22 | totalCounter.Increase() 23 | 24 | serviceCounter, err := handler.ServiceCounterHandler.GetCounter(public.FlowServicePrefix + serviceDetail.Info.ServiceName) 25 | if err != nil { 26 | c.conn.Write([]byte(err.Error())) 27 | c.Abort() 28 | return 29 | } 30 | serviceCounter.Increase() 31 | c.Next() 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /tcp_proxy_middleware/tcp_flow_limit.go: -------------------------------------------------------------------------------- 1 | package tcp_proxy_middleware 2 | 3 | import ( 4 | "fmt" 5 | "github.com/didi/gatekeeper/handler" 6 | "github.com/didi/gatekeeper/model" 7 | "github.com/didi/gatekeeper/public" 8 | "strings" 9 | ) 10 | 11 | func TCPFlowLimitMiddleware() func(c *TcpSliceRouterContext) { 12 | return func(c *TcpSliceRouterContext) { 13 | serverInterface := c.Get("service") 14 | if serverInterface == nil { 15 | c.conn.Write([]byte("get service empty")) 16 | c.Abort() 17 | return 18 | } 19 | serviceDetail := serverInterface.(*model.ServiceDetail) 20 | serviceFlowNum := serviceDetail.PluginConf.GetPath("http_flow_limit","service_flow_limit_num").MustInt() 21 | serviceFlowType := serviceDetail.PluginConf.GetPath("http_flow_limit","service_flow_limit_type").MustInt() 22 | if serviceFlowNum != 0 { 23 | serviceLimiter, err := handler.FlowLimiterHandler.GetLimiter( 24 | public.FlowServicePrefix+serviceDetail.Info.ServiceName, float64(serviceFlowNum), serviceFlowType, true) 25 | if err != nil { 26 | c.conn.Write([]byte(err.Error())) 27 | c.Abort() 28 | return 29 | } 30 | if !serviceLimiter.Allow() { 31 | c.conn.Write([]byte(fmt.Sprintf("service flow limit %v", serviceFlowNum))) 32 | c.Abort() 33 | return 34 | } 35 | } 36 | 37 | splits := strings.Split(c.conn.RemoteAddr().String(), ":") 38 | clientIP := "" 39 | if len(splits) == 2 { 40 | clientIP = splits[0] 41 | } 42 | clientIpFlowNum := serviceDetail.PluginConf.GetPath("tcp_flow_limit","clientip_flow_limit_num").MustInt() 43 | clientIpFlowType := serviceDetail.PluginConf.GetPath("tcp_flow_limit","clientip_flow_limit_type").MustInt() 44 | if clientIpFlowNum > 0 { 45 | clientLimiter, err := handler.FlowLimiterHandler.GetLimiter(public.FlowServicePrefix+serviceDetail.Info.ServiceName+"_"+clientIP, float64(clientIpFlowNum), clientIpFlowType, true) 46 | if err != nil { 47 | c.conn.Write([]byte(err.Error())) 48 | c.Abort() 49 | return 50 | } 51 | if !clientLimiter.Allow() { 52 | c.conn.Write([]byte(fmt.Sprintf("%v flow limit %v", clientIP, clientIpFlowNum))) 53 | c.Abort() 54 | return 55 | } 56 | } 57 | c.Next() 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /tcp_proxy_middleware/tcp_white_list.go: -------------------------------------------------------------------------------- 1 | package tcp_proxy_middleware 2 | 3 | import ( 4 | "fmt" 5 | "github.com/didi/gatekeeper/public" 6 | "strings" 7 | ) 8 | 9 | //匹配接入方式 基于请求信息 10 | func TCPWhiteListMiddleware() func(c *TcpSliceRouterContext) { 11 | return func(c *TcpSliceRouterContext) { 12 | serviceDetail, err := c.GetServiceDetail() 13 | if err != nil { 14 | c.conn.Write([]byte(err.Error())) 15 | c.Abort() 16 | return 17 | } 18 | 19 | splits := strings.Split(c.conn.RemoteAddr().String(), ":") 20 | clientIP := "" 21 | if len(splits) == 2 { 22 | clientIP = splits[0] 23 | } 24 | 25 | whiteListStr := serviceDetail.PluginConf.GetPath("tcp_whiteblacklist", "ip_white_list").MustString() 26 | if whiteListStr != "" { 27 | if !public.InIPSliceStr(clientIP, whiteListStr) { 28 | c.conn.Write([]byte(fmt.Sprintf("%s not in white ip list", clientIP))) 29 | c.Abort() 30 | return 31 | } 32 | } 33 | 34 | c.Next() 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /tcp_server/tcp_conn.go: -------------------------------------------------------------------------------- 1 | package tcp_server 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "net" 7 | "runtime" 8 | ) 9 | 10 | type tcpKeepAliveListener struct { 11 | *net.TCPListener 12 | } 13 | 14 | //todo 思考点:继承方法覆写方法时,只要使用非指针接口 15 | func (ln tcpKeepAliveListener) Accept() (net.Conn, error) { 16 | tc, err := ln.AcceptTCP() 17 | if err != nil { 18 | return nil, err 19 | } 20 | return tc, nil 21 | } 22 | 23 | type contextKey struct { 24 | name string 25 | } 26 | 27 | func (k *contextKey) String() string { 28 | return "tcp_proxy context value " + k.name 29 | } 30 | 31 | type conn struct { 32 | server *TcpServer 33 | cancelCtx context.CancelFunc 34 | rwc net.Conn 35 | remoteAddr string 36 | } 37 | 38 | func (c *conn) close() { 39 | c.rwc.Close() 40 | } 41 | 42 | func (c *conn) serve(ctx context.Context) { 43 | defer func() { 44 | if err := recover(); err != nil && err != ErrAbortHandler { 45 | const size = 64 << 10 46 | buf := make([]byte, size) 47 | buf = buf[:runtime.Stack(buf, false)] 48 | fmt.Printf("tcp: panic serving %v: %v\n%s", c.remoteAddr, err, buf) 49 | } 50 | c.close() 51 | }() 52 | c.remoteAddr = c.rwc.RemoteAddr().String() 53 | ctx = context.WithValue(ctx, LocalAddrContextKey, c.rwc.LocalAddr()) 54 | if c.server.Handler == nil { 55 | panic("handler empty") 56 | } 57 | c.server.Handler.ServeTCP(ctx, c.rwc) 58 | } 59 | -------------------------------------------------------------------------------- /test_suites/README.md: -------------------------------------------------------------------------------- 1 | # Integration test use document 2 | -1. The goconvey command must be installed to ensure that the command can be executed 3 | ``` 4 | cd $GOPATH 5 | go get github.com/smartystreets/goconvey 6 | goconvey 7 | ``` 8 | 9 | -2. Create a test database and import the data structure 10 | ``` 11 | mysql -h 127.0.0.1 -u root -p -e "CREATE DATABASE gatekeeper_test DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;" 12 | mysql -h 127.0.0.1 -u root -p gatekeeper_test