Browse Source

Make cache persistent

tags/v1.2
Moritz Marquardt 6 months ago
parent
commit
773701ea88
4 changed files with 549 additions and 9 deletions
  1. 1
    0
      .dockerignore
  2. 1
    0
      .gitignore
  3. 13
    1
      notify.go
  4. 534
    8
      web/data.go

+ 1
- 0
.dockerignore View File

@@ -1,3 +1,4 @@
chihuahua
chihuahua.yml
Dockerfile
cache.json

+ 1
- 0
.gitignore View File

@@ -1,3 +1,4 @@
/chihuahua
/chihuahua.yml
/web/data.go
/cache.json

+ 13
- 1
notify.go View File

@@ -15,9 +15,15 @@ import (
"gopkg.in/gomail.v2"
)

var cache = map[string]types.CheckStatus{}
var cache map[string]types.CheckStatus

func Notify(settings []map[string]string, servers map[string]*types.Server) {
if cache == nil {
cache = map[string]types.CheckStatus{}
data, _ := ioutil.ReadFile("cache.json")
json.Unmarshal(data, &cache)
}

logg.Tag("notify").Debug("Processing notifications...")
for serverName, server := range servers {
for checkName, check := range server.Checks {
@@ -31,6 +37,12 @@ func Notify(settings []map[string]string, servers map[string]*types.Server) {
cache[serverName+"/"+checkName] = check.Status
}
}

data, _ := json.Marshal(cache)
err := ioutil.WriteFile("cache.json", data, 0644)
if err != nil {
logg.Tag("notify", "cache").Warn("Couldn't save cache: %s", err)
}
}

func send(settings []map[string]string, checkName string, check *types.Check) {

+ 534
- 8
web/data.go View File

@@ -1,15 +1,541 @@
// Package web Code generated by go-bindata. (@generated) DO NOT EDIT.
// sources:
// web/data.go
// web/favicon.ico
// web/index.html
// web/logo.svg
// web/script.js
// web/icons/alert-triangle-outline.svg
// web/icons/checkmark-outline.svg
// web/icons/close-outline.svg
// web/icons/hard-drive-outline.svg
// web/icons/question-mark-outline.svg
package web

/////////////////////////////////////
// WARNING //
/////////////////////////////////////
// This is a placeholder file! //
// Run "go generate" in the parent //
// directory to generate contents. //
/////////////////////////////////////
import (
"bytes"
"compress/gzip"
"fmt"
"net/http"
"io"
"io/ioutil"
"os"
"path/filepath"
"strings"
"time"
)

import "net/http"
func bindataRead(data []byte, name string) ([]byte, error) {
gz, err := gzip.NewReader(bytes.NewBuffer(data))
if err != nil {
return nil, fmt.Errorf("Read %q: %v", name, err)
}

var buf bytes.Buffer
_, err = io.Copy(&buf, gz)
clErr := gz.Close()

if err != nil {
return nil, fmt.Errorf("Read %q: %v", name, err)
}
if clErr != nil {
return nil, err
}

return buf.Bytes(), nil
}

type asset struct {
bytes []byte
info os.FileInfo
}

type bindataFileInfo struct {
name string
size int64
mode os.FileMode
modTime time.Time
}

// Name return file name
func (fi bindataFileInfo) Name() string {
return fi.name
}

// Size return file size
func (fi bindataFileInfo) Size() int64 {
return fi.size
}

// Mode return file mode
func (fi bindataFileInfo) Mode() os.FileMode {
return fi.mode
}

// Mode return file modify time
func (fi bindataFileInfo) ModTime() time.Time {
return fi.modTime
}

// IsDir return file whether a directory
func (fi bindataFileInfo) IsDir() bool {
return fi.mode&os.ModeDir != 0
}

// Sys return file is sys mode
func (fi bindataFileInfo) Sys() interface{} {
return nil
}


type assetFile struct {
*bytes.Reader
name string
childInfos []os.FileInfo
childInfoOffset int
}

type assetOperator struct{}

// Open implement http.FileSystem interface
func (f *assetOperator) Open(name string) (http.File, error) {
var err error
if len(name) > 0 && name[0] == '/' {
name = name[1:]
}
content, err := Asset(name)
if err == nil {
return &assetFile{name: name, Reader: bytes.NewReader(content)}, nil
}
children, err := AssetDir(name)
if err == nil {
childInfos := make([]os.FileInfo, 0, len(children))
for _, child := range children {
childPath := filepath.Join(name, child)
info, errInfo := AssetInfo(filepath.Join(name, child))
if errInfo == nil {
childInfos = append(childInfos, info)
} else {
childInfos = append(childInfos, newDirFileInfo(childPath))
}
}
return &assetFile{name: name, childInfos: childInfos}, nil
} else {
// If the error is not found, return an error that will
// result in a 404 error. Otherwise the server returns
// a 500 error for files not found.
if strings.Contains(err.Error(), "not found") {
return nil, os.ErrNotExist
}
return nil, err
}
}

// Close no need do anything
func (f *assetFile) Close() error {
return nil
}

// Readdir read dir's children file info
func (f *assetFile) Readdir(count int) ([]os.FileInfo, error) {
if len(f.childInfos) == 0 {
return nil, os.ErrNotExist
}
if count <= 0 {
return f.childInfos, nil
}
if f.childInfoOffset+count > len(f.childInfos) {
count = len(f.childInfos) - f.childInfoOffset
}
offset := f.childInfoOffset
f.childInfoOffset += count
return f.childInfos[offset : offset+count], nil
}

// Stat read file info from asset item
func (f *assetFile) Stat() (os.FileInfo, error) {
if len(f.childInfos) != 0 {
return newDirFileInfo(f.name), nil
}
return AssetInfo(f.name)
}

// newDirFileInfo return default dir file info
func newDirFileInfo(name string) os.FileInfo {
return &bindataFileInfo{
name: name,
size: 0,
mode: os.FileMode(2147484068), // equal os.FileMode(0644)|os.ModeDir
modTime: time.Time{}}
}

// AssetFile return a http.FileSystem instance that data backend by asset
func AssetFile() http.FileSystem {
return &assetOperator{}
}

var _dataGo = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x01\x00\x00\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00")

func dataGoBytes() ([]byte, error) {
return bindataRead(
_dataGo,
"data.go",
)
}

func dataGo() (*asset, error) {
bytes, err := dataGoBytes()
if err != nil {
return nil, err
}

info := bindataFileInfo{name: "data.go", size: 0, mode: os.FileMode(420), modTime: time.Unix(1554895319, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}

var _faviconIco = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\x9a\x4d\x6b\x13\x5f\x18\xc5\x4f\x48\x43\xfe\x8b\x69\x92\xbf\x08\x51\x10\x93\x45\x0a\x41\x37\xa9\x8b\xa6\xae\xe2\xa2\x6b\x41\x41\x9a\x58\x2a\x09\x16\x69\x02\x6d\x97\x8a\x9b\x04\xa4\xba\xec\x27\x28\xee\xc4\xa5\x5b\x37\x12\x11\x74\xe1\xa2\x1f\x40\x90\xe8\x27\x88\x46\x24\x42\xcc\x91\x3b\xf7\x8e\xa6\xa6\x79\x69\xc4\x79\xa9\xcf\x0f\x2e\x87\xb9\x9d\xb9\xcf\x39\x43\x21\x77\x9e\x19\x20\x84\x30\x72\x39\xa5\x69\x3c\x5d\x00\x96\x00\xa4\xd3\xe6\x38\x01\xbc\x5f\x00\x12\x09\x7d\xbc\x39\x07\x3c\x5c\x02\xb2\x00\x72\x00\xca\xd0\xf3\x36\x19\x08\x82\x20\x08\x82\x20\x08\x82\x4d\x39\xb3\xde\x54\xc3\x6b\x1f\xb3\xa0\x7c\x17\x61\x51\x8d\xa0\x65\x18\xf4\x1e\xb4\x0c\x45\x58\x8d\xdf\xbd\xab\x71\x3b\xbf\xcb\xed\x6d\x36\xbc\xf6\x37\x8e\x09\xde\x9d\xe1\xcb\x0c\x53\x7a\xf7\x65\x86\x51\xde\xd5\xbc\xf2\x7a\x84\x7f\xdf\x64\x18\xe7\xdd\x39\xc7\xcf\x19\x46\xf8\x1f\xf2\x15\xa0\x0c\x23\xfd\x04\x20\xc3\x44\x1f\x7e\xce\x30\x2d\x92\xc1\x1f\x48\x06\x7f\x20\x19\xfc\xc1\x09\xce\x10\x18\xff\x18\xce\x10\x28\xef\x0e\x26\x43\x20\xbd\x0b\x82\x20\x08\x82\x20\x08\xff\x16\xb4\x71\x43\x0f\x8c\x3e\x32\x1a\xd6\xda\x0f\x69\xed\x41\x6b\x17\x75\x5b\xdb\x28\xd8\xda\xca\xa5\xb4\xde\x8b\xeb\xf9\x5e\xd4\xd6\x6f\xfd\xb0\x59\x3f\x64\xb4\x61\xb4\x59\xd7\xda\x2a\x68\x6d\xa7\xb4\x76\xe3\x5a\x7b\x51\xad\x7d\xa3\x34\xf3\x7c\xec\xca\x7d\x38\xac\x59\xc0\xfe\x3a\xeb\xca\xe0\x77\x5a\x09\xaf\xff\x2b\x04\x41\x08\x32\x1b\x97\x1a\xcb\x5e\xd5\xae\x2c\xde\xad\x96\x22\xc9\xbe\x17\xef\xb1\x6f\xa5\xae\x5f\x2b\x45\xce\xf4\x7f\xbe\x13\x5d\xbc\xff\xcc\xad\xda\x25\xcc\x5f\x2e\xc1\xfa\xec\xd4\x5e\x3f\xbb\xc2\xad\xea\x17\x57\xfa\x5a\xab\x88\x2d\x17\x61\x7d\x72\x6a\xdf\x8c\x5f\xfc\xb8\xb5\xd9\x71\xa5\x2f\x58\x44\x2c\x3f\x58\xbb\x08\xeb\xf5\x55\x9c\x9e\x3f\xa2\x3f\xf9\x57\x3c\x98\xfa\x6d\x53\xfb\xcd\x1a\x4e\xc5\x9c\xbf\xb9\xec\xe1\xf9\x60\x6d\xb7\x3d\x8c\x43\x3c\x88\x87\x09\x1e\x2a\x1e\x7a\x78\x55\xab\xd1\x72\xbb\xbe\xf1\x50\xf1\xaa\xb6\x20\x08\xc2\x34\x70\x1a\x3a\x24\x3f\x90\x6c\x92\x6c\xd4\xc9\xff\x0a\xe4\xb9\x14\xf9\x22\x4e\x32\x4a\xf2\x01\xc9\x97\x24\x5b\x24\xdb\x24\xbb\x53\x2d\xab\xc8\x9a\xb6\x44\x7a\xb0\x4f\x31\xe7\xf5\x5d\x09\x06\x1b\xf9\xdd\x64\xf9\x42\x6d\x7f\x96\x6b\xd5\x3e\x7a\xcd\xca\xbc\x2b\x45\x92\xac\xae\xbe\xdd\x3b\xce\xb5\x6a\x2f\xaa\xf6\xc3\xea\x99\xe4\xce\xca\x93\xfd\xe3\xee\x35\x6e\xe0\xff\xb8\xda\xd3\xaa\x67\x0b\x1c\xfe\xbd\x9e\x79\xbf\xe2\xb3\x35\x3a\x3b\x3b\x3c\x3f\xeb\x1a\x7f\x72\xed\x49\x45\xf7\x36\x7f\x8d\xef\x04\xba\x04\xda\x75\xe0\x6b\x01\x60\x0a\xe0\x1e\xc0\x83\xe1\x73\xd5\xf8\x11\x00\x00\xff\xff\xdc\xb3\xfb\xe4\xee\x3a\x00\x00")

func faviconIcoBytes() ([]byte, error) {
return bindataRead(
_faviconIco,
"favicon.ico",
)
}

func faviconIco() (*asset, error) {
bytes, err := faviconIcoBytes()
if err != nil {
return nil, err
}

info := bindataFileInfo{name: "favicon.ico", size: 15086, mode: os.FileMode(420), modTime: time.Unix(1554826459, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}

var _indexHtml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xb4\x56\x4f\x73\xdb\xb6\x12\xbf\xe7\x53\x6c\xf0\xe6\xbd\xc4\x13\x83\x90\xfd\xde\x9b\x74\x68\x8a\x87\xa6\x39\xf4\x90\xb6\x33\xc9\xa5\x47\x10\x58\x11\xb0\x40\x80\x05\x40\xca\xae\xc7\xdf\xbd\x43\x40\x94\x68\x59\x76\x2f\x4d\x32\x63\x62\x85\xc5\xee\x6f\xff\xfd\x80\xea\xed\x4f\xbf\x7e\xfa\xf6\xfb\x6f\x9f\x41\xc5\xce\xd4\x6f\xaa\xfc\x01\x00\xa8\x14\x72\x99\x97\x49\x8c\x3a\x1a\xac\xbf\xa2\x1f\xd1\xc3\xd7\xc8\xe3\x10\x2a\x96\x7f\x3c\x2a\x19\x6d\xb7\xe0\xd1\xac\x49\x50\xce\x47\x31\x44\xd0\xc2\x59\x02\xca\xe3\x66\x4d\x36\x7c\x9c\xc4\x42\x0b\x47\xea\x37\xc7\x63\x1d\x46\x0e\x42\x71\x1f\x30\xae\xc9\x10\x37\xf4\x07\x52\x9f\x6c\x5b\xde\xe1\x9a\x8c\x1a\x77\xbd\xf3\x91\x80\x70\x36\xa2\x8d\x6b\xb2\xd3\x32\xaa\xb5\xc4\x51\x0b\xa4\x49\xb8\x04\x6d\x75\xd4\xdc\xd0\x20\xb8\xc1\xf5\xd5\x13\x67\x0b\x8c\xf1\xde\x60\x50\x88\x71\x06\xa8\x62\xec\x43\xc9\x98\x90\xf6\x36\x14\xc2\xb8\x41\x6e\x0c\xf7\x58\x08\xd7\x31\x7e\xcb\xef\x98\xd1\x4d\x60\xcd\x60\x3a\xce\x56\xc5\xc7\xe2\x7f\x4c\x84\xbd\x5c\x74\xda\x16\x22\x84\x25\xf2\xb7\x94\xc2\x37\xa5\x03\x04\x1d\x11\x74\x80\x21\x68\xdb\xc2\xe7\x91\xc3\xcf\xc2\xd9\x00\x14\x66\x97\xad\x8e\x6a\x68\xb2\xa3\xed\x88\x8e\xe1\xc8\xa9\xce\x4a\x74\x61\x32\x81\x3e\xca\xd3\x3f\xdd\xb5\x53\x4a\x2d\x3c\x80\x42\xdd\xaa\x58\xc2\x55\xf1\x11\xbb\x1b\x18\xd1\x47\x2d\xb8\xa1\xdc\xe8\xd6\x96\x10\x5d\x7f\x03\x1d\xf7\xad\xb6\x25\xd0\x55\x71\x8d\x1d\xac\xe6\xc5\xe3\x13\xa3\x85\xe2\x81\x46\xbc\x8b\x74\xa7\x12\xf8\xbd\x93\xcb\xbc\xd3\x70\xb1\x6d\xbd\x1b\xac\xa4\x8d\xe1\x62\xbb\x04\xb1\xd1\x26\xa2\x2f\x41\xdb\xc9\xff\xfb\xab\xd5\xea\xdf\x17\x37\xa7\xe6\xa7\xfa\x71\x6d\xd1\x97\x1b\xed\x43\xa4\x42\x69\x23\x6b\xe5\xe1\x01\xa4\x0e\xbd\xe1\xf7\x25\x58\x67\xf1\xf4\xdc\xc6\xb9\x88\x93\x56\xcf\xa5\xd4\xb6\xa5\x8d\x8b\xd1\x75\x25\xfc\xd7\x4f\x11\x37\xee\x8e\x06\xc5\xa5\xdb\x95\xb0\x82\xab\xd5\x6a\x54\x87\xef\xbf\x36\x7c\xfa\xbf\x84\x52\xb1\x45\x3e\x2b\x76\xec\xf9\xaa\x71\xf2\x7e\xd9\x83\x5c\x5b\x10\x86\x87\xb0\x26\x01\x45\xd4\x53\x57\x6b\xb9\x26\xbc\xef\xc9\xd3\x72\x54\xfb\xfd\x59\xfd\x10\x29\x81\xe4\x6c\x4d\x72\x05\x68\x74\x7d\x09\xd7\xd8\x11\x18\xe9\xc6\xf9\x35\x79\x1f\xd2\x78\x5d\x42\xfe\xfe\xc2\x3b\xbc\x00\x6d\xf7\x62\x20\x50\x6e\xf1\x7e\xf2\x3f\xef\x9e\x78\xce\x73\xeb\xcf\xfd\x78\x7d\x00\x3f\x34\x69\x6a\x4f\xc1\xcc\x69\x5c\x15\xff\xc7\x8e\xd4\x95\xee\xda\xf9\x48\x1e\xe1\xe0\x45\x5e\x06\xa6\xb8\x97\x54\x7a\x3d\x22\x75\x43\x34\xda\x62\x11\xc6\x96\xd4\x53\x77\x7a\x67\xdb\xfa\xe1\x61\x11\x02\x3c\x3e\x4e\x69\x4e\x1b\x15\x53\xd7\x67\xe0\x49\x3d\x1e\xb3\x65\x86\xce\x86\x03\xbc\x8d\xc1\x3b\xba\xf3\xbc\x2f\x61\xfa\x7b\x26\xe2\x64\x81\x4f\x8d\x6e\x70\xb6\xd2\x73\x8b\x06\xb2\x2d\xd0\x81\x3a\x8b\xf4\x8f\x81\xfb\xf8\xbc\x0a\x87\xc0\x8f\x65\x10\x0a\xc5\xf6\x12\xd2\xe7\xa4\x08\xcf\x6b\x00\x1f\xe0\x1d\x7b\x07\x1f\x8e\xea\x2f\x60\x4c\x38\xfb\x27\x08\xe9\xd4\x70\xda\xb6\xc7\x68\x9d\x8d\x74\xb7\x1f\xe2\xc6\x19\x49\xa0\xdc\x1f\x98\x54\xd1\xff\x78\x9f\xb9\x37\x63\x2c\x42\x12\x2e\x5e\xf1\x98\xbc\x3e\x2b\x66\x79\xa8\xe6\x4b\x06\x2b\xa6\x5f\x37\xfa\xf0\x70\x8c\x18\x1e\x1f\x5f\x0e\x99\xf5\xaf\xe4\x63\x51\xf9\x9c\x91\xc6\x38\xb1\x3d\xe4\x63\x3f\xe4\xfb\xea\xe8\xcd\x9a\x64\x98\x12\x23\xd7\x26\xfc\x5d\xdc\xbd\xc7\xd9\x52\x62\x31\x1a\x7a\x2e\xb0\x84\xde\x63\xea\xa9\x1b\x48\xed\xd5\xfa\x89\x2e\xae\x48\x5d\x09\x27\xb1\xae\x42\xc7\x8d\xa9\xe7\x00\x67\x67\x45\xf4\xba\x7b\x7f\x91\xdb\x39\x69\x54\x2c\xeb\xb3\xde\xe3\x2b\x31\x32\xa9\xc7\x7f\x3e\x05\xe8\xbd\xf3\xdf\x33\x01\x33\xa8\xc3\x45\x20\xb9\x6d\xd1\x93\x63\x62\x12\x84\xef\x90\x96\x8a\xed\xc7\xf9\x0c\x55\x3c\x3f\x55\xb1\x3d\xe1\x2e\xe8\x9a\x4d\x7c\x5d\x57\xcd\x82\x0b\xab\xfd\xcd\xb1\x8f\x2a\x4b\xa7\xd4\xfd\x84\x88\xd2\x03\x03\x0e\xe1\x0b\xb4\x11\x3d\xca\x73\xa4\xfb\x42\x8b\xa7\xc1\x4b\xa3\x66\x5c\xeb\x12\x49\x9e\x90\xea\x92\x8a\xd2\x95\x7c\x85\xdd\xe1\x66\x4e\xc2\x6a\xc1\xab\x15\x3f\x7d\xa8\x38\x89\x0d\xfa\xb6\x70\xbe\x65\x9d\xeb\xb8\x67\x42\x69\x35\x70\x35\x70\x52\x7f\x9a\x97\x15\xe3\x35\xfc\xa7\x19\x8c\xb9\x81\xa8\x10\x52\x9d\x30\x44\xd8\xf1\x28\x94\x74\x2d\x38\x0b\xc8\x7d\x54\x07\xa6\x86\xe6\x1e\x9e\xb9\xeb\x1c\xed\xb8\x2f\x24\x92\xfa\x8b\xf3\x3a\xfe\x09\x5f\xb8\x9f\x78\x55\xc6\xc9\xc5\x99\x6a\xf5\xa7\xb5\x7a\x52\xbe\x8a\xe5\x32\x2c\xdf\x67\x41\x78\xdd\xc7\x9c\xb5\xc5\x7b\xac\xb8\x0d\x12\x8d\x1e\x7d\x61\x31\x32\xdb\x77\x6c\x1c\x70\xa2\xa9\xac\x5f\x9f\x37\x90\xd7\xc5\x6d\x38\xd5\xac\x58\xbe\xe0\x2b\x96\x9f\xbc\x7f\x05\x00\x00\xff\xff\xdd\xd6\xcd\x91\x0a\x0b\x00\x00")

func indexHtmlBytes() ([]byte, error) {
return bindataRead(
_indexHtml,
"index.html",
)
}

func indexHtml() (*asset, error) {
bytes, err := indexHtmlBytes()
if err != nil {
return nil, err
}

info := bindataFileInfo{name: "index.html", size: 2826, mode: os.FileMode(420), modTime: time.Unix(1554838928, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}

var _logoSvg = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x6c\xd0\x41\x6f\xc2\x20\x14\x07\xf0\xbb\x9f\xe2\xe5\x79\x2d\x05\x8b\xd3\xa6\x2b\x35\xd9\xa2\xa7\xed\x0b\xec\xd6\x14\x2c\x64\xd8\x9a\xc2\x40\xb3\xec\xbb\x2f\xb5\x2e\x26\x53\x0e\x24\x3c\xf8\xc1\x9f\x57\x6e\x4e\x07\x0b\x41\x0d\xce\xf4\x9d\xc0\x45\xca\x10\x36\x55\xe9\x42\x0b\x5a\x99\x56\x7b\x81\xcb\x1c\xc1\x48\x81\x8d\x56\xcd\x27\x42\x30\x2a\xbe\xf4\x27\x81\x0c\x18\x2c\x73\x18\xb7\xa3\x91\x5e\x4f\x27\x4f\x07\xdb\x39\x81\xda\xfb\x63\x41\x69\x8c\x31\x8d\x3c\xed\x87\x96\x66\x8c\x31\xea\x42\x8b\x55\x29\xd5\xde\x55\xa5\xf3\x67\xab\xaa\x19\x5c\x46\x1a\x0c\x39\x0e\xe6\x50\x0f\x67\xf8\xbe\xd6\x00\xf6\xc6\xda\x02\xe6\xbb\xdd\x6a\xbb\xda\x3e\x5f\xcb\x3f\xb3\x7b\x93\x5c\x16\x75\xd3\xa8\xce\xff\xf3\x64\xf8\xb2\xaa\x00\x15\x54\xd7\x4b\xf9\xf0\x92\x87\xae\x80\x39\x7b\x65\xec\x29\xbf\x91\x71\x2e\xe9\x94\xbb\xa4\xd3\x2f\x8e\xb5\xd7\xd0\xd8\xda\x39\x81\xb7\x3c\x08\x52\xe0\xfb\x62\x95\xf0\xcc\xf2\x84\xbf\xf1\x75\xb2\x58\x5b\xc2\x09\xff\x40\x7a\x87\xa6\xf7\x27\x93\x65\xa3\x21\x3c\xe1\x24\x27\x79\xf2\x27\xc6\xce\x55\xbf\x01\x00\x00\xff\xff\x67\x0b\x1a\xfd\xad\x01\x00\x00")

func logoSvgBytes() ([]byte, error) {
return bindataRead(
_logoSvg,
"logo.svg",
)
}

func logoSvg() (*asset, error) {
bytes, err := logoSvgBytes()
if err != nil {
return nil, err
}

info := bindataFileInfo{name: "logo.svg", size: 429, mode: os.FileMode(420), modTime: time.Unix(1554826381, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}

var _scriptJs = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xac\x93\xc1\x6e\xdb\x30\x0c\x86\xef\x7e\x0a\x42\xbb\xd8\x80\xed\xa5\x01\x76\x49\xd0\x1d\xf6\x0a\x1b\x7a\x19\x76\x60\x65\xc6\xd6\xe2\x48\x1e\x49\xd5\x1b\x0a\xbf\xfb\xa0\xc4\x5d\xb3\xb6\x5e\x72\x18\x2f\x82\x4c\xfe\x9f\x7e\xfd\x86\x46\xe7\x9b\x30\xd6\x38\x0c\x70\x0b\x9e\x46\xb8\x8b\x94\x3f\x66\x00\x00\xd4\x6f\xc0\xbc\xc3\x61\x30\xe5\x71\xdf\xa0\xe2\x06\x4e\xbd\x54\x42\xfc\x40\x2c\x1b\x78\x9c\x4e\x03\xf3\x72\x20\xed\x42\x23\xe7\xa3\x1d\x61\x43\xfc\xe9\xd7\x67\x45\x8d\x92\xcb\x71\x29\xce\x06\x8e\xbc\xd1\xa9\xed\x60\xa1\x9b\xca\xa2\x10\xac\x36\xc0\xa4\x91\x3d\x7c\x35\x1d\x4a\x75\x8f\x76\xdf\x72\x88\xbe\xa9\x24\x5a\x4b\x22\xa6\x84\x63\x47\xe9\xa7\x56\x63\xe7\x94\xcc\xb7\xed\xdb\xb0\x9b\x65\xd8\x88\xec\x9d\x6f\x17\xa5\xeb\x65\x69\x83\xbe\x25\xbe\xce\x46\x43\x3b\x8c\xbd\x2e\xc3\xee\x7b\xb4\xfb\xbf\x59\x6f\x3b\x9b\xfe\xec\xe6\x1f\x91\xca\xd9\xe0\xff\x77\xee\x26\x41\xe5\xbd\xed\xc8\xee\x0f\xc8\xfb\x2a\x44\xed\x9d\xa7\x5a\x1e\x5a\x73\x31\xe8\x59\x8d\x3d\xb1\x56\xca\x0e\x7d\xdb\xd3\x35\x88\xf5\x2b\x03\x7d\x90\x4b\xca\x97\xf1\xce\xd2\x1f\x91\x44\x5d\xf0\xd5\x05\xff\xaf\x32\x9d\xca\x6c\x2a\xb6\x59\xb6\x8b\xde\x26\x00\xc4\xa1\x41\xa5\xfc\x29\xb6\x1d\xa9\xed\x72\x73\x0a\x47\x4c\x51\x6b\x47\x3e\x67\xb8\xfd\x08\x5c\x7f\x97\xe0\xf3\xe2\xfc\xdb\x73\xd6\x77\x91\x6a\x21\xcd\x71\x18\x4a\x30\xf3\xdb\x32\x25\x70\xb1\x3d\x7b\x71\xfa\xc5\x1d\x28\x44\xcd\x4f\xc7\x96\x70\xb3\x5a\xad\xe6\x89\xa9\xa8\x2d\xa6\xd3\x89\x5f\xb0\xd3\x95\x43\x4f\x35\x31\x07\x4e\xed\x7f\x33\xd7\x1f\x9e\x91\xd9\x94\x3d\xdd\x70\x9b\xfd\x0e\x00\x00\xff\xff\x4b\xa4\xbe\x4c\x2d\x04\x00\x00")

func scriptJsBytes() ([]byte, error) {
return bindataRead(
_scriptJs,
"script.js",
)
}

func scriptJs() (*asset, error) {
bytes, err := scriptJsBytes()
if err != nil {
return nil, err
}

info := bindataFileInfo{name: "script.js", size: 1069, mode: os.FileMode(420), modTime: time.Unix(1554838381, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}

var _iconsAlertTriangleOutlineSvg = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x6c\x90\xbd\x8e\xdb\x30\x10\x84\x5f\x65\xc0\x2a\x29\xb8\xe2\x2e\xa9\xbf\xc0\x34\x90\x54\x29\x9c\x36\x3d\x21\x2b\x92\x00\xd9\x32\x28\x42\xb2\xef\xe9\x03\xc9\x97\x5c\xee\x90\x82\x03\x72\x66\xf6\x03\xb1\x87\x79\xe9\x70\xbf\x8c\xd7\xd9\xab\x3e\xa5\xdb\x97\x2c\x5b\xd7\x95\x56\x4b\x53\xec\x32\x31\xc6\x64\xf3\xd2\x29\x2c\x43\xbb\x7e\x9b\xee\x5e\x19\x18\x88\x83\x38\x75\x3c\x74\x38\x87\x14\xf4\x35\x5c\x5a\xaf\x4e\xe1\xd1\x46\xc8\x47\x3b\x8c\x6d\x4c\x3a\xc5\x21\x5c\xbb\xb1\x55\xc7\x43\x6c\x9b\x84\x75\x38\xa7\xde\x2b\x71\x0a\x7d\x3b\x74\x7d\x7a\xde\x53\x0c\xd7\xf9\xd7\x14\x2f\x5e\xc5\x29\x85\xd4\x7e\xaa\x0d\x58\xc0\xf2\x59\x61\xba\x85\x66\x48\x0f\xaf\x8c\xca\x8e\x87\x5b\x48\x3d\xce\x5e\xfd\x10\xa1\xbc\x00\x17\x64\x4f\xec\xa8\xaa\x61\x29\xaf\x82\x25\x67\xb1\xcb\xf6\x61\xa3\x73\x2a\x2b\x98\x13\x93\x73\x7b\x37\x58\xfc\x89\xc8\xe4\xb0\x5f\x2d\xd9\x12\xbb\xec\x2e\x1c\x59\x0b\xe1\x9e\x73\xb2\x2e\x7c\x4c\x85\x6a\xa7\x99\x8a\x02\xef\x38\xda\x92\x71\x2f\x17\xcd\x54\x42\xc8\xe4\x81\xc9\x32\x76\xd9\x3a\xac\x99\xb8\xa6\x22\xff\xbe\xd1\xff\x1b\x6a\x2a\x72\x30\x5e\x2d\x18\xcd\x63\x49\x45\xa5\x59\xa8\xdc\x26\x5c\x85\x5d\x9e\xb1\x90\x2d\x60\xb6\x46\x89\xad\x21\xe1\x6d\x94\xcc\x06\x37\xfc\xb2\x6d\xab\x19\x62\x33\xb6\x68\xee\x5e\xb1\x28\x34\x0f\xaf\xb8\x50\x88\x5e\xf1\xbb\x65\xb2\xa0\xfa\xcb\x30\x9a\xc1\x8b\x7b\x7b\x43\x60\x7e\xd6\xff\xe6\xfa\x89\xcf\xba\xd7\x33\x2f\xdd\xf1\x77\x00\x00\x00\xff\xff\x05\xd7\xcf\x31\x53\x02\x00\x00")

func iconsAlertTriangleOutlineSvgBytes() ([]byte, error) {
return bindataRead(
_iconsAlertTriangleOutlineSvg,
"icons/alert-triangle-outline.svg",
)
}

func iconsAlertTriangleOutlineSvg() (*asset, error) {
bytes, err := iconsAlertTriangleOutlineSvgBytes()
if err != nil {
return nil, err
}

info := bindataFileInfo{name: "icons/alert-triangle-outline.svg", size: 595, mode: os.FileMode(420), modTime: time.Unix(1548652600, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}

var _iconsCheckmarkOutlineSvg = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x5c\x8e\x4d\x6e\xac\x30\x10\x84\xaf\xd2\xea\xbd\xdb\x6e\xdb\x8f\x9f\x27\xcc\x22\xeb\xe4\x10\x16\x58\x36\x1a\x18\x10\x58\x78\x26\xa7\x8f\x50\x66\x33\x59\x74\xab\x54\x55\xd2\x57\xdd\x71\x46\x78\x2c\xf3\xfd\x70\x98\x72\xde\xfe\x4b\x59\x4a\xa1\x62\x68\xdd\xa3\xd4\x4a\x29\x79\x9c\x11\xe1\x9c\x42\xf9\x58\x1f\x0e\x15\x28\xd0\x16\xb4\xc5\xbe\x8b\x30\xfa\xec\xc5\xdd\x2f\xc1\xe1\xa7\x7f\x86\x1d\xf4\x5f\x7b\x48\x61\xb8\x2d\x7e\xbf\x61\xdf\xed\x61\xc8\x50\xa6\x31\x27\x87\xda\x22\xa4\x30\xc5\x94\x7f\xf5\xba\xf9\x61\xca\x4f\x87\x0a\x65\xdf\x6d\x3e\x27\x18\x1d\x7e\xb5\xd4\x54\xc0\x8d\x67\x60\xb8\xd0\x2c\xa8\x36\x82\x8c\x9e\x85\xa5\xa6\x12\xff\x88\xeb\x57\x78\x7d\x26\x5b\x09\x26\x53\xcf\x96\x58\x83\x25\xd3\x42\x43\x96\x45\x4b\xfa\xbd\xd6\x00\x93\xb1\xb3\x68\x89\x2d\xb0\x7a\x07\x90\x31\xdf\xd7\x0c\x19\x5f\x77\x9c\xb1\xff\x09\x00\x00\xff\xff\xf2\x9a\x77\x4d\x2b\x01\x00\x00")

func iconsCheckmarkOutlineSvgBytes() ([]byte, error) {
return bindataRead(
_iconsCheckmarkOutlineSvg,
"icons/checkmark-outline.svg",
)
}

func iconsCheckmarkOutlineSvg() (*asset, error) {
bytes, err := iconsCheckmarkOutlineSvgBytes()
if err != nil {
return nil, err
}

info := bindataFileInfo{name: "icons/checkmark-outline.svg", size: 299, mode: os.FileMode(420), modTime: time.Unix(1548652600, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}

var _iconsCloseOutlineSvg = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x5c\x8f\xdd\x6a\xf4\x20\x10\x86\x6f\x65\x98\xa3\xef\x3b\x88\x8e\xc6\x42\xb7\xc4\x1c\xf4\x78\x7b\x11\xb2\x6b\x4d\x20\x89\x41\x87\xb8\xdb\xab\x2f\xf9\x69\xd9\x2d\x32\x2f\xfa\xf2\xf8\x88\x4d\x5e\x02\xdc\xc6\x61\xca\x16\x3b\xe6\xf9\x4d\xca\x52\x8a\x28\xb5\x88\x29\x48\x4d\x44\x32\x2f\x01\x61\xe9\x7d\x79\x8f\x37\x8b\x04\x04\xda\x80\x36\xd8\x36\x01\xae\x8e\x5d\x35\xb9\xd1\x5b\x3c\xbb\xbb\x4f\xa0\xff\xd6\x97\x21\x66\x8f\x6d\x93\xfc\x85\xa1\xf4\x57\xee\x2c\x6a\x83\xd0\xf9\x3e\x74\xbc\xef\x39\xb9\x29\x7f\xc6\x34\x5a\x4c\x91\x1d\xfb\x7f\xea\x95\x40\x69\x50\xfa\x3f\x42\x9c\xdd\xa5\xe7\xbb\x45\x42\xd9\x36\xb3\xe3\x0e\xae\x16\x3f\x54\x2d\x8c\x02\xa5\x07\x23\xea\xca\x08\x7d\x72\x0a\x14\xd0\x3a\x95\x12\x46\x6f\x71\x5e\x25\x24\x5e\x4e\xc3\x86\x54\x46\xd4\x07\x46\x07\x06\x6b\xac\x0e\xf8\x01\xe0\x41\xb6\xaf\x9d\xfb\x2d\xb6\x23\x0d\x4f\x3c\x3c\x99\x0f\xe4\xe1\xce\xfe\xd8\xd7\xfa\x03\x19\x8e\xc9\x4b\x68\xbf\x03\x00\x00\xff\xff\xb2\x9e\xbf\x56\x80\x01\x00\x00")

func iconsCloseOutlineSvgBytes() ([]byte, error) {
return bindataRead(
_iconsCloseOutlineSvg,
"icons/close-outline.svg",
)
}

func iconsCloseOutlineSvg() (*asset, error) {
bytes, err := iconsCloseOutlineSvgBytes()
if err != nil {
return nil, err
}

info := bindataFileInfo{name: "icons/close-outline.svg", size: 384, mode: os.FileMode(420), modTime: time.Unix(1548652600, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}

var _iconsHardDriveOutlineSvg = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x5c\x91\x4b\xcb\xe3\x20\x14\x86\xff\xca\xe1\xec\x3d\xf1\x78\x6b\x32\xc4\xc0\xcc\xaa\x8b\xaf\xdb\xd9\x4b\x12\x62\xa0\x37\x12\x31\x6d\x7f\xfd\xd0\x0b\x6d\x33\x88\xa0\xcf\xfb\x3e\x22\x5a\xcf\x79\x80\xcb\x61\x7f\x9c\x3d\xc6\x94\xce\xbf\x8a\x62\x59\x16\x5a\x34\x9d\xa6\xa1\x50\x52\xca\x62\xce\x03\x42\x1e\xfb\xe5\xcf\xe9\xe2\x51\x82\x04\x65\x40\x19\x6c\xea\x01\xba\x90\x82\x38\x86\x43\xef\xf1\x27\x5c\xfb\x09\xd4\xff\x38\x86\xa9\x13\xdd\x34\xe6\x1e\x9b\x7a\xea\xdb\x04\xcb\xd8\xa5\xe8\x51\x19\x84\xd8\x8f\x43\x4c\xcf\xf5\xe9\x1c\xda\x31\x5d\x3d\x4a\x2c\x9a\xfa\x1c\x52\x84\xce\xe3\x4e\x49\xda\x54\xc0\x4c\xda\xec\x85\x26\x6d\x84\x23\x57\xfe\xd6\xa0\x41\x3e\x06\x1b\xda\x38\xd0\xdb\x8a\x94\x09\x6f\x2c\x14\xb9\x0a\x98\x9c\x7b\x5a\x70\xb7\x82\x02\xf5\x8a\x49\x31\x55\x7f\xb9\xfc\x18\xa0\x41\x47\x56\xdf\x40\xe8\x2c\x2c\x6d\xdc\xda\x13\x54\xdd\x76\xe5\xfd\x4c\x4b\xd6\x06\x06\x7e\xde\x03\xa8\x12\x64\x6d\xb4\x64\xd5\x8a\x92\xb5\x3f\x5c\x92\x2e\x81\x79\x6b\xc9\xa9\xdb\x8e\x4b\xe0\x6a\xeb\x3e\x35\xc1\x82\xb3\xb0\x91\x4d\xb6\xdf\x14\xf8\xb6\x7a\x0e\x76\xc0\x36\x0a\xf3\xee\x3c\xfe\x23\xae\xf6\x42\x3d\x9c\x76\x9c\xda\x7d\x0f\xed\xc5\x63\x89\xd0\x5e\x3d\xb2\x43\x98\x3c\xf2\x3d\x2d\x86\xd7\x9c\xf3\xd0\xfc\x0b\x00\x00\xff\xff\xf9\xff\x22\xdd\x06\x02\x00\x00")

func iconsHardDriveOutlineSvgBytes() ([]byte, error) {
return bindataRead(
_iconsHardDriveOutlineSvg,
"icons/hard-drive-outline.svg",
)
}

func iconsHardDriveOutlineSvg() (*asset, error) {
bytes, err := iconsHardDriveOutlineSvgBytes()
if err != nil {
return nil, err
}

info := bindataFileInfo{name: "icons/hard-drive-outline.svg", size: 518, mode: os.FileMode(420), modTime: time.Unix(1548652600, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}

var _iconsQuestionMarkOutlineSvg = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x5c\x8d\xe1\x4e\xc3\x30\x0c\x84\x5f\xe5\xe4\x5f\xf0\xa3\x6b\x9c\x6d\x1a\x43\x4d\x25\xf8\x0d\x0f\x11\x75\x21\x8d\x58\x9b\x91\x98\x64\xe3\xe9\x51\x61\x42\x80\x2c\x4b\xe7\x3b\xeb\xbb\x2e\x17\x8f\xf3\x74\x9c\xb3\xa1\x51\xe4\x74\xdf\xb6\xb5\xd6\x55\x5d\xaf\x62\xf2\xad\x56\x4a\xb5\xb9\x78\x42\x09\xae\x3e\xc6\xb3\x21\x05\x05\xbd\x81\xde\x50\xdf\x79\x1c\xac\xd8\x66\xb6\x93\x33\xf4\x64\x2f\x2e\x41\xff\xb7\xdf\xde\x5d\x96\x10\xe7\x66\xb2\xe9\x95\xfa\x2e\xb9\x41\x50\xc3\x41\x46\x43\x7a\x43\x18\x5d\xf0\xa3\x7c\x6b\x49\x76\xce\x2f\x31\x4d\x86\x52\x14\x2b\xee\x86\xef\x14\x58\x83\xf5\x2d\x21\x9e\xec\x10\xe4\x62\x48\x51\xdb\x77\x27\x2b\x23\x0e\x86\x9e\x79\x87\xfd\xc3\x16\x5b\xa8\xaf\xd9\x61\x6f\x19\x7c\xbd\x34\x14\xd6\x58\x43\x61\xf1\x16\xf5\x93\x35\x0c\x2e\xfa\xef\x6f\x69\x78\xc5\xbf\x60\x0b\xfb\x63\x69\x1b\x42\x1a\x8e\x0e\xc3\xd9\x10\x6b\xc2\x70\x31\xc4\x7b\x42\x32\xc4\x4b\xdc\xfa\xeb\xe6\xe2\xfb\xcf\x00\x00\x00\xff\xff\x53\x23\xf6\xfd\x53\x01\x00\x00")

func iconsQuestionMarkOutlineSvgBytes() ([]byte, error) {
return bindataRead(
_iconsQuestionMarkOutlineSvg,
"icons/question-mark-outline.svg",
)
}

func iconsQuestionMarkOutlineSvg() (*asset, error) {
bytes, err := iconsQuestionMarkOutlineSvgBytes()
if err != nil {
return nil, err
}

info := bindataFileInfo{name: "icons/question-mark-outline.svg", size: 339, mode: os.FileMode(420), modTime: time.Unix(1548652600, 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.
func Asset(name string) ([]byte, error) {
cannonicalName := strings.Replace(name, "\\", "/", -1)
if f, ok := _bindata[cannonicalName]; ok {
a, err := f()
if err != nil {
return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err)
}
return a.bytes, nil
}
return nil, fmt.Errorf("Asset %s not found", name)
}

// MustAsset is like Asset but panics when Asset would return an error.
// It simplifies safe initialization of global variables.
func MustAsset(name string) []byte {
a, err := Asset(name)
if err != nil {
panic("asset: Asset(" + name + "): " + err.Error())
}

return a
}

// AssetInfo loads and returns the asset info for the given name.
// It returns an error if the asset could not be found or
// could not be loaded.
func AssetInfo(name string) (os.FileInfo, error) {
cannonicalName := strings.Replace(name, "\\", "/", -1)
if f, ok := _bindata[cannonicalName]; ok {
a, err := f()
if err != nil {
return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err)
}
return a.info, nil
}
return nil, fmt.Errorf("AssetInfo %s not found", name)
}

// AssetNames returns the names of the assets.
func AssetNames() []string {
names := make([]string, 0, len(_bindata))
for name := range _bindata {
names = append(names, name)
}
return names
}

// _bindata is a table, holding each asset generator, mapped to its name.
var _bindata = map[string]func() (*asset, error){
"data.go": dataGo,
"favicon.ico": faviconIco,
"index.html": indexHtml,
"logo.svg": logoSvg,
"script.js": scriptJs,
"icons/alert-triangle-outline.svg": iconsAlertTriangleOutlineSvg,
"icons/checkmark-outline.svg": iconsCheckmarkOutlineSvg,
"icons/close-outline.svg": iconsCloseOutlineSvg,
"icons/hard-drive-outline.svg": iconsHardDriveOutlineSvg,
"icons/question-mark-outline.svg": iconsQuestionMarkOutlineSvg,
}

// AssetDir returns the file names below a certain
// directory embedded in the file by go-bindata.
// For example if you run go-bindata on data/... and data contains the
// following hierarchy:
// data/
// foo.txt
// img/
// a.png
// b.png
// then AssetDir("data") would return []string{"foo.txt", "img"}
// AssetDir("data/img") would return []string{"a.png", "b.png"}
// AssetDir("foo.txt") and AssetDir("notexist") would return an error
// AssetDir("") will return []string{"data"}.
func AssetDir(name string) ([]string, error) {
node := _bintree
if len(name) != 0 {
cannonicalName := strings.Replace(name, "\\", "/", -1)
pathList := strings.Split(cannonicalName, "/")
for _, p := range pathList {
node = node.Children[p]
if node == nil {
return nil, fmt.Errorf("Asset %s not found", name)
}
}
}
if node.Func != nil {
return nil, fmt.Errorf("Asset %s not found", name)
}
rv := make([]string, 0, len(node.Children))
for childName := range node.Children {
rv = append(rv, childName)
}
return rv, nil
}

type bintree struct {
Func func() (*asset, error)
Children map[string]*bintree
}

var _bintree = &bintree{nil, map[string]*bintree{
"data.go": &bintree{dataGo, map[string]*bintree{}},
"favicon.ico": &bintree{faviconIco, map[string]*bintree{}},
"icons": &bintree{nil, map[string]*bintree{
"alert-triangle-outline.svg": &bintree{iconsAlertTriangleOutlineSvg, map[string]*bintree{}},
"checkmark-outline.svg": &bintree{iconsCheckmarkOutlineSvg, map[string]*bintree{}},
"close-outline.svg": &bintree{iconsCloseOutlineSvg, map[string]*bintree{}},
"hard-drive-outline.svg": &bintree{iconsHardDriveOutlineSvg, map[string]*bintree{}},
"question-mark-outline.svg": &bintree{iconsQuestionMarkOutlineSvg, map[string]*bintree{}},
}},
"index.html": &bintree{indexHtml, map[string]*bintree{}},
"logo.svg": &bintree{logoSvg, map[string]*bintree{}},
"script.js": &bintree{scriptJs, map[string]*bintree{}},
}}

// RestoreAsset restores an asset under the given directory
func RestoreAsset(dir, name string) error {
data, err := Asset(name)
if err != nil {
return err
}
info, err := AssetInfo(name)
if err != nil {
return err
}
err = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755))
if err != nil {
return err
}
err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode())
if err != nil {
return err
}
err = os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime())
if err != nil {
return err
}
return nil
}

// RestoreAssets restores an asset under the given directory recursively
func RestoreAssets(dir, name string) error {
children, err := AssetDir(name)
// File
if err != nil {
return RestoreAsset(dir, name)
}
// Dir
for _, child := range children {
err = RestoreAssets(dir, filepath.Join(name, child))
if err != nil {
return err
}
}
return nil
}

func _filePath(dir, name string) string {
cannonicalName := strings.Replace(name, "\\", "/", -1)
return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...)
}

Loading…
Cancel
Save