From b79e198e25b5b62986b9f048aa46343ba376410c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kurk=C3=B3=20Mih=C3=A1ly?= Date: Tue, 22 Aug 2017 12:18:42 +0300 Subject: [PATCH] dashboard: React client --- dashboard/assets.go | 52 ++--- dashboard/assets/components/dashboard.js | 282 +++++++++++++++++++++++ dashboard/assets/dashboard.html | 185 +++------------ dashboard/assets/js/handlers.js | 112 --------- dashboard/dashboard.go | 111 +++------ 5 files changed, 370 insertions(+), 372 deletions(-) create mode 100644 dashboard/assets/components/dashboard.js delete mode 100644 dashboard/assets/js/handlers.js diff --git a/dashboard/assets.go b/dashboard/assets.go index 78f5491e51..b10ac86507 100644 --- a/dashboard/assets.go +++ b/dashboard/assets.go @@ -1,7 +1,7 @@ // Code generated by go-bindata. // sources: +// assets/components/dashboard.js // assets/dashboard.html -// assets/js/handlers.js // DO NOT EDIT! package dashboard @@ -69,7 +69,27 @@ func (fi bindataFileInfo) Sys() interface{} { return nil } -var _dashboardHtml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\x5a\xdb\x6e\xdb\x3c\x12\xbe\x2f\xd0\x77\xe0\xaf\xde\x2e\xc5\xd8\x29\xb6\x45\x57\x36\xd0\xa6\x45\xb7\x40\xf7\x00\x34\x0b\xec\x5e\x19\x34\x39\x96\xe8\x50\xa4\x4a\x52\x76\xfc\xf6\x0b\x4a\x94\xe3\x93\x2c\xd9\x71\x8b\xe4\x47\xae\x6c\x69\x8e\x9c\x8f\xc3\xd1\x0c\x98\xfc\xf1\xf9\x5f\x37\xb7\xff\xfb\xf7\x17\x94\xb9\x5c\x8e\x5f\xbf\x4a\xfc\x2f\x92\x54\xa5\xa3\x08\x54\x34\x7e\xfd\x0a\x21\x84\x92\x0c\x28\x0f\xff\xab\xe7\x1c\x1c\x45\x2c\xa3\xc6\x82\x1b\x45\xa5\x9b\xe1\xf7\xd1\x1e\x3d\x73\xae\xc0\xf0\xb3\x14\x8b\x51\xf4\x5f\xfc\x9f\x8f\xf8\x46\xe7\x05\x75\x62\x2a\x21\x42\x4c\x2b\x07\xca\x8d\xa2\x6f\x5f\x46\xc0\x53\xd8\x17\x57\x34\x87\x51\xb4\x10\xb0\x2c\xb4\x71\x1b\x12\x4b\xc1\x5d\x36\xe2\xb0\x10\x0c\x70\xf5\xf0\x17\x24\x94\x70\x82\x4a\x6c\x19\x95\x30\x1a\x78\x6d\x1b\xfa\x9c\x70\x12\xc6\x5f\x35\xfa\xe2\x32\x30\x50\xe6\xe8\x33\xb5\xd9\x54\x53\xc3\x13\x52\x13\xb7\xf8\xa5\x50\x77\xc8\x80\x1c\x45\x36\xd3\xc6\xb1\xd2\x21\xc1\xb4\x8a\x90\x5b\x15\x30\x8a\x44\x4e\x53\x20\x82\xe9\x08\x65\x06\x66\xa3\xc8\xaf\xd4\x7e\x20\x04\x82\xfa\x58\x9b\x94\xcc\xe8\xc2\x0b\xc5\x9e\x8f\x1c\xd0\xbf\x2d\xca\xb8\x9a\xdb\x98\x49\x5d\xf2\x99\xa4\x06\x62\xa6\x73\x42\xe7\xf4\x9e\x48\x31\xb5\xc4\x2d\x85\x73\x60\xf0\x54\x6b\x67\x9d\xa1\x05\xb9\x8e\xaf\xe3\x77\x84\x59\x4b\xd6\xef\xe2\x5c\xa8\x98\x59\x1b\x05\xdf\xdd\x4a\x82\xcd\x00\x5c\x84\xc8\xf8\x51\xe6\x67\x5a\x39\x4c\x97\x60\x75\x0e\xe4\x6d\xfc\x2e\xbe\xaa\x2c\x6f\xbe\xfe\x75\xc6\x53\x50\x0e\x24\x48\x49\xc9\x20\xbe\x0e\xa6\x59\x69\x9d\xce\xdb\x8d\x36\x3b\x97\x84\xad\x1b\x1e\xa7\x9a\xaf\x10\x93\xd4\xda\x51\xa4\xe8\x02\xe7\x7c\x6b\xe3\x71\xb1\x68\xa8\x7e\xbb\x51\xa1\xc0\x20\x2f\xb3\xc9\xb5\xcb\x99\x53\xa1\x26\x6b\xf6\x5d\xce\x7d\xbd\x12\xe7\x1c\x5f\x23\x09\x33\x37\x61\x5a\x1e\x12\xd8\x15\x6a\x78\x91\x65\x46\x4b\x89\x7d\x56\xb4\xc9\xed\xca\x2a\xba\x98\x52\x83\x14\x5d\x4c\xaa\xad\x1e\xa1\x2a\x4c\xa3\x68\xaa\x0d\x07\xf3\x01\x5d\xfd\xed\x98\xaa\x4a\x1d\x0d\x88\xf1\x26\x6d\x62\x7f\x4e\x44\x8d\x09\x2b\x1c\x04\xe5\xe3\x44\x34\x6f\x67\x14\xcd\x28\x2e\xe8\x32\x1a\x27\x44\x8c\x51\x62\x0b\xaa\xda\xb2\xb0\xa2\x25\x84\x1e\x5b\x14\xe1\x62\xb1\x95\x48\x47\xe3\x2c\x81\x9a\x99\xb8\xf7\xc6\x3b\xe5\xfe\xc0\x18\x59\xc1\xc1\x07\x2a\x07\x55\x22\x8c\xbb\xa2\x2b\xb8\x5f\x77\x25\x82\xbd\x48\xb4\xb5\x1d\xfc\x9b\x89\x27\xa3\x4c\x70\x0e\x0a\x17\x46\x28\x87\xd6\xb4\xce\x88\x6f\xee\xaf\x4a\x17\x30\x27\xb4\xea\x92\xab\x64\x4b\xb9\x81\x7d\xb5\x2e\xdc\xc7\xe6\x5a\x5e\x8a\x71\x42\xf7\x80\xcc\x74\x0e\x01\xc9\xbf\xeb\x1c\x6a\x38\xb7\x59\x58\x06\x0b\xa3\x15\xe6\x7a\xa9\x3c\x6b\x0f\x50\x3b\x9c\x67\x99\x90\xbc\x57\xc4\x0e\xaf\xe2\xf0\xbe\x1d\xaf\x37\xde\xc0\x7b\x97\x10\x29\x7e\x89\xf6\xe1\x19\xda\x13\x52\xca\xbe\x40\xf5\xd7\xdc\x82\x29\x70\xe1\x02\xa6\xff\x04\xb7\xd4\xe6\x4e\xa8\xf4\x59\x21\xab\xd6\x6e\x87\xe0\x7b\xa7\x1e\xd6\x32\x78\x2c\xb6\x1d\xfa\x87\x4f\x19\x5d\x0e\xf6\xce\xe9\x22\x00\x7c\x7b\x5f\x68\x2d\x9f\x15\xb8\xae\x72\x39\x04\xbe\xf6\xff\x22\x09\x7b\x40\xef\x93\x4f\x55\x47\xa7\xb2\x39\x7f\xbf\xeb\xd4\x3e\x2b\x20\xa5\x4e\x6d\x08\xb7\xf7\xfd\x22\x20\xee\xe8\x7c\xf2\x00\xfa\x0f\x05\xdf\x32\x39\xac\x03\x8c\x3f\x56\xd6\x41\xfe\xac\x80\xb4\x95\xcb\x21\xec\xb5\xff\x17\x01\xf3\x80\xde\x27\x0f\x28\x93\x5a\x85\x8c\xfc\x24\x35\xbb\x63\x19\x15\xea\x59\xa1\x39\x5d\xbb\x1d\x22\xff\xb0\x8e\x8b\xa0\x7a\x44\xff\xd3\x40\xb7\x87\xc6\xa6\x7b\x38\x9b\xee\x7b\x0b\xd2\xaf\xb9\x68\xd3\xd5\xde\xc1\x54\xda\x9d\x2e\x7c\x7b\x27\x52\xea\x5b\x84\xc3\xea\x37\x1b\x0a\xa7\x8b\x89\xa2\x8b\x3e\x8d\xa7\xef\x1a\xbb\xb6\x59\x52\xed\xc8\x9a\x3f\x42\x46\xfb\xde\xf2\xc1\x9d\x53\x3a\x1d\xaf\xc8\xe9\x34\x95\xd0\xab\xcf\xa1\x55\x13\x56\xb5\x47\x8d\xd4\x81\x43\xd7\xd6\x29\xda\x9d\x6f\xdd\x48\x2b\xda\x46\xef\x42\x6e\xef\x7d\xb5\x2b\x0e\x01\xd7\xc2\x5b\xd0\x14\x9a\x11\x58\x37\xc2\x46\xa4\x59\x3d\x5c\x08\x78\xf8\xc6\xb3\x0f\xde\x7d\x87\x0a\xde\x1d\x1c\x3a\xfe\xfe\xf8\x56\x02\x13\x09\x33\xd7\x0b\xdf\xec\xfa\xf0\xb4\x00\x25\x36\xa7\x52\x8e\x7f\x38\xea\x84\x75\x82\xd9\x84\xd4\x6f\x12\x92\x5d\x5f\x28\x9f\x7f\xc1\x94\x61\x13\x20\x7d\x74\x7e\xb3\x67\xa6\x1e\x1a\xfd\x15\xf9\x3f\x36\x0f\x7f\xee\x2d\x1e\x0c\x7b\x45\x72\x43\xd5\xfd\xa4\xa0\x0a\x5a\xa7\x4e\xc7\x45\x7b\x21\xbe\x25\x9e\x0d\xc7\xff\x80\x5c\x9b\x15\x2a\xad\xdf\xc2\x01\xba\xba\xde\x93\xbc\x22\x11\xa1\x4a\x0b\x1b\x18\x0e\xcf\xaf\x8c\xf5\xb4\x0b\x57\x09\x80\xaa\x95\x4e\x9c\xd6\x72\xaa\xef\xcf\xac\x94\x0f\x08\x48\x5a\x58\xc0\x52\xa8\xbb\xfd\x73\xa6\x29\xf1\x65\xd1\xf7\xb4\xd9\x33\x77\x56\xad\x6d\xbc\xe0\x46\x17\xf5\xe7\xc5\x69\x2a\xd0\xe6\x74\xef\x4d\xb4\xab\x0e\x87\x73\x15\x71\xea\x68\x78\xd8\x30\x16\x0e\x97\x69\xe9\x9c\x56\x11\xa2\x46\x50\x0c\xf7\x05\x55\x1c\xb8\x8f\x8c\xb4\x07\x8e\xe4\xa5\x01\xc5\xb2\x73\xc3\x84\xb6\x21\x5f\x3b\x5a\x8f\xe0\xc2\x61\x77\xc6\x77\xd1\x5a\xf7\xe6\xf7\xcb\x9b\x68\xfc\x03\x9c\x13\x2a\xb5\x68\x70\x9e\xaf\xe8\x3c\x68\x3b\xdd\x19\xfe\x76\x77\x4e\xf8\xfa\x7a\x84\xa5\x9d\xac\x93\xba\x3d\xe5\x3c\xed\x37\x65\xdb\x89\x6b\x3f\x5e\x21\xfa\x19\x3c\x81\x77\xeb\x88\x0e\xdf\x08\x27\x1d\xd2\x8c\xaa\x05\xb5\xe1\x6b\xca\x9f\xc8\xdf\x85\x82\x1b\xdf\xab\x7a\xaf\x6b\xea\x85\x1d\xef\xc3\xd7\x5d\x52\xd1\x9f\xa9\x4e\x7e\x53\x53\x5d\x2a\x8e\x9c\xa1\xb3\x99\x60\x4d\xa9\x2c\x86\x05\x09\xa4\xdb\x9a\xf2\x52\x29\x5f\x2a\x65\x4f\x87\x5f\x2a\xe5\x4b\xa5\x7c\xa9\x94\x87\xac\x3d\xba\x52\x86\x63\xfa\x69\x95\xca\x2e\xfa\xb9\x5d\xed\x29\x36\x8f\xcf\x3c\xf6\x07\x19\x2d\x9c\x33\xad\x1d\x98\x8e\x99\x47\xcd\xd4\x63\xb0\x51\x94\x52\xd6\xb5\xee\x18\xde\x5f\xd7\x57\x4d\x10\x46\x9f\x9a\x7b\x35\xe8\x23\xcf\x85\x42\xb7\x90\x17\x92\x3a\x40\xd3\xd5\x43\x25\x58\xdf\x61\xd1\x52\x1b\x29\xa6\x31\xd3\x79\x34\xbe\x09\x4f\xed\xe9\x76\x34\xb0\x27\x41\x91\x90\xd6\x20\xd4\x11\xef\x0a\xe4\xae\xe2\xfd\xbd\x92\x58\x66\x44\xe1\x90\x35\xac\xf7\xad\x9d\xf9\xcf\x12\xcc\x8a\x0c\xe3\x41\xfc\x36\x3c\x54\xb7\x75\xe6\xd5\x3c\xae\x56\x38\xbe\x84\x09\xac\xb4\xf3\x76\xde\xc6\x03\x52\x50\x76\x47\x53\xe0\x8d\x41\x4f\x8a\x9b\x97\x97\x36\x5f\x65\x7d\x3c\xb7\x64\x18\x5f\xc5\x57\xeb\xc7\x8b\xe9\x6f\xbb\xf3\x35\xdf\xbd\xf2\x75\x41\x9b\x33\xa9\x1d\xb9\x8a\xdf\xc7\xd7\x4d\x08\xfd\x9b\x4b\x5a\xd8\xbb\xcd\x35\xdf\xba\xcc\xb5\x63\xa9\xc5\xd6\xdc\x92\x8c\x2a\x2e\xc1\xd8\x43\xbe\x25\x64\xaa\xf9\x6a\xfc\xfa\x55\x42\xc2\xbd\xc6\xff\x07\x00\x00\xff\xff\x29\x20\x88\x50\xe9\x28\x00\x00") +var _componentsDashboardJs = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xcc\x5a\x7b\x6f\x1b\x37\x12\xff\xdf\x80\xbf\xc3\x9c\x7a\x68\x65\x54\xd2\xca\xf6\xa5\x0d\xf4\x02\x12\x27\x69\x7d\x88\x93\x22\x36\x50\x1c\x8c\xc0\xa5\x76\x47\xbb\x84\xb9\xe4\x82\xe4\xca\x36\x02\x7d\xf7\x03\xb9\x0f\xed\x53\x5a\x3b\x76\x51\x01\x41\x24\x92\xf3\xe3\x0c\x39\x6f\xfa\x2c\x20\x52\x8f\x3c\x5c\x91\x98\x69\x35\xf2\x99\x58\x12\x36\x62\xe8\x23\xf7\x60\x0e\xdf\x0e\x0f\x00\x00\x90\x93\x25\x43\x6f\x02\x2b\xc2\x14\x1e\x1e\x6c\xa6\x87\x07\x87\x07\xae\xe0\x4a\xc3\x99\x08\x23\xc1\x91\x6b\x98\xc3\x17\x24\xae\x1e\xe5\x23\xd3\xc3\x03\xc7\x81\xfa\xb2\x73\xbe\x42\xc9\x45\x69\xe1\xe1\x81\xcb\x88\x52\x70\xc6\x90\xc8\x15\xbd\x07\xbc\xd7\xc8\x3d\x55\x20\x4c\x99\x91\xc8\x3d\x94\xfd\xa3\xec\x77\x32\xa6\x63\xc9\xa1\xbf\x1d\x31\x9f\x99\x47\xd7\x60\x61\x3f\x91\x10\xe7\x3d\x37\xc5\xee\x39\x8b\xed\xc2\xa3\x69\xf2\x7d\x73\x78\xb0\xd9\xb2\x71\x81\x3c\x3e\xd7\x18\x3e\x0b\x1b\x8c\x2e\x66\x64\x31\xa3\x05\x5e\xbe\xfd\xb5\x22\xf0\xef\x6f\x3a\xa0\x6a\x14\x49\x11\xa9\x51\x3e\xb7\xf9\x0b\x36\xce\xa2\x38\xa5\xf1\x5e\x6f\xaa\xc2\xac\x08\xac\xc8\xd0\x0d\x70\x2d\x05\x1f\x7a\xe2\x8e\xf7\x9c\xc5\xcc\x21\x8b\xf2\xe6\x96\x81\x98\x15\x29\x39\x59\x83\x1b\x50\xe6\xdd\x84\xc8\xe3\x5e\x03\x81\xf9\x94\x78\x33\xab\x25\xf2\x4d\x03\xb6\x13\xb3\x0a\xc2\xcc\x61\xb4\xd3\x01\x9f\x19\xd8\xe7\x3b\x61\x08\x24\xae\xe6\x45\xbe\xcd\xc0\xa6\xe1\x28\x1d\xb2\xe8\xcc\xe4\x25\x32\x74\xf5\xb3\x70\x59\xbf\x06\x45\x3d\x1c\xb6\xdd\xc2\x2c\x57\xc2\xd2\xb5\x0f\x03\x11\x62\x0f\x8c\x24\xf3\xde\xef\xe6\x7b\xcb\x0d\xce\xb6\x67\x6c\x4f\xa6\xe7\x11\x15\x2c\x05\x91\xde\x28\xd0\x21\xcb\x20\xde\x65\xa3\xc7\x25\xbb\xf8\x1e\xa4\x93\x46\xa4\x99\x93\x09\xf4\x08\x61\xd1\xa3\x3a\xc3\xff\x84\xfa\x4e\xc8\x5b\xca\xfd\xef\x14\x79\x0b\xd4\x2c\xf3\x93\x38\xf5\x50\xdd\x6a\x11\x65\x7b\x5c\xdd\x47\x42\xb0\xef\x64\x34\x01\x79\x46\x26\xb5\x71\xe4\x19\xfa\x47\xe1\xab\x7f\x1a\x83\x4b\x22\x87\xae\x09\x4a\x43\x91\xed\x71\xf9\xa0\x34\x86\xcd\x5b\xb4\xa0\xb8\x4c\xf0\x5c\xcc\xb7\x4c\xb8\xb7\x6e\x40\x28\xff\x4e\x61\x0b\x40\x8f\x92\xb7\xe2\x23\x9b\x7d\xcd\x25\xf5\xf0\x2d\x91\x2f\x12\xf7\x04\x1b\x86\xde\xf0\x14\x18\xae\xf4\x8d\xdb\xac\x95\x55\xa2\x6c\x2d\x28\x57\x0a\xc6\x86\x6b\x8a\x77\xad\xe7\x57\xa1\xe5\x64\xbd\x24\x12\x38\x59\xdf\x68\xaa\x8d\xc2\x29\xfd\xc0\x70\xfe\xed\xdb\x52\x48\x0f\xe5\x04\xc6\x9b\x4d\x0b\x96\xc5\x23\x2d\xf7\x50\xd8\x43\x51\x8d\x29\x7a\x39\xb4\xa6\x91\x31\x22\x77\x3d\x67\x01\x33\x15\x11\xbe\xf8\x4d\xc0\x7b\x1d\xa0\xc4\x38\x84\xdc\x4b\xcd\x1c\x3b\xd7\x1c\x33\x93\x7b\xf3\xe8\xba\x6d\x2e\x4b\x54\x5a\x7d\xa6\x39\x13\xea\x19\x46\x3d\x34\x5a\x6d\xdd\x7c\x91\xcf\x90\x50\x6e\x43\xf0\x8d\x59\x02\x01\xf5\x3c\xe4\xc3\x48\x52\xae\x21\x9f\x6b\x3b\xf2\xa6\x63\x4f\xb0\xd0\xd5\x54\xb4\xaa\x7a\x4e\xbb\x8d\x6d\x6d\x12\xec\x3d\x82\x96\xb9\xa6\xf1\xea\x58\xb3\x0d\x5c\x89\xe8\x13\x59\x53\x9f\x18\x09\x5e\xc2\x12\xb4\x88\x6e\x38\x59\x77\xd1\x7f\xa3\xbc\xbb\x2e\x60\x66\xd3\xa8\xed\xfa\x1e\x48\xc1\x12\xba\x54\x80\xc7\x5c\x9d\x01\xd3\xc2\xf7\x59\x6b\x34\xcf\x29\x89\xd5\x2a\x7b\xd7\xdd\x28\x2c\x55\x83\x85\x2c\x89\x54\xad\x21\x3f\x27\x6c\xb5\x0e\xd8\xab\x1e\x9c\x3c\xaf\x7a\xec\x4e\xc8\x6d\x9d\x21\x63\x57\x0b\xd9\xb7\xc9\x5e\x49\x3f\x54\x1c\x61\x36\x3e\xdd\x0e\xdb\xd4\x50\x69\xa2\x71\x5b\xed\x64\x1f\x1b\x86\x26\xc0\x63\xc6\x06\xdb\x99\x4d\xce\x59\xb6\x6d\xca\xc7\x3b\xea\x5d\x88\x98\xeb\xb2\x5e\x6e\x37\x18\x59\x3c\x98\x03\xc7\x3b\xb0\x75\x57\xdf\x4e\x7a\x44\x93\x41\x75\x6f\xfd\x10\xe1\x04\x7e\x62\x94\xe3\x4f\x83\xf2\x94\x59\x3f\x81\x52\x76\x4e\xa4\x7e\x67\x50\x0a\x5c\x16\x4e\xb0\xdd\x68\xe8\xaa\x5f\xe3\xef\x5f\xf3\xb9\x15\xf9\xa8\xc6\x52\x65\xe5\x28\x8e\x3c\xa2\xb1\x5f\x3c\xce\x7c\x37\xe8\x6a\x92\xcd\x25\x50\x07\xfb\xbc\xbf\x89\x08\xc7\xf6\x04\xab\xb6\x3c\x8d\x15\x3b\x94\x39\x38\xa9\xd7\x0a\x30\x73\x82\x93\x5d\x44\xf5\xa4\x3e\x89\x7e\x43\x49\xfd\x40\x83\x65\xf2\x46\x0b\xc1\x96\xe2\x7e\xaf\x6d\x27\x75\x4c\x39\x74\x33\x12\x29\x1c\x32\xca\x6f\x9b\x03\x5d\x56\x02\xc6\x51\x6b\x01\x58\xda\xa3\x5c\xf8\xb4\xb0\x51\xdc\xc6\x93\x22\xb2\xf5\x65\x17\x3f\x93\x45\xee\x1f\x7a\x4d\x10\xc3\xd4\x63\x59\x25\x4e\x7f\x14\x36\x48\x5d\xe8\x32\xd6\x5a\xf0\x1e\x10\x49\xc9\x10\xef\x23\xc2\x3d\xf4\x8c\xb8\x4c\xb5\x04\xfb\x3b\x89\xdc\x0d\x3a\xc9\x0f\xf5\x4b\xcb\xb9\x4b\x42\x74\xc2\xc4\xbe\xe0\x5b\x39\xb0\x45\x41\xf2\xc5\x25\x6a\x4d\xb9\xaf\xe0\xb8\x1b\x3f\xd0\xed\x5e\xf6\x6e\x77\xf2\xec\xdb\x35\x94\xf7\x4f\x40\x6a\xd0\x6b\x26\x76\x2b\xb5\x99\x7f\x0e\x7d\xde\x23\xc0\xfe\x34\x6e\x57\x84\xab\xf9\x18\x57\x70\x8d\x5c\xef\xf4\x32\x2e\xe1\x6b\xa2\xc0\xf6\x2a\xfa\xc6\x0e\x8e\x60\xbe\x48\xdb\x2d\xe6\x27\xcc\xad\x75\x6c\x36\x8f\x65\xe9\xe9\x81\xf5\x8b\xb8\x7b\x89\x6c\x4b\x8a\xc6\x8a\x61\x66\xe2\x78\xb3\x68\xf5\xa2\xe5\x17\x30\x5f\x54\x98\x7e\xb9\x57\xc3\xe3\x93\x5e\x33\x71\x52\xa4\x5d\x60\x28\xe4\x03\xc4\x8a\xf8\x08\xca\x96\x8e\x4e\x68\xc7\x1c\xca\x63\x85\x2d\xc4\x79\x10\x2d\x47\x24\x33\xaa\x46\x09\x7d\x43\xf7\xab\xb1\x0c\x7c\x51\xf1\xce\xf9\x52\xc4\xdc\x03\x2d\xc9\x6a\x45\x5d\x88\x4e\x22\x27\x1d\xbb\x4a\x86\x9e\x22\x60\x8a\xd6\x41\xc2\x6e\x0a\xf5\x07\xf1\xf1\x2c\xb1\x85\x17\x51\x2c\x13\x5a\x6d\x21\x9b\x39\xeb\x96\xe2\xbe\x4a\xd8\x35\x5b\x88\x88\x8f\xc3\xfd\x09\x43\xb5\xba\x30\x04\x37\xa6\x70\xde\x1b\xe8\x83\xd3\xe6\x9a\x14\x66\x2a\x24\x8c\x2d\x2e\x35\xd1\x54\x69\xea\xaa\x99\x93\x8c\xcc\x9c\xe0\xf4\x99\x0b\x35\xe8\xe4\x00\x8d\x73\x48\xd4\xa4\x41\x73\xda\x9d\xd4\x2e\xdc\x27\x79\x2a\xfb\x8c\x70\xf1\xfe\xe2\xf3\x97\xff\xdd\x5c\xbe\xb9\xf8\xe3\xe3\xfb\x9b\x8f\xe7\x17\xe7\x57\x30\x87\x93\xf1\x78\x0a\x8e\x03\x17\xe4\x9e\x86\x71\x08\x3c\x0e\x97\x28\x41\xac\x20\xb1\x5c\xeb\x50\x41\x91\x30\x62\xa8\x32\xa8\xab\x2f\x6f\x3e\x7c\x38\x3f\xeb\x8e\x95\x99\x5c\x19\x2c\x53\xf9\xed\x15\xfe\xcd\x15\x8a\x9a\x54\x87\xcd\x27\x11\xbc\x71\xca\x7c\x18\x59\x22\x53\x13\xb8\xfe\x3a\x68\x5e\x60\x84\x54\x68\xc0\xaf\x5b\x20\x72\x98\x09\xf4\x9a\xfc\x6c\x0b\xb0\xf9\x2c\x89\x7b\xeb\x4b\xe3\xb3\xce\x04\x13\x72\x02\x3d\xe9\x2f\x49\xff\xf4\xf5\x00\x8e\x5f\xbf\x1a\xc0\xf1\xab\xff\x0c\x60\x3c\x3a\x3d\x3e\xda\x89\x62\x7b\x49\x3b\x11\x7e\xdd\x09\x10\x09\xca\xf5\xdb\xe7\x41\xe9\x26\x51\x07\xa4\xdf\xc5\x1a\x65\x1d\xee\x87\xd5\x6a\xd5\x91\xb6\x2e\xd0\xc9\xc9\x78\x90\xfd\xdb\x7d\xa6\x85\x23\xf9\x93\x7a\x3a\x98\xc0\xf1\x8e\xd5\x49\x29\x7a\xfd\xb5\x79\xc5\xa6\x61\x7c\xd3\x80\x96\xda\xd5\xdf\xa5\xab\x0d\x11\xf3\x09\xaa\x3a\x80\xd7\xe6\x6e\xc7\xbf\x58\x45\x7d\xb4\x9e\x96\xc8\x7f\x1d\x3f\x4d\x4d\x1f\x0f\xd2\x45\x98\x2e\x40\x2f\xa1\xa4\xc7\xaf\x8e\x07\xc7\xaf\x7f\x1d\x9c\x8c\x5f\xfd\xc3\x94\xb4\xf8\x7b\x53\xec\xab\x24\x5f\x93\x3e\x88\xed\xe8\x28\x98\x43\x3f\x54\x7e\x92\xd2\x6f\xa9\x18\xea\x2c\x14\xcd\x6b\x8d\x94\x2c\xbf\x9c\x96\xd7\x67\xe1\xa6\x89\x20\x9d\x9b\x16\xdb\x2d\x8e\x03\x1f\x28\x63\xa0\x03\x84\xbc\x6f\x0e\x77\x54\x07\x10\x11\xa5\xed\x39\x94\x5a\x3f\xa1\xf2\x47\x21\x6a\x49\x5d\x65\xbb\x3e\x31\xf7\x70\x45\x39\x7a\xb5\xd6\x8f\xe3\x24\xef\xf2\x40\x39\xb8\x44\x61\x75\x0f\xa2\x40\x44\xc8\xd1\x83\x25\xae\x84\xc4\x32\x75\x22\xdd\x28\x31\x63\x98\xc3\xf5\xd7\x69\xa5\xb3\x94\x48\xb3\x63\x45\x0a\x91\x19\xfa\xf5\xf8\x6b\x56\x2a\xb5\x82\xb5\xad\x2d\xaf\x4e\x2f\x06\xe6\x50\x38\x8d\xfa\x7d\x64\x4b\x23\x29\x5c\x54\x4a\xc8\x0a\x41\x3e\x3e\xad\x1d\xdc\xb9\x06\xaa\x20\x12\x4a\xd1\x25\x43\xd0\x02\x7c\xd4\x40\xb8\x30\xb9\x1f\x84\xa8\x6c\xa1\x72\x17\x50\x86\xb0\xa2\x8c\x51\xee\xdb\xe3\x25\x52\x92\x07\x05\xcb\x07\x88\x62\x15\xf4\x8f\x06\xa0\x04\x50\xae\x34\x12\xaf\xb6\x49\x14\x6b\x4b\x14\x50\xa5\x8d\x92\x69\x61\x7f\x2e\xd1\xa7\x9c\x1b\x44\x93\xbe\xe4\xa0\x65\xf2\x95\x90\xd0\x37\xc2\x51\x23\x14\x86\x23\x86\xdc\xd7\x01\x0c\xe1\x78\x0a\x14\x16\x73\x18\xc3\x8f\x3f\x36\x66\x5e\x8b\xf2\xdd\xa6\x94\x53\x18\x0e\x69\x4d\x87\x6a\x9a\x30\x8a\xb9\x0a\xe8\x4a\xf7\x43\x0c\xaf\xe9\xd7\x91\xa6\x21\x8e\x54\xbc\x54\x5a\x52\xee\x97\x46\x73\x8e\x5e\x1d\x1d\x4d\x5b\x03\xc8\xb3\xe3\xb6\x68\x5d\x75\x83\x35\x61\x31\xee\xe2\xab\x15\x20\x57\x9b\x56\x98\x4d\x53\x0f\x16\xb5\x29\x0f\xb0\x5f\x3f\xe1\x1d\x09\x61\x21\x27\x4c\xfe\x6f\x76\x99\x79\x30\x4e\xbf\xd4\x57\x55\x58\xda\x54\x59\x4e\x4a\xb8\xb6\xa6\xb0\xe3\xa4\xfe\xb2\xc1\x15\x59\xff\xb8\xdb\x13\x99\xb5\x0d\x2a\x07\xf3\xf9\xbc\x49\x43\x3b\x68\x61\x72\x13\x1d\xd4\xaa\x7d\x61\x9b\x9e\xec\x87\xee\x48\xb2\xd9\xe1\x50\x47\xd6\x3d\x6c\x0f\xb0\xa6\xef\x95\x99\x1d\x3a\x5f\x11\xf8\xf9\x80\xdb\x0e\xa8\xba\x43\xa3\x09\xb4\x9e\x55\x4e\x9d\x9b\x51\x0b\xc0\x1e\xa3\xd9\x63\x35\x9d\xcc\xa6\x83\xdd\xec\x34\x9c\x92\x8d\x98\x3a\x51\x30\x1c\x31\xe1\x17\xde\x67\xf2\x82\x78\xba\x7d\xd2\x71\x05\xe7\xe8\x6a\x93\x75\x34\xa5\x1c\x0a\xe5\x1a\x65\xfa\xe4\xf4\x27\x2e\x2f\x85\x7b\x8b\xba\xdf\xbb\x53\x13\xc7\xe9\xc1\xcf\xc0\x84\x6b\x9f\x2a\x47\x81\x50\x1a\x7e\x86\x9e\x43\x22\xda\x2b\x32\xe6\x38\x70\xf5\xf9\xdd\x67\xe8\xdf\xc6\xf2\x56\x84\x54\xd1\xa3\x09\x7c\x41\x25\xd8\x1a\xed\xc9\x26\x19\xc6\x92\x72\xaf\x92\xbe\x04\x44\xa7\xb9\x4b\x01\x2e\xe1\x68\x24\x78\x16\xf8\xe6\xd0\xc7\x35\x72\x5d\x65\x3f\x83\x09\x95\x0f\x73\xf8\xef\xe5\xe7\x4f\xa3\x88\x48\x85\xc9\x6a\xab\x00\xd5\x7b\xa6\x2b\x9b\x7b\x59\x5f\xd0\xf8\x82\x05\x4d\xde\xa9\x76\xfa\xf6\x36\x03\x92\xbd\x6f\x25\x79\x9d\x4d\xea\xa6\xe5\x34\xb0\x41\x2a\xdb\xb6\xde\xde\x06\x28\xd4\x57\x34\x44\x11\xeb\xbe\x85\xcc\x6f\x6c\x00\xa7\xe3\xf1\xf8\x68\xba\xcd\x27\xa7\x8f\x79\x50\xcc\x71\xfa\x9d\xde\xf9\xba\xfe\x99\x08\xd7\x84\x72\x94\xb0\x14\xde\x43\x97\x76\x9a\xfd\x2b\x85\x9c\xac\xb5\xb9\x96\xfe\x69\x4b\x6b\xab\xa8\xf4\xec\xdf\xba\xaa\xd8\x53\x2c\xf5\xa3\x8a\x99\x71\x7b\x3f\x6a\x25\x84\x46\xf9\x88\x6e\x5e\x14\x33\x96\x3c\xe2\xed\xeb\xe6\xfd\x86\x5c\x23\x43\xc6\x08\x0c\xe1\xad\x10\x5a\x69\x49\x22\x78\xe3\x85\x94\xc3\x15\x86\x11\x23\x1a\x4d\x26\x97\x3f\xda\x04\x5a\x47\xc6\x06\x5d\x53\x00\x31\xba\x1c\xb9\x22\xec\x2d\xce\xd2\x5f\x4f\x7f\x73\x87\x6e\x4f\x1a\xad\x67\xf1\xd8\xc6\xdc\xff\x03\x00\x00\xff\xff\x72\x1b\xde\x84\x3f\x2c\x00\x00") + +func componentsDashboardJsBytes() ([]byte, error) { + return bindataRead( + _componentsDashboardJs, + "components/dashboard.js", + ) +} + +func componentsDashboardJs() (*asset, error) { + bytes, err := componentsDashboardJsBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "components/dashboard.js", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + +var _dashboardHtml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xb4\x96\xdf\x6f\x23\x27\x10\xc7\xdf\x4f\xba\xff\x81\xf0\x92\x44\x3a\xc0\x8e\xd3\xdc\xe9\xba\x6b\xb5\x4d\xd2\x2a\x0f\x55\xaa\x36\x27\xb5\x4f\x15\x86\xf1\x2e\x36\x0b\x7b\x30\x6b\xc7\xff\x7d\xb5\x3f\xec\xee\xd6\x76\xdb\xbb\x38\x7e\x31\x0c\xf0\xfd\xcc\x30\x30\x6c\x72\x76\xf7\x78\xfb\xf4\xc7\x2f\xf7\x24\xc7\xc2\x4e\xdf\xbe\x49\xea\x7f\x62\xa5\xcb\x52\x0a\x8e\x4e\xdf\xbe\x21\x84\x90\x24\x07\xa9\xbb\x76\xd3\x2f\x00\x25\x51\xb9\x0c\x11\x30\xa5\x9f\x9e\x7e\x64\x1f\xe8\xde\x78\x8e\x58\x32\xf8\x5c\x99\x55\x4a\x7f\x67\x9f\xbe\x67\xb7\xbe\x28\x25\x9a\x99\x05\x4a\x94\x77\x08\x0e\x53\xfa\x70\x9f\x82\xce\x60\x7f\xb9\x93\x05\xa4\x74\x65\x60\x5d\xfa\x80\xbd\x15\x6b\xa3\x31\x4f\x35\xac\x8c\x02\xd6\x74\xde\x11\xe3\x0c\x1a\x69\x59\x54\xd2\x42\x3a\xae\xd5\x7a\x7a\x68\xd0\xc2\xf4\x27\x4f\xee\x31\x87\x00\x55\x41\xee\x64\xcc\x67\x5e\x06\x9d\x88\x76\xb0\x37\xdb\x1a\xb7\x24\x01\x6c\x4a\x63\xee\x03\xaa\x0a\x89\x51\xde\x51\x82\x9b\x12\x52\x6a\x0a\x99\x81\x30\xca\x53\x92\x07\x98\xa7\xb4\x8e\x33\x7e\x14\x02\x3a\x71\xee\x43\x26\xe6\x72\x55\x2f\xe2\xf5\x3c\x31\xf4\xa6\xd1\x1f\x2e\x55\xda\x2d\x22\x57\xd6\x57\x7a\x6e\x65\x00\xae\x7c\x21\xe4\x42\x3e\x0b\x6b\x66\x51\xe0\xda\x20\x42\x60\x33\xef\x31\x62\x90\xa5\x98\xf0\x09\x7f\x2f\x54\x8c\x62\x67\xe3\x85\x71\x5c\xc5\x48\x3b\xdf\x71\x63\x21\xe6\x00\x48\x89\xd8\x0b\xef\x8b\xf0\x73\xef\x90\xc9\x35\x44\x5f\x80\xb8\xe6\xef\xf9\xa8\x21\xf7\xcd\xaf\x07\xcf\xc0\x21\x58\xb0\x56\x8a\x31\x9f\x74\x68\x55\x45\xf4\xc5\x71\xe8\x70\xc3\xa3\x0a\xa6\x44\x12\x83\xfa\xdf\xd4\x99\x9c\x81\x65\x11\xa5\xd3\xd2\x7a\x07\xe2\x86\x5f\xdd\xf0\x51\x6b\x6f\xb8\x8b\x48\xa7\x89\x68\xa5\xa7\x2f\x83\x05\x90\x0a\xc5\xf8\x1b\x7e\xc3\xc7\x6d\xe7\xf5\x09\x4c\x77\xfb\x77\x6a\x0a\x0b\xbe\x42\x08\xe2\x9a\x8f\xf9\xd5\xc0\x74\x08\xf7\x32\xe0\xe2\x73\x05\x61\x23\xae\xf8\x98\x5f\x77\x9d\x53\xc7\xd4\xaa\x32\xe7\xb1\xe6\x5c\xf3\xb1\x28\xa5\x5a\xca\x0c\xf4\x16\x58\x0f\xf1\xad\xf1\xd4\xf8\xdb\x5c\x06\xe4\x8b\x28\xae\xf8\x88\x8f\x76\xdd\x93\xe9\x1f\xab\x2c\x8b\x7f\x16\x96\x13\x32\xe7\xd6\xa3\x18\xf1\x0f\x7c\xb2\xdd\xc2\xda\x72\xf8\x64\x24\xa2\xf7\xf4\x24\x33\xaf\x37\xc3\x43\x73\xc6\xd8\xd6\x85\xb6\x3a\x23\x3c\xa3\x58\xc4\x67\xda\xba\xf4\x9b\x2f\x00\x73\xe3\x32\x12\xa0\xb4\x52\xd5\xad\xe6\x0e\xf7\x58\x8c\x4d\x0f\x2b\x0e\x82\xaa\x5c\xb9\xcc\x9a\x48\x8c\x9b\x43\x70\xfe\xbb\x49\x53\x09\xb5\x89\xb8\x35\x0d\x63\xf8\x4a\x5d\xa6\x7c\x51\x7a\x07\x0e\x0f\x10\xfe\x1e\x3c\x0d\xab\xbd\x99\x87\x40\xc7\xee\x6c\x43\xea\xb1\xb4\x59\x11\xa3\x53\xaa\xb7\x2f\x2a\x25\xca\xca\x18\x53\xea\xe4\x8a\x15\xba\x5e\xaa\xcd\xea\xe0\x5d\xef\xa5\xac\x4d\x4a\xeb\xee\x2e\xc6\x28\x76\xaa\xff\x75\x00\xf7\xa4\x7a\x73\xea\xdf\xaf\x75\x21\xba\x7b\xfc\x99\x07\x70\x1a\xc2\x45\xb2\xfb\x02\x10\xd3\x77\x44\x7b\x55\x15\xf5\xa6\x66\x80\xf7\x16\xea\xe6\x0f\x9b\x07\x7d\x71\xbe\xe3\x9f\x5f\x5e\x7e\xdb\xa3\x1e\xac\x60\x87\x0f\x63\xe7\xce\x20\x43\xdb\xe9\x0f\xdd\xb9\x79\x81\x53\xfb\x99\x3f\x96\xa9\x33\xc6\x9e\x1e\xef\x1e\xc9\xc5\xb2\x0a\x4b\x5f\x98\x68\x2e\x3f\x92\x02\x5c\xf5\x27\xfa\x2c\xb3\x40\xb4\x87\xe8\xce\x91\xac\x7d\x58\x92\x32\xf8\x12\x82\xdd\x0c\xf5\xff\x3d\x71\x5f\xfb\xa4\x2f\x06\x2f\xfa\x91\x42\xd0\x5d\xfe\x44\x34\x9f\xaa\x7f\x05\x00\x00\xff\xff\x4d\x62\xae\x61\xba\x0a\x00\x00") func dashboardHtmlBytes() ([]byte, error) { return bindataRead( @@ -89,26 +109,6 @@ func dashboardHtml() (*asset, error) { return a, nil } -var _jsHandlersJs = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xbc\x57\xff\x6f\x9b\xc8\x13\xfd\xdd\x92\xff\x87\xf9\xf8\x23\xb5\x58\xa5\x60\xa7\x97\x6b\x64\x44\xa5\x36\x4d\xef\x7c\x4a\x2e\x55\x12\xa9\x3a\x45\x51\xb5\xc0\x00\xab\x2c\xbb\x68\x77\x49\x1a\x55\xfe\xdf\x4f\x0b\x18\x0c\xfe\x52\x7a\xca\x5d\xa4\x48\x78\x79\x6f\xde\x30\xfb\x66\x16\x4e\x53\x22\xb5\x13\x61\x4c\x0a\xa6\x95\x93\x30\x11\x10\xe6\x30\x4c\x90\x47\xe0\xc3\xf7\xf1\x08\x00\x00\x39\x09\x18\x46\x0b\x88\x09\x53\x38\x1e\xad\xc6\xa3\xf1\xc8\x75\xe1\x9c\x72\x84\xd0\x84\x80\x58\x48\xc8\x30\x13\xf2\x69\x3c\x7a\x20\x12\x42\xfd\x0d\x7c\x88\x44\x58\x64\xc8\xb5\x93\xa0\x3e\x63\x68\x2e\x3f\x3c\x2d\x23\x6b\x52\x41\x0d\xbf\xcc\x60\x32\xf5\x2a\x5a\x6f\x1d\x7c\xe0\xf8\x08\xe5\xb5\x15\xea\x6f\xf6\x3a\x23\xfd\x94\xe3\x02\x5e\x32\xca\xf1\xa5\x5d\x2d\x45\x44\x93\xc5\xfa\xbe\xf9\x63\x24\x40\xa6\x16\x70\x7b\x67\xb7\x8b\x06\xa5\x50\x9b\xe5\x0d\x68\x03\x5f\xc0\x44\x3d\x29\x8d\x99\x5b\x65\xe2\x52\x5e\x28\x9c\xd8\x5d\x68\x40\xc2\xfb\x44\x8a\x82\x47\xa7\x82\x09\xb9\x80\x89\x4c\x02\x62\xbd\x39\xb1\x61\x7e\x72\x6c\xc3\xfc\xf8\x17\x1b\x66\xce\x9b\xf9\x74\x8b\x29\x64\x84\xf2\x20\xeb\xed\x16\x29\x17\x94\xeb\x0f\xff\x9c\x39\x2c\xdb\x3d\xec\xdf\xc5\x03\xca\xed\x10\xff\x8f\xe3\xf8\x00\x7e\x3b\xd9\xa3\xa3\x99\xbd\xfe\xdf\xae\xcb\xc6\x23\x7e\xa1\x91\x4e\x17\x30\xef\x21\xaa\xed\xbd\xbd\x6b\x57\x57\xf5\xf5\xca\x1e\x8f\x56\xc6\x40\xbb\x3c\xa9\x25\x89\x63\x1a\x56\xee\xaa\x7f\x9c\x1e\xf6\x66\x8d\xda\x36\x67\xff\x46\xc7\x9d\x6d\xec\xff\xc2\xa4\xf9\x51\xee\x2e\x79\x60\x76\xe4\xa6\x12\x1e\xea\x51\x1b\x4e\xcc\xc6\xcf\x7e\x2d\x1d\x3a\xc8\xa0\x1d\xca\xdb\xd9\x70\x7f\x0e\x23\x0e\x49\x74\x1f\xf9\xb9\xdc\x39\x3f\x9e\xdb\xf3\x93\xb7\xf6\xd1\xec\xf8\xdf\x74\xe7\xcd\xe5\xc7\x4b\xb0\xee\x0b\x79\x2f\x32\xaa\xe8\x74\x01\x12\x33\xf1\x80\xa0\x34\xd1\x34\x84\x07\xc2\x0a\x54\x40\x62\x8d\x12\x22\x0c\x8a\x24\xa1\x3c\x19\x8f\x42\xc1\x95\x86\x8b\xb3\x8b\xcb\xab\xbf\xbe\x5e\xbf\xbf\xf8\x7c\x7e\xf6\xf5\x7c\x79\xb1\xbc\x01\x1f\x8e\x66\x33\xcf\x75\xbf\x7f\x77\xaa\x89\x75\x4d\xb2\x9c\xe1\x39\xcd\xa8\x5e\xad\x3c\x70\x5d\xb8\x20\xdf\x68\x56\x64\xc0\x8b\x2c\x40\x09\x22\xae\xa7\x6c\x99\x34\xa8\x12\xaf\xd6\x1a\x37\x57\xef\x3f\x7d\x5a\x9e\xee\x15\xa9\x6d\x3e\x44\xa5\x86\xee\x94\xf9\x7c\x75\x79\x7a\x76\x7d\x7d\x79\xb5\x57\x28\x97\x22\x44\xa5\x84\x1c\x22\xd5\x80\x7b\x62\xe3\x51\x5c\xf0\x50\x53\xc1\xa1\xc8\x23\xa2\xab\xbe\x55\x56\xa6\x92\xa9\xe9\xc2\x6a\x8b\x5c\x17\x3e\x51\xc6\x40\xa7\x08\x11\x51\x69\x20\x88\x8c\xe0\x91\xea\x14\x72\xa2\x74\x19\xb3\x42\xd2\xd8\x50\x9d\x0c\xb5\xa4\xa1\x82\xff\xf9\x3e\x14\x3c\xc2\x98\x72\x8c\xa6\x9b\x6d\xed\xba\x70\xca\x90\x48\xa0\x1c\x42\xa2\xb0\x1f\x9b\x28\x10\x39\x72\x8c\x20\xc0\x58\x48\x6c\x99\xbd\x13\xd0\x31\xe2\x4e\x35\x25\xc0\x87\xdb\x3b\xaf\x85\xf6\xe7\xd1\x21\xec\xce\xb0\xeb\x39\x73\x3b\xbb\x2b\xaf\x87\x09\xec\x63\xb5\xbc\xf6\x24\x07\x1f\x36\x0a\x56\x5b\xd4\xeb\x22\xdb\xcd\xeb\x82\x9b\x75\xaf\x53\xd7\xa5\x06\xaa\x20\x17\x4a\xd1\x80\x21\x68\x01\x09\x6a\x20\x5c\xe8\x14\x8d\xac\x52\x24\x41\x78\x4c\x29\x43\x88\x29\x63\x94\x27\x65\xf5\x89\x94\xe4\x49\x41\xf0\x04\x79\xa1\x52\x6b\x6a\x83\x12\x40\xb9\xd2\x48\xa2\x8e\x40\x5e\xe8\x92\x90\x52\xa5\xcd\x23\x68\x51\xfe\x0c\x30\xa1\x9c\x9b\x68\xc6\xdb\x4d\xc0\x96\x6a\x8e\x1b\xcb\x3c\x10\x35\x0f\x52\x3e\xa9\xc3\x90\x27\x3a\x85\xd7\x30\xf7\x80\xc2\x3b\x1f\x66\xf0\xe2\xc5\xce\x46\x7e\x77\x68\xe7\xeb\x38\x1e\xbc\x7e\x4d\x3b\x3e\xfb\x81\x63\x9c\x82\xab\x94\xc6\xda\xaa\x30\xb7\xf4\xce\xd1\x34\x43\x47\x15\x81\xd2\x92\xf2\xa4\x7f\xa3\xc9\xf7\x78\x3a\xf5\xba\x32\x87\xdc\xf6\x9c\x3a\x83\x9c\xba\x43\xb0\x9c\x9d\xc3\x92\xde\x1b\xad\xb1\xdc\xce\x80\xab\xfd\xed\x54\x4d\x17\x6b\x7a\xa8\x79\x76\x60\x24\xea\x42\xf2\x7a\x61\xb5\x31\x8f\x2a\x6c\x6f\xe8\x94\x0d\xb5\x7f\xe6\x18\xdc\x0f\x2d\x04\xbe\xef\xef\xf2\xdf\x4f\xb9\xaa\x2a\xd6\x4f\x19\x64\x37\x65\xd8\x5e\xff\x8c\xdc\x00\xf2\x81\x6d\xdc\xcc\xb8\x9c\x12\x6d\xe1\xb7\x0c\xdd\xbb\xb3\xc7\xd1\x07\x8b\xf2\x3c\x12\xc3\x8a\xd8\xd7\xda\x72\xf7\xc0\x7a\x36\x71\x9a\x5e\xd9\x0e\xf5\x0c\xdd\xb1\x6a\xbf\x32\x7f\x2b\x3f\x4a\xcd\x51\x41\xcd\x67\xa8\x32\x03\x39\x15\x2c\x2a\x87\x70\x58\x48\x89\x5c\x97\x2f\x50\x85\x5a\x8f\xe6\xe6\xa4\xad\x5e\xdc\x15\xca\x07\x94\xeb\x2f\x84\x8f\x65\xef\x00\x81\x0c\x75\x2a\x22\x13\x4e\x62\x28\x38\xc7\x50\x43\x91\x0b\x5e\xe3\x81\x09\xa5\xaa\x00\xed\x7d\x1f\xd6\x6f\x14\x56\xd3\x32\x35\xbc\xfa\x1a\xf8\x82\xc1\xb5\x08\xef\x51\x5b\x93\x47\xb5\x70\xdd\x09\xbc\x02\x26\x42\x62\x28\x4e\x2a\x94\x86\x57\x30\x71\x49\x4e\x27\xd3\xe6\xcc\xac\x02\x38\x82\xaf\x4f\xaf\x0d\x15\x7c\x40\xae\x3b\xdd\x59\x9e\xae\x2a\x01\x1f\xfe\xb8\xbe\xfc\xd3\xc9\x89\x54\x58\xc1\xca\x1d\xda\xac\x32\x8d\xc1\x2a\xa1\xbe\x0f\xbc\x60\x6c\xab\xcb\x3b\x03\xa8\x33\x84\xcc\xdf\xd6\x3b\x53\x6f\x50\x35\x79\x87\x4c\x28\xec\xd5\x06\x14\xea\x1b\x9a\xa1\x28\xb4\xd5\xd4\xcf\x86\x37\xb3\xd9\x6c\xea\xc1\xca\x6b\x37\xf8\x4c\x69\x12\x30\xaa\x52\x20\xf0\x88\x81\x2a\xcb\x07\x35\xc3\xbc\xbb\xd5\x27\xf0\xfb\xcf\xcb\x5a\x72\x3c\x6a\x22\x5a\x53\xef\xef\x00\x00\x00\xff\xff\x34\xdc\x83\x8a\xc5\x10\x00\x00") - -func jsHandlersJsBytes() ([]byte, error) { - return bindataRead( - _jsHandlersJs, - "js/handlers.js", - ) -} - -func jsHandlersJs() (*asset, error) { - bytes, err := jsHandlersJsBytes() - if err != nil { - return nil, err - } - - info := bindataFileInfo{name: "js/handlers.js", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)} - a := &asset{bytes: bytes, info: info} - return a, nil -} - // Asset loads and returns the asset for the given name. // It returns an error if the asset could not be found or // could not be loaded. @@ -161,8 +161,8 @@ func AssetNames() []string { // _bindata is a table, holding each asset generator, mapped to its name. var _bindata = map[string]func() (*asset, error){ + "components/dashboard.js": componentsDashboardJs, "dashboard.html": dashboardHtml, - "js/handlers.js": jsHandlersJs, } // AssetDir returns the file names below a certain @@ -205,10 +205,10 @@ type bintree struct { Children map[string]*bintree } var _bintree = &bintree{nil, map[string]*bintree{ - "dashboard.html": &bintree{dashboardHtml, map[string]*bintree{}}, - "js": &bintree{nil, map[string]*bintree{ - "handlers.js": &bintree{jsHandlersJs, map[string]*bintree{}}, + "components": &bintree{nil, map[string]*bintree{ + "dashboard.js": &bintree{componentsDashboardJs, map[string]*bintree{}}, }}, + "dashboard.html": &bintree{dashboardHtml, map[string]*bintree{}}, }} // RestoreAsset restores an asset under the given directory diff --git a/dashboard/assets/components/dashboard.js b/dashboard/assets/components/dashboard.js new file mode 100644 index 0000000000..956b5d4e79 --- /dev/null +++ b/dashboard/assets/components/dashboard.js @@ -0,0 +1,282 @@ +Chart.defaults.global.legend = { enabled: false }; + +const Component = React.Component; +// const Component = Inferno.Component; + +// isNullOrUndefined returns true if a variable is null or undefined. +let isNullOrUndefined = variable => variable === null || typeof variable === 'undefined'; + +// MenuItem renders an item for a Menu component and the belonging submenu items, if there is any. +class MenuItem extends Component { + render = () =>
  • + + + {this.props.text} +
    + + { + // Render dropdown menu only if there are children. + isNullOrUndefined(this.props.children) ||
      + {React.Children.map(this.props.children, child =>
    • {child}
    • )} +
    + } +
  • ; +} + +// Menu renders a menu component. +let Menu = () => ; + +let Clearfix = () =>
    ; + +// SideBar renders a sidebar component. +let SideBar = () =>
    +
    + + + +
    +
    ; + +// TopNavigation renders a top navigation component. +let TopNavigation = () =>
    + +
    ; + +// Chart name is already in use. +// ChartComponent renders a chart component and updates it, when the related data changes. +class ChartComponent extends Component { + constructor(props) { + super(props); + this.state = {}; + } + + componentDidMount = () => this.state.chart = new Chart(this.data, { + type: this.props.type, + data: this.props.data, + }); + + render = () => { + if (!isNullOrUndefined(this.state.chart)) { + this.state.chart.update(); + } + + return ( +
    +
    +
    +

    {this.props.text}

    +
      +
    • + { + // Render dropdown menu only if there are children. + isNullOrUndefined(this.props.children) ||
    • + +
        + {React.Children.map(this.props.children, child =>
      • {child}
      • )} +
      +
    • + } +
    • +
    • +
    + +
    +
    + {/* The chart will be generated here after the component is mounted (this.componentDidMount). */} + { + this.data = data + }}/> +
    +
    +
    + ); + } +} + +// Row renders a row component of charts only if there is any chart. +class Row extends Component { + render = () => isNullOrUndefined(this.props.children) ||
    {this.props.children}
    ; +} + +// PageContent renders a component for the page content. +class PageContent extends Component { + render = () =>
    +
    +
    +
    +

    Go Ethereum Dashboard + Statistics +

    +
    +
    + + + + Settings 1 + Settings 2 + + + + +
    +
    ; +} + +// Footer renders a footer. +let Footer = () =>
    +
    + Gentelella - Bootstrap Admin Template by Colorlib +
    + +
    ; + +const MEMORY_SAMPLE_LIMIT = 200; // Maximum number of memory data samples. +const TRAFFIC_SAMPLE_LIMIT = 200; // Maximum number of traffic data samples. + +// Dashboard renders a full dashboard component, which makes connection with the server and waits for messages. +// When there is a message, correspondingly updates the page's content. +class Dashboard extends Component { + constructor(props) { + super(props); + this.state = { + charts: { + memory: { + labels: [], + datasets: [{ + label: "system/memory/inuse", + backgroundColor: "rgba(38, 185, 154, 0.31)", + borderColor: "rgba(38, 185, 154, 0.7)", + pointBorderColor: "rgba(38, 185, 154, 0.7)", + pointBackgroundColor: "rgba(38, 185, 154, 0.7)", + pointHoverBackgroundColor: "#fff", + pointHoverBorderColor: "rgba(220,220,220,1)", + pointBorderWidth: 1, + data: [], + }], + }, + traffic: { + labels: [], + datasets: [{ + label: "p2p/InboundTraffic", + backgroundColor: "rgba(3, 88, 106, 0.3)", + borderColor: "rgba(3, 88, 106, 0.70)", + pointBorderColor: "rgba(3, 88, 106, 0.70)", + pointBackgroundColor: "rgba(3, 88, 106, 0.70)", + pointHoverBackgroundColor: "#fff", + pointHoverBorderColor: "rgba(151,187,205,1)", + pointBorderWidth: 1, + data: [], + }], + }, + }, + }; + } + + updateCharts = msg => { + let memory = this.state.charts.memory; + let traffic = this.state.charts.traffic; + + // Fill the dashboard with the past data. metrics is set only in the first msg, after the connection is established. + if (msg.metrics !== undefined) { + // Clear the arrays to prevent data confusion with the previous connection. + memory.labels = []; + traffic.labels = []; + memory.datasets[0].data = []; + traffic.datasets[0].data = []; + + let mem = msg.metrics.memory; + let traff = msg.metrics.processor; // TODO (kurkomisi): !!! + + // Put the past data to the beginning of the arrays. This prevents confusion with the next msg data, which goes to the end. + for (let i = mem.length - 1; i >= 0 && MEMORY_SAMPLE_LIMIT > memory.labels.length; --i) { + memory.labels.unshift(mem[i].time.substring(mem[i].time.length - 5)); + traffic.labels.unshift(mem[i].time.substring(mem[i].time.length - 5)); + memory.datasets[0].data.unshift(mem[i].value); + traffic.datasets[0].data.unshift(traff[i].value); + } + + this.setState({charts: {memory: memory, traffic: traffic,}}); // Update the components. + return; + } + + // Put the new data to the end of the arrays. + if (msg.memory !== undefined) { + // Remove the first elements in case the samples' amount exceeds the limit. + if (memory.labels.length === MEMORY_SAMPLE_LIMIT) { + memory.labels.shift(); + traffic.labels.shift(); + memory.datasets[0].data.shift(); + traffic.datasets[0].data.shift(); + } + memory.labels.push(msg.memory.time.substring(msg.memory.time.length - 5)); + traffic.labels.push(msg.memory.time.substring(msg.memory.time.length - 5)); + memory.datasets[0].data.push(msg.memory.value); + traffic.datasets[0].data.push(msg.processor.value); + + this.setState({charts: {memory: memory, traffic: traffic,}}); // Update the components. + } + }; + + reconnect = () => { + let server = new WebSocket("ws://" + location.host + "/api"); + let that = this; + + server.onmessage = event => { + let msg = JSON.parse(event.data); + if (isNullOrUndefined(msg)) { + return; + } + that.updateCharts(msg); + }; + + server.onclose = () => setTimeout(that.reconnect, 3000); + }; + + componentDidMount = () => this.reconnect(); + + render = () =>
    +
    + + + +
    +
    +
    ; +} \ No newline at end of file diff --git a/dashboard/assets/dashboard.html b/dashboard/assets/dashboard.html index 5f25bf2512..b14965d859 100644 --- a/dashboard/assets/dashboard.html +++ b/dashboard/assets/dashboard.html @@ -1,179 +1,48 @@ - + Go Ethereum Dashboard - - - -
    -
    -
    -
    - - -
    - - - - -
    -
    - - -
    - -
    - - - -
    -
    -
    -
    -

    Go Ethereum Dashboard Statistics

    -
    -
    - -
    - -
    -
    -
    -
    -

    Memory usage system/memory/inuse

    - -
    -
    -
    - -
    -
    -
    - -
    -
    -
    -

    Inbound traffic p2p/InboundTraffic

    - -
    -
    -
    - -
    -
    -
    -
    - -
    -
    -
    - - - -
    -
    - Gentelella - Bootstrap Admin Template by Colorlib -
    -
    -
    - -
    -
    + + + + - - + + + + + + + + + + + + + + + + + + + - + \ No newline at end of file diff --git a/dashboard/assets/js/handlers.js b/dashboard/assets/js/handlers.js deleted file mode 100644 index 91f14d0324..0000000000 --- a/dashboard/assets/js/handlers.js +++ /dev/null @@ -1,112 +0,0 @@ -Chart.defaults.global.legend = { - enabled: false -} - -// Line chart for memory -var ctx = document.getElementById("memoryLineChart"); -var memoryLineChart = new Chart(ctx, { - type: 'line', - data: { - labels: [], - datasets: [{ - label: "system/memory/inuse", - backgroundColor: "rgba(38, 185, 154, 0.31)", - borderColor: "rgba(38, 185, 154, 0.7)", - pointBorderColor: "rgba(38, 185, 154, 0.7)", - pointBackgroundColor: "rgba(38, 185, 154, 0.7)", - pointHoverBackgroundColor: "#fff", - pointHoverBorderColor: "rgba(220,220,220,1)", - pointBorderWidth: 1, - data: [] - }] - }, -}); - -// Line chart for traffic -var trafficCtx = document.getElementById("trafficLineChart"); -var trafficLineChart = new Chart(trafficCtx, { - type: 'line', - data: { - labels: [], - datasets: [{ - label: "p2p/InboundTraffic", - backgroundColor: "rgba(3, 88, 106, 0.3)", - borderColor: "rgba(3, 88, 106, 0.70)", - pointBorderColor: "rgba(3, 88, 106, 0.70)", - pointBackgroundColor: "rgba(3, 88, 106, 0.70)", - pointHoverBackgroundColor: "#fff", - pointHoverBorderColor: "rgba(151,187,205,1)", - pointBorderWidth: 1, - data: [] - }] - }, -}); - -//TODO (kurkomisi): remove static values after debugging -const MEMORY_SAMPLE_LIMIT = 200;//{{.memorySampleLimit}}; // Maximum number of memory data samples -const TRAFFIC_SAMPLE_LIMIT = 200;//{{.trafficSampleLimit}}; // Maximum number of traffic data samples -const PROCESSOR_SAMPLE_LIMIT = 200;//{{.processorSampleLimit}}; // Maximum number of processor data samples - -function updateCharts(msg) { - - // Fill the dashboard with past data - if(msg.metrics !== undefined) { - // Clear in case the dashboard was opened before - memoryLineChart.data.labels = []; - trafficLineChart.data.labels = []; - memoryLineChart.data.datasets[0].data = []; - trafficLineChart.data.datasets[0].data = []; - - var memory = msg.metrics.memory; - var processor = msg.metrics.processor; - // It is possible to get another message while filling the arrays by push(), so instead - // put the history to the beginning of the arrays - for (var i = memory.length - 1; i >= 0 && MEMORY_SAMPLE_LIMIT > memoryLineChart.data.labels.length; --i) { - memoryLineChart.data.labels.unshift(memory[i].time.substring(memory[i].time.length - 5)); - trafficLineChart.data.labels.unshift(memory[i].time.substring(memory[i].time.length - 5)); - memoryLineChart.data.datasets[0].data.unshift(memory[i].value); - trafficLineChart.data.datasets[0].data.unshift(processor[i].value); - } - memoryLineChart.update(); - trafficLineChart.update(); - return; - } - - // update - if(msg.memory !== undefined) { - if(memoryLineChart.data.labels.length === MEMORY_SAMPLE_LIMIT) { - memoryLineChart.data.labels.shift(); - trafficLineChart.data.labels.shift(); - memoryLineChart.data.datasets[0].data.shift(); - trafficLineChart.data.datasets[0].data.shift(); - } - memoryLineChart.data.labels.push(msg.memory.time.substring(msg.memory.time.length - 5)); - trafficLineChart.data.labels.push(msg.memory.time.substring(msg.memory.time.length - 5)); - memoryLineChart.data.datasets[0].data.push(msg.memory.value); - trafficLineChart.data.datasets[0].data.push(msg.processor.value); - memoryLineChart.update(); - trafficLineChart.update(); - } -} - -// Global variables to hold the current status of the dashboard -var server; - -// Define a method to reconnect upon server loss -var reconnect = function() { - server = new WebSocket("ws://" + location.host + "/api"); - - server.onmessage = function(event) { - var msg = JSON.parse(event.data); - if (msg === null) { - return; - } - - updateCharts(msg) - } - - server.onclose = function() { setTimeout(reconnect, 3000); }; -} - -// Establish a websocket connection to the API server -reconnect(); \ No newline at end of file diff --git a/dashboard/dashboard.go b/dashboard/dashboard.go index d887961bf2..989495b592 100644 --- a/dashboard/dashboard.go +++ b/dashboard/dashboard.go @@ -48,13 +48,13 @@ type dashboard struct { config *Config listener net.Listener - conns []*client // Currently live websocket connections - Metrics *metricSamples `json:"metrics,omitempty"` - Stats *status `json:"stats,omitempty"` - lock sync.RWMutex // Lock protecting the dashboard's internals + conns map[uint32]*client // Currently live websocket connections + Metrics *metricSamples `json:"metrics,omitempty"` + Stats *status `json:"stats,omitempty"` + lock sync.RWMutex // Lock protecting the dashboard's internals - closing map[*chan chan error]bool // Channels used for graceful exit - mapLock sync.RWMutex // Lock protecting the closing map's internals + closing chan chan error // Channel used for graceful exit + wg sync.WaitGroup } type client struct { @@ -81,9 +81,10 @@ type status struct { // New creates a new dashboard instance with the given configuration. func New(config *Config) (*dashboard, error) { return &dashboard{ + conns: make(map[uint32]*client), config: config, Metrics: &metricSamples{}, - closing: make(map[*chan chan error]bool), + closing: make(chan chan error), }, nil } @@ -95,6 +96,7 @@ func (db *dashboard) APIs() []rpc.API { return nil } // Start implements node.Service, starting the data collection thread and the listening server of the dashboard. func (db *dashboard) Start(server *p2p.Server) error { + db.wg.Add(1) go db.collectData() http.HandleFunc("/", db.webHandler) @@ -106,20 +108,13 @@ func (db *dashboard) Start(server *p2p.Server) error { } db.listener = listener - go func() { - if err := http.Serve(listener, nil); err != nil { - log.Warn("Server failed", "err", err) - } - }() + go http.Serve(listener, nil) return nil } // Stop implements node.Service, stopping the data collection thread and the connection listener of the dashboard. func (db *dashboard) Stop() error { - db.lock.Lock() - defer db.lock.Unlock() - var err error // Close the connection listener if err = db.listener.Close(); err != nil { @@ -127,18 +122,19 @@ func (db *dashboard) Stop() error { } errc := make(chan error) - for closing := range db.closing { - *closing <- errc - <-errc - close(*closing) - } + db.closing <- errc + <-errc + db.lock.Lock() for _, c := range db.conns { if err := c.conn.Close(); err != nil { c.logger.Warn("Failed to close connection", "err", err) } } - db.conns = db.conns[:0] + db.lock.Unlock() + + db.wg.Wait() + log.Info("Dashboard stopped") return err } @@ -179,31 +175,28 @@ func (db *dashboard) webHandler(w http.ResponseWriter, r *http.Request) { // apiHandler handles requests for the dashboard. func (db *dashboard) apiHandler(conn *websocket.Conn) { + id := atomic.AddUint32(&nextId, 1) client := &client{ conn: conn, msg: make(chan *map[string]interface{}, 128), - logger: log.New("id", atomic.AddUint32(&nextId, 1)), + logger: log.New("id", id), } - loss := make(chan int) + loss := make(chan bool, 1) // Buffered channel as sender may exit early // Start listening for messages to send. + db.wg.Add(1) go func() { - closing := db.addClosing() - defer db.removeClosing(closing) + defer db.wg.Done() for { select { - case errc := <-*closing: - errc <- nil - return - case val := <-loss: - loss <- val - 1 + case <-loss: return case msg := <-client.msg: if err := websocket.JSON.Send(client.conn, msg); err != nil { client.logger.Warn("Failed to send the message", "msg", msg, "err", err) - // TODO (kurkomisi): Handle message loss + return } } } @@ -216,36 +209,18 @@ func (db *dashboard) apiHandler(conn *websocket.Conn) { // Start tracking the connection and drop at connection loss. db.lock.Lock() - db.conns = append(db.conns, client) + db.conns[id] = client db.lock.Unlock() - - go func() { - closing := db.addClosing() - defer db.removeClosing(closing) - - select { - case errc := <-*closing: - errc <- nil - case val := <-loss: - loss <- val - 1 - db.lock.Lock() - for i, c := range db.conns { - if c.conn == client.conn { - db.conns = append(db.conns[:i], db.conns[i+1:]...) - break - } - } - db.lock.Unlock() - } + defer func() { + db.lock.Lock() + delete(db.conns, id) + db.lock.Unlock() }() for { fail := []byte{} if _, err := conn.Read(fail); err != nil { - loss <- 2 - if val := <-loss; val > 0 { - loss <- val - } + loss <- true return } // Ignore all messages @@ -254,12 +229,11 @@ func (db *dashboard) apiHandler(conn *websocket.Conn) { // collectData collects the required data to plot on the dashboard. func (db *dashboard) collectData() { - closing := db.addClosing() - defer db.removeClosing(closing) + defer db.wg.Done() for { select { - case errc := <-*closing: + case errc := <-db.closing: errc <- nil return case <-time.After(db.config.Refresh): @@ -283,10 +257,7 @@ func (db *dashboard) collectData() { // update updates the dashboards through the live websocket connections. func (db *dashboard) update(processor *data, memory *data) { - db.lock.Lock() - defer db.lock.Unlock() - - // if the samples' # exceeds the limit, just remove the first element + // Remove the first elements in case the samples' amount exceeds the limit. first := 0 if len(db.Metrics.Processor) == processorSampleLimit { first = 1 @@ -303,6 +274,7 @@ func (db *dashboard) update(processor *data, memory *data) { "memory": memory, } + db.lock.Lock() for _, c := range db.conns { select { case c.msg <- msg: @@ -310,18 +282,5 @@ func (db *dashboard) update(processor *data, memory *data) { c.logger.Warn("Client message queue is full") } } -} - -func (db *dashboard) addClosing() *chan chan error { - closing := make(chan chan error) - db.mapLock.Lock() - db.closing[&closing] = true - db.mapLock.Unlock() - return &closing -} - -func (db *dashboard) removeClosing(closing *chan chan error) { - db.mapLock.Lock() - delete(db.closing, closing) - db.mapLock.Unlock() + db.lock.Unlock() }