mirror of
https://github.com/gusaul/grpcox.git
synced 2024-12-26 02:40:10 +00:00
save and reuse active connections
This commit is contained in:
parent
02a4feb17b
commit
f44b1a7f78
|
@ -16,7 +16,8 @@ import (
|
||||||
// GrpCox - main object
|
// GrpCox - main object
|
||||||
type GrpCox struct {
|
type GrpCox struct {
|
||||||
KeepAlive float64
|
KeepAlive float64
|
||||||
PlainText bool
|
|
||||||
|
activeConn map[string]*Resource
|
||||||
|
|
||||||
// TODO : utilize below args
|
// TODO : utilize below args
|
||||||
headers []string
|
headers []string
|
||||||
|
@ -30,14 +31,28 @@ type GrpCox struct {
|
||||||
isUnixSocket func() bool
|
isUnixSocket func() bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// InitGrpCox constructor
|
||||||
|
func InitGrpCox() *GrpCox {
|
||||||
|
return &GrpCox{
|
||||||
|
activeConn: make(map[string]*Resource),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// GetResource - open resource to targeted grpc server
|
// GetResource - open resource to targeted grpc server
|
||||||
func (g *GrpCox) GetResource(ctx context.Context, target string) (*Resource, error) {
|
func (g *GrpCox) GetResource(ctx context.Context, target string, plainText, isRestartConn bool) (*Resource, error) {
|
||||||
|
if conn, ok := g.activeConn[target]; ok {
|
||||||
|
if !isRestartConn && conn.refClient != nil && conn.clientConn != nil {
|
||||||
|
return conn, nil
|
||||||
|
}
|
||||||
|
g.CloseActiveConns(target)
|
||||||
|
}
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
r := new(Resource)
|
r := new(Resource)
|
||||||
h := append(g.headers, g.reflectHeaders...)
|
h := append(g.headers, g.reflectHeaders...)
|
||||||
md := grpcurl.MetadataFromHeaders(h)
|
md := grpcurl.MetadataFromHeaders(h)
|
||||||
refCtx := metadata.NewOutgoingContext(ctx, md)
|
refCtx := metadata.NewOutgoingContext(ctx, md)
|
||||||
r.clientConn, err = g.dial(ctx, target)
|
r.clientConn, err = g.dial(ctx, target, plainText)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -46,10 +61,40 @@ func (g *GrpCox) GetResource(ctx context.Context, target string) (*Resource, err
|
||||||
r.descSource = grpcurl.DescriptorSourceFromServer(ctx, r.refClient)
|
r.descSource = grpcurl.DescriptorSourceFromServer(ctx, r.refClient)
|
||||||
r.headers = h
|
r.headers = h
|
||||||
|
|
||||||
|
g.activeConn[target] = r
|
||||||
return r, nil
|
return r, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *GrpCox) dial(ctx context.Context, target string) (*grpc.ClientConn, error) {
|
// GetActiveConns - get all saved active connection
|
||||||
|
func (g *GrpCox) GetActiveConns(ctx context.Context) []string {
|
||||||
|
result := make([]string, len(g.activeConn))
|
||||||
|
i := 0
|
||||||
|
for k := range g.activeConn {
|
||||||
|
result[i] = k
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// CloseActiveConns - close conn by host or all
|
||||||
|
func (g *GrpCox) CloseActiveConns(host string) error {
|
||||||
|
if host == "all" {
|
||||||
|
for k, v := range g.activeConn {
|
||||||
|
v.Close()
|
||||||
|
delete(g.activeConn, k)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := g.activeConn[host]; ok {
|
||||||
|
v.Close()
|
||||||
|
delete(g.activeConn, host)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *GrpCox) dial(ctx context.Context, target string, plainText bool) (*grpc.ClientConn, error) {
|
||||||
dialTime := 10 * time.Second
|
dialTime := 10 * time.Second
|
||||||
ctx, cancel := context.WithTimeout(ctx, dialTime)
|
ctx, cancel := context.WithTimeout(ctx, dialTime)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
@ -69,7 +114,7 @@ func (g *GrpCox) dial(ctx context.Context, target string) (*grpc.ClientConn, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var creds credentials.TransportCredentials
|
var creds credentials.TransportCredentials
|
||||||
if !g.PlainText {
|
if !plainText {
|
||||||
var err error
|
var err error
|
||||||
creds, err = grpcurl.ClientTransportCredentials(g.insecure, g.cacert, g.cert, g.key)
|
creds, err = grpcurl.ClientTransportCredentials(g.insecure, g.cacert, g.cert, g.key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -119,7 +119,7 @@ func (r *Resource) Describe(symbol string) (string, string, error) {
|
||||||
|
|
||||||
// Invoke - invoking gRPC function
|
// Invoke - invoking gRPC function
|
||||||
func (r *Resource) Invoke(ctx context.Context, symbol string, in io.Reader) (string, time.Duration, error) {
|
func (r *Resource) Invoke(ctx context.Context, symbol string, in io.Reader) (string, time.Duration, error) {
|
||||||
// because of grpcurl directlu fmt.Printf on their invoke function
|
// because of grpcurl directly fmt.Printf on their invoke function
|
||||||
// so we stub the Stdout using os.Pipe
|
// so we stub the Stdout using os.Pipe
|
||||||
backUpStdout := os.Stdout
|
backUpStdout := os.Stdout
|
||||||
defer func() {
|
defer func() {
|
||||||
|
|
|
@ -6,12 +6,25 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"github.com/gusaul/grpcox/core"
|
"github.com/gusaul/grpcox/core"
|
||||||
)
|
)
|
||||||
|
|
||||||
func index(w http.ResponseWriter, r *http.Request) {
|
// Handler hold all handler methods
|
||||||
|
type Handler struct {
|
||||||
|
g *core.GrpCox
|
||||||
|
}
|
||||||
|
|
||||||
|
// InitHandler Constructor
|
||||||
|
func InitHandler() *Handler {
|
||||||
|
return &Handler{
|
||||||
|
g: core.InitGrpCox(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Handler) index(w http.ResponseWriter, r *http.Request) {
|
||||||
body := new(bytes.Buffer)
|
body := new(bytes.Buffer)
|
||||||
err := indexHTML.Execute(body, make(map[string]string))
|
err := indexHTML.Execute(body, make(map[string]string))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -23,7 +36,27 @@ func index(w http.ResponseWriter, r *http.Request) {
|
||||||
w.Write(body.Bytes())
|
w.Write(body.Bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
func getLists(w http.ResponseWriter, r *http.Request) {
|
func (h *Handler) getActiveConns(w http.ResponseWriter, r *http.Request) {
|
||||||
|
response(w, h.g.GetActiveConns(context.TODO()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Handler) closeActiveConns(w http.ResponseWriter, r *http.Request) {
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
host := vars["host"]
|
||||||
|
if host == "" {
|
||||||
|
writeError(w, fmt.Errorf("Invalid Host"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err := h.g.CloseActiveConns(strings.Trim(host, " "))
|
||||||
|
if err != nil {
|
||||||
|
writeError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response(w, map[string]bool{"success": true})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Handler) getLists(w http.ResponseWriter, r *http.Request) {
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
host := vars["host"]
|
host := vars["host"]
|
||||||
if host == "" {
|
if host == "" {
|
||||||
|
@ -33,16 +66,14 @@ func getLists(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
service := vars["serv_name"]
|
service := vars["serv_name"]
|
||||||
|
|
||||||
g := new(core.GrpCox)
|
|
||||||
useTLS, _ := strconv.ParseBool(r.Header.Get("use_tls"))
|
useTLS, _ := strconv.ParseBool(r.Header.Get("use_tls"))
|
||||||
g.PlainText = !useTLS
|
restart, _ := strconv.ParseBool(r.FormValue("restart"))
|
||||||
|
|
||||||
res, err := g.GetResource(context.Background(), host)
|
res, err := h.g.GetResource(context.Background(), host, !useTLS, restart)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
writeError(w, err)
|
writeError(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer res.Close()
|
|
||||||
|
|
||||||
result, err := res.List(service)
|
result, err := res.List(service)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -53,7 +84,7 @@ func getLists(w http.ResponseWriter, r *http.Request) {
|
||||||
response(w, result)
|
response(w, result)
|
||||||
}
|
}
|
||||||
|
|
||||||
func describeFunction(w http.ResponseWriter, r *http.Request) {
|
func (h *Handler) describeFunction(w http.ResponseWriter, r *http.Request) {
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
host := vars["host"]
|
host := vars["host"]
|
||||||
if host == "" {
|
if host == "" {
|
||||||
|
@ -67,16 +98,13 @@ func describeFunction(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
g := new(core.GrpCox)
|
|
||||||
useTLS, _ := strconv.ParseBool(r.Header.Get("use_tls"))
|
useTLS, _ := strconv.ParseBool(r.Header.Get("use_tls"))
|
||||||
g.PlainText = !useTLS
|
|
||||||
|
|
||||||
res, err := g.GetResource(context.Background(), host)
|
res, err := h.g.GetResource(context.Background(), host, !useTLS, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
writeError(w, err)
|
writeError(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer res.Close()
|
|
||||||
|
|
||||||
// get param
|
// get param
|
||||||
result, _, err := res.Describe(funcName)
|
result, _, err := res.Describe(funcName)
|
||||||
|
@ -109,7 +137,7 @@ func describeFunction(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func invokeFunction(w http.ResponseWriter, r *http.Request) {
|
func (h *Handler) invokeFunction(w http.ResponseWriter, r *http.Request) {
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
host := vars["host"]
|
host := vars["host"]
|
||||||
if host == "" {
|
if host == "" {
|
||||||
|
@ -123,16 +151,13 @@ func invokeFunction(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
g := new(core.GrpCox)
|
|
||||||
useTLS, _ := strconv.ParseBool(r.Header.Get("use_tls"))
|
useTLS, _ := strconv.ParseBool(r.Header.Get("use_tls"))
|
||||||
g.PlainText = !useTLS
|
|
||||||
|
|
||||||
res, err := g.GetResource(context.Background(), host)
|
res, err := h.g.GetResource(context.Background(), host, !useTLS, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
writeError(w, err)
|
writeError(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer res.Close()
|
|
||||||
|
|
||||||
// get param
|
// get param
|
||||||
result, timer, err := res.Invoke(context.Background(), funcName, r.Body)
|
result, timer, err := res.Invoke(context.Background(), funcName, r.Body)
|
||||||
|
|
|
@ -8,18 +8,26 @@ import (
|
||||||
|
|
||||||
// Init - routes initialization
|
// Init - routes initialization
|
||||||
func Init(router *mux.Router) {
|
func Init(router *mux.Router) {
|
||||||
router.HandleFunc("/", index)
|
h := InitHandler()
|
||||||
|
|
||||||
|
router.HandleFunc("/", h.index)
|
||||||
|
|
||||||
ajaxRoute := router.PathPrefix("/server/{host}").Subrouter()
|
ajaxRoute := router.PathPrefix("/server/{host}").Subrouter()
|
||||||
ajaxRoute.HandleFunc("/services", corsHandler(getLists)).Methods(http.MethodGet, http.MethodOptions)
|
ajaxRoute.HandleFunc("/services", corsHandler(h.getLists)).Methods(http.MethodGet, http.MethodOptions)
|
||||||
ajaxRoute.HandleFunc("/service/{serv_name}/functions", corsHandler(getLists)).Methods(http.MethodGet, http.MethodOptions)
|
ajaxRoute.HandleFunc("/service/{serv_name}/functions", corsHandler(h.getLists)).Methods(http.MethodGet, http.MethodOptions)
|
||||||
ajaxRoute.HandleFunc("/function/{func_name}/describe", corsHandler(describeFunction)).Methods(http.MethodGet, http.MethodOptions)
|
ajaxRoute.HandleFunc("/function/{func_name}/describe", corsHandler(h.describeFunction)).Methods(http.MethodGet, http.MethodOptions)
|
||||||
ajaxRoute.HandleFunc("/function/{func_name}/invoke", corsHandler(invokeFunction)).Methods(http.MethodPost, http.MethodOptions)
|
ajaxRoute.HandleFunc("/function/{func_name}/invoke", corsHandler(h.invokeFunction)).Methods(http.MethodPost, http.MethodOptions)
|
||||||
|
|
||||||
|
// get list of active connection
|
||||||
|
router.HandleFunc("/active/get", corsHandler(h.getActiveConns)).Methods(http.MethodGet, http.MethodOptions)
|
||||||
|
// close active connection
|
||||||
|
router.HandleFunc("/active/close/{host}", corsHandler(h.closeActiveConns)).Methods(http.MethodDelete, http.MethodOptions)
|
||||||
|
|
||||||
assetsPath := "index"
|
assetsPath := "index"
|
||||||
router.PathPrefix("/css/").Handler(http.StripPrefix("/css/", http.FileServer(http.Dir(assetsPath+"/css/"))))
|
router.PathPrefix("/css/").Handler(http.StripPrefix("/css/", http.FileServer(http.Dir(assetsPath+"/css/"))))
|
||||||
router.PathPrefix("/js/").Handler(http.StripPrefix("/js/", http.FileServer(http.Dir(assetsPath+"/js/"))))
|
router.PathPrefix("/js/").Handler(http.StripPrefix("/js/", http.FileServer(http.Dir(assetsPath+"/js/"))))
|
||||||
router.PathPrefix("/font/").Handler(http.StripPrefix("/font/", http.FileServer(http.Dir(assetsPath+"/font/"))))
|
router.PathPrefix("/font/").Handler(http.StripPrefix("/font/", http.FileServer(http.Dir(assetsPath+"/font/"))))
|
||||||
|
router.PathPrefix("/img/").Handler(http.StripPrefix("/img/", http.FileServer(http.Dir(assetsPath+"/img/"))))
|
||||||
}
|
}
|
||||||
|
|
||||||
func corsHandler(h http.HandlerFunc) http.HandlerFunc {
|
func corsHandler(h http.HandlerFunc) http.HandlerFunc {
|
||||||
|
|
|
@ -53,6 +53,101 @@ body {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* connections */
|
||||||
|
.connections {
|
||||||
|
font-weight: 100;
|
||||||
|
background: #efefef;
|
||||||
|
width: 240px;
|
||||||
|
height: 320px;
|
||||||
|
position: fixed;
|
||||||
|
top: 200px;
|
||||||
|
z-index: 100;
|
||||||
|
-webkit-box-shadow: -3px 0px 5px 0px rgba(0,0,0,0.2);
|
||||||
|
box-shadow: -3px 0px 5px 0px rgba(0,0,0,0.2);
|
||||||
|
left: -190px;
|
||||||
|
transition: all .3s;
|
||||||
|
-webkit-transition: all .3s;
|
||||||
|
color: #222;
|
||||||
|
}
|
||||||
|
|
||||||
|
.connections:hover, .connections:focus {
|
||||||
|
transform: translate3d(190px, 0, 0);
|
||||||
|
animation-timing-function: 1s ease-in
|
||||||
|
}
|
||||||
|
|
||||||
|
.connections .title {
|
||||||
|
top: 50%;
|
||||||
|
position: absolute;
|
||||||
|
-webkit-transform: translateY(-50%);
|
||||||
|
-ms-transform: translateY(-50%);
|
||||||
|
transform: translateY(-50%);
|
||||||
|
transform: rotate(270deg);
|
||||||
|
right: -50px;
|
||||||
|
font-weight: 800;
|
||||||
|
font-size: 15px
|
||||||
|
}
|
||||||
|
|
||||||
|
.connections .nav {
|
||||||
|
position: absolute;
|
||||||
|
-webkit-transform: translateY(-50%);
|
||||||
|
-ms-transform: translateY(-50%);
|
||||||
|
transform: translateY(-50%);
|
||||||
|
font-weight: 100;
|
||||||
|
overflow-y: scroll;
|
||||||
|
padding-left: 10px;
|
||||||
|
padding-right: 30px;
|
||||||
|
top: 160px;
|
||||||
|
height: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.connections .nav li {
|
||||||
|
padding-bottom: 12px;
|
||||||
|
list-style-type: none
|
||||||
|
}
|
||||||
|
|
||||||
|
.connections .nav li i {
|
||||||
|
color: #a20000;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.connections .nav li a:hover { color: #aaa }
|
||||||
|
|
||||||
|
.dots {
|
||||||
|
position: absolute;
|
||||||
|
left: -65px;
|
||||||
|
top: -39px;
|
||||||
|
color: #b70000;
|
||||||
|
}
|
||||||
|
|
||||||
|
circle {
|
||||||
|
stroke: #ce2828;
|
||||||
|
fill: #a20000;
|
||||||
|
stroke-width: 2px;
|
||||||
|
stroke-opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pulse {
|
||||||
|
fill: white;
|
||||||
|
fill-opacity: 0;
|
||||||
|
transform-origin: 50% 50%;
|
||||||
|
animation-duration: 2s;
|
||||||
|
animation-name: pulse;
|
||||||
|
animation-iteration-count: infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes pulse {
|
||||||
|
from {
|
||||||
|
stroke-width: 3px;
|
||||||
|
stroke-opacity: 1;
|
||||||
|
transform: scale(0.3);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
stroke-width: 0;
|
||||||
|
stroke-opacity: 0;
|
||||||
|
transform: scale(2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* loading spinner */
|
/* loading spinner */
|
||||||
.spinner {
|
.spinner {
|
||||||
margin: 10px auto;
|
margin: 10px auto;
|
||||||
|
|
BIN
index/img/favicon.png
Normal file
BIN
index/img/favicon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.0 KiB |
|
@ -5,6 +5,7 @@
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||||
<meta http-equiv="x-ua-compatible" content="ie=edge">
|
<meta http-equiv="x-ua-compatible" content="ie=edge">
|
||||||
<title>gRPCox - gRPC Testing Environment</title>
|
<title>gRPCox - gRPC Testing Environment</title>
|
||||||
|
<link rel="icon" href="/img/favicon.png" type="image/x-icon" />
|
||||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">
|
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">
|
||||||
<link href="css/bootstrap.min.css" rel="stylesheet">
|
<link href="css/bootstrap.min.css" rel="stylesheet">
|
||||||
<link href="css/mdb.min.css" rel="stylesheet">
|
<link href="css/mdb.min.css" rel="stylesheet">
|
||||||
|
@ -23,8 +24,8 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="custom-control custom-checkbox">
|
<div class="custom-control custom-checkbox">
|
||||||
<input type="checkbox" class="custom-control-input" id="use-tls">
|
<input type="checkbox" class="custom-control-input" id="restart-conn">
|
||||||
<label class="custom-control-label" for="use-tls">Use TLS</label>
|
<label class="custom-control-label" for="restart-conn">Restart Connection</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="other-elem" id="choose-service" style="display: none">
|
<div class="other-elem" id="choose-service" style="display: none">
|
||||||
|
@ -92,6 +93,16 @@
|
||||||
</svg>
|
</svg>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
<div class="connections">
|
||||||
|
<div class="title">
|
||||||
|
<svg class="dots" expanded = "true" height = "100px" width = "100px"><circle cx = "50%" cy = "50%" r = "7px"></circle><circle class = "pulse" cx = "50%" cy = "50%" r = "10px"></circle></svg>
|
||||||
|
<span></span> Active Connection(s)
|
||||||
|
</div>
|
||||||
|
<div id="conn-list-template" style="display:none"><li><i class="fa fa-close"></i> <span class="ip"></span></li></div>
|
||||||
|
<ul class="nav">
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="spinner" style="display: none">
|
<div class="spinner" style="display: none">
|
||||||
<div class="rect1"></div>
|
<div class="rect1"></div>
|
||||||
<div class="rect2"></div>
|
<div class="rect2"></div>
|
||||||
|
|
|
@ -4,15 +4,20 @@ $('#get-services').click(function(){
|
||||||
var t = get_valid_target();
|
var t = get_valid_target();
|
||||||
if (target != t) {
|
if (target != t) {
|
||||||
target = t;
|
target = t;
|
||||||
use_tls = $('#use-tls').is(":checked");
|
use_tls = "false";
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var restart = "0"
|
||||||
|
if($('#restart-conn').is(":checked")) {
|
||||||
|
restart = "1"
|
||||||
|
}
|
||||||
|
|
||||||
$('.other-elem').hide();
|
$('.other-elem').hide();
|
||||||
var button = $(this).html();
|
var button = $(this).html();
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: "server/"+target+"/services",
|
url: "server/"+target+"/services?restart="+restart,
|
||||||
global: true,
|
global: true,
|
||||||
method: "GET",
|
method: "GET",
|
||||||
success: function(res){
|
success: function(res){
|
||||||
|
@ -38,6 +43,7 @@ $('#get-services').click(function(){
|
||||||
show_loading();
|
show_loading();
|
||||||
},
|
},
|
||||||
complete: function(){
|
complete: function(){
|
||||||
|
applyConnCount();
|
||||||
$(this).html(button);
|
$(this).html(button);
|
||||||
hide_loading();
|
hide_loading();
|
||||||
}
|
}
|
||||||
|
@ -186,4 +192,56 @@ function show_loading() {
|
||||||
|
|
||||||
function hide_loading() {
|
function hide_loading() {
|
||||||
$('.spinner').hide();
|
$('.spinner').hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$(".connections ul").on("click", "i", function(){
|
||||||
|
$parent = $(this).parent("li");
|
||||||
|
var ip = $(this).siblings("span").text();
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: "/active/close/" + ip,
|
||||||
|
global: true,
|
||||||
|
method: "DELETE",
|
||||||
|
success: function(res){
|
||||||
|
if(res.data.success) {
|
||||||
|
$parent.remove();
|
||||||
|
updateCountNum();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: err,
|
||||||
|
beforeSend: function(xhr){
|
||||||
|
$(this).attr("class", "fa fa-spinner");
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function updateCountNum() {
|
||||||
|
$(".connections .title span").html($(".connections ul li").length);
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyConnCount() {
|
||||||
|
$.ajax({
|
||||||
|
url: "active/get",
|
||||||
|
global: true,
|
||||||
|
method: "GET",
|
||||||
|
success: function(res){
|
||||||
|
$(".connections .title span").html(res.data.length);
|
||||||
|
$(".connections .nav").html("");
|
||||||
|
res.data.forEach(function(item){
|
||||||
|
$list = $("#conn-list-template").clone();
|
||||||
|
$list.find(".ip").html(item);
|
||||||
|
$(".connections .nav").append($list.html());
|
||||||
|
});
|
||||||
|
},
|
||||||
|
error: err,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function refreshConnCount() {
|
||||||
|
applyConnCount();
|
||||||
|
setTimeout(refreshConnCount, 5000);
|
||||||
|
}
|
||||||
|
|
||||||
|
$(document).ready(function(){
|
||||||
|
refreshConnCount();
|
||||||
|
});
|
||||||
|
|
Loading…
Reference in New Issue
Block a user