diff --git a/.gitignore b/.gitignore index 95f1ad2b..9f3801c5 100644 --- a/.gitignore +++ b/.gitignore @@ -16,5 +16,4 @@ .DS_Store .env* .tmp -dist -ui \ No newline at end of file +dist \ No newline at end of file diff --git a/Makefile b/Makefile index 82eb15f2..d2daa1d9 100644 --- a/Makefile +++ b/Makefile @@ -111,7 +111,7 @@ bindata: mkdir -p .tmp/data cp -r ui .tmp/data/ cp -r xiang .tmp/data/ - go-bindata -o data.go .tmp/data/... + go-bindata -fs -o data.go -prefix ".tmp/data/" .tmp/data/... rm -rf .tmp/data # 编译可执行文件 diff --git a/bindata.go b/bindata.go index 1726a249..6335026a 100644 --- a/bindata.go +++ b/bindata.go @@ -1,7 +1,12 @@ // Code generated for package main by go-bindata DO NOT EDIT. (@generated) // sources: -// unit/.DS_Store -// unit/service.sh +// .tmp/data/ui/index.html +// .tmp/data/xiang/apis/README.md +// .tmp/data/xiang/apis/sys.http.json +// .tmp/data/xiang/flows/README.md +// .tmp/data/xiang/flows/table/get.flow.json +// .tmp/data/xiang/models/README.md +// .tmp/data/xiang/models/user.json package main import ( @@ -10,6 +15,7 @@ import ( "fmt" "io" "io/ioutil" + "net/http" "os" "path/filepath" "strings" @@ -78,42 +84,228 @@ func (fi bindataFileInfo) Sys() interface{} { return nil } -var _unitDsStore = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\xd8\x31\x0a\x02\x31\x10\x85\xe1\x37\x31\x45\xc0\x26\xa5\x65\x1a\x0f\xe0\x0d\xc2\xb2\x9e\xc0\x0b\x58\x78\x05\xfb\x1c\x5d\x96\x79\x60\x60\xd5\x4e\x8c\xcb\xfb\x40\xfe\x05\x37\x2a\x16\x31\x23\x00\x9b\xee\xb7\x13\x90\x01\x24\x78\x71\xc4\x4b\x89\x8f\x95\xd0\x5d\x1b\x5f\x43\x44\x44\x44\xc6\x66\x9e\xb4\xff\xf5\x07\x11\x91\xe1\x2c\xfb\x43\x61\x2b\xdb\xbc\xc6\xe7\x03\x1b\xbb\x35\x99\x2d\x6c\x65\x9b\xd7\x78\x5f\x60\x23\x9b\xd8\xcc\x16\xb6\xb2\xcd\xcb\x4d\xcb\x38\x7c\x18\xdf\xd9\x38\xa1\x18\xa7\x10\x2b\x6c\xfd\xce\x77\x23\xf2\xef\x76\x9e\xbc\xfc\xfe\x9f\xdf\xcf\xff\x22\xb2\x61\x16\xe7\xcb\x3c\x3d\x07\x82\xf5\x0d\x00\xae\xdd\xf5\xa7\x43\x40\xf0\x3f\x0b\x0f\xdd\x5a\x1d\x04\x44\x06\xf3\x08\x00\x00\xff\xff\x6a\x00\x88\x6d\x04\x18\x00\x00") +type assetFile struct { + *bytes.Reader + name string + childInfos []os.FileInfo + childInfoOffset int +} -func unitDsStoreBytes() ([]byte, error) { +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 + } + + // 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 _uiIndexHTML = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xca\x48\xcd\xc9\xc9\x57\x28\xcf\x2f\xca\x49\xe1\x02\x04\x00\x00\xff\xff\x2d\x3b\x08\xaf\x0c\x00\x00\x00") + +func uiIndexHTMLBytes() ([]byte, error) { return bindataRead( - _unitDsStore, - "unit/.DS_Store", + _uiIndexHTML, + "ui/index.html", ) } -func unitDsStore() (*asset, error) { - bytes, err := unitDsStoreBytes() +func uiIndexHTML() (*asset, error) { + bytes, err := uiIndexHTMLBytes() if err != nil { return nil, err } - info := bindataFileInfo{name: "unit/.DS_Store", size: 6148, mode: os.FileMode(420), modTime: time.Unix(1630927699, 0)} + info := bindataFileInfo{name: "ui/index.html", size: 12, mode: os.FileMode(420), modTime: time.Unix(1631095267, 0)} a := &asset{bytes: bytes, info: info} return a, nil } -var _unitServiceSh = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xbc\x96\x6d\x6f\xda\xc8\x17\xc5\xdf\xf3\x29\xce\x7f\xfe\xde\x1a\xba\xb2\xc3\x43\x31\x14\x44\xb5\x94\x78\x53\x24\x1a\x28\xf6\x36\xaa\x36\x2b\xe4\x9a\x81\x8c\x6a\xc6\xae\x67\x9c\x26\xea\xe6\xbb\xaf\xc6\x36\x8e\xc9\x03\x4a\xd2\x28\x79\x41\x3c\xdc\x99\x73\xef\x3d\xf3\xf3\x4d\xfe\xff\xbf\x83\xaf\x8c\x1f\x7c\xf5\xc4\x59\xc5\x91\x5e\x2c\x3f\x5e\x3a\x9f\x26\x6d\xb3\x53\xad\xe1\x67\x05\x00\x96\xa1\xff\x8d\xc6\x88\x92\x20\xc0\xe6\x52\x7c\x0f\x7a\x6d\xb3\x63\x36\xdb\xe5\x60\x9c\x70\x18\x06\xf7\x36\x74\x90\x6e\x69\x9b\x1d\x18\x4b\x18\x11\x5a\xad\xba\xd5\x53\x1f\x30\x28\x3e\x7e\x71\x3e\x4d\x16\xf3\xe9\xd4\x5d\xcc\x86\x8e\x73\x32\x9d\x1f\x0e\x1a\xcd\xd6\x9b\xb6\xb5\xa3\x0c\xc3\x58\xd2\x95\x97\x04\xd2\xf0\x12\x79\x46\xb9\x64\xbe\x27\x59\xc8\x8d\x28\x48\xd6\x8c\x67\x39\x16\xdc\x93\xec\x9c\x2e\x22\x4f\x88\x1f\x61\xbc\x4c\xeb\x39\xf1\x98\x64\x7c\x0d\xb2\x2d\x83\x00\xe4\x33\x8d\x05\x0b\x79\x0f\x7a\xa6\xaf\x03\x42\xd5\x2d\x7b\xd0\x0f\xce\xbd\xf8\x20\x4e\xf8\x41\x7a\x60\x99\xff\x32\x55\x5c\x07\xa2\x30\x96\xbd\xb4\x07\x20\x35\x06\xa3\x70\xb3\x49\x38\x93\x97\x70\x68\x7c\x4e\x63\x54\x8f\x66\x93\x1a\x41\xab\x5e\xf6\x23\x08\xd7\x02\xdb\x12\xca\x01\x7a\x41\xfd\x22\x90\x3d\xc0\x48\xe2\x30\x94\x30\xa2\xdc\x0a\x83\x82\x8c\xe6\xf6\xd0\xb5\x71\x38\x74\x87\xef\x87\x8e\x8d\x8b\x84\x63\xf4\x61\x38\x1f\x8e\x5c\x7b\x0e\xc7\x76\x91\xc8\x55\x17\xa3\xe9\x64\xa2\xf6\xa9\xc5\x62\x4d\x39\x8d\xbd\x60\xe1\x33\xf2\x0b\x29\xff\x72\xec\xb9\x4a\xf7\x87\xfe\x9b\x8e\xf1\xa1\x7d\xec\x8e\xff\x1c\xdb\x87\x78\xff\x05\x7a\xb6\x5b\x7f\x92\xfc\xd1\x7c\x78\xec\xc2\xb1\x27\xf6\xc8\xc5\xf4\x58\xa5\x30\x5f\xc3\x9d\x42\xbf\x48\xb8\xae\xb2\x91\x7e\xe5\xaa\x52\x82\xb0\x6b\xd6\xf7\x40\xd8\x35\xeb\x66\xd3\xda\x07\x61\xd7\xac\x5f\x43\xd8\x7d\x38\x84\x99\xf2\xb3\x41\xd8\x35\xeb\xbb\x10\x66\xfa\xcf\x0a\xa1\x81\xa3\xd9\xe4\x7e\x08\xbb\x66\xfd\xee\x2b\x53\x16\xbd\x30\x84\x0f\x4a\xf9\x74\x08\xf7\xcb\x3f\x14\xc2\x9d\x51\x68\xed\x1d\x85\x96\xd9\x6e\xec\x1f\x85\xd6\x35\x85\x9d\xc7\x8c\x42\xa5\xfc\x8c\xa3\xd0\xba\x39\x0a\x95\xfe\xcb\x8e\x42\xeb\xbe\xc1\x61\xbd\xfc\x28\x7c\x40\xca\x5f\x19\x85\xfb\xe4\x1f\x47\xe1\x2c\x14\x72\x1d\x53\xf1\xf6\x1e\x10\xa3\x3c\xde\x7b\x6b\xde\x37\x0e\xa3\x6b\x89\x9c\xc5\xf6\x9b\x56\xb3\xa7\x3e\x54\x41\xb3\xa9\xe3\x1e\xcd\x6d\xe7\x16\x89\xb7\xa4\x0b\xa6\x4a\x8a\x0a\xab\xbc\x46\x45\x05\xe3\x4c\x22\x8a\x43\x9f\x0a\x01\x3f\xdc\x44\x01\x95\xb4\x8f\x98\x7a\xcb\x4b\xac\xc2\x18\x42\x35\x85\x24\xba\x13\x94\x92\xee\x2d\x67\xcb\x5d\x88\x04\x46\xf1\x05\x0c\x1f\x24\x4a\xdd\xf6\xa1\xdf\xc1\x8b\x4e\xf0\x44\xb5\xd3\x9b\x2c\xe0\x64\xec\x7e\xc0\xd6\xa8\x82\x83\x53\xf2\xe4\x14\x7a\x86\xc3\x70\x32\xc1\x6c\x3e\xfe\x3c\x9e\xd8\x47\xb6\xa3\xb0\x28\x3a\x38\x25\x17\x09\x3f\x25\x90\xa1\xaa\xa0\xaf\xba\xb9\xaa\x54\xc6\x62\xae\x3c\x2d\x98\x48\x6f\x5a\xcb\x66\x91\x7f\x46\xfd\x6f\x42\xc6\x03\xad\x99\xae\x63\x2a\x06\x5a\xb5\x6c\xb4\xd6\x40\xf3\xdd\xab\x06\xfe\xc5\x3a\xa6\x11\x88\xb6\x3d\x42\x6a\xe9\x09\xb6\xc2\xdf\x20\x5a\x4c\x05\xc1\x60\x00\x42\xf0\x4f\x1f\x6a\x00\xa5\x51\xf5\x43\xfd\xb3\x10\xa4\x9e\xbd\x01\x34\x10\xf4\x46\xa4\x91\x45\x56\x4c\x15\x9b\x83\xf3\xa0\x62\x03\x2a\x21\xd9\x86\x86\x89\x1c\x68\xad\x4a\xa1\x68\x70\x90\xf4\x8d\x50\x04\x6a\x4a\x01\x66\x96\x83\x89\x14\xaf\x81\x56\xcd\x4d\x01\x49\xe3\xe4\x76\x5f\x92\x6d\x18\x5f\x0f\x32\xf2\x7e\x9c\xb1\x80\x22\x6d\x34\x97\xc8\x9a\xad\xab\x6e\xf3\xeb\x2c\xba\x12\x01\xa5\x11\x1a\xc5\xfa\x11\x49\x4b\x5d\xa9\xe4\xda\xcf\xec\xe1\xea\xf7\xc6\xae\x67\xaa\xc3\xbc\xa5\xe2\x0e\xb4\x6c\x2f\x0c\xfa\x3d\x7d\x56\xb6\xdc\xba\x8b\x6b\xd7\xb1\xf2\x58\x40\x97\xa6\xca\x15\x26\xb2\xba\x3d\x52\x23\x78\xf7\xaa\xb9\x73\x60\x07\x87\xd4\xce\x9b\x3b\xe8\x05\x93\xa5\x8e\x57\x2c\xf7\x84\xd3\x4a\x29\xa3\x5a\x13\x75\xc9\x7e\xb8\xd9\x78\x7c\xa9\xee\xd5\xf7\x04\x85\x96\x7f\x01\x96\x55\xba\xfd\x03\x5d\xc3\xce\x3f\x79\xfd\xfe\x75\xb4\x6d\x76\xca\xd1\xb6\xd9\xd9\x8d\x5a\xbb\x51\x2b\x8f\x96\xde\xb4\x7c\x43\x69\x6a\xe6\x7b\x5e\xd7\xa0\x55\xb3\x92\xa3\x80\xaa\xfa\x18\x8f\x12\x89\xbc\xc8\xd4\x9f\x1a\xfa\xfd\x0a\x15\x9e\xff\x5f\x00\x00\x00\xff\xff\x78\x34\x52\x42\x17\x0d\x00\x00") +var _xiangApisReadmeMd = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x52\x56\x70\x0c\xf0\x54\x78\xd6\xb7\xf4\x69\xff\x62\x2e\x40\x00\x00\x00\xff\xff\xcc\x30\x5e\xae\x0d\x00\x00\x00") -func unitServiceShBytes() ([]byte, error) { +func xiangApisReadmeMdBytes() ([]byte, error) { return bindataRead( - _unitServiceSh, - "unit/service.sh", + _xiangApisReadmeMd, + "xiang/apis/README.md", ) } -func unitServiceSh() (*asset, error) { - bytes, err := unitServiceShBytes() +func xiangApisReadmeMd() (*asset, error) { + bytes, err := xiangApisReadmeMdBytes() if err != nil { return nil, err } - info := bindataFileInfo{name: "unit/service.sh", size: 3351, mode: os.FileMode(493), modTime: time.Unix(1630927699, 0)} + info := bindataFileInfo{name: "xiang/apis/README.md", size: 13, mode: os.FileMode(420), modTime: time.Unix(1631095267, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + +var _xiangApisSysHTTPJSON = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x4c\x90\x31\x4e\x03\x31\x10\x45\x7b\x9f\x62\x34\xa2\x0c\xce\x42\xe9\x8e\x02\x10\x1d\x05\x5d\x94\xc2\xec\x1a\xe2\x28\x6b\x5b\x33\x36\x28\x8a\x72\x05\x38\x02\x05\xe2\x04\x94\x39\x0f\xe4\x1a\x68\xbc\x4b\x92\xc6\x92\xdf\xfb\xf3\x35\x9a\x8d\x02\xc0\x60\x7b\x87\x06\x70\xff\xbd\xdb\xef\x3e\x7e\xdf\xbe\x7e\xde\x3f\x71\x22\xe6\xc5\x11\xfb\x18\x44\x5e\xe8\x46\x37\x03\xed\x1c\xb7\xe4\x53\x1e\xcd\xe9\xd8\xd5\xfd\xdd\x90\x79\xa6\x58\x92\x58\x5e\xf3\x48\x8a\xa5\x4e\xc8\xa3\xb3\xe4\xe8\x7c\xf9\x9a\x07\x91\x6c\x5e\x30\x1a\x98\x29\x00\x80\x4d\x7d\x47\x2c\xf1\xa9\x0f\x4f\x71\x6a\x7c\x57\xd3\x55\xf5\x2e\x2f\x62\xed\xba\xbd\x7e\x38\xe2\x44\xb1\x75\x2c\x55\xd8\xc7\xce\xad\x58\x17\x76\xa4\x6f\x7c\x38\x99\xf5\xb2\xf3\x0c\xcf\x92\x25\xdb\x6b\x69\x05\x34\xf5\xc3\x38\x3f\xa4\x62\xc9\x68\x0e\xbb\x00\x20\x67\x9b\x8b\x54\x5f\x36\xcd\xe4\x88\xf3\x3a\xd5\xd3\xd9\x94\x56\xbe\xb5\x72\x92\xe9\x92\x63\xc0\x31\xb2\x55\xff\xef\x5c\x6d\xd5\x5f\x00\x00\x00\xff\xff\x8a\x81\x76\x11\x6e\x01\x00\x00") + +func xiangApisSysHTTPJSONBytes() ([]byte, error) { + return bindataRead( + _xiangApisSysHTTPJSON, + "xiang/apis/sys.http.json", + ) +} + +func xiangApisSysHTTPJSON() (*asset, error) { + bytes, err := xiangApisSysHTTPJSONBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "xiang/apis/sys.http.json", size: 366, mode: os.FileMode(420), modTime: time.Unix(1631095267, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + +var _xiangFlowsReadmeMd = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x52\x56\x78\xba\x7d\xe9\x93\xbd\x73\x9e\x6d\x6d\xe4\x02\x04\x00\x00\xff\xff\x8c\xe8\x04\x3a\x0c\x00\x00\x00") + +func xiangFlowsReadmeMdBytes() ([]byte, error) { + return bindataRead( + _xiangFlowsReadmeMd, + "xiang/flows/README.md", + ) +} + +func xiangFlowsReadmeMd() (*asset, error) { + bytes, err := xiangFlowsReadmeMdBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "xiang/flows/README.md", size: 12, mode: os.FileMode(420), modTime: time.Unix(1631095267, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + +var _xiangFlowsTableGetFlowJSON = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x74\x90\x4d\x4a\xc4\x40\x10\x85\xf7\x39\x45\x51\xb8\x0c\x21\xba\x9c\xab\xc8\x20\x9d\x74\x31\x36\x56\xd2\x43\xff\x8c\x8b\xa6\x41\x2f\xe1\xca\x8d\x27\xd0\x1b\x78\x19\xc9\x39\xa4\x3a\x33\x99\x04\x71\x13\xc2\x7b\xaf\xbe\x7e\x55\xa9\x02\x40\x56\x1d\x31\xee\x00\xa7\xf7\x97\xe9\xed\xeb\xe7\xfb\x63\x7a\xfd\xc4\x5a\xac\x13\x39\x6f\xec\x28\xe6\x6d\xd3\x36\xed\xac\x6a\xf2\xbd\x33\xc7\x70\x76\xfe\x8e\x8d\x56\x93\xc7\x1d\xdc\x57\x00\x00\xa9\x7c\x45\x56\x03\xc9\x40\xf4\xe4\x4a\xb0\xa8\x47\x67\x7b\xf2\x12\xc7\xc1\x6a\x62\xdf\x88\xdf\x1c\x28\x5c\x33\xca\x1d\xae\xbc\x35\xb3\xb8\x9e\x98\xfa\x20\x3e\x1a\x8d\xf5\xf9\xa1\x5a\x78\x9d\x61\xc2\x7d\xbd\x4e\xb3\x19\x8c\x84\xef\xda\x8d\x6c\x9d\x26\x57\x1e\x49\x80\xbd\xe5\x38\x94\xe5\x7a\x47\x2a\x90\x7e\x50\x41\x80\x76\x59\x5a\x6e\x80\x90\xb7\xe8\xe7\x47\x72\xb4\x2d\x5a\xca\xae\x81\x3e\xa8\x10\xbd\xc0\x4e\x8a\x63\xb9\x07\x8d\xaa\x63\xd2\x08\xb9\xfe\x7f\xee\xb2\xd3\x32\x95\xd2\x8d\x19\x9b\x36\xe7\xb9\x98\x48\x6c\x9e\x08\x21\xaf\x20\xfb\xe5\xff\xa2\x2e\x8d\xd1\xc6\x50\xba\x0a\xc9\xc6\x90\x33\xce\x69\x49\xee\xab\x5c\xfd\x06\x00\x00\xff\xff\x34\x26\x65\xb9\x1d\x02\x00\x00") + +func xiangFlowsTableGetFlowJSONBytes() ([]byte, error) { + return bindataRead( + _xiangFlowsTableGetFlowJSON, + "xiang/flows/table/get.flow.json", + ) +} + +func xiangFlowsTableGetFlowJSON() (*asset, error) { + bytes, err := xiangFlowsTableGetFlowJSONBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "xiang/flows/table/get.flow.json", size: 541, mode: os.FileMode(420), modTime: time.Unix(1631095267, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + +var _xiangModelsReadmeMd = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x52\x56\x78\x36\x75\xc3\xb3\xde\x75\xcf\x56\x2c\x7c\x3a\xaf\x9b\x0b\x10\x00\x00\xff\xff\xb5\xd1\x53\xc2\x0f\x00\x00\x00") + +func xiangModelsReadmeMdBytes() ([]byte, error) { + return bindataRead( + _xiangModelsReadmeMd, + "xiang/models/README.md", + ) +} + +func xiangModelsReadmeMd() (*asset, error) { + bytes, err := xiangModelsReadmeMdBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "xiang/models/README.md", size: 15, mode: os.FileMode(420), modTime: time.Unix(1631095267, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + +var _xiangModelsUserJSON = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\x59\xed\x53\x13\x57\x17\xff\xce\x5f\x71\x67\x1f\xe7\x19\x7d\x9e\x64\xdc\xdd\x20\x86\x7c\x32\x48\x9d\xc1\xb7\x22\xf8\x82\x28\x65\x36\xd9\x9b\xb0\x74\xb3\x89\xd9\x5d\x0c\xa4\x99\x09\xb5\x22\x54\x10\xb4\x50\x64\x4a\x1d\xb5\x82\xb4\xbe\x44\x2a\x23\x19\x94\xf1\x8f\x69\xee\x66\xf9\xd4\x7f\xa1\xb3\x2f\xd9\x2c\xbb\x37\x4b\x02\xb6\x3a\xd2\x4f\x84\x7b\xf7\x9e\xf3\xbb\xbf\x73\xee\x39\xe7\x9e\x9b\x6d\x02\x80\x10\x98\x04\x24\x42\x80\x28\xcf\xae\x28\xe3\xeb\x84\x4f\x1b\x93\x98\x08\xaf\x0d\x6a\x1f\xd8\x3e\x91\x45\x98\xd6\x3f\x00\x80\x88\x26\x13\x09\x28\x48\xd5\x95\xea\xa3\x95\xca\x1c\x14\xe2\x9c\xa0\xaf\xe8\x10\x84\x64\x7b\x1b\xd1\x04\x40\x4e\x17\x1c\x4d\xf2\x72\x42\x10\x89\x10\xb8\xa2\x7f\x9a\x05\x04\xcf\x44\x20\xaf\x7f\xdb\x4e\xf8\xaa\xba\x38\x56\xfb\x4f\x1a\x4e\x41\x73\xce\x90\x00\x4c\x4c\xc0\xb6\x10\xdd\xf9\x16\xcd\x8d\x99\xca\x6d\x70\x13\x8c\x20\xf7\x6b\x72\x2a\x13\x15\x69\x11\x2e\xde\x21\x48\x30\x6e\xed\x46\x93\x06\x85\xb8\x34\x40\x84\xc0\x11\xd2\x1a\xb3\xed\x51\x99\xc8\xa3\xd5\x07\x2e\x4d\x32\xcf\x9b\x54\x49\x69\x19\x5a\xe3\x9c\xc0\xc2\x8c\x73\x70\x88\xe1\x39\x96\x91\xb8\xa4\x6d\xff\xf6\x0d\xe9\x1f\x25\xa0\x34\x90\x64\x35\x8d\x1a\xd8\x64\xcc\xd2\xa5\xcf\x32\xe9\xb8\xbe\x96\xe0\x4c\xf8\x7d\xbe\xed\x8b\x45\x91\x89\xeb\x5b\xcc\x66\x39\x21\x25\x4b\xb9\x5c\x79\xf5\x2d\x7a\x70\x7b\x6b\x76\x41\x2d\x14\x7c\x20\x9b\xd5\x59\xcb\xe5\xd0\xc6\x6c\xa9\xb8\xa1\xcc\xbd\x42\x2f\xe6\x09\x4b\x46\xce\xb7\x13\xaa\x04\x27\xe0\x21\x91\x1e\x50\xaa\x3a\xd1\x93\xa7\xa5\x8d\x3b\xa4\x4d\xa3\xf9\xab\xaf\xc9\xa6\xdf\x6d\x62\x63\x17\x6e\x13\xeb\x16\x75\xd9\x17\x0a\x72\xa2\x3a\x9a\x4c\x69\x9c\xeb\xb4\x31\xac\x8e\x1f\x10\xa2\xc4\xc4\x62\xda\x0f\xdd\xa5\xfb\x70\x16\x57\xd7\x96\xd1\xf4\xba\xa1\x18\xe8\x0b\x41\xf9\xe5\xa3\xf2\xcc\x18\xba\x7b\xdf\x07\x74\x01\x00\xdd\xbd\x8f\xd6\x97\x7c\x40\x93\x02\x6c\x07\x48\x97\xc5\xc2\x18\x23\xf3\xba\x2c\x53\xdd\x3f\xe6\x1e\xa2\x94\xe6\x84\xf8\x9e\xbc\x43\x2d\x2c\x95\x8a\x1b\xe8\xc5\x7c\xf9\xf9\x72\xa9\xf8\x7b\x23\x3e\xb2\x8d\xfe\xed\xc0\x76\x32\x40\x4d\xa0\xa5\xe2\x14\x5a\x5c\x51\x5f\x16\xd1\x74\x41\x9d\xbc\x81\x7e\x5a\xc3\xc1\x35\xec\x74\x58\x17\x7e\x58\x97\xdc\xa8\xa3\x29\x13\xb7\x95\xc5\x0d\x34\xbd\x8e\x09\x27\xc9\x08\xc7\x63\xbc\xcd\x24\xbb\xfe\x48\xe2\xd2\x80\xf5\x87\x68\x7a\x38\xa5\x2f\x08\x7f\xd1\x4d\x7c\x86\x5e\x92\x62\x24\x09\xa6\x6b\x44\x13\xe2\x2b\xea\x4a\xc0\xdf\xda\x77\xf5\x2a\x9b\x6d\xcd\x1d\xa8\x03\xa3\xf2\xf0\x1d\x7a\x37\x6d\x60\x6c\x3c\xbc\x2c\xbc\x45\x9b\x73\xa8\x30\x56\x7e\x38\xea\x36\x7c\x8a\x11\xc5\xeb\xc9\x34\x26\x91\xd4\x34\x3d\x7d\xa4\x05\x67\x7b\xbc\x1e\xcb\xd2\x9d\xe1\xee\xee\x4b\x5f\x76\xb5\xef\x9b\x48\x91\xe0\x84\xd3\x06\x65\x58\x74\x2d\x75\xe5\x14\xb5\xb0\x54\x9e\x5d\x6d\xf1\x53\xc1\xd2\xe6\xd4\x9f\xef\x26\xd1\x93\xa7\xe8\xd5\x34\x1a\x5b\x40\x2f\xe6\x95\xc2\xd4\x1f\xf9\x51\x23\xcd\xa1\x7b\x93\xe5\xe7\x5a\x4c\x57\x1e\x7c\xa7\x8c\xcf\x34\x84\x93\xc9\x78\xe1\xa4\x82\x9f\x0a\x50\xef\x43\x75\x85\xf4\xb7\xf6\xfd\xdf\xcb\xd8\xdb\xc0\xaa\xb7\x5e\xa3\x57\x77\xd1\xe4\x4d\x34\xf3\xac\x54\xcc\x97\x8a\xbf\x35\x5e\x2f\xec\x00\x28\xec\xef\xdd\x13\x20\x8d\xc2\x0a\x7f\x1f\x10\x16\xe3\x1f\xd9\x1b\x2c\x9b\x59\x3f\x20\xac\x63\xff\x39\xf0\xdf\xff\xed\x09\x98\xe1\x57\x0d\xc7\x47\xf4\xf4\x07\x34\x33\xe5\x8e\x8c\xfa\xdf\xfa\xa3\x62\x10\x9b\x10\x9d\xc2\xf7\x77\xcc\xa3\x77\x36\xae\x61\xd6\xad\xc5\xbc\xba\x3c\x4a\x6b\xde\xd6\x60\x09\xef\x1d\xcc\x9a\xeb\xa8\xe4\x4b\xc5\x29\xf5\xc6\xa6\xfa\xe6\xa6\xfa\xfe\x16\x4d\xba\x20\xd4\xe7\x54\xea\xc6\xb3\xd2\xdb\x4d\xb5\x30\xaa\x95\xd9\xb8\xbc\xcb\xb1\x51\xe6\x43\x64\xdd\x9a\x8a\xf0\x25\xd6\x47\xbb\xd4\x7d\xd2\xf5\xd8\x41\xad\x14\xa3\x82\xb9\x43\xdf\x18\xbf\x9a\x73\x3d\x87\x3c\xeb\x32\x13\xd4\x9e\xea\x32\x75\x6d\x59\x19\x5f\x2f\x6d\x2e\x6c\x3d\xfe\xd9\xed\x1f\x11\x86\x67\x84\x28\x26\x00\x71\x35\x2f\xf7\x34\x36\x02\xd9\xd5\x1c\x44\x63\xf3\xa5\xcd\x85\x43\xb8\x7b\x1c\xf9\xef\x05\xdf\xd3\xd6\x7b\xb8\xe0\x87\x3b\x3b\xc0\x29\x38\xec\x36\xf2\xd7\xf6\xc1\xdd\x46\x00\xb7\xf4\x1a\x67\x5c\x16\xb8\x6b\xb2\x6b\x74\xdf\x1c\x72\xad\x40\x0c\xfb\x7b\x19\xff\x88\x51\x6a\x64\x83\x5e\x77\x2f\x27\xa8\xf2\xec\xea\xae\x2b\xdb\xfa\xbd\x04\x15\xc6\xb6\xee\x2d\xb9\x1d\x45\x84\xd1\x34\x94\x76\xe9\x2b\xb5\xfc\x01\x9f\x21\x1c\x9e\xe5\x44\xb4\xbf\x13\x85\xcb\x87\x02\xb4\xf7\x05\xde\xe9\x43\x01\xfa\xef\x75\xa2\xf2\xcb\x3c\xba\x83\x69\x17\xa7\xa1\x28\xe3\xca\x59\x09\x66\x24\xac\xed\x5d\x82\xb6\x3b\x91\x37\x0a\x65\xe2\x57\xb4\x3a\x57\x7a\xff\x48\x19\x2d\xb8\xb1\xc0\x8c\x94\x66\xdc\x50\x06\xc5\xa4\x80\x85\x52\x43\x5a\x23\x80\xca\xdf\xbf\x51\xf2\xa3\xf8\x8d\xea\x9d\x4d\xe3\x03\x00\x05\x4d\x24\x0b\x94\xc5\x09\x65\x6e\xdc\x07\x58\x4e\x34\x07\xe6\x1f\x2a\x73\xe3\x98\x83\x29\x31\x92\x2c\xee\xd4\xa5\xb5\x75\x4b\x4d\x0d\xd8\x0e\xae\x35\x07\x88\x8a\x62\x5b\xfb\xf6\x33\x3f\x79\x1e\x8d\x55\x4f\x5e\x6a\xa2\xac\xb3\xab\x6a\x0a\x3f\x6c\x49\xae\x75\xe4\x9a\x00\xd0\x95\x12\x69\xc8\x5b\x8c\x67\x8d\xa7\x9f\x21\x86\x97\xa1\xed\xe5\xc7\xe9\x26\x56\x73\xbd\x6a\xf7\xca\x03\x4e\x08\x50\x2e\xef\x31\x3b\xc9\x96\xe5\x8d\xbb\x42\x08\x10\x74\x80\x6c\xa1\x9b\xa9\xd6\x60\x80\xa4\xa8\xa3\x24\x49\x1d\xb1\x09\x34\x5a\xb8\x21\x40\x50\x81\x56\x92\x24\x49\x8a\xa2\xa8\xea\xb4\xd5\xe8\x0b\x01\x22\x3a\xd4\x7d\xea\x58\xd7\xe5\x96\xea\xac\x56\x89\x84\x00\x71\xa2\x2d\x10\xcb\x1c\x87\xe7\xaa\x13\x66\xe6\x09\x01\xa2\xe7\xcc\x79\xf6\x6c\xd7\x45\x2e\x1e\x89\x5f\xe0\xc2\x9d\x2c\x77\xf2\x78\x8c\xb9\x14\xbf\x14\x1d\xa1\x3b\x99\x73\x3d\xd7\x6d\x6b\x8c\x43\x81\xf3\x76\xe3\xec\x87\x40\x56\x93\x9c\x31\xce\xdf\x3a\x61\x52\xed\x3c\xbe\x15\xf6\x8c\x97\x88\xfa\xa8\x73\xbe\x46\xd8\xa9\x0b\xd2\x2d\x54\x6b\x90\x26\x69\xf2\x28\x49\x37\xf7\x78\x50\x47\xd3\x34\x8d\xa7\xee\xda\xc5\x63\xf2\x79\xaa\xbd\xc3\x45\xdd\xc9\xf6\x01\xba\x97\xbb\x20\xe1\xa8\xbb\xde\x06\x2f\x0f\x9e\x3e\x7a\x62\x30\x12\x1d\x4a\x85\xd9\xb6\x74\x46\x6a\x3f\x11\x1b\x1c\x66\xc5\x64\xe7\xa9\x81\xae\xb3\xbb\xa4\x0e\x2d\xbd\xde\x89\x3a\xe7\xbb\x4d\x95\x3a\xda\x45\x9d\xed\x05\xd4\x8b\x39\xd2\x93\xb9\x40\x20\x10\x68\x90\xb9\x9e\x5e\x8a\x3e\xc3\x75\x8e\x7c\x24\xe6\xac\x73\xad\x07\x58\xdc\x29\xb6\xf7\x73\xf4\x47\x52\x27\xab\x8e\x47\xd9\x7e\x83\x95\x7e\xb3\xe0\xb6\x65\x1d\xeb\x81\xb8\xfa\x7e\x6b\x71\xd8\xe7\x36\x88\xb1\x1e\x6b\x60\x57\xaa\x46\x37\x57\x94\x1f\x6f\x29\xbf\xe4\xcb\x6b\x8f\x6b\xe5\xff\xfe\x98\xcc\xf3\xce\x94\x5f\xc5\x64\x16\x09\x6e\x20\xd6\x32\x07\x63\x56\xee\xca\x02\x42\xe2\x12\x50\x94\x98\x44\x4a\xac\xe4\x28\x40\x88\xc9\x98\xd4\xcf\x42\x1e\x4a\xb0\x32\x0a\x72\x4d\xb9\xa6\xbf\x02\x00\x00\xff\xff\xdf\xdf\x90\x77\x9c\x1f\x00\x00") + +func xiangModelsUserJSONBytes() ([]byte, error) { + return bindataRead( + _xiangModelsUserJSON, + "xiang/models/user.json", + ) +} + +func xiangModelsUserJSON() (*asset, error) { + bytes, err := xiangModelsUserJSONBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "xiang/models/user.json", size: 8092, mode: os.FileMode(420), modTime: time.Unix(1631095267, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -170,8 +362,13 @@ func AssetNames() []string { // _bindata is a table, holding each asset generator, mapped to its name. var _bindata = map[string]func() (*asset, error){ - "unit/.DS_Store": unitDsStore, - "unit/service.sh": unitServiceSh, + "ui/index.html": uiIndexHTML, + "xiang/apis/README.md": xiangApisReadmeMd, + "xiang/apis/sys.http.json": xiangApisSysHTTPJSON, + "xiang/flows/README.md": xiangFlowsReadmeMd, + "xiang/flows/table/get.flow.json": xiangFlowsTableGetFlowJSON, + "xiang/models/README.md": xiangModelsReadmeMd, + "xiang/models/user.json": xiangModelsUserJSON, } // AssetDir returns the file names below a certain @@ -214,7 +411,27 @@ type bintree struct { Children map[string]*bintree } -var _bintree = &bintree{nil, map[string]*bintree{}} +var _bintree = &bintree{nil, map[string]*bintree{ + "ui": {nil, map[string]*bintree{ + "index.html": {uiIndexHTML, map[string]*bintree{}}, + }}, + "xiang": {nil, map[string]*bintree{ + "apis": {nil, map[string]*bintree{ + "README.md": {xiangApisReadmeMd, map[string]*bintree{}}, + "sys.http.json": {xiangApisSysHTTPJSON, map[string]*bintree{}}, + }}, + "flows": {nil, map[string]*bintree{ + "README.md": {xiangFlowsReadmeMd, map[string]*bintree{}}, + "table": {nil, map[string]*bintree{ + "get.flow.json": {xiangFlowsTableGetFlowJSON, map[string]*bintree{}}, + }}, + }}, + "models": {nil, map[string]*bintree{ + "README.md": {xiangModelsReadmeMd, map[string]*bintree{}}, + "user.json": {xiangModelsUserJSON, map[string]*bintree{}}, + }}, + }}, +}} // RestoreAsset restores an asset under the given directory func RestoreAsset(dir, name string) error { diff --git a/config.go b/config.go index 4e65e375..1bc528cb 100644 --- a/config.go +++ b/config.go @@ -23,13 +23,14 @@ type Config struct { // XiangConfig 象传应用引擎配置 type XiangConfig struct { - Mode string `json:"mode,omitempty" env:"XIANG_MODE" envDefault:"release"` // 象传引擎模式 debug/release/test - Source string `json:"source,omitempty" env:"XIANG_SOURCE" envDefault:"fs://."` // 源码路径(用于调试时载入数据) - Root string `json:"root,omitempty" env:"XIANG_ROOT" envDefault:"."` // 应用文件目录 - RootAPI string `json:"root_api,omitempty" env:"XIANG_ROOT_API"` // 应用API文件目录 - RootModel string `json:"root_model,omitempty" env:"XIANG_ROOT_MODEL"` // 应用模型文件目录 - RootFLow string `json:"root_flow,omitempty" env:"XIANG_ROOT_FLOW"` // 应用工作流文件目录 - RootPlugin string `json:"root_plugin,omitempty" env:"XIANG_ROOT_FLOW"` // 应用插件文件目录 + Mode string `json:"mode,omitempty" env:"XIANG_MODE" envDefault:"release"` // 象传引擎模式 debug/release/test + Source string `json:"source,omitempty" env:"XIANG_SOURCE" envDefault:"fs://."` // 源码路径(用于调试时载入数据) + Path string `json:"path,omitempty" env:"XIANG_PATH" envDefault:"bin://xiang"` // 引擎文件目录 + Root string `json:"root,omitempty" env:"XIANG_ROOT" envDefault:"fs://."` // 应用文件目录 + RootAPI string `json:"root_api,omitempty" env:"XIANG_ROOT_API"` // 应用API文件目录 + RootModel string `json:"root_model,omitempty" env:"XIANG_ROOT_MODEL"` // 应用模型文件目录 + RootFLow string `json:"root_flow,omitempty" env:"XIANG_ROOT_FLOW"` // 应用工作流文件目录 + RootPlugin string `json:"root_plugin,omitempty" env:"XIANG_ROOT_FLOW"` // 应用插件文件目录 } // ServiceConfig 服务配置 diff --git a/config_test.go b/config_test.go index 1cd08f31..8b9bd199 100644 --- a/config_test.go +++ b/config_test.go @@ -19,8 +19,8 @@ func TestNewConfig(t *testing.T) { } assert.Equal(t, cfg.Mode, os.Getenv("XIANG_MODE")) - assert.Equal(t, cfg.Source, os.Getenv("XIANG_SOURCE")) assert.Equal(t, cfg.Root, os.Getenv("XIANG_ROOT")) + assert.Equal(t, cfg.Path, os.Getenv("XIANG_PATH")) assert.Equal(t, cfg.Service.Debug, vBool(os.Getenv("XIANG_SERVICE_DEBUG"))) assert.Equal(t, strings.Join(cfg.Service.Allow, "|"), os.Getenv("XIANG_SERVICE_ALLOW")) diff --git a/init_test.go b/init_test.go new file mode 100644 index 00000000..06ef3ef7 --- /dev/null +++ b/init_test.go @@ -0,0 +1,19 @@ +package main + +import ( + "os" + "testing" +) + +var cfg Config + +func TestMain(m *testing.M) { + cfg = NewConfig() + + // Run test suites + exitVal := m.Run() + + // we can do clean up code here + + os.Exit(exitVal) +} diff --git a/load.go b/load.go index d7cb6ce6..ddf54e0a 100644 --- a/load.go +++ b/load.go @@ -1,6 +1,126 @@ package main +import ( + "io/ioutil" + "os" + "path" + "path/filepath" + "strings" + + "github.com/yaoapp/gou" + "github.com/yaoapp/kun/exception" +) + +// Script 脚本文件类型 +type Script struct { + Name string + Type string + Content []byte +} + // Load 根据配置加载 API, FLow, Model, Plugin func Load() error { return nil } + +// LoadEngine 载入引擎 API, Flow, Model 配置 +func LoadEngine(from string) { + + var scripts []Script + if strings.HasPrefix(from, "fs://") || !strings.Contains(from, "://") { + root := strings.TrimPrefix(from, "fs://") + scripts = getFilesFS(root, ".json") + } else if strings.HasPrefix(from, "bin://") { + root := strings.TrimPrefix(from, "bin://") + scripts = getFilesBin(root, ".json") + } + + if scripts == nil { + exception.New("读取文件失败", 500, from).Throw() + } + + if len(scripts) == 0 { + exception.New("读取文件失败, 未找到任何可执行脚本", 500, from).Throw() + } + + // 加载 API, Flow, Models + for _, script := range scripts { + switch script.Type { + case "models": + gou.LoadModel(string(script.Content), script.Name) + break + case "flows": + gou.LoadFlow(string(script.Content), script.Name) + break + case "api": + gou.LoadAPI(string(script.Content), script.Name) + break + } + } + +} + +// getFilesFS 遍历目录,读取文件列表 +func getFilesFS(root string, typ string) []Script { + files := []Script{} + root = path.Join(root, "/") + filepath.Walk(root, func(path string, info os.FileInfo, err error) error { + if err != nil { + exception.Err(err, 500).Throw() + return err + } + if strings.HasSuffix(path, typ) { + filename := strings.TrimPrefix(path, root+"/") + name, typ := getTypeName(filename) + + file, err := os.Open(path) + if err != nil { + exception.Err(err, 500).Throw() + } + + defer file.Close() + content, err := ioutil.ReadAll(file) + if err != nil { + exception.Err(err, 500).Throw() + } + files = append(files, Script{ + Name: name, + Type: typ, + Content: content, + }) + } + return nil + }) + return files +} + +// getFilesBin 从 bindata 中读取文件列表 +func getFilesBin(root string, typ string) []Script { + files := []Script{} + binfiles := AssetNames() + for _, path := range binfiles { + if strings.HasSuffix(path, typ) { + file := strings.TrimPrefix(path, root+"/") + name, typ := getTypeName(file) + content, err := Asset(path) + if err != nil { + exception.Err(err, 500).Throw() + } + files = append(files, Script{ + Name: name, + Type: typ, + Content: content, + }) + } + } + return files +} + +func getTypeName(path string) (name string, typ string) { + namer := strings.Split(path, ".") + nametypes := strings.Split(namer[0], "/") + name = strings.Join(nametypes[1:], ".") + typ = nametypes[0] + + return name, typ +} diff --git a/load_test.go b/load_test.go index 017a209f..f62ad923 100644 --- a/load_test.go +++ b/load_test.go @@ -1,6 +1,7 @@ package main import ( + "path" "testing" "github.com/stretchr/testify/assert" @@ -10,3 +11,20 @@ func TestLoad(t *testing.T) { err := Load() assert.Nil(t, err) } + +// 从文件系统载入引擎文件 +func TestLoadEngineFS(t *testing.T) { + root := "fs://" + path.Join(cfg.Source, "/xiang") + assert.NotPanics(t, func() { + LoadEngine(root) + }) + +} + +// 从BinDataz载入引擎文件 +func TestLoadEngineBin(t *testing.T) { + root := "bin://xiang" + assert.NotPanics(t, func() { + LoadEngine(root) + }) +} diff --git a/ui/index.html b/ui/index.html new file mode 100644 index 00000000..3b18e512 --- /dev/null +++ b/ui/index.html @@ -0,0 +1 @@ +hello world diff --git a/xiang/apis/sys.http.json b/xiang/apis/sys.http.json new file mode 100644 index 00000000..a5f9ea3d --- /dev/null +++ b/xiang/apis/sys.http.json @@ -0,0 +1,19 @@ +{ + "name": "系统接口", + "version": "1.0.0", + "description": "系统接口API", + "group": "sys", + "guard": "bearer-jwt", + "paths": [ + { + "path": "/info/:id", + "method": "GET", + "process": "models.user.Find", + "in": ["$param.id", ":params"], + "out": { + "status": 200, + "type": "application/json" + } + } + ] +} diff --git a/xiang/flows/table/get.flow.json b/xiang/flows/table/get.flow.json new file mode 100644 index 00000000..669ff2da --- /dev/null +++ b/xiang/flows/table/get.flow.json @@ -0,0 +1,23 @@ +{ + "label": "最新信息", + "version": "1.0.0", + "description": "最新信息", + "nodes": [ + { + "name": "user", + "process": "models.user.get", + "args": [ + { + "select": ["id", "name", "mobile"], + "limit": 20, + "orders": [{ "column": "created_at", "option": "desc" }], + "wheres": [ + { "column": "status", "value": "enabled" }, + { "column": "name", "value": "{{$in.0}}", "op": "like" } + ] + } + ], + "outs": ["{{$out}}"] + } + ] +} diff --git a/xiang/models/user.json b/xiang/models/user.json new file mode 100644 index 00000000..30cef2e5 --- /dev/null +++ b/xiang/models/user.json @@ -0,0 +1,320 @@ +{ + "name": "用户", + "table": { + "name": "user", + "comment": "用户表", + "engine": "InnoDB" + }, + "columns": [ + { "label": "ID", "name": "id", "type": "ID" }, + { + "label": "厂商", + "name": "manu_id", + "type": "bigInteger", + "length": 50, + "comment": "所属厂商", + "nullable": true, + "index": true, + "validations": [ + { + "method": "typeof", + "args": ["integer"], + "message": "{{input}}类型错误, {{label}}应为数字" + }, + { + "method": "min", + "args": [0], + "message": "{{label}}应大于0" + } + ] + }, + { + "label": "类型", + "name": "type", + "type": "enum", + "option": ["admin", "staff", "user"], + "comment": "账号类型 admin 管理员, staff 员工, user 用户", + "default": "staff", + "index": true, + "validations": [ + { + "method": "typeof", + "args": ["string"], + "message": "{{input}}类型错误, {{label}}应该为字符串" + }, + { + "method": "enum", + "args": ["admin", "staff", "user"], + "message": "{{input}}不在许可范围, {{label}}应该为 admin/staff/user" + } + ] + }, + { + "label": "手机号", + "name": "mobile", + "type": "string", + "length": 50, + "comment": "手机号", + "index": true, + "crypt": "AES", + "validations": [ + { + "method": "typeof", + "args": ["string"], + "message": "{{input}}类型错误, {{label}}应该为字符串" + }, + { + "method": "pattern", + "args": ["^1[3-9]\\d{9}$"], + "message": "{{input}}格式错误" + } + ] + }, + { + "label": "登录密码", + "name": "password", + "type": "string", + "length": 256, + "comment": "登录密码", + "crypt": "PASSWORD", + "index": true, + "validations": [ + { + "method": "typeof", + "args": ["string"], + "message": "{{input}}类型错误, {{label}}应该为字符串" + }, + { + "method": "minLength", + "args": [6], + "message": "{{label}}应该由6-18位,大小写字母、数字和符号构成" + }, + { + "method": "maxLength", + "args": [18], + "message": "{{label}}应该由6-18位,大小写字母、数字和符号构成" + }, + { + "method": "pattern", + "args": ["[0-9]+"], + "message": "{{label}}应该至少包含一个数字" + }, + { + "method": "pattern", + "args": ["[A-Z]+"], + "message": "{{label}}应该至少包含一个大写字母" + }, + { + "method": "pattern", + "args": ["[a-z]+"], + "message": "{{label}}应该至少包含一个小写字母" + }, + { + "method": "pattern", + "args": ["[@#$&*]+"], + "message": "{{label}}应该至少包含一个符号" + } + ] + }, + { + "label": "姓名", + "name": "name", + "type": "string", + "length": 80, + "comment": "姓名", + "index": true, + "validations": [ + { + "method": "typeof", + "args": ["string"], + "message": "{{input}}类型错误, {{label}}应该为字符串" + }, + { + "method": "minLength", + "args": [2], + "message": "{{label}}至少需要2个字" + }, + { + "method": "maxLength", + "args": [40], + "message": "{{label}}不能超过20个字" + } + ] + }, + { + "label": "身份证号码", + "name": "idcard", + "type": "string", + "length": 256, + "comment": "身份证号码", + "crypt": "AES", + "nullable": true, + "index": true, + "validations": [ + { + "method": "typeof", + "args": ["string"], + "message": "{{input}}类型错误, {{label}}应该为字符串" + }, + { + "method": "pattern", + "args": ["^(\\d{18})|(\\d{14}X)$"], + "message": "{{label}}格式错误" + } + ] + }, + { + "label": "账户余额", + "name": "balance", + "type": "integer", + "length": 20, + "comment": "账户余额(冗余)", + "default": 0, + "index": true, + "validations": [ + { + "method": "typeof", + "args": ["integer"], + "message": "{{input}}类型错误, {{label}}应为数字" + }, + { + "method": "min", + "args": [0], + "message": "{{label}}应大于0" + } + ] + }, + { + "label": "API Key", + "name": "key", + "type": "string", + "length": 256, + "comment": "API Key", + "nullable": true, + "unique": true, + "validations": [ + { + "method": "typeof", + "args": ["string"], + "message": "{{input}}类型错误, {{label}}应该为字符串" + }, + { + "method": "pattern", + "args": ["^[0-9A-Za-z@#$&*]{8}$"], + "message": " {{label}}应该由8位,大小写字母、数字和符号构成" + } + ] + }, + { + "label": "API 密钥", + "name": "secret", + "type": "string", + "length": 256, + "nullable": true, + "crypt": "AES", + "comment": "API 密钥", + "index": true, + "validations": [ + { + "method": "typeof", + "args": ["string"], + "message": "{{input}}类型错误, {{label}}应该为字符串" + }, + { + "method": "pattern", + "args": ["^[0-9A-Za-z@#$&*]{32}$"], + "message": "{{label}}应该由32位,大小写字母、数字和符号构成" + } + ] + }, + { + "label": "简历", + "name": "resume", + "type": "text", + "comment": "简历", + "nullable": true + }, + { + "label": "扩展信息", + "name": "extra", + "type": "json", + "comment": "扩展信息", + "nullable": true + }, + { + "label": "状态", + "comment": "用户状态 enabled 有效, disabled 无效", + "name": "status", + "type": "enum", + "default": "enabled", + "option": ["enabled", "disabled"], + "index": true, + "validations": [ + { + "method": "typeof", + "args": ["string"], + "message": "{{input}}类型错误, {{label}}应该为字符串" + }, + { + "method": "enum", + "args": ["enabled", "disabled"], + "message": "{{input}}不在许可范围, {{label}}应该为 enabled/disabled" + } + ] + } + ], + "relations": {}, + "values": [ + { + "name": "管理员", + "manu_id": 1, + "type": "admin", + "idcard": "230624198301170015", + "mobile": "13900001111", + "password": "cvSK@RY6", + "key": "FB3fxCeQ", + "secret": "XMTdNRVigbgUiAPdiJCfaWgWcz2PaQXw", + "status": "enabled", + "extra": { "sex": "男" } + }, + { + "name": "员工", + "manu_id": 1, + "type": "staff", + "idcard": "23082619820207024X", + "mobile": "13900002222", + "password": "qV@uT1DI", + "key": "JDh2ZiUt", + "secret": "wBeYjL7FjbcvpAdBrxtDFfjydsoPKhRN", + "status": "enabled", + "extra": { "sex": "女" } + }, + { + "name": "用户", + "manu_id": 2, + "type": "user", + "idcard": "23082619820207004X", + "mobile": "13900003333", + "password": "qV@uT1DI", + "key": "XZ12MiPz", + "secret": "wBeYjL7FjbcvpAdBrxtDFfjydsoPKhRN", + "status": "enabled", + "extra": { "sex": "女" } + } + ], + "indexes": [ + { + "comment": "厂商用户", + "name": "manu_id_mobile_unique", + "columns": ["manu_id", "mobile"], + "type": "unique" + }, + { + "comment": "简历全文检索", + "name": "resume_fulltext", + "columns": ["resume"], + "type": "fulltext" + } + ], + "option": { "timestamps": true, "soft_deletes": true } +} diff --git a/xiang_test.go b/xiang_test.go index 9d7de322..8566a419 100644 --- a/xiang_test.go +++ b/xiang_test.go @@ -6,7 +6,7 @@ import ( "github.com/stretchr/testify/assert" ) -func TestMain(t *testing.T) { +func TestMainCMD(t *testing.T) { assert.NotPanics(t, func() { main() })