Merge branch 'YaoApp:main' into main

This commit is contained in:
Max 2022-09-01 13:15:04 +08:00 committed by GitHub
commit 2e1ef3ecb2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
174 changed files with 5544 additions and 1518 deletions

View file

@ -16,6 +16,7 @@ jobs:
run: |
mkdir -p ./pr
echo ${{ github.event.number }} > ./pr/NR
echo ${{ github.event.pull_request.head.sha }} > ./pr/SHA
- uses: actions/upload-artifact@v2
with:
name: pr

View file

@ -7,10 +7,28 @@ on:
workflows: ["Receive PR"]
types:
- completed
env:
YAO_DEV: ${{ github.WORKSPACE }}
YAO_ENV: development
YAO_ROOT: ${{ github.WORKSPACE }}/tests
YAO_HOST: 0.0.0.0
YAO_PORT: 5099
YAO_SESSION: "memory"
YAO_LOG: "./logs/application.log"
YAO_LOG_MODE: "TEXT"
YAO_JWT_SECRET: "bLp@bi!oqo-2U+hoTRUG"
YAO_DB_AESKEY: "ZLX=T&f6refeCh-ro*r@"
OSS_TEST_ID: ${{ secrets.OSS_TEST_ID}}
OSS_TEST_SECRET: ${{ secrets.OSS_TEST_SECRET}}
ROOT_PLUGIN: ${{ github.WORKSPACE }}/../../../data/gou-unit/plugins
jobs:
upload:
UnitTest:
runs-on: ubuntu-latest
strategy:
matrix:
go: [1.18.2]
db: [MySQL8.0, MySQL5.7, SQLite3]
if: >
${{ github.event.workflow_run.event == 'pull_request' &&
github.event.workflow_run.conclusion == 'success' }}
@ -35,21 +53,147 @@ jobs:
});
var fs = require('fs');
fs.writeFileSync('${{github.workspace}}/pr.zip', Buffer.from(download.data));
- name: "Unzip"
- name: "Read NR & SHA"
run: |
unzip pr.zip
ls -l
cat NR
cat SHA
echo HEAD=$(cat SHA) >> $GITHUB_ENV
echo NR=$(cat NR) >> $GITHUB_ENV
- name: "Comment on PR"
uses: actions/github-script@v3
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const { NR } = process.env
var fs = require('fs');
var issue_number = Number(fs.readFileSync('./NR'));
var issue_number = NR;
await github.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue_number,
body: 'Everything is OK. Thank you for the PR!'
body: 'Thank you for the PR! The ${{ matrix.db }} test workflow is running, the results of the run will be commented later.'
});
- name: Setup Cache
uses: actions/cache@v2
with:
path: |
~/.cache/go-build
~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Checkout Kun
uses: actions/checkout@v2
with:
repository: yaoapp/kun
path: kun
- name: Checkout Xun
uses: actions/checkout@v2
with:
repository: yaoapp/xun
path: xun
- name: Checkout Gou
uses: actions/checkout@v2
with:
repository: yaoapp/gou
path: gou
- name: Checkout V8Go
uses: actions/checkout@v2
with:
repository: rogchap/v8go
ref: 5e91d3d9dcabd2986f901b6b31590e49fc3c4dd8
path: v8go
- name: Move Kun, Xun, Gou, V8Go
run: |
mv kun ../
mv xun ../
mv gou ../
mv v8go ../
ls -l .
ls -l ../
- name: Checkout pull request HEAD commit
uses: actions/checkout@v2
with:
ref: ${{ env.HEAD }}
- name: Setup ${{ matrix.db }}
uses: ./.github/actions/setup-db
with:
kind: "${{ matrix.db }}"
db: "xiang"
user: "xiang"
password: ${{ secrets.UNIT_PASS }}
- name: Setup Go ${{ matrix.go }}
uses: actions/setup-go@v3
with:
go-version: ${{ matrix.go }}
- name: Setup Go Tools
run: |
make tools
- name: Setup ENV & Host
env:
PASSWORD: ${{ secrets.UNIT_PASS }}
run: |
sudo echo "127.0.0.1 local.iqka.com" | sudo tee -a /etc/hosts
echo "YAO_DB_DRIVER=$DB_DRIVER" >> $GITHUB_ENV
echo "GITHUB_WORKSPACE:\n" && ls -l $GITHUB_WORKSPACE
if [ "$DB_DRIVER" = "mysql" ]; then
echo "YAO_DB_PRIMARY=$DB_USER:$PASSWORD@$DB_HOST" >> $GITHUB_ENV
elif [ "$DB_DRIVER" = "postgres" ]; then
echo "YAO_DB_PRIMARY=postgres://$DB_USER:$PASSWORD@$DB_HOST" >> $GITHUB_ENV
else
echo "YAO_DB_PRIMARY=$YAO_ROOT/$DB_HOST" >> $GITHUB_ENV
fi
echo ".:\n" && ls -l .
echo "..:\n" && ls -l ..
ping -c 1 -t 1 local.iqka.com
- name: Test Prepare
run: |
make vet
make fmt-check
make misspell-check
make plugin
mkdir -p $YAO_ROOT/plugins/
mv $ROOT_PLUGIN/user.so $YAO_ROOT/plugins/
ls -l $YAO_ROOT/plugins/
make migrate
- name: Run test
run: |
make test
- name: Codecov Report
uses: codecov/codecov-action@v2
with:
token: ${{ secrets.CODECOV_TOKEN }} # not required for public repos
- name: "Comment on PR"
uses: actions/github-script@v3
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const { NR } = process.env
var fs = require('fs');
var issue_number = NR;
await github.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue_number,
body: '✨DONE✨ ${{ matrix.db }} passed.'
});

View file

@ -14,7 +14,7 @@ jobs:
release:
strategy:
matrix:
go: [1.17]
go: [1.18.2]
runs-on: "ubuntu-latest"
steps:
- name: Arm Build
@ -55,7 +55,6 @@ jobs:
with:
repository: yaoapp/gou
path: gou
token: ${{ secrets.TOKEN }}
- name: Checkout V8Go
uses: actions/checkout@v2
@ -69,7 +68,6 @@ jobs:
with:
repository: yaoapp/xgen
path: ui
token: ${{ secrets.TOKEN }}
- name: Move Kun, Xun, Gou, UI, V8Go
run: |
@ -85,17 +83,13 @@ jobs:
uses: actions/checkout@v2
- name: Setup Go ${{ matrix.go }}
uses: actions/setup-go@v2
uses: actions/setup-go@v3
with:
go-version: ${{ matrix.go }}
- name: Setup Go Tools
run: |
if [[ "${GO111MODULE}" = "on" ]]; then go mod download; fi
if [[ "${GO111MODULE}" = "on" ]]; then export PATH="${GOPATH}/bin:${GOROOT}/bin:${PATH}"; fi
if [[ "${GO111MODULE}" = "on" ]]; then make tools; fi
go get github.com/go-bindata/go-bindata/...
go get github.com/elazarl/go-bindata-assetfs/...
make tools
- name: Make Artifacts Linux
run: |
@ -114,15 +108,15 @@ jobs:
run: |
for file in ./dist/release/*; do coscmd upload $file /archives/; done;
- name: Configure COS For Beijing
env:
SECRET_ID: ${{ secrets.COS_ID }}
SECRET_KEY: ${{ secrets.COS_KEY }}
BUCKET: release-bj-1252011659
REGION: ap-beijing
run: |
coscmd config -a $SECRET_ID -s $SECRET_KEY -b $BUCKET -r $REGION
# - name: Configure COS For Beijing
# env:
# SECRET_ID: ${{ secrets.COS_ID }}
# SECRET_KEY: ${{ secrets.COS_KEY }}
# BUCKET: release-bj-1252011659
# REGION: ap-beijing
# run: |
# coscmd config -a $SECRET_ID -s $SECRET_KEY -b $BUCKET -r $REGION
- name: Push To Beijing
run: |
for file in ./dist/release/*; do coscmd upload $file /archives/; done;
# - name: Push To Beijing
# run: |
# for file in ./dist/release/*; do coscmd upload $file /archives/; done;

View file

@ -14,7 +14,7 @@ jobs:
release:
strategy:
matrix:
go: [1.17]
go: [1.18.2]
runs-on: "macos-11"
steps:
- name: Install coscmd
@ -47,7 +47,6 @@ jobs:
with:
repository: yaoapp/gou
path: gou
token: ${{ secrets.TOKEN }}
- name: Checkout V8Go
uses: actions/checkout@v2
@ -61,7 +60,6 @@ jobs:
with:
repository: yaoapp/xgen
path: ui
token: ${{ secrets.TOKEN }}
- name: Move Kun, Xun, Gou, UI, V8Go
run: |
@ -77,17 +75,13 @@ jobs:
uses: actions/checkout@v2
- name: Setup Go ${{ matrix.go }}
uses: actions/setup-go@v2
uses: actions/setup-go@v3
with:
go-version: ${{ matrix.go }}
- name: Setup Go Tools
run: |
if [[ "${GO111MODULE}" = "on" ]]; then go mod download; fi
if [[ "${GO111MODULE}" = "on" ]]; then export PATH="${GOPATH}/bin:${GOROOT}/bin:${PATH}"; fi
if [[ "${GO111MODULE}" = "on" ]]; then make tools; fi
go get github.com/go-bindata/go-bindata/...
go get github.com/elazarl/go-bindata-assetfs/...
make tools
- name: Make Artifacts MacOS
run: |
@ -106,15 +100,15 @@ jobs:
run: |
for file in ./dist/release/*; do coscmd upload $file /archives/; done;
- name: Configure COS For Beijing
env:
SECRET_ID: ${{ secrets.COS_ID }}
SECRET_KEY: ${{ secrets.COS_KEY }}
BUCKET: release-bj-1252011659
REGION: ap-beijing
run: |
coscmd config -a $SECRET_ID -s $SECRET_KEY -b $BUCKET -r $REGION
# - name: Configure COS For Beijing
# env:
# SECRET_ID: ${{ secrets.COS_ID }}
# SECRET_KEY: ${{ secrets.COS_KEY }}
# BUCKET: release-bj-1252011659
# REGION: ap-beijing
# run: |
# coscmd config -a $SECRET_ID -s $SECRET_KEY -b $BUCKET -r $REGION
- name: Push To Beijing
run: |
for file in ./dist/release/*; do coscmd upload $file /archives/; done;
# - name: Push To Beijing
# run: |
# for file in ./dist/release/*; do coscmd upload $file /archives/; done;

View file

@ -24,7 +24,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
go: [1.17]
go: [1.18.2]
db: [MySQL8.0, MySQL5.7, SQLite3]
steps:
- name: Setup Cache
@ -80,22 +80,20 @@ jobs:
kind: "${{ matrix.db }}"
db: "xiang"
user: "xiang"
password: 123456
password: ${{ secrets.UNIT_PASS }}
- name: Setup Go ${{ matrix.go }}
uses: actions/setup-go@v2
uses: actions/setup-go@v3
with:
go-version: ${{ matrix.go }}
- name: Setup Go Tools
run: |
if [[ "${GO111MODULE}" = "on" ]]; then go mod download; fi
if [[ "${GO111MODULE}" = "on" ]]; then export PATH="${GOPATH}/bin:${GOROOT}/bin:${PATH}"; fi
if [[ "${GO111MODULE}" = "on" ]]; then make tools; fi
make tools
- name: Setup ENV & Host
env:
PASSWORD: 123456
PASSWORD: ${{ secrets.UNIT_PASS }}
run: |
sudo echo "127.0.0.1 local.iqka.com" | sudo tee -a /etc/hosts
echo "YAO_DB_DRIVER=$DB_DRIVER" >> $GITHUB_ENV

View file

@ -1,9 +1,11 @@
GO ?= go
GIT ?= git
GOFMT ?= gofmt "-s"
PACKAGES ?= $(shell $(GO) list ./...)
VETPACKAGES ?= $(shell $(GO) list ./... | grep -v /examples/)
GOFILES := $(shell find . -name "*.go")
VERSION := $(shell grep 'const VERSION =' share/const.go |awk '{print $$4}' |sed 's/\"//g')
COMMIT := $(shell git log | head -n 1 | awk '{print substr($$2, 0, 12)}')
# ROOT_DIR := $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))
TESTFOLDER := $(shell $(GO) list ./... | grep -vE 'examples|tests*|config')
@ -76,9 +78,10 @@ misspell:
.PHONY: tools
tools:
go install golang.org/x/lint/golint; \
go install github.com/client9/misspell/cmd/misspell;
go install golang.org/x/lint/golint@latest; \
go install github.com/client9/misspell/cmd/misspell@latest; \
go install github.com/go-bindata/go-bindata/...@latest;
# make plugin
.PHONY: plugin
plugin:
@ -130,6 +133,9 @@ artifacts-linux: clean
rm -rf .tmp/data
rm -rf .tmp/ui
# Replace PRVERSION
sed -ie "s/const PRVERSION = \"DEV\"/const PRVERSION = \"${COMMIT}\"/g" share/const.go
# Making artifacts
mkdir -p dist
CGO_ENABLED=1 CGO_LDFLAGS="-static" GOOS=linux GOARCH=amd64 go build -v -o dist/yao-${VERSION}-linux-amd64
@ -141,6 +147,10 @@ artifacts-linux: clean
ls -l dist/release/
dist/release/yao-${VERSION}-linux-amd64 version
# Reset const
# cp -f share/const.goe share/const.go
# rm -f share/const.goe
# make artifacts-macos
.PHONY: artifacts-macos
artifacts-macos: clean
@ -158,6 +168,9 @@ artifacts-macos: clean
rm -rf .tmp/data
rm -rf .tmp/ui
# Replace PRVERSION
sed -ie "s/const PRVERSION = \"DEV\"/const PRVERSION = \"${COMMIT}\"/g" share/const.go
# Making artifacts
mkdir -p dist
CGO_ENABLED=1 GOOS=darwin GOARCH=amd64 go build -v -o dist/yao-${VERSION}-darwin-amd64
@ -169,6 +182,10 @@ artifacts-macos: clean
ls -l dist/release/
dist/release/yao-${VERSION}-darwin-amd64 version
# Reset const
# cp -f share/const.goe share/const.go
# rm -f share/const.goe
.PHONY: debug
debug: clean
mkdir -p dist/release
@ -180,11 +197,19 @@ debug: clean
go-bindata -fs -pkg data -o data/bindata.go -prefix ".tmp/data/" .tmp/data/...
rm -rf .tmp/data
# Replace PRVERSION
sed -ie "s/const PRVERSION = \"DEV\"/const PRVERSION = \"${COMMIT}-debug\"/g" share/const.go
# Making artifacts
mkdir -p dist
CGO_ENABLED=1 go build -v -o dist/release/yao-debug
chmod +x dist/release/yao-debug
# Reset const
cp -f share/const.goe share/const.go
rm -f share/const.goe
.PHONY: release
release: clean
mkdir -p dist/release
@ -193,7 +218,37 @@ release: clean
# Building UI
git clone https://github.com/YaoApp/xgen.git .tmp/ui
sed -ie "s/url('\/icon/url('\/xiang\/icon/g" .tmp/ui/public/icon/md_icon.css
cd .tmp/ui && npm install && npm run build
cd .tmp/ui && cnpm install && npm run build
# Packing
mkdir -p .tmp/data
cp -r .tmp/ui/dist .tmp/data/ui
cp -r yao .tmp/data/
go-bindata -fs -pkg data -o data/bindata.go -prefix ".tmp/data/" .tmp/data/...
rm -rf .tmp/data
rm -rf .tmp/ui
# Replace PRVERSION
sed -ie "s/const PRVERSION = \"DEV\"/const PRVERSION = \"${COMMIT}\"/g" share/const.go
# Making artifacts
mkdir -p dist
CGO_ENABLED=1 go build -v -o dist/release/yao
chmod +x dist/release/yao
# Reset const
cp -f share/const.goe share/const.go
rm share/const.goe
.PHONY: linux-release
linux-release: clean
mkdir -p dist/release
mkdir .tmp
# Building UI
git clone https://github.com/YaoApp/xgen.git .tmp/ui
sed -ie "s/url('\/icon/url('\/xiang\/icon/g" .tmp/ui/public/icon/md_icon.css
cd .tmp/ui && yarn install && yarn build
# Packing
mkdir -p .tmp/data
@ -205,7 +260,7 @@ release: clean
# Making artifacts
mkdir -p dist
CGO_ENABLED=1 go build -v -o dist/release/yao
CGO_ENABLED=1 CGO_LDFLAGS="-static" go build -v -o dist/release/yao
chmod +x dist/release/yao
# make clean

View file

@ -15,8 +15,8 @@
Twitter
</a>
·
<a aria-label="slack" href="https://join.slack.com/t/yaoapps/shared_invite/zt-13dm0cwvo-R9Q8xFGbrLZUffeygm9tXQ" target="_blank">
Slack
<a aria-label="discord" href="https://discord.gg/nsKmCXwvxU" target="_blank">
Discord
</a>
</p>
@ -35,17 +35,35 @@
</a>
</p>
![intro](docs/1.intro.png)
![intro](docs/architecture.png)
[中文介绍](README.zh-CN.md)
YAO is an open-source application engine, written in Golang, in the form of a command-line tool that can be downloaded and used immediately. It is suitable for developing business systems, website/APP API, admin panel, self-built low-code platforms, etc.
YAO adopts a flow-based programming model to implement various functions by writing YAO DSL (Logical Description in JSON format) or using JavaScript to write processes. The YAO DSL can be written in several ways:
1. Purely hand-written
2. Use automated scripts to generate contextual logic
3. Use the visual editor to create by "drag and drop"
**Discord:** https://discord.gg/nsKmCXwvxU
**Documentation:** https://yaoapps.com/en-US/doc
## Demo
### Customer Relationship Management System
A Customer Relationship Management System developed by Yao.
[https://demo-crm.yaoapps.com](https://demo-crm.yaoapps.com/xiang/login/admin?autoLogin=true)
DEMO: [View Demo](https://demo-crm.yaoapps.com/xiang/login/admin?autoLogin=true)
SOURCE: [https://github.com/YaoApp/demo-crm](https://github.com/YaoApp/demo-crm)
INFRA: https://letsinfra.com/openapp/crm
### Intelligent warehouse management system
@ -55,7 +73,7 @@ An example of cloud + edge IoT application, an unattended intelligent warehouse
## Introduce
**Yao allows developers to create web services by processes.** Yao is a low-code engine that creates a database model, writes API services, and describes dashboard interface just by JSON for web & hardware, no code, and 10x productivity.
**Yao allows developers to create web services by processes.** Yao is a app engine that creates a database model, writes API services, and describes dashboard interface just by JSON for web & hardware, and 10x productivity.
Yao is based on the **flow-based** programming idea, developed in the **Go** language, and supports multiple ways to expand the data stream processor. This makes Yao extremely versatile, which can replace programming languages in most scenarios, and is 10 times more efficient than traditional programming languages in terms of reusability and coding efficiency; application performance and resource ratio Better than **PHP**, **JAVA** and other languages.
@ -69,7 +87,7 @@ Run the script under terminal: (MacOS/Linux)
curl -fsSL https://website.yaoapps.com/install.sh | bash
```
For Windows users, please refer to the Installation and Debugging chapter: [Installation and debugging](https://yaoapps.com/en-US/doc/a.Introduction/b.Install)
For Windows users, please refer to the Installation and Debugging chapter: [Installation and debugging](https://yaoapps.com/en-US/doc/Introduction/Install)
## Getting Started
@ -121,4 +139,4 @@ yao start
## About Yao
Yao's name is derived from the Chinese character **yao (yáo)**, the basic symbol that makes up the Eight Trigrams. The Eight Trigrams is a symbol system created by the ancient god Fuxi after observing and summarizing the laws of nature, which can refer to everything. Yao has two states of yin and yang, like 0 and 1. The transformation of yin and yang of Yao drives the replacement of Eight Trigrams, so as to summarize and record the development law of things.
Yao's name is derived from the Chinese character ** (yáo)**, the basic symbol that makes up the Eight Trigrams. The Eight Trigrams is a symbol system created by the ancient god Fuxi after observing and summarizing the laws of nature, which can refer to everything. Yao has two states of yin and yang, like 0 and 1. The transformation of yin and yang of Yao drives the replacement of Eight Trigrams, so as to summarize and record the development law of things.

View file

@ -3,33 +3,53 @@
[![UnitTest](https://github.com/YaoApp/yao/actions/workflows/unit-test.yml/badge.svg)](https://github.com/YaoApp/yao/actions/workflows/unit-test.yml)
[![codecov](https://codecov.io/gh/YaoApp/yao/branch/main/graph/badge.svg?token=294Y05U71J)](https://codecov.io/gh/YaoApp/yao)
![intro](docs/1.intro.png)
Yao 是一款支持快速创建 Web 服务和管理后台的开源低代码应用引擎。
![intro](docs/architecture.png)
[English](README.md)
YAO 是一款开源应用引擎,使用 Golang 编写,以一个命令行工具的形式存在, 下载即用。适合用于开发业务系统、网站/APP API 接口、管理后台、自建低代码平台等。
YAO 采用 flow-based 的编程模式,通过编写 YAO DSL (JSON 格式逻辑描述) 或使用 JavaScript 编写处理器,实现各种功能。 YAO DSL 可以有多种编写方式:
1. 纯手工编写
2. 使用自动化脚本,根据上下文逻辑生成
3. 使用可视化编辑器,通过“拖拉拽”制作
官网: [https://yaoapps.com](https://yaoapps.com)
文档: [https://yaoapps.com/doc](https://yaoapps.com/doc)
微信群: 添加微信 17192175099 备注(开发交流)
![微信群](docs/wechat.jpg)
## 演示
### 客户管理系统
使用 Yao 搭建的一套通用 CRM 管理系统。
[https://demo-crm.yaoapps.com](https://demo-crm.yaoapps.com/xiang/login/admin?autoLogin=true)
演示: [查看演示](https://demo-crm.yaoapps.com/xiang/login/admin?autoLogin=true)
源码: https://github.com/YaoApp/demo-crm
Infra 一键部署: https://letsinfra.com/openapp/crm
### 智能仓库管理系统
使用 Yao 搭建的云+边物联网应用支持人脸识别、RFID 的无人值守智能仓库管理系统。
[https://demo-wms.yaoapps.com](https://demo-crm.yaoapps.com/xiang/login/admin?autoLogin=true)
演示: [查看演示](https://demo-crm.yaoapps.com/xiang/login/admin?autoLogin=true)
源码: https://github.com/YaoApp/yao-wms
Infra 一键部署: https://letsinfra.com/openapp/wms
## 介绍
Yao 是一个只需使用 JSON 即可创建数据库模型、编写 API 接口、描述管理后台界面的低代码引擎,使用 Yao 构建的应用可运行在云端或物联网设备上。 开发者不需要写一行代码,就可以拥有 10 倍生产力。
Yao 是一个只需使用 JSON 即可创建数据库模型、编写 API 接口、描述管理后台界面的应用引擎,使用 Yao 构建的应用可运行在云端或物联网设备上。 开发者不需要写一行代码,就可以拥有 10 倍生产力。
Yao 基于 **flow-based** 编程思想,采用 **Go** 语言开发,支持多种方式扩展数据流处理器。这使得 Yao 具有极好的**通用性**,大部分场景下可以代替编程语言, 在复用性和编码效率上是传统编程语言的 **10 倍**;应用性能和资源占比上优于 **PHP**, **JAVA** 等语言。

View file

@ -3,6 +3,7 @@ package api
import (
"fmt"
"path/filepath"
"strings"
"github.com/yaoapp/gou"
"github.com/yaoapp/kun/log"
@ -23,14 +24,33 @@ func LoadFrom(dir string, prefix string) error {
if share.DirNotExists(dir) {
return fmt.Errorf("%s does not exists", dir)
}
messages := []string{}
err := share.Walk(dir, ".http.json", func(root, filename string) {
name := prefix + share.SpecName(root, filename)
content := share.ReadFile(filename)
_, err := gou.LoadAPIReturn(string(content), name)
_, err := gou.LoadAPIReturn(string(content), name, "bearer-jwt")
if err != nil {
log.With(log.F{"root": root, "file": filename}).Error(err.Error())
messages = append(messages, fmt.Sprintf("%s %s", name, err.Error()))
}
})
// Load WebSocket Server
err = share.Walk(dir, ".ws.json", func(root, filename string) {
name := prefix + share.SpecName(root, filename)
content := share.ReadFile(filename)
_, err := gou.LoadWebSocketServer(string(content), name)
if err != nil {
log.With(log.F{"root": root, "file": filename}).Error(err.Error())
messages = append(messages, fmt.Sprintf("%s %s", name, err.Error()))
}
})
if len(messages) > 0 {
return fmt.Errorf("%s", strings.Join(messages, ";"))
}
return err
}

View file

@ -5,6 +5,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/yaoapp/gou"
"github.com/yaoapp/gou/websocket"
"github.com/yaoapp/yao/config"
)
@ -20,5 +21,12 @@ func check(t *testing.T) {
for key := range gou.APIs {
keys = append(keys, key)
}
assert.Equal(t, 4, len(keys))
wskeys := []string{}
for key := range websocket.Upgraders {
wskeys = append(wskeys, key)
}
assert.Equal(t, 5, len(keys))
assert.Equal(t, 1, len(wskeys))
}

View file

@ -1,35 +1,32 @@
package app
import (
"fmt"
"os"
"path/filepath"
"strings"
jsoniter "github.com/json-iterator/go"
l "github.com/yaoapp/gou/lang"
"github.com/yaoapp/kun/exception"
"github.com/yaoapp/kun/log"
"github.com/yaoapp/kun/maps"
"github.com/yaoapp/yao/config"
"github.com/yaoapp/yao/data"
"github.com/yaoapp/yao/lang"
"github.com/yaoapp/yao/share"
"github.com/yaoapp/yao/xfs"
)
// langs 语言包
var langs = map[string]map[string]string{}
// Load 加载应用信息
// Load Application
func Load(cfg config.Config) {
Init(cfg)
LoadInfo(cfg.Root)
LoadLang(cfg)
lang := strings.ToLower(share.App.Lang)
// Load language packs
lang.Load(cfg)
// Set language pack
share.App.L = map[string]string{}
if l, has := langs[lang]; has {
share.App.L = l
if l.Default != nil {
share.App.L = l.Default.Global
}
// Load Info
LoadInfo(cfg.Root)
}
// L 语言包
@ -40,49 +37,6 @@ func L(word string) string {
return word
}
// Init 应用初始化
func Init(cfg config.Config) {
// // UI文件目录
// if _, err := os.Stat(cfg.RootUI); os.IsNotExist(err) {
// err := os.MkdirAll(cfg.RootUI, os.ModePerm)
// if err != nil {
// xlog.Printf("创建目录失败(%s) %s", cfg.RootUI, err)
// os.Exit(1)
// }
// content, err := data.Asset("xiang/data/index.html")
// if err != nil {
// xlog.Printf("读取文件失败(%s) %s", cfg.RootUI, err)
// os.Exit(1)
// }
// err = ioutil.WriteFile(filepath.Join(cfg.RootUI, "/index.html"), content, os.ModePerm)
// if err != nil {
// xlog.Printf("复制默认文件失败(%s) %s", cfg.RootUI, err)
// os.Exit(1)
// }
// }
// // 数据库目录
// if _, err := os.Stat(cfg.RootDB); os.IsNotExist(err) {
// err := os.MkdirAll(cfg.RootDB, os.ModePerm)
// if err != nil {
// xlog.Printf("创建目录失败(%s) %s", cfg.RootDB, err)
// os.Exit(1)
// }
// }
// // 文件数据目录
// if _, err := os.Stat(cfg.RootData); os.IsNotExist(err) {
// err := os.MkdirAll(cfg.RootData, os.ModePerm)
// if err != nil {
// xlog.Printf("创建目录失败(%s) %s", cfg.RootData, err)
// os.Exit(1)
// }
// }
}
// LoadInfo 应用信息
func LoadInfo(root string) {
info := defaultInfo()
@ -106,71 +60,15 @@ func LoadInfo(root string) {
info.Icons.Set("png", xfs.Encode(fs.MustReadFile("/yao/icons/icon.png")))
}
info.L = share.App.L
share.App = info
}
// LoadLang 加载语言包
func LoadLang(cfg config.Config) error {
var defaults = []share.Script{}
if os.Getenv("YAO_DEV") != "" {
defaults = share.GetFilesFS(filepath.Join(os.Getenv("YAO_DEV"), "yao"), ".json")
} else {
defaults = share.GetFilesBin("yao", ".json")
}
for _, lang := range defaults {
if lang.Type != "langs" {
continue
}
content := lang.Content
name := strings.ToLower(lang.Name) // 这个读取函数需要优化
lang := map[string]string{}
err := jsoniter.Unmarshal(content, &lang)
if err != nil {
log.With(log.F{"name": name}).Error(err.Error())
}
langs[name] = lang
}
if share.BUILDIN {
return LoadLangBuildIn("langs")
}
return LoadLangFrom(filepath.Join(cfg.Root, "langs"))
}
// LoadLangBuildIn 从制品中读取
func LoadLangBuildIn(dir string) error {
return nil
}
// LoadLangFrom 从特定目录加载
func LoadLangFrom(dir string) error {
if share.DirNotExists(dir) {
return fmt.Errorf("%s does not exists", dir)
}
err := share.Walk(dir, ".json", func(root, filename string) {
name := strings.ToLower(share.SpecName(root, filename))
content := share.ReadFile(filename)
lang := map[string]string{}
err := jsoniter.Unmarshal(content, &lang)
if err != nil {
log.With(log.F{"root": root, "file": filename}).Error(err.Error())
}
if _, has := langs[name]; !has {
langs[name] = map[string]string{}
}
for src, dst := range lang {
langs[name][src] = dst
}
})
return err
}
// defaultInfo 读取默认应用信息
func defaultInfo() share.AppInfo {
info := share.AppInfo{

View file

@ -1,6 +1,7 @@
package app
import (
"os"
"testing"
"github.com/stretchr/testify/assert"
@ -9,7 +10,8 @@ import (
)
func TestLoad(t *testing.T) {
os.Setenv("YAO_LANG", "zh-cn")
Load(config.Conf)
assert.Equal(t, "Yao", share.App.L["Yao"])
assert.Equal(t, "Xiang", share.App.L["象传"])
assert.Equal(t, "YAO", share.App.L["Yao"])
assert.Equal(t, "象传", share.App.L["Xiang"])
}

8
brain/README.md Normal file
View file

@ -0,0 +1,8 @@
# Brain
the robot who helps you build various types of applications
the robot has two modes :
1. Generate an App DSL based on the rules
2. Connect AI cloud to generate app DSL based on input ( Not Supported Yet )

View file

@ -0,0 +1 @@
package listen

View file

@ -0,0 +1 @@
package send

View file

@ -0,0 +1,7 @@
# Zouyu
Zouyu is a benevolent beast in ancient Chinese myths and legends. It is a very precious mythical beast in Lin's country. It is like a tiger, with colorful patterns on its body, and its tail is longer than its body. It looks like a tiger with white hair and black stripes, but it does not eat live animals. Travel thousands of miles a day.
## Functions
Connect a Database, generates a dashboard applications

View file

@ -0,0 +1,3 @@
package zouyu
type conn struct{}

View file

@ -0,0 +1,14 @@
package zouyu
// Run the data
func Run(dsn string) {}
func (conn *conn) models() {}
func (conn *conn) apis() {}
func (conn *conn) flows() {}
func (conn *conn) tables() {}
func (conn *conn) env() {}

18
brain/brain.go Normal file
View file

@ -0,0 +1,18 @@
package brain
// NewBehaviors Returns the behaviors based on input words
func NewBehaviors(words string) (*Behaviors, error) {
resp, err := NPL(words)
if err != nil {
return nil, err
}
return resp.Behaviors, nil
}
// Run the behaviors
func (behaviors *Behaviors) Run() {}
// NPL the Natural language processing
func NPL(words string) (*Response, error) {
return &Response{Behaviors: &Behaviors{}}, nil
}

11
brain/brain_test.go Normal file
View file

@ -0,0 +1,11 @@
package brain
import (
"fmt"
"testing"
)
func TestNewBehaviors(t *testing.T) {
behaviors, err := NewBehaviors("hello")
fmt.Println(behaviors, err)
}

1
brain/cli/README.md Normal file
View file

@ -0,0 +1 @@
# Command Line Interface

1
brain/cli/cli.go Normal file
View file

@ -0,0 +1 @@
package cli

3
brain/cloud/README.md Normal file
View file

@ -0,0 +1,3 @@
# Cloud
Connecting AI cloud to analyze behaviors

9
brain/types.go Normal file
View file

@ -0,0 +1,9 @@
package brain
// Behaviors the Behaviors struct
type Behaviors struct{}
// Response NPL Response
type Response struct {
Behaviors *Behaviors
}

1
brain/web/README.md Normal file
View file

@ -0,0 +1 @@
# WEB Interface

35
cert/cert.go Normal file
View file

@ -0,0 +1,35 @@
package cert
import (
"fmt"
"path/filepath"
"github.com/yaoapp/gou/ssl"
"github.com/yaoapp/kun/log"
"github.com/yaoapp/yao/config"
"github.com/yaoapp/yao/share"
)
// Load 加载API
func Load(cfg config.Config) error {
var root = filepath.Join(cfg.Root, "certs")
return LoadFrom(root, "")
}
// LoadFrom 从特定目录加载
func LoadFrom(dir string, prefix string) error {
if share.DirNotExists(dir) {
return fmt.Errorf("%s does not exists", dir)
}
err := share.Walk(dir, ".pem", func(root, filename string) {
name := prefix + share.SpecName(root, filename)
_, err := ssl.LoadCertificateFrom(filename, name)
if err != nil {
log.With(log.F{"root": root, "file": filename}).Error(err.Error())
}
})
return err
}

46
cert/cert_test.go Normal file
View file

@ -0,0 +1,46 @@
package cert
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/yaoapp/gou"
"github.com/yaoapp/gou/ssl"
"github.com/yaoapp/yao/config"
)
func TestLoad(t *testing.T) {
Load(config.Conf)
LoadFrom("not a path", "404.")
check(t)
}
func TestProcessSign(t *testing.T) {
Load(config.Conf)
args := []interface{}{"hello world", "private", "SHA256"}
signature, err := gou.NewProcess("ssl.Sign", args...).Exec()
if err != nil {
t.Fatal(err)
}
assert.Equal(t, "EDHf3C9TXEk7y8LzIk5czLefXZyGxcMDVMcbNuBBegDkTqnPsRQnhFtNOgCdox8lI3MzLatwjoljoMY4Qk+sHGd5mAHMpiREa1gRFSVYpA2xvXZ3+KsfOHAdICQrfUdy59QaJGo6iGPNGG8PQOXHPTVNn6LMfryat9+f4l21DPAZiT0RyCUgFZE3/Qv8Z/6J4AsIXMSKZD6BGPPHUxGe7UBrXZvcR5dX25EiNjuH2OO38YJnDiTRVw14UI5fk/mQrwRdezj5tSKFCyHt912BZExXtkHISiYFNTZ/2RhOup5Xx6o3GvrEOdshrnN80Lwu1Aaju+lnZp13hDz4P6hU7w==", signature)
}
func TestProcessVerify(t *testing.T) {
Load(config.Conf)
signature := "EDHf3C9TXEk7y8LzIk5czLefXZyGxcMDVMcbNuBBegDkTqnPsRQnhFtNOgCdox8lI3MzLatwjoljoMY4Qk+sHGd5mAHMpiREa1gRFSVYpA2xvXZ3+KsfOHAdICQrfUdy59QaJGo6iGPNGG8PQOXHPTVNn6LMfryat9+f4l21DPAZiT0RyCUgFZE3/Qv8Z/6J4AsIXMSKZD6BGPPHUxGe7UBrXZvcR5dX25EiNjuH2OO38YJnDiTRVw14UI5fk/mQrwRdezj5tSKFCyHt912BZExXtkHISiYFNTZ/2RhOup5Xx6o3GvrEOdshrnN80Lwu1Aaju+lnZp13hDz4P6hU7w=="
args := []interface{}{"hello world", signature, "cert", "SHA256"}
res, err := gou.NewProcess("ssl.Verify", args...).Exec()
if err != nil {
t.Fatal(err)
}
assert.True(t, res.(bool))
}
func check(t *testing.T) {
keys := []string{}
for key := range ssl.Certificates {
keys = append(keys, key)
}
assert.Equal(t, 3, len(keys))
}

View file

@ -6,6 +6,7 @@ import (
jsoniter "github.com/json-iterator/go"
"github.com/yaoapp/gou"
"github.com/yaoapp/gou/lang"
"github.com/yaoapp/kun/exception"
"github.com/yaoapp/kun/log"
"github.com/yaoapp/yao/config"
@ -65,9 +66,7 @@ func LoadFrom(dir string, prefix string) error {
// LoadChart 载入数据表格
func LoadChart(source []byte, name string) (*Chart, error) {
chart := &Chart{
Flow: gou.Flow{
Name: name,
},
Flow: &gou.Flow{Name: name},
}
err := jsoniter.Unmarshal(source, chart)
if err != nil {
@ -77,6 +76,12 @@ func LoadChart(source []byte, name string) (*Chart, error) {
chart.Prepare()
chart.SetupAPIs()
Charts[name] = chart
// Apply a language pack
if lang.Default != nil {
lang.Default.Apply(Charts[name])
}
return chart, nil
}

View file

@ -1,16 +1,22 @@
package chart
import (
"os"
"testing"
"github.com/stretchr/testify/assert"
"github.com/yaoapp/yao/config"
"github.com/yaoapp/yao/lang"
"github.com/yaoapp/yao/model"
"github.com/yaoapp/yao/query"
"github.com/yaoapp/yao/share"
)
func TestLoad(t *testing.T) {
os.Setenv("YAO_LANG", "zh-hk")
lang.Load(config.Conf)
share.DBConnect(config.Conf.DB)
model.Load(config.Conf)
query.Load(config.Conf)
@ -25,5 +31,15 @@ func check(t *testing.T) {
for key := range Charts {
keys = append(keys, key)
}
assert.Equal(t, 1, len(keys))
assert.Equal(t, 3, len(keys))
lang := Charts["lang"]
output := lang.Output.(map[string]interface{})
assert.Equal(t, "{{$in}}", output["參數"])
filters := lang.Page.Layout["filters"].([]interface{})
begin := filters[0].(map[string]interface{})
assert.Equal(t, "開始時間", begin["name"])
assert.Equal(t, "請選擇開始時間", lang.Filters["開始時間"].Input.Props["placeholder"])
}

125
chart/lang.go Normal file
View file

@ -0,0 +1,125 @@
package chart
// Lang for applying a language pack
func (chart *Chart) Lang(trans func(widget string, inst string, value *string) bool) {
inst := chart.Flow.Name
widget := "chart"
trans(widget, inst, &chart.Name)
trans(widget, inst, &chart.Label)
trans(widget, inst, &chart.Description)
trans(widget, inst, &chart.Page.Primary)
transMap(widget, inst, chart.Page.Layout, trans)
chart.Output = transAny(widget, inst, chart.Output, trans)
// Filters
for name, filter := range chart.Filters {
new := name
trans(widget, inst, &new)
trans(widget, inst, &filter.Label)
delete(chart.Filters, name)
// Props
transMap(widget, inst, filter.Input.Props, trans)
chart.Filters[new] = filter
}
}
func transAny(widget string, inst string, input interface{}, trans func(widget string, inst string, value *string) bool) interface{} {
switch input.(type) {
case []interface{}:
values := input.([]interface{})
transArr(widget, inst, values, trans)
input = values
break
case map[string]interface{}:
values := input.(map[string]interface{})
for name, value := range values {
new := name
newValue := value
switch value.(type) {
case string:
val := value.(string)
trans(widget, inst, &val)
newValue = val
break
case []interface{}:
vals := value.([]interface{})
transArr(widget, inst, vals, trans)
newValue = vals
break
case map[string]interface{}:
vals := value.(map[string]interface{})
transMap(widget, inst, vals, trans)
newValue = vals
break
}
trans(widget, inst, &new)
delete(values, name)
values[new] = newValue
}
input = values
break
}
return input
}
func transMap(widget string, inst string, values map[string]interface{}, trans func(widget string, inst string, value *string) bool) {
for key, value := range values {
switch value.(type) {
case string:
v := value.(string)
trans(widget, inst, &v)
values[key] = v
break
case []interface{}:
v := value.([]interface{})
transArr(widget, inst, v, trans)
values[key] = v
break
case map[string]interface{}:
v := value.(map[string]interface{})
transMap(widget, inst, v, trans)
values[key] = v
break
}
}
}
func transArr(widget string, inst string, values []interface{}, trans func(widget string, inst string, value *string) bool) {
for key, value := range values {
switch value.(type) {
case string:
v := value.(string)
trans(widget, inst, &v)
values[key] = v
break
case []interface{}:
v := value.([]interface{})
transArr(widget, inst, v, trans)
values[key] = v
break
case map[string]interface{}:
v := value.(map[string]interface{})
transMap(widget, inst, v, trans)
values[key] = v
break
}
}
}

View file

@ -33,6 +33,10 @@ func ProcessData(process *gou.Process) interface{} {
}
}
// with Session
chart.Flow.WithGlobal(process.Global)
chart.Flow.WithSID(process.Sid)
return chart.GetData(params)
}

View file

@ -3,12 +3,13 @@ package chart
import (
"net/url"
"testing"
"time"
"github.com/gin-gonic/gin"
"github.com/stretchr/testify/assert"
"github.com/yaoapp/gou"
"github.com/yaoapp/gou/session"
"github.com/yaoapp/kun/any"
"github.com/yaoapp/kun/utils"
"github.com/yaoapp/yao/config"
_ "github.com/yaoapp/yao/helper"
"github.com/yaoapp/yao/model"
@ -68,10 +69,36 @@ func TestProcessData(t *testing.T) {
}
process := gou.NewProcess("xiang.chart.Data", args...)
response := ProcessData(process)
utils.Dump(response)
// utils.Dump(response)
assert.NotNil(t, response)
res := any.Of(response).Map().Dot()
assert.Equal(t, "北京", res.Get("合并.0.城市"))
}
func TestProcessDataGlobalSession(t *testing.T) {
params := url.Values{
"from": []string{"1981-01-01", "1990-01-01"},
}
params.Set("to", "2049-12-31")
args := []interface{}{
"session",
params,
&gin.Context{},
}
sid := session.ID()
data := time.Now().String()
session.Global().ID(sid).Set("id", data)
process := gou.
NewProcess("xiang.chart.Data", args...).
WithSID(sid).
WithGlobal(map[string]interface{}{"foo": "bar"})
response := ProcessData(process)
res := any.Of(response).Map().Dot()
assert.Equal(t, data, res.Get("ID"))
}

View file

@ -7,7 +7,7 @@ import (
// Chart 图表格式
type Chart struct {
gou.Flow
*gou.Flow
APIs map[string]share.API `json:"apis,omitempty"`
Filters map[string]share.Filter `json:"filters,omitempty"`
Page share.Page `json:"page,omitempty"`

1
cmd/brain.go Normal file
View file

@ -0,0 +1 @@
package cmd

205
cmd/dump.go Normal file
View file

@ -0,0 +1,205 @@
package cmd
import (
"archive/zip"
"errors"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
"time"
"github.com/fatih/color"
"github.com/spf13/cobra"
"github.com/yaoapp/gou"
"github.com/yaoapp/kun/exception"
"github.com/yaoapp/yao/config"
"github.com/yaoapp/yao/engine"
)
var dumpModel string
var dumpCmd = &cobra.Command{
Use: "dump",
Short: L("Dump the application data"),
Long: L("Dump the application data"),
Run: func(cmd *cobra.Command, args []string) {
defer func() {
err := exception.Catch(recover())
if err != nil {
fmt.Println(color.RedString(L("Fatal: %s"), err.Error()))
}
}()
Boot()
path, err := filepath.Abs(".")
if err != nil {
fmt.Println(color.RedString(L("Fatal: %s"), err.Error()))
os.Exit(1)
}
output := filepath.Join(fmt.Sprintf("%s-%s.zip", filepath.Base(path), time.Now().Format("20060102150405")))
if len(args) > 0 {
output = args[0]
}
output, err = filepath.Abs(output)
if err != nil {
fmt.Println(color.RedString(L("Fatal: %s"), err.Error()))
os.Exit(1)
}
_, err = os.Stat(output)
if !errors.Is(err, os.ErrNotExist) {
fmt.Println(color.RedString("%s exists", output))
os.Exit(1)
}
// Load model
err = engine.Load(config.Conf)
if err != nil {
fmt.Println(color.RedString(L("Fatal: %s"), err.Error()))
os.Exit(1)
}
if dumpModel != "" {
fmt.Println(color.YellowString(L("Not supported yet")))
os.Exit(1)
return
}
// Export models
files := []string{}
for _, mod := range gou.Models {
fmt.Printf("\r%s", color.GreenString(L("Export the models: %s (%s)"), mod.Name, mod.MetaData.Table.Name))
jsonfiles, err := mod.Export(5000, func(curr, total int) {
fmt.Printf("\r%s", strings.Repeat(" ", 80))
fmt.Printf("\r%s", color.GreenString(L("Export the models: %s (%s) %d/%d"), mod.Name, mod.MetaData.Table.Name, curr, total))
})
if err != nil {
fmt.Println(color.RedString(L("Fatal: %s"), err.Error()))
os.Exit(1)
}
files = append(files, jsonfiles...)
}
fmt.Printf("\r%s", strings.Repeat(" ", 80))
fmt.Printf("\r%s\n", color.GreenString(L("Export the models: ✨DONE✨")))
// Compress files
err = zipfiles(files, output, func(file string) {
fmt.Printf("\r%s", strings.Repeat(" ", 80))
fmt.Printf("\r%s", color.GreenString(L("Compress the files: %s"), file))
})
fmt.Printf("\r%s", strings.Repeat(" ", 80))
fmt.Printf("\r%s\n", color.GreenString(L("Compress the files: ✨DONE✨")))
if err != nil {
fmt.Println(color.RedString(L("Fatal: %s"), err.Error()))
os.Exit(1)
}
fmt.Println(color.GreenString("File: %s", output))
},
}
// func init() {
// // dumpCmd.PersistentFlags().StringVarP(&dumpModel, "name", "n", "", L("Model name"))
// }
// gzipfiles
func zipfiles(files []string, output string, process func(file string)) error {
outpath := filepath.Dir(output)
os.MkdirAll(outpath, 0755)
outfile, err := os.Create(output)
if err != nil {
fmt.Println(color.RedString(L("Fatal: %s"), err.Error()))
os.Exit(1)
}
defer outfile.Close()
w := zip.NewWriter(outfile)
defer func() {
w.Close()
if err != nil {
fmt.Println(color.RedString(L("Fatal: %s"), err.Error()))
os.Exit(1)
}
}()
for _, file := range files {
addFile(w, file, "model", process)
}
// Add data path
dataPath := filepath.Join(config.Conf.Root, "data")
_, err = os.Stat(dataPath)
if err == nil {
addFolder(w, dataPath, "data", process)
}
return nil
}
func addFile(w *zip.Writer, file, baseInZip string, process func(file string)) {
process(filepath.Join(baseInZip, filepath.Base(file)))
dat, err := ioutil.ReadFile(file)
if err != nil {
fmt.Println(color.RedString(L("Fatal: %s"), err.Error()))
os.Exit(1)
}
// Add some files to the archive.
f, err := w.Create(filepath.Join(baseInZip, filepath.Base(file)))
if err != nil {
fmt.Println(color.RedString(L("Fatal: %s"), err.Error()))
os.Exit(1)
}
_, err = f.Write(dat)
if err != nil {
fmt.Println(color.RedString(L("Fatal: %s"), err.Error()))
os.Exit(1)
}
os.Remove(file)
}
func addFolder(w *zip.Writer, basePath, baseInZip string, process func(file string)) {
// Open the Directory
files, err := ioutil.ReadDir(basePath)
if err != nil {
fmt.Println(color.RedString(L("Fatal: %s"), err.Error()))
os.Exit(1)
}
for _, file := range files {
process(filepath.Join(baseInZip, file.Name()))
if !file.IsDir() {
dat, err := ioutil.ReadFile(filepath.Join(basePath, file.Name()))
if err != nil {
fmt.Println(color.RedString(L("Fatal: %s"), err.Error()))
os.Exit(1)
}
// Add some files to the archive.
f, err := w.Create(filepath.Join(baseInZip, file.Name()))
if err != nil {
fmt.Println(color.RedString(L("Fatal: %s"), err.Error()))
os.Exit(1)
}
_, err = f.Write(dat)
if err != nil {
fmt.Println(color.RedString(L("Fatal: %s"), err.Error()))
os.Exit(1)
}
} else if file.IsDir() {
// Recurse
newBase := filepath.Join(basePath, file.Name())
addFolder(w, newBase, filepath.Join(baseInZip, file.Name()), process)
}
}
}

View file

@ -301,7 +301,8 @@ func makeFile(name string, source string) {
makeFileContent(name, []byte(source))
}
func makeFileContent(filename string, content []byte) {
func makeFileContent(name string, content []byte) {
filename := filepath.Join(config.Conf.Root, name)
err := os.WriteFile(filename, content, 0644)
if err != nil {
fmt.Println(color.RedString(L("Fatal: %s"), err.Error()))

View file

@ -15,6 +15,7 @@ import (
var name string
var force bool = false
var resetModel bool = false
var migrateCmd = &cobra.Command{
Use: "migrate",
Short: L("Update database schema"),
@ -43,23 +44,57 @@ var migrateCmd = &cobra.Command{
if name != "" {
mod, has := gou.Models[name]
if has {
mod.Migrate(true)
fmt.Printf(color.WhiteString(L("Update schema model: %s (%s) "), mod.Name, mod.MetaData.Table.Name) + "\t")
if !has {
fmt.Println(color.RedString(L("Model: %s does not exits"), name))
return
}
if resetModel {
err := mod.DropTable()
if err != nil {
fmt.Printf(color.RedString(L("FAILURE\n%s"), err.Error()) + "\n")
return
}
}
err := mod.Migrate(false)
if err != nil {
fmt.Printf(color.RedString(L("FAILURE\n%s"), err.Error()) + "\n")
return
}
fmt.Printf(color.GreenString(L("SUCCESS")) + "\n")
return
}
// Do Stuff Here
for _, mod := range gou.Models {
fmt.Println(color.GreenString(L("Update schema model: %s (%s) "), mod.Name, mod.MetaData.Table.Name))
mod.Migrate(true)
fmt.Printf(color.WhiteString(L("Update schema model: %s (%s) "), mod.Name, mod.MetaData.Table.Name) + "\t")
if resetModel {
err := mod.DropTable()
if err != nil {
fmt.Printf(color.RedString(L("FAILURE\n%s"), err.Error()) + "\n")
continue
}
}
err := mod.Migrate(false)
if err != nil {
fmt.Printf(color.RedString(L("FAILURE\n%s"), err.Error()) + "\n")
continue
}
fmt.Printf(color.GreenString(L("SUCCESS")) + "\n")
}
fmt.Println(color.GreenString(L("✨DONE✨")))
// fmt.Println(color.GreenString(L("✨DONE✨")))
},
}
func init() {
migrateCmd.PersistentFlags().StringVarP(&name, "name", "n", "", L("Model name"))
migrateCmd.PersistentFlags().BoolVarP(&force, "force", "", false, L("Force migrate"))
migrateCmd.PersistentFlags().BoolVarP(&resetModel, "reset", "", false, L("Drop the table if exist"))
}

206
cmd/restore.go Normal file
View file

@ -0,0 +1,206 @@
package cmd
import (
"archive/zip"
"errors"
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"strings"
"time"
"github.com/fatih/color"
"github.com/spf13/cobra"
"github.com/yaoapp/gou"
"github.com/yaoapp/kun/exception"
"github.com/yaoapp/yao/config"
"github.com/yaoapp/yao/engine"
"github.com/yaoapp/yao/share"
)
var restoreForce bool = false
var restoreCmd = &cobra.Command{
Use: "restore",
Short: L("Restore the application data"),
Long: L("Restore the application data"),
Run: func(cmd *cobra.Command, args []string) {
defer func() {
err := exception.Catch(recover())
if err != nil {
fmt.Println(color.RedString(L("Fatal: %s"), err.Error()))
}
}()
if len(args) < 1 {
fmt.Println(color.RedString(L("Not enough arguments")))
fmt.Println(color.WhiteString(share.BUILDNAME + " help"))
os.Exit(1)
}
zipfile, err := filepath.Abs(args[0])
if err != nil {
fmt.Println(color.RedString(L("Fatal: %s"), err.Error()))
os.Exit(1)
}
Boot()
if !restoreForce && config.Conf.Mode == "production" {
fmt.Println(color.WhiteString(L("TRY:")), color.GreenString("%s restore --force", share.BUILDNAME))
exception.New(L("Retore is not allowed on production mode."), 403).Throw()
}
// Unzip files
dst := unzipFile(zipfile, func(file string) {
fmt.Printf("\r%s", strings.Repeat(" ", 80))
fmt.Printf("\r%s", color.GreenString(L("Unzip the file: %s"), file))
})
// 加载数据模型
err = engine.Load(config.Conf)
if err != nil {
fmt.Println(color.RedString(L("Fatal: %s"), err.Error()))
os.Exit(1)
}
// Restore models
restoreModels(filepath.Join(dst, "model"))
// Restore Data
restoreData(filepath.Join(dst, "data"))
// Clean
os.RemoveAll(dst)
fmt.Println(color.GreenString(L("✨DONE✨")))
},
}
func init() {
restoreCmd.PersistentFlags().BoolVarP(&restoreForce, "force", "", false, L("Force restore"))
}
func restoreData(basePath string) {
_, err := os.Stat(basePath)
if err != nil {
fmt.Println(color.RedString(L("Fatal: %s"), err.Error()))
os.Exit(1)
}
// Clean Data
dataPath := filepath.Join(config.Conf.Root, "data")
_, err = os.Stat(dataPath)
if err == nil {
os.RemoveAll(dataPath)
}
err = os.Rename(basePath, dataPath)
if err != nil {
fmt.Println(color.RedString(L("Fatal: %s"), err.Error()))
os.Exit(1)
}
}
func restoreModels(basePath string) {
files, err := ioutil.ReadDir(basePath)
if err != nil {
fmt.Println(color.RedString(L("Fatal: %s"), err.Error()))
os.Exit(1)
}
// Migrate models
for _, mod := range gou.Models {
fmt.Printf("\r%s", strings.Repeat(" ", 80))
fmt.Printf(color.GreenString(L("\rUpdate schema model: %s (%s) "), mod.Name, mod.MetaData.Table.Name))
err := mod.Migrate(true)
if err != nil {
fmt.Println(color.RedString(L("Fatal: %s"), err.Error()))
os.Exit(1)
}
}
fmt.Println("")
for _, file := range files {
namer := strings.Split(file.Name(), ".")
name := strings.Join(namer[:len(namer)-2], ".")
if mod, has := gou.Models[name]; has {
fmt.Printf("\r%s", strings.Repeat(" ", 80))
fmt.Printf(color.GreenString(L("\rRestore model: %s (%s) "), mod.Name, mod.MetaData.Table.Name))
err := mod.Import(filepath.Join(basePath, file.Name()))
if err != nil {
fmt.Println(color.RedString(L("Fatal: %s"), err.Error()))
os.Exit(1)
}
}
}
}
func unzipFile(file string, process func(file string)) string {
_, err := os.Stat(file)
if errors.Is(err, os.ErrNotExist) {
fmt.Println(color.RedString("%s not exists", file))
os.Exit(1)
}
if err != nil {
fmt.Println(color.RedString(L("Fatal: %s"), err.Error()))
os.Exit(1)
}
dst := filepath.Join(os.TempDir(), fmt.Sprintf("%s-%s", filepath.Base(file), time.Now().Format("20060102150405")))
os.MkdirAll(dst, 0755)
archive, err := zip.OpenReader(file)
if err != nil {
fmt.Println(color.RedString(L("Fatal: %s"), err.Error()))
os.Exit(1)
}
defer archive.Close()
for _, f := range archive.File {
filePath := filepath.Join(dst, f.Name)
process(f.Name)
if !strings.HasPrefix(filePath, filepath.Clean(dst)+string(os.PathSeparator)) {
fmt.Println(color.RedString(L("Fatal: invalid file path")))
os.Exit(1)
}
if f.FileInfo().IsDir() {
os.MkdirAll(filePath, os.ModePerm)
continue
}
if err := os.MkdirAll(filepath.Dir(filePath), os.ModePerm); err != nil {
fmt.Println(color.RedString(L("Fatal: %s"), err.Error()))
os.Exit(1)
}
dstFile, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
if err != nil {
fmt.Println(color.RedString(L("Fatal: %s"), err.Error()))
os.Exit(1)
}
fileInArchive, err := f.Open()
if err != nil {
fmt.Println(color.RedString(L("Fatal: %s"), err.Error()))
os.Exit(1)
}
if _, err := io.Copy(dstFile, fileInArchive); err != nil {
fmt.Println(color.RedString(L("Fatal: %s"), err.Error()))
os.Exit(1)
}
dstFile.Close()
fileInArchive.Close()
}
return dst
}

View file

@ -45,6 +45,7 @@ var langs = map[string]string{
"NEXT:": "下一步:",
"Listening": " 监听",
"✨LISTENING✨": "✨服务正在运行✨",
"✨STOPPED✨": "✨服务已停止✨",
"SessionPort": "会话服务端口",
"Force migrate": "强制更新数据表结构",
"Migrate is not allowed on production mode.": "Migrate 不能再生产环境下使用",
@ -91,6 +92,11 @@ func init() {
startCmd,
runCmd,
initCmd,
serviceCmd,
dumpCmd,
restoreCmd,
socketCmd,
websocketCmd,
)
// rootCmd.SetHelpCommand(helpCmd)
rootCmd.PersistentFlags().StringVarP(&appPath, "app", "a", "", L("Application directory"))
@ -105,42 +111,25 @@ func Execute() {
}
}
// Boot 设定配置 (这个逻辑有 BUG )
// Boot 设定配置
func Boot() {
if envFile == "" && appPath != "" {
root, err := filepath.Abs(appPath)
root := config.Conf.Root
if appPath != "" {
r, err := filepath.Abs(appPath)
if err != nil {
exception.New("Root error %s", 500, err.Error()).Throw()
}
root = r
}
if envFile != "" {
config.Conf = config.LoadFrom(envFile)
} else {
config.Conf = config.LoadFrom(filepath.Join(root, ".env"))
config.Conf.Root = root
if config.Conf.Mode == "production" {
config.Production()
} else if config.Conf.Mode == "development" {
config.Development()
}
return
}
if envFile != "" && appPath == "" { // 指定环境变量文件
// config.SetEnvFile(envFile)
config.Conf = config.LoadFrom(envFile)
}
if envFile != "" && appPath != "" {
root, err := filepath.Abs(appPath)
if err != nil {
exception.New("Root error %s", 500, err.Error()).Throw()
}
config.Conf = config.LoadFrom(envFile)
config.Conf.Root = root
if config.Conf.Mode == "production" {
config.Production()
} else if config.Conf.Mode == "development" {
config.Development()
}
if config.Conf.Mode == "production" {
config.Production()
} else if config.Conf.Mode == "development" {
config.Development()
}
}

View file

@ -20,6 +20,7 @@ var runCmd = &cobra.Command{
Short: L("Execute process"),
Long: L("Execute process"),
Run: func(cmd *cobra.Command, args []string) {
defer share.SessionStop()
defer gou.KillPlugins()
defer func() {
err := exception.Catch(recover())

173
cmd/service.go Normal file
View file

@ -0,0 +1,173 @@
package cmd
import (
"fmt"
"os"
"path/filepath"
"strings"
"github.com/fatih/color"
"github.com/spf13/cobra"
"github.com/yaoapp/gou"
"github.com/yaoapp/kun/exception"
"github.com/yaoapp/yao/config"
"github.com/yaoapp/yao/share"
)
var serviceCmd = &cobra.Command{
Use: "service",
Short: L("Service manager"),
Long: L("Service manager"),
Run: func(cmd *cobra.Command, args []string) {
loadService(config.Conf)
command := "ps"
if len(args) > 0 {
command = args[0]
}
if len(args) > 1 {
args = args[1:]
} else {
args = []string{}
}
switch command {
case "ps":
services()
break
case "install", "start", "stop", "remove", "status":
serviceManage(command, args)
break
default:
serviceHelp()
}
},
}
func serviceHelp() {
fmt.Println("Usage:")
fmt.Println(" ", color.GreenString(L("%s service [ps|start|stop|install|status] [all|name]"), share.BUILDNAME))
fmt.Println("")
}
func serviceManageAll(command string) {
for name := range gou.Services {
if name == "all" {
continue
}
serviceManage(command, []string{name})
}
}
func serviceManage(command string, args []string) {
if len(args) == 0 {
serviceHelp()
os.Exit(1)
}
defer func() {
err := exception.Catch(recover())
if err != nil {
fmt.Println(color.RedString(L("Fatal: %s"), err.Error()))
}
}()
name := args[0]
if name == "all" {
serviceManageAll(command)
return
}
srv := gou.SelectService(name)
var status = ""
var err error
switch command {
case "install":
status, err = srv.Install()
break
case "start":
status, err = srv.Start()
break
case "stop":
status, err = srv.Stop()
break
case "remove":
status, err = srv.Remove()
break
case "status":
status, err = srv.Status()
break
}
if err != nil {
fmt.Println(color.RedString(L("Fatal: %s"), err.Error()))
}
fmt.Println(status)
}
func services() {
for name, srv := range gou.Services {
status, err := srv.Status()
if err != nil {
status = err.Error()
}
fmt.Println(
color.GreenString("%s", name),
srv.Name, "....",
statusInfo(status),
)
}
fmt.Println("")
}
func statusInfo(message string) string {
if strings.Contains(message, "stopped") {
return statusStop()
} else if strings.Contains(message, "not installed") {
return statusNotInstalled()
} else if strings.Contains(message, "running") {
return statusRunning()
}
return statusUnknown()
}
func statusUnknown() string {
return fmt.Sprintf("[ %s ]", color.WhiteString("UNKNOWN"))
}
func statusRunning() string {
return fmt.Sprintf("[ %s ]", color.GreenString("RUNNING"))
}
func statusNotInstalled() string {
return fmt.Sprintf("[ %s ]", color.YellowString("NOT INSTALLED"))
}
func statusStop() string {
return fmt.Sprintf("[ %s ]", color.RedString("STOPED"))
}
// Load 加载API
func loadService(cfg config.Config) {
var root = filepath.Join(cfg.Root, "services")
loadServiceFrom(root, "")
}
func loadServiceFrom(dir string, prefix string) error {
if share.DirNotExists(dir) {
return fmt.Errorf("%s does not exists", dir)
}
err := share.Walk(dir, ".srv.json", func(root, filename string) {
name := prefix + share.SpecName(root, filename)
content := share.ReadFile(filename)
_, err := gou.LoadService(string(content), name)
if err != nil {
fmt.Println(color.RedString(L("Fatal: %s"), err.Error()))
os.Exit(1)
}
})
return err
}

110
cmd/socket.go Normal file
View file

@ -0,0 +1,110 @@
package cmd
import (
"fmt"
"strings"
"github.com/fatih/color"
jsoniter "github.com/json-iterator/go"
"github.com/spf13/cobra"
"github.com/yaoapp/gou"
"github.com/yaoapp/kun/exception"
"github.com/yaoapp/yao/config"
"github.com/yaoapp/yao/engine"
"github.com/yaoapp/yao/share"
)
var socketCmd = &cobra.Command{
Use: "socket",
Short: L("Open a socket connection"),
Long: L("Open a socket connection"),
Run: func(cmd *cobra.Command, args []string) {
defer share.SessionStop()
defer gou.KillPlugins()
defer func() {
err := exception.Catch(recover())
if err != nil {
fmt.Println(color.RedString(L("Fatal: %s"), err.Error()))
}
}()
Boot()
cfg := config.Conf
cfg.Session.IsCLI = true
engine.Load(cfg)
if len(args) < 1 {
fmt.Println(color.RedString(L("Not enough arguments")))
fmt.Println(color.WhiteString(share.BUILDNAME + " help"))
return
}
name := args[0]
pargs := []interface{}{}
for i, arg := range args {
if i == 0 {
continue
}
// 解析参数
if strings.HasPrefix(arg, "::") {
arg := strings.TrimPrefix(arg, "::")
var v interface{}
err := jsoniter.Unmarshal([]byte(arg), &v)
if err != nil {
fmt.Println(color.RedString(L("Arguments: %s"), err.Error()))
return
}
pargs = append(pargs, v)
fmt.Println(color.WhiteString("args[%d]: %s", i-1, arg))
} else if strings.HasPrefix(arg, "\\::") {
arg := "::" + strings.TrimPrefix(arg, "\\::")
pargs = append(pargs, arg)
fmt.Println(color.WhiteString("args[%d]: %s", i-1, arg))
} else {
pargs = append(pargs, arg)
fmt.Println(color.WhiteString("args[%d]: %s", i-1, arg))
}
}
socket, has := gou.Sockets[name]
if !has {
fmt.Println(color.RedString(L("%s not exists!"), name))
return
}
if socket.Mode != "client" {
fmt.Println(color.RedString(L("%s not supported yet!"), socket.Mode))
return
}
host := socket.Host
port := socket.Port
argsLen := len(pargs)
if argsLen > 0 {
if inputHost, ok := pargs[0].(string); ok {
host = inputHost
}
}
if argsLen > 1 {
if inputPort, ok := pargs[1].(string); ok {
port = inputPort
}
}
fmt.Println(color.WhiteString("\n---------------------------------"))
fmt.Println(color.WhiteString(socket.Name))
fmt.Println(color.WhiteString("---------------------------------"))
fmt.Println(color.GreenString("Mode: %s", socket.Mode))
fmt.Println(color.GreenString("Host: %s://%s", socket.Protocol, host))
fmt.Println(color.GreenString("Port: %s", port))
fmt.Println(color.WhiteString("--------------------------------------"))
err := socket.Open(pargs...)
if err != nil {
fmt.Println(color.RedString(L("%s"), err.Error()))
return
}
},
}

View file

@ -1,14 +1,21 @@
package cmd
import (
"context"
"fmt"
"os"
"os/signal"
"path/filepath"
"strings"
"syscall"
"time"
"github.com/fatih/color"
"github.com/spf13/cobra"
"github.com/yaoapp/gou"
"github.com/yaoapp/gou/task"
"github.com/yaoapp/gou/websocket"
"github.com/yaoapp/kun/log"
"github.com/yaoapp/yao/config"
"github.com/yaoapp/yao/engine"
"github.com/yaoapp/yao/service"
@ -16,14 +23,19 @@ import (
)
var startDebug = false
var startAlpha = false
var startDisableWatching = false
var startCmd = &cobra.Command{
Use: "start",
Short: L("Start Engine"),
Long: L("Start Engine"),
Run: func(cmd *cobra.Command, args []string) {
defer service.Stop(func() { fmt.Println(L("Service stopped")) })
// recive interrupt signal
interrupt := make(chan os.Signal, 1)
signal.Notify(interrupt, os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT)
// defer service.Stop(func() { fmt.Println(L("Service stopped")) })
Boot()
if startDebug { // 强制 debug 模式启动
@ -46,27 +58,6 @@ var startCmd = &cobra.Command{
host = "127.0.0.1"
}
if mode == "development" {
fmt.Println(color.WhiteString("\n---------------------------------"))
fmt.Println(color.WhiteString(L("API List")))
fmt.Println(color.WhiteString("---------------------------------"))
for _, api := range gou.APIs { // API信息
if len(api.HTTP.Paths) <= 0 {
continue
}
fmt.Printf(color.CyanString("\n%s(%d)\n", api.Name, len(api.HTTP.Paths)))
for _, p := range api.HTTP.Paths {
fmt.Println(
colorMehtod(p.Method),
color.WhiteString(filepath.Join("/api", api.HTTP.Group, p.Path)),
"\tprocess:", p.Process)
}
}
}
fmt.Println(color.WhiteString("\n---------------------------------"))
fmt.Println(color.WhiteString(share.App.Name), color.WhiteString(share.App.Version), mode)
fmt.Println(color.WhiteString("---------------------------------"))
@ -78,33 +69,135 @@ var startCmd = &cobra.Command{
fmt.Println(color.WhiteString(L("Frontend")), color.GreenString(" http://%s%s/", host, port))
fmt.Println(color.WhiteString(L("Dashboard")), color.GreenString(" http://%s%s/xiang/login/admin", host, port))
fmt.Println(color.WhiteString(L("API")), color.GreenString(" http://%s%s/api", host, port))
fmt.Println(color.WhiteString(L("SessionPort")), color.GreenString(" %d", share.SessionPort))
fmt.Println(color.WhiteString(L("Listening")), color.GreenString(" %s:%d", config.Conf.Host, config.Conf.Port))
fmt.Println("")
// 调试模式
// development mode
if mode == "development" {
printApis(false)
printTasks(false)
printSchedules(false)
}
if mode == "development" && !startDisableWatching {
// Watching
fmt.Println(color.WhiteString("\n---------------------------------"))
fmt.Println(color.WhiteString(L("Watching")))
fmt.Println(color.WhiteString("---------------------------------"))
service.Watch(config.Conf)
}
// 启用内测功能
if startAlpha {
for _, srv := range gou.Servers {
fmt.Println(color.WhiteString("\n---------------------------------"))
fmt.Println(color.WhiteString(srv.Name))
fmt.Println(color.WhiteString("---------------------------------"))
fmt.Println(color.GreenString("Host: %s://%s", srv.Protocol, srv.Host))
fmt.Println(color.GreenString("Port: %s\n\n", srv.Port))
go srv.Start()
}
if mode == "production" {
printApis(true)
printTasks(true)
printSchedules(true)
}
// Start server
go service.Start()
fmt.Println(color.GreenString(L("✨LISTENING✨")))
service.Start()
for {
select {
case <-interrupt:
ctx, canceled := context.WithTimeout(context.Background(), (5 * time.Second))
defer canceled()
service.StopWithContext(ctx, func() {
fmt.Println(color.GreenString(L("✨STOPPED✨")))
})
return
}
}
},
}
func printSchedules(silent bool) {
if silent {
for name, sch := range gou.Schedules {
process := fmt.Sprintf("Process: %s", sch.Process)
if sch.TaskName != "" {
process = fmt.Sprintf("Task: %s", sch.TaskName)
}
log.Info("[Schedule] %s %s %s %s", sch.Schedule, name, sch.Name, process)
}
return
}
fmt.Println(color.WhiteString("\n---------------------------------"))
fmt.Println(color.WhiteString(L("Schedules List (%d)"), len(gou.Schedules)))
fmt.Println(color.WhiteString("---------------------------------"))
for name, sch := range gou.Schedules {
process := fmt.Sprintf("Process: %s", sch.Process)
if sch.TaskName != "" {
process = fmt.Sprintf("Task: %s", sch.TaskName)
}
fmt.Printf(color.CyanString("[Schedule] %s %s", sch.Schedule, name))
fmt.Printf(color.WhiteString("\t%s\t%s\n", sch.Name, process))
}
}
func printTasks(silent bool) {
if silent {
for _, t := range task.Tasks {
log.Info("[Task] %s workers:%d", t.Option.Name, t.Option.WorkerNums)
}
return
}
fmt.Println(color.WhiteString("\n---------------------------------"))
fmt.Println(color.WhiteString(L("Tasks List (%d)"), len(task.Tasks)))
fmt.Println(color.WhiteString("---------------------------------"))
for _, t := range task.Tasks {
fmt.Printf(color.CyanString("[Task] %s", t.Option.Name))
fmt.Printf(color.WhiteString("\t workers: %d\n", t.Option.WorkerNums))
}
}
func printApis(silent bool) {
if silent {
for _, api := range gou.APIs {
if len(api.HTTP.Paths) <= 0 {
continue
}
log.Info("[API] %s(%d)", api.Name, len(api.HTTP.Paths))
for _, p := range api.HTTP.Paths {
log.Info("%s %s %s", p.Method, filepath.Join("/api", api.HTTP.Group, p.Path), p.Process)
}
}
for name, upgrader := range websocket.Upgraders { // WebSocket
log.Info("[WebSocket] GET /websocket/%s process:%s", name, upgrader.Process)
}
return
}
fmt.Println(color.WhiteString("\n---------------------------------"))
fmt.Println(color.WhiteString(L("API List")))
fmt.Println(color.WhiteString("---------------------------------"))
for _, api := range gou.APIs { // API信息
if len(api.HTTP.Paths) <= 0 {
continue
}
fmt.Printf(color.CyanString("\n%s(%d)\n", api.Name, len(api.HTTP.Paths)))
for _, p := range api.HTTP.Paths {
fmt.Println(
colorMehtod(p.Method),
color.WhiteString(filepath.Join("/api", api.HTTP.Group, p.Path)),
"\tprocess:", p.Process)
}
}
fmt.Printf(color.CyanString("\n%s(%d)\n", "WebSocket", len(websocket.Upgraders)))
for name, upgrader := range websocket.Upgraders { // WebSocket
fmt.Println(
colorMehtod("GET"),
color.WhiteString(filepath.Join("/websocket", name)),
"\tprocess:", upgrader.Process)
}
}
func colorMehtod(method string) string {
method = strings.ToUpper(method)
switch method {
@ -119,5 +212,5 @@ func colorMehtod(method string) string {
func init() {
startCmd.PersistentFlags().BoolVarP(&startDebug, "debug", "", false, L("Development mode"))
startCmd.PersistentFlags().BoolVarP(&startAlpha, "alpha", "", false, L("Enabled unstable features"))
startCmd.PersistentFlags().BoolVarP(&startDisableWatching, "disable-watching", "", false, L("Disable watching"))
}

View file

@ -12,7 +12,13 @@ var versionCmd = &cobra.Command{
Short: L("Show version"),
Long: L("Show version"),
Run: func(cmd *cobra.Command, args []string) {
version := share.VERSION
if share.PRVERSION != "" {
version = fmt.Sprintf("%s-%s", share.VERSION, share.PRVERSION)
}
// Do Stuff Here
fmt.Println(share.VERSION)
fmt.Println(version)
},
}

71
cmd/websocket.go Normal file
View file

@ -0,0 +1,71 @@
package cmd
import (
"fmt"
"strings"
"github.com/fatih/color"
"github.com/spf13/cobra"
"github.com/yaoapp/gou"
"github.com/yaoapp/kun/exception"
"github.com/yaoapp/yao/config"
"github.com/yaoapp/yao/engine"
"github.com/yaoapp/yao/share"
)
var websocketCmd = &cobra.Command{
Use: "websocket",
Short: L("Open a websocket connection"),
Long: L("Open a websocket connection"),
Run: func(cmd *cobra.Command, args []string) {
defer share.SessionStop()
defer gou.KillPlugins()
defer func() {
err := exception.Catch(recover())
if err != nil {
fmt.Println(color.RedString(L("Fatal: %s"), err.Error()))
}
}()
Boot()
cfg := config.Conf
cfg.Session.IsCLI = true
engine.Load(cfg)
if len(args) < 1 {
fmt.Println(color.RedString(L("Not enough arguments")))
fmt.Println(color.WhiteString(share.BUILDNAME + " help"))
return
}
name := args[0]
websocket, has := gou.WebSockets[name]
if !has {
fmt.Println(color.RedString(L("%s not exists!"), name))
return
}
url := websocket.URL
protocols := websocket.Protocols
argsLen := len(args)
if argsLen > 1 {
url = args[1]
}
if argsLen > 2 {
protocols = args[2:]
}
fmt.Println(color.WhiteString("\n---------------------------------"))
fmt.Println(color.WhiteString(websocket.Name))
fmt.Println(color.WhiteString("---------------------------------"))
fmt.Println(color.GreenString(" URL: %s", url))
fmt.Println(color.GreenString("Protocols: %s", strings.Join(protocols, ",")))
fmt.Println(color.WhiteString("--------------------------------------"))
pargs := append([]string{url}, protocols...)
err := websocket.Open(pargs...)
if err != nil {
fmt.Println(color.RedString(L("%s"), err.Error()))
return
}
},
}

View file

@ -60,7 +60,7 @@ func Load() Config {
// Production 设定为生产环境
func Production() {
Conf.Mode = "production"
log.SetLevel(log.ErrorLevel)
log.SetLevel(log.InfoLevel)
log.SetFormatter(log.TEXT)
if Conf.LogMode == "JSON" {
log.SetFormatter(log.JSON)
@ -124,252 +124,3 @@ func CloseLog() {
}
}
}
// // Config 系统配置
// type Config struct {
// XiangConfig
// Service ServiceConfig `json:"service,omitempty"`
// Session SessionConfig `json:"session,omitempty"`
// Database DatabaseConfig `json:"database,omitempty"`
// JWT JWTConfig `json:"jwt,omitempty"`
// Log LogConfig `json:"log,omitempty"`
// }
// // JWTConfig JWT配置
// type JWTConfig struct {
// Secret string `json:"secret,omitempty" env:"YAO_JWT_SECRET"` // JWT 密钥
// }
// // LogConfig 日志配置
// type LogConfig struct {
// Access string `json:"access,omitempty" env:"XIANG_LOG_ACCESS" envDefault:"os://stdout"` // 服务访问日志
// Error string `json:"error,omitempty" env:"XIANG_LOG_ERROR" envDefault:"os://stderr"` // 服务错误日志
// DB string `json:"database,omitempty" env:"XIANG_LOG_DB" envDefault:"os://stdout"` // 数据库日志
// Plugin string `json:"plugin,omitempty" env:"XIANG_LOG_PLUGIN" envDefault:"os://stdout"` // 插件日志
// }
// // 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://."` // 源码路径(用于单元测试载入数据)
// Path string `json:"path,omitempty" env:"XIANG_PATH" envDefault:"bin://xiang"` // 引擎文件目录
// Root string `json:"root,omitempty" env:"XIANG_ROOT" envDefault:"fs://."` // 应用文件目录
// RootUI string `json:"root_ui,omitempty" env:"XIANG_ROOT_UI"` // 应用界面静态文件目录
// RootDB string `json:"root_db,omitempty" env:"XIANG_ROOT_DB"` // 应用SQLite数据库目录
// RootData string `json:"root_data,omitempty" env:"XIANG_ROOT_DATA"` // 应用数据文件目录
// 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_PLUGIN"` // 应用业务插件文件目录
// RootLib string `json:"root_lib,omitempty" env:"XIANG_ROOT_LIB"` // 应用资料库文件目录
// RootTable string `json:"root_table,omitempty" env:"XIANG_ROOT_TABLE"` // 应用数据表格文件目录
// RootChart string `json:"root_chart,omitempty" env:"XIANG_ROOT_CHART"` // 应用分析图表文件目录
// RootPage string `json:"root_page,omitempty" env:"XIANG_ROOT_PAGE"` // 应用通用页面文件目录
// RootScreen string `json:"root_screen,omitempty" env:"XIANG_ROOT_SCREEN"` // 应用大屏文件目录
// RootForm string `json:"root_form,omitempty" env:"XIANG_ROOT_FORM"` // 表单文件目录
// RootWorkFlow string `json:"root_workflow,omitempty" env:"XIANG_ROOT_WORKFLOW"` // 工作流文件目录
// }
// // ServiceConfig 服务配置
// type ServiceConfig struct {
// Debug bool `json:"debug,omitempty" env:"XIANG_SERVICE_DEBUG" envDefault:"false"` // DEBUG 开关
// HTTPS bool `json:"https,omitempty" env:"XIANG_SERVICE_HTTPS" envDefault:"false"` // HTTPS 开关
// Cert string `json:"cert,omitempty" env:"XIANG_SERVICE_CERT"` // HTTPS 证书
// Key string `json:"key,omitempty" env:"XIANG_SERVICE_KEY"` // HTTPS 证书密钥
// Allow []string `json:"allow,omitempty" env:"XIANG_SERVICE_ALLOW" envSeparator:"|"` // 跨域访问域名列表
// Host string `json:"host,omitempty" env:"XIANG_SERVICE_HOST" envDefault:"127.0.0.1"` // 服务监听IP
// Port int `json:"port,omitempty" env:"XIANG_SERVICE_PORT" envDefault:"5099"` // 服务监听端口
// }
// SessionConfig 会话服务器
type SessionConfig struct {
Debug bool `json:"debug,omitempty" env:"XIANG_SESSION_DEBUG" envDefault:"false"` // DEBUG 开关
Hosting bool `json:"hosting,omitempty" env:"XIANG_SESSION_HOSTING" envDefault:"true"` // 会话服务器
IsCLI bool `json:"iscli,omitempty" env:"XIANG_SESSION_ISCLI" envDefault:"false"` // 是否为客户端启动
Host string `json:"host,omitempty" env:"XIANG_SESSION_HOST" envDefault:"127.0.0.1"` // 会话服务器IP
Port int `json:"port,omitempty" env:"XIANG_SESSION_PORT" envDefault:"3322"` // 会话服务器端口
}
// // DatabaseConfig 数据库配置
// type DatabaseConfig struct {
// Debug bool `json:"debug,omitempty" env:"XIANG_DB_DEBUG" envDefault:"false"` // DEBUG 开关
// Driver string `json:"driver,omitempty" env:"XIANG_DB_DRIVER" envDefault:"sqlite3"` // 数据库驱动 ( sqlite3, mysql, postgres)
// Primary []string `json:"primary,omitempty" env:"XIANG_DB_PRIMARY" envSeparator:"|" envDefault:"file:./db/xiang.db"` // 主库连接DSN
// Secondary []string `json:"secondary,omitempty" env:"XIANG_DB_SECONDARY" envSeparator:"|"` // 从库连接DSN
// AESKey string `json:"aeskey,omitempty" env:"XIANG_DB_AESKEY"` // 加密存储KEY
// }
// // NewConfig 创建配置文件
// func NewConfig(envfile ...string) Config {
// filename := os.Getenv("XIANG_ENV_FILE")
// if filename == "" {
// filename = ".env"
// }
// if len(envfile) > 0 {
// file, err := filepath.Abs(envfile[0])
// if err == nil {
// // log.Printf("加载环境配置文件%s出错 %s\n", envfile[0], err.Error())
// // } else {
// filename = file
// }
// }
// godotenv.Overload(filename)
// // if err != nil {
// // log.Printf("加载环境配置文件%s出错 %s\n", filename, err.Error())
// // }
// cfg := Config{}
// if err := env.Parse(&cfg); err != nil {
// exception.New("解析配置文件出错 %s", 500, err.Error()).Throw()
// }
// cfg.SetDefaults()
// return cfg
// }
// // NewConfigFrom 创建配置文件
// func NewConfigFrom(input io.Reader) Config {
// cfg := Config{}
// err := helper.UnmarshalFile(input, &cfg)
// if err != nil {
// exception.New("解析配置文件出错 %s", 500, err.Error()).Throw()
// }
// cfg.SetDefaults()
// return cfg
// }
// // SetDefaults 设定默认值
// func (cfg *Config) SetDefaults() {
// pwd, err := os.Getwd()
// if err != nil {
// log.Fatal(err.Error())
// }
// if cfg.Root == "fs://." || cfg.Root == "." {
// cfg.Root = pwd
// }
// if cfg.RootAPI == "" {
// cfg.RootAPI = cfg.Root + "/apis"
// }
// if cfg.RootFLow == "" {
// cfg.RootFLow = cfg.Root + "/flows"
// }
// if cfg.RootModel == "" {
// cfg.RootModel = cfg.Root + "/models"
// }
// if cfg.RootPlugin == "" {
// cfg.RootPlugin = cfg.Root + "/plugins"
// }
// if cfg.RootTable == "" {
// cfg.RootTable = cfg.Root + "/tables"
// }
// if cfg.RootLib == "" {
// cfg.RootLib = cfg.Root + "/libs"
// }
// if cfg.RootChart == "" {
// cfg.RootChart = cfg.Root + "/charts"
// }
// if cfg.RootPage == "" {
// cfg.RootPage = cfg.Root + "/pages"
// }
// if cfg.RootWorkFlow == "" {
// cfg.RootWorkFlow = cfg.Root + "/workflows"
// }
// if cfg.RootData == "" {
// cfg.RootData = cfg.Root + "/data"
// }
// if cfg.RootDB == "" {
// cfg.RootDB = cfg.Root + "/db"
// }
// if cfg.RootUI == "" {
// cfg.RootUI = cfg.Root + "/ui"
// }
// if len(cfg.Database.Primary) > 0 {
// if strings.HasPrefix(cfg.Database.Primary[0], "file:.") {
// cfg.Database.Primary[0] = strings.ReplaceAll(cfg.Database.Primary[0], "file:.", pwd)
// } else if strings.HasPrefix(cfg.Database.Primary[0], "file:/") {
// cfg.Database.Primary[0] = strings.ReplaceAll(cfg.Database.Primary[0], "file:.", cfg.RootDB)
// }
// }
// // 过滤数据
// cfg.RootDB = strings.TrimPrefix(cfg.RootDB, "fs://")
// cfg.RootDB = strings.TrimPrefix(cfg.RootDB, "file://")
// cfg.RootData = strings.TrimPrefix(cfg.RootData, "fs://")
// cfg.RootData = strings.TrimPrefix(cfg.RootData, "file://")
// cfg.RootUI = strings.TrimPrefix(cfg.RootUI, "fs://")
// cfg.RootUI = strings.TrimPrefix(cfg.RootUI, "file://")
// cfg.RootLib = strings.TrimPrefix(cfg.RootLib, "fs://")
// cfg.RootLib = strings.TrimPrefix(cfg.RootLib, "file://")
// }
// // SetEnvFile 指定ENV文件
// func SetEnvFile(filename string) {
// Conf = NewConfig(filename)
// }
// // SetAppPath 设定应用目录
// func SetAppPath(root string, envfile ...string) {
// fullpath, err := filepath.Abs(root)
// if err != nil {
// log.Panic("设定应用目录%s出错 %s\n", root, err.Error())
// }
// // 创建应用目录
// pathinfo, err := os.Stat(fullpath)
// if os.IsNotExist(err) {
// err := os.MkdirAll(fullpath, os.ModePerm)
// if err != nil {
// log.Panic("创建目录失败(%s) %s", root, err)
// }
// }
// pathinfo, err = os.Stat(fullpath)
// if !pathinfo.IsDir() {
// log.Panic("检查应用目录失败(%s) ", err)
// }
// if !pathinfo.IsDir() {
// log.Panic("应用目录不是文件夹(%s) ", root)
// }
// // Set ENV
// if len(envfile) > 0 {
// Conf = NewConfig(envfile[0])
// }
// // 从加载配置文件
// Conf.Root = fullpath
// Conf.RootAPI = filepath.Join(fullpath, "/apis")
// Conf.RootFLow = filepath.Join(fullpath, "/flows")
// Conf.RootModel = filepath.Join(fullpath, "/models")
// Conf.RootPlugin = filepath.Join(fullpath, "/plugins")
// Conf.RootTable = filepath.Join(fullpath, "/tables")
// Conf.RootChart = filepath.Join(fullpath, "/charts")
// Conf.RootPage = filepath.Join(fullpath, "/pages")
// Conf.RootWorkFlow = filepath.Join(fullpath, "/workflows")
// Conf.RootData = filepath.Join(fullpath, "/data")
// Conf.RootUI = filepath.Join(fullpath, "/ui")
// Conf.RootDB = filepath.Join(fullpath, "/db")
// Conf.RootLib = filepath.Join(fullpath, "/libs")
// }
// // IsDebug 是否为调试模式
// func IsDebug() bool {
// return Conf.Mode == "debug"
// }

View file

@ -11,8 +11,9 @@ type Config struct {
Log string `json:"log,omitempty" env:"YAO_LOG"` // 服务日志地址
LogMode string `json:"log_mode,omitempty" env:"YAO_LOG_MODE" envDefault:"TEXT"` // 服务日志模式 JSON|TEXT
// Session string `json:"session,omitempty" env:"YAO_SESSION" envDefault:"memory"` // 用户会话模式 memory|redis|database
JWTSecret string `json:"jwt_secret,omitempty" env:"YAO_JWT_SECRET"` // JWT 密钥
DB DBConfig `json:"db,omitempty"` // 数据库配置
JWTSecret string `json:"jwt_secret,omitempty" env:"YAO_JWT_SECRET"` // JWT 密钥
DB DBConfig `json:"db,omitempty"` // 数据库配置
AllowFrom []string `json:"allowfrom,omitempty" envSeparator:"|" env:"YAO_ALLOW_FROM" ` // Domain list the separator is |
Session SessionConfig `json:"session,omitempty"`
}
@ -23,3 +24,15 @@ type DBConfig struct {
Secondary []string `json:"secondary,omitempty" env:"YAO_DB_SECONDARY" envSeparator:"|"` // 从库连接DSN
AESKey string `json:"aeskey,omitempty" env:"YAO_DB_AESKEY"` // 加密存储KEY
}
// SessionConfig 会话服务器
type SessionConfig struct {
Store string `json:"store,omitempty" env:"YAO_SESSION_STORE" envDefault:"file"` // The session store. redis | file
File string `json:"file,omitempty" env:"YAO_SESSION_FILE"` // The file path
Host string `json:"host,omitempty" env:"YAO_SESSION_HOST" envDefault:"127.0.0.1"` // The redis host
Port string `json:"port,omitempty" env:"YAO_SESSION_PORT" envDefault:"6379"` // The redis port
Password string `json:"password,omitempty" env:"YAO_SESSION_PASSWORD"` // The redis password
Username string `json:"username,omitempty" env:"YAO_SESSION_USERNAME"` // The redis username
DB string `json:"db,omitempty" env:"YAO_SESSION_DB" envDefault:"1"` // The redis username
IsCLI bool `json:"iscli,omitempty" env:"YAO_SESSION_ISCLI" envDefault:"false"` // Command Line Start
}

61
crypto/crypto.go Normal file
View file

@ -0,0 +1,61 @@
package crypto
import (
"crypto"
"crypto/hmac"
"encoding/base64"
"fmt"
"golang.org/x/crypto/md4"
)
func init() {
crypto.RegisterHash(crypto.MD4, md4.New)
}
var hashTypes = map[string]crypto.Hash{
"MD4": crypto.MD4,
"MD5": crypto.MD5,
"SHA1": crypto.SHA1,
"SHA224": crypto.SHA224,
"SHA256": crypto.SHA256,
"SHA384": crypto.SHA384,
"SHA512": crypto.SHA512,
"MD5SHA1": crypto.MD5SHA1,
"RIPEMD160": crypto.RIPEMD160,
"SHA3_224": crypto.SHA3_224,
"SHA3_256": crypto.SHA3_256,
"SHA3_384": crypto.SHA3_384,
"SHA3_512": crypto.SHA3_512,
"SHA512_224": crypto.SHA512_224,
"SHA512_256": crypto.SHA512_256,
"BLAKE2s_256": crypto.BLAKE2s_256,
"BLAKE2b_256": crypto.BLAKE2b_256,
"BLAKE2b_384": crypto.BLAKE2b_384,
"BLAKE2b_512": crypto.BLAKE2b_512,
}
// Hash string
func Hash(hash crypto.Hash, value string) (string, error) {
h := hash.New()
_, err := h.Write([]byte(value))
if err != nil {
return "", err
}
return fmt.Sprintf("%x", h.Sum(nil)), nil
}
// Hmac the Keyed-Hash Message Authentication Code (HMAC)
func Hmac(hash crypto.Hash, value string, key string, encoding ...string) (string, error) {
mac := hmac.New(hash.New, []byte(key))
_, err := mac.Write([]byte(value))
if err != nil {
return "", err
}
if len(encoding) > 0 && encoding[0] == "base64" {
return base64.StdEncoding.EncodeToString(mac.Sum(nil)), nil
}
return fmt.Sprintf("%x", mac.Sum(nil)), nil
}

68
crypto/crypto_test.go Normal file
View file

@ -0,0 +1,68 @@
package crypto
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/yaoapp/gou"
)
func TestMD4(t *testing.T) {
// Hash
args := []interface{}{"MD4", "123456"}
res := gou.NewProcess("yao.crypto.hash", args...).Run()
assert.Equal(t, "585028aa0f794af812ee3be8804eb14a", res)
// HMac
args = append(args, "123456")
res = gou.NewProcess("yao.crypto.hmac", args...).Run()
assert.Equal(t, "356f45727db95d65843b2794474d741c", res)
}
func TestMD5(t *testing.T) {
// Hash
args := []interface{}{"MD5", "123456"}
res := gou.NewProcess("yao.crypto.hash", args...).Run()
assert.Equal(t, "e10adc3949ba59abbe56e057f20f883e", res)
// HMac
args = append(args, "123456")
res = gou.NewProcess("yao.crypto.hmac", args...).Run()
assert.Equal(t, "30ce71a73bdd908c3955a90e8f7429ef", res)
}
func TestSHA1(t *testing.T) {
// Hash
args := []interface{}{"SHA1", "123456"}
res := gou.NewProcess("yao.crypto.hash", args...).Run()
assert.Equal(t, "7c4a8d09ca3762af61e59520943dc26494f8941b", res)
// HMac
args = append(args, "123456")
res = gou.NewProcess("yao.crypto.hmac", args...).Run()
assert.Equal(t, "74b55b6ab2b8e438ac810435e369e3047b3951d0", res)
}
func TestSHA256(t *testing.T) {
// Hash
args := []interface{}{"SHA256", "123456"}
res := gou.NewProcess("yao.crypto.hash", args...).Run()
assert.Equal(t, "8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92", res)
// HMac
args = append(args, "123456")
res = gou.NewProcess("yao.crypto.hmac", args...).Run()
assert.Equal(t, "b8ad08a3a547e35829b821b75370301dd8c4b06bdd7771f9b541a75914068718", res)
}
func TestSHA1Base64(t *testing.T) {
// Hash
args := []interface{}{"SHA1", "123456"}
res := gou.NewProcess("yao.crypto.hash", args...).Run()
assert.Equal(t, "7c4a8d09ca3762af61e59520943dc26494f8941b", res)
// HMac
args = append(args, "123456", "base64")
res = gou.NewProcess("yao.crypto.hmac", args...).Run()
assert.Equal(t, "dLVbarK45DisgQQ142njBHs5UdA=", res)
}

59
crypto/process.go Normal file
View file

@ -0,0 +1,59 @@
package crypto
import (
"github.com/yaoapp/gou"
"github.com/yaoapp/kun/exception"
)
func init() {
gou.RegisterProcessHandler("yao.crypto.hash", ProcessHash)
gou.RegisterProcessHandler("yao.crypto.hmac", ProcessHmac)
}
// ProcessHash yao.crypto.hash Crypto Hash
// Args[0] string: the hash function name. MD4/MD5/SHA1/SHA224/SHA256/SHA384/SHA512/MD5SHA1/RIPEMD160/SHA3_224/SHA3_256/SHA3_384/SHA3_512/SHA512_224/SHA512_256/BLAKE2s_256/BLAKE2b_256/BLAKE2b_384/BLAKE2b_512
// Args[1] string: value
func ProcessHash(process *gou.Process) interface{} {
process.ValidateArgNums(2)
typ := process.ArgsString(0)
value := process.ArgsString(1)
h, has := hashTypes[typ]
if !has {
exception.New("%s does not support", 400, typ).Throw()
}
res, err := Hash(h, value)
if err != nil {
exception.New("%s error: %s value: %s", 400, typ, err, value).Throw()
}
return res
}
// ProcessHmac yao.crypto.hmac Crypto the Keyed-Hash Message Authentication Code (HMAC) Hash
// Args[0] string: the hash function name. MD4/MD5/SHA1/SHA224/SHA256/SHA384/SHA512/MD5SHA1/RIPEMD160/SHA3_224/SHA3_256/SHA3_384/SHA3_512/SHA512_224/SHA512_256/BLAKE2s_256/BLAKE2b_256/BLAKE2b_384/BLAKE2b_512
// Args[1] string: value
// Args[2] string: key
// Args[3] string: base64
func ProcessHmac(process *gou.Process) interface{} {
process.ValidateArgNums(3)
typ := process.ArgsString(0)
value := process.ArgsString(1)
key := process.ArgsString(2)
h, has := hashTypes[typ]
if !has {
exception.New("%s does not support", 400, typ).Throw()
}
encoding := ""
if process.NumOfArgs() > 3 {
encoding = process.ArgsString(3)
}
res, err := Hmac(h, value, key, encoding)
if err != nil {
exception.New("%s error: %s value: %s", 400, typ, err, value).Throw()
}
return res
}

View file

@ -208,7 +208,7 @@ func uiIndexHtml() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "ui/index.html", size: 22, mode: os.FileMode(420), modTime: time.Unix(1645092418, 0)}
info := bindataFileInfo{name: "ui/index.html", size: 22, mode: os.FileMode(420), modTime: time.Unix(1655826122, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -228,7 +228,7 @@ func yaoDs_store() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "yao/.DS_Store", size: 6148, mode: os.FileMode(420), modTime: time.Unix(1645092418, 0)}
info := bindataFileInfo{name: "yao/.DS_Store", size: 6148, mode: os.FileMode(420), modTime: time.Unix(1655826122, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -248,7 +248,7 @@ func yaoApisReadmeMd() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "yao/apis/README.md", size: 13, mode: os.FileMode(420), modTime: time.Unix(1645092418, 0)}
info := bindataFileInfo{name: "yao/apis/README.md", size: 13, mode: os.FileMode(420), modTime: time.Unix(1655826122, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -268,7 +268,7 @@ func yaoApisChartHttpJson() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "yao/apis/chart.http.json", size: 624, mode: os.FileMode(420), modTime: time.Unix(1645092418, 0)}
info := bindataFileInfo{name: "yao/apis/chart.http.json", size: 624, mode: os.FileMode(420), modTime: time.Unix(1655826122, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -288,7 +288,7 @@ func yaoApisImportHttpJson() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "yao/apis/import.http.json", size: 1676, mode: os.FileMode(420), modTime: time.Unix(1645092418, 0)}
info := bindataFileInfo{name: "yao/apis/import.http.json", size: 1676, mode: os.FileMode(420), modTime: time.Unix(1655826122, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -308,12 +308,12 @@ func yaoApisPageHttpJson() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "yao/apis/page.http.json", size: 609, mode: os.FileMode(420), modTime: time.Unix(1645092418, 0)}
info := bindataFileInfo{name: "yao/apis/page.http.json", size: 609, mode: os.FileMode(420), modTime: time.Unix(1655826122, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
var _yaoApisStorageHttpJson = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xc4\x91\xcf\x4e\xfa\x40\x10\xc7\xef\x7d\x8a\xc9\xe6\x77\x2c\x6d\x7f\x1e\xb9\x79\x50\x62\x62\x22\x41\x38\x11\x0e\x63\x3b\x94\x62\xd9\x5d\xf7\x8f\x4a\x08\x17\x1f\x40\xaf\xde\x8d\x89\x17\x1f\x0b\x9f\xc3\xec\x52\x8a\x18\x4c\x08\xc1\x78\xd9\xa6\xf3\x99\xfd\xee\x7c\x76\x67\x01\x00\xe3\x38\x21\xd6\x04\xb6\x78\x7f\x5e\x3c\xbc\x7d\x3c\xbe\x2e\x9e\x5e\x58\xe8\xc8\x2d\x29\x5d\x08\xee\xe0\xff\x28\x89\x92\x65\x35\x23\x9d\xaa\x42\x9a\x8a\x7c\xdd\x76\xdc\x3e\x5b\xf6\xe4\x4a\x58\xe9\xe8\x7d\x81\x3c\x8f\xb5\x11\x0a\x73\xaa\x98\x45\x95\x39\x76\x45\xa8\x48\x35\xc6\x77\x66\x09\x24\x9a\x91\x66\x4d\xe8\x07\x00\x00\x33\xbf\x56\x65\xd7\x1e\x5b\x59\x0a\xcc\x7c\xaf\x07\x13\x32\x23\xe1\x93\xda\x17\x97\xdd\x75\xbd\x3e\xa0\xb1\xae\x49\x25\x52\xd2\xba\x1e\x29\x1a\xea\xa8\xf7\x2d\xaf\x70\x3e\x7d\xf6\x6f\x58\x94\x14\xb9\x85\x0d\x6a\x26\xac\x61\xcd\x7a\x26\x00\xa6\x0d\x1a\xeb\xf2\x8e\x92\x24\x5c\x97\xcd\x54\xfa\xcb\x44\x29\xcb\x22\x45\x77\x49\xf1\x58\x0b\xce\xaa\x96\xb9\xff\xce\xc3\x1f\x14\x15\x61\xe6\x4f\xde\x22\xd9\x3a\xd9\xc3\xb1\x43\x98\x9d\x6e\x04\xae\x2c\x6f\x2c\xa9\x69\xe4\x1f\x3f\x84\xd5\x2f\xf1\x54\x64\x7f\xe0\x6d\x55\x79\x30\xe5\x16\x99\x5e\xe7\x7c\x47\x61\x3f\xf7\xfe\xba\xc5\x04\x73\x8a\x25\xcf\x77\xf4\x34\xe2\x9a\xf8\x21\x4d\xbb\x9b\x81\x5b\x5c\x7f\xed\x2d\x03\x80\x41\x30\x0f\x3e\x03\x00\x00\xff\xff\xb6\x5b\xa0\x8d\x44\x04\x00\x00")
var _yaoApisStorageHttpJson = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xc4\x92\xbd\x4e\xfb\x30\x10\xc0\xf7\x3c\x85\x65\xfd\xc7\x34\xc9\x9f\xb1\x1b\x03\x54\x48\x48\x54\xa5\x9d\xaa\x0e\x47\x72\x4d\x5d\x52\xdb\xf8\x03\xa8\xaa\x2e\x3c\x00\xac\xec\x08\x89\x85\xc7\x2a\xcf\x81\xec\xa4\xe9\x07\x85\x22\x24\xc4\xe2\xc8\xf7\xbb\xf3\xf9\xe7\xcb\x2c\x20\x84\x72\x98\x20\x6d\x12\xba\x78\x7d\x5c\xdc\xbd\xbc\xdd\x3f\x2f\x1e\x9e\x68\xe8\xc8\x35\x2a\xcd\x04\x77\xf0\x7f\x94\x44\x49\x19\xcd\x50\xa7\x8a\x49\x53\x91\xf5\xb2\xc3\xf6\x49\x99\x93\x2b\x61\xa5\xa3\xb7\x0c\x78\x1e\x6b\x23\x14\xe4\x58\x31\x0b\x2a\x73\xec\x02\x41\xa1\x6a\x8c\x6f\x4c\x09\x24\x98\x91\xa6\x4d\xd2\x0f\x08\x21\x64\xe6\xd7\x2a\xec\xd2\x63\x2b\x0b\x01\x99\xcf\xf5\x60\x82\x66\x24\xfc\x49\xed\xb3\xf3\xee\x2a\x2e\x95\x48\x51\xeb\xba\x7d\x34\xd4\x51\x6f\xab\x96\xb9\xbb\xf7\xe9\xbf\x21\x2b\x30\x72\x0b\x1d\xd4\x4c\x58\x43\x9b\x75\x7f\x42\xa8\x36\x60\xac\x3b\xef\x20\x49\xc2\x55\xd8\x4c\xa5\x7f\x38\x90\xb2\x60\x29\xb8\x07\x89\xc7\x5a\x70\x5a\xa5\xcc\xfd\x77\x1e\x7e\xa2\xa3\x10\x32\xdf\x79\x87\x50\xeb\x68\x8f\x4f\x07\x21\x3b\xde\x28\x5e\x1a\x5d\x59\x54\xd3\xc8\x0f\x35\x24\xcb\x2d\xf2\x54\x64\x7f\xe0\x68\x55\xb1\x57\xaf\xfe\x1f\x1a\x5f\x2b\xb7\xd0\xf4\x3a\xa7\xdf\x14\xf6\xf7\x5e\xdb\x8a\x4b\xe4\x1f\xf5\xb7\xb4\x57\xba\x6c\x02\x39\xc6\x92\xe7\x74\x9f\x61\x79\xf2\x4f\x46\xd8\x42\xd3\xdd\x2c\xde\x61\xf4\x6b\x13\x0b\x08\x19\x04\xf3\xe0\x3d\x00\x00\xff\xff\xeb\x8c\x85\x1f\x02\x04\x00\x00")
func yaoApisStorageHttpJsonBytes() ([]byte, error) {
return bindataRead(
@ -328,12 +328,12 @@ func yaoApisStorageHttpJson() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "yao/apis/storage.http.json", size: 1092, mode: os.FileMode(420), modTime: time.Unix(1645092418, 0)}
info := bindataFileInfo{name: "yao/apis/storage.http.json", size: 1026, mode: os.FileMode(420), modTime: time.Unix(1655826122, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
var _yaoApisTableHttpJson = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xd4\x56\xcd\x6e\xd3\x40\x10\xbe\xe7\x29\x56\xab\x1e\x53\x3b\x70\xf4\x0d\x89\x1f\xe5\x44\x51\x41\x1c\xaa\x1e\xa6\xde\x21\x99\xd6\x5e\x6f\xf7\x27\xc1\x54\x79\x04\xe0\xc4\x1d\x84\x90\x38\x21\x1e\x80\xd7\x21\xe2\x31\xd0\xae\x4d\x52\x87\xa4\x0d\xb1\x90\xd3\x4b\xa2\x9d\x99\x6f\x34\xdf\xf7\x79\xec\xbd\xea\x31\xc6\x25\xe4\xc8\x13\xc6\xe7\x1f\xbe\xcf\xdf\x7e\xfb\xf5\xe9\xeb\xfc\xe3\x8f\xf9\xbb\x2f\x3f\xdf\x7f\xe6\x7d\x9f\x9f\xa0\x36\x54\x48\x5f\x72\x2f\x1a\x44\x83\x2a\x2a\xd0\xa4\x9a\x94\xad\x33\x7f\x83\x1f\x1c\x0d\xab\xca\x91\x2e\x9c\xf2\x35\xaf\x09\xe4\x28\xb6\x70\x96\x61\x9d\x71\xa0\x85\xcf\x9c\x21\x68\xd4\x87\xe7\x53\x5b\x25\x14\xd8\xb1\xe1\x09\x3b\xe9\x31\xc6\xd8\x55\xf8\xad\xc3\xbe\x3c\x4e\xfc\xcc\xb1\x41\xd0\xe9\x38\x20\x42\x3a\x47\x3b\x2e\x42\xbf\x27\x8f\x9e\x2f\xc3\x4a\x17\x29\x1a\xb3\x98\x20\x0a\x13\x44\xc7\x2b\x68\xf2\x3c\x4e\xf8\x81\x02\x0d\x79\x14\x44\xe9\x33\x9e\x5c\x3a\xd4\xe5\x61\x08\xfa\xf3\x41\x38\x47\x0a\x46\xb8\x72\x34\xf4\x06\xf9\xe9\xa2\x5d\xe1\x2c\x4f\x16\xa3\x33\xc6\x8d\x05\xeb\xfc\x14\xf7\x07\x83\xfe\x32\x6c\x4b\x15\xd4\x07\xa5\x32\x4a\xc1\xeb\x19\x9f\x9b\x42\xf2\xba\x64\x16\xfe\x67\xfd\x1b\x95\x78\x45\x52\xc4\x09\x89\x5d\xb5\x78\x4c\x52\xdc\xa6\x44\x7d\x24\xd1\x15\x49\x83\x19\xa6\x76\x77\xbb\x9b\xe8\x8a\xe4\x72\xc4\x06\xdb\x6b\xe1\xca\xe0\x0b\x2c\xa7\x85\x16\x6b\x32\x1b\x00\x13\xc8\xdc\xba\x78\x46\x39\xd9\x3f\xb4\xbb\x12\x12\x26\xb8\x4e\xc6\xa3\xa7\xc7\x5b\xe8\xd8\x00\x6f\x58\x1a\x05\x65\x56\x40\x67\x4f\xca\xa5\xa3\xf4\xa2\x15\xcb\x67\xbe\xc3\x5d\xa0\x2a\x30\x43\x8b\x9b\x76\x7f\x2b\xae\x0f\x43\x8b\xbd\x5f\x7f\x92\x06\xf5\xda\xf5\xdf\x8a\xe5\x70\x05\xbe\x91\x65\xb0\x33\x4a\x8b\xcc\xe5\xd2\x34\x62\x61\xa7\x4d\xc7\x4e\x4f\xc7\xa8\x77\x7f\xae\x2b\xaf\x5f\x36\x7b\x6c\xf1\xe5\xeb\x96\x33\xc9\x96\x84\x87\xf2\x56\xe3\xab\xd7\x33\x09\x73\xfd\xb3\xae\x29\x07\x5d\x76\xc5\xde\x29\x01\xad\x1d\x7f\x11\x9a\xfc\xbb\xe3\x7b\xf0\x6e\xab\xf9\xb7\x70\xbf\x22\xdf\xd2\xfd\x3d\x50\xc2\xa0\xb5\x24\x47\xbb\xdf\x7d\x56\xe0\x37\xaa\x50\xdf\xb3\xfe\x1b\xd5\x1e\x63\xa7\xbd\x59\xef\x77\x00\x00\x00\xff\xff\x8a\x3e\xea\x17\x7c\x0c\x00\x00")
var _yaoApisTableHttpJson = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xd4\x56\xcb\x6e\xd3\x40\x14\xdd\xfb\x2b\x46\xa3\x2e\x53\x3b\xb0\xf4\x0e\x89\x87\xb2\xa2\xa8\x20\x16\x55\x17\x17\xcf\x25\x19\x6a\xcf\x4c\xe7\x91\x62\xaa\x7c\x02\xb0\x62\x0f\x42\x48\xac\x10\x1f\xc0\xef\x10\xf1\x19\x68\xc6\x26\xa9\x4d\xd2\x9a\x58\xc8\xe9\x26\xd1\x3d\xf7\xe1\x7b\xce\xb1\xc7\xbe\x8c\x08\xa1\x02\x0a\xa4\x29\xa1\xcb\x0f\xdf\x97\x6f\xbf\xfd\xfa\xf4\x75\xf9\xf1\xc7\xf2\xdd\x97\x9f\xef\x3f\xd3\x91\xcf\xcf\x51\x1b\x2e\x85\x2f\xb9\x13\x8f\xe3\x71\x85\x32\x34\x99\xe6\xca\xd6\x99\xbf\x9b\xef\x1d\x4d\xaa\xca\xa9\x96\x4e\xf9\x9a\xd7\x1c\xc4\x34\xb1\xf0\x22\xc7\x3a\xe3\x40\x33\x9f\x09\xd8\x61\x15\x86\x8c\x02\x3b\x33\x34\x25\x27\x11\x21\x84\x5c\x86\xdf\x1a\xf6\xf5\x49\xea\x97\x4e\x0c\x82\xce\x66\xa1\x23\xa4\x0b\xb4\x33\x19\x06\x3e\x7a\xf0\x74\x0d\x2b\x2d\x33\x34\x66\xb5\x42\x1c\x2e\x17\x1f\xb7\xba\xb9\x27\x72\x42\x0f\x14\x68\x28\xe2\xa0\xca\x88\xd0\xf4\xdc\xa1\x2e\x0f\x03\xe8\xe3\x83\x10\xc7\x0a\xa6\xd8\x0a\x0d\x7f\x83\xf4\x74\x35\x4e\x3a\x4b\xd3\xd5\xea\x84\x50\x63\xc1\x3a\xbf\xc5\xdd\xf1\x78\xb4\x86\x6d\xa9\x82\xfc\xa0\x54\xce\x33\xf0\x82\x26\xaf\x8c\x14\xb4\x2e\x59\x84\xff\xc5\xe8\x5a\x25\x5e\x72\xc1\x92\x94\xb3\x5d\xb5\x78\xc8\x05\xbb\x49\x89\x3a\xf4\x17\x69\xc9\x32\x10\x69\x83\x39\x66\x76\x77\xfb\x9b\xdd\x15\xe9\xf5\x8a\x0d\xf6\x57\xe0\xca\xf0\x33\x2c\x2f\xa4\x66\x1b\x32\x5b\x1a\xe6\x90\xbb\x4d\x78\xce\x0b\x6e\xff\xd0\x1e\x4a\x48\x98\xe3\x26\x19\x8f\x1e\x1f\x77\xd0\xb1\xd1\xbc\xe5\x21\x52\x50\xe6\x12\xd8\x50\x77\xca\xb9\xe3\xd9\x59\x2f\x96\x4f\xfc\x84\xdb\x40\x95\x61\x8e\x16\xb7\x9d\x05\x9d\xb8\xde\x0f\x23\xba\x1f\x07\x03\x31\xe5\xc2\xa0\xde\xf8\xf8\x77\x62\x39\x69\xb5\x6f\x65\x19\xec\x8c\x33\x99\xbb\x42\x98\x06\x16\x9e\x69\x33\xb0\xd3\x17\x33\xd4\xbb\xdf\xd7\x95\xd7\xcf\x9b\x33\x3a\xbc\x09\x87\xe5\xcc\x45\x4f\xc2\x13\x71\xa3\xf1\xd5\xf1\xcc\x99\xb9\xfa\x9a\xd7\xbc\x00\x5d\x0e\xc5\xde\x29\x06\xbd\x1d\x7f\x16\x86\xfc\xbb\xe3\x7b\x70\xb6\xd5\xfc\x7b\xb8\x5f\x91\xef\xe9\xfe\x1e\x28\x61\xd0\x5a\x2e\xa6\xbb\x7f\xfb\xb4\xda\xaf\x55\xa1\xfe\xce\xfa\x6f\x54\x23\x42\x4e\xa3\x45\xf4\x3b\x00\x00\xff\xff\x8d\x38\xb9\xa1\x8d\x0c\x00\x00")
func yaoApisTableHttpJsonBytes() ([]byte, error) {
return bindataRead(
@ -348,7 +348,7 @@ func yaoApisTableHttpJson() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "yao/apis/table.http.json", size: 3196, mode: os.FileMode(420), modTime: time.Unix(1645092418, 0)}
info := bindataFileInfo{name: "yao/apis/table.http.json", size: 3213, mode: os.FileMode(420), modTime: time.Unix(1655826122, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -368,7 +368,7 @@ func yaoApisUserHttpJson() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "yao/apis/user.http.json", size: 1453, mode: os.FileMode(420), modTime: time.Unix(1645092418, 0)}
info := bindataFileInfo{name: "yao/apis/user.http.json", size: 1453, mode: os.FileMode(420), modTime: time.Unix(1655826122, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -388,7 +388,7 @@ func yaoApisXiangHttpJson() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "yao/apis/xiang.http.json", size: 1239, mode: os.FileMode(420), modTime: time.Unix(1645092418, 0)}
info := bindataFileInfo{name: "yao/apis/xiang.http.json", size: 1239, mode: os.FileMode(420), modTime: time.Unix(1655826122, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -408,7 +408,7 @@ func yaoDataDs_store() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "yao/data/.DS_Store", size: 6148, mode: os.FileMode(420), modTime: time.Unix(1645092418, 0)}
info := bindataFileInfo{name: "yao/data/.DS_Store", size: 6148, mode: os.FileMode(420), modTime: time.Unix(1655826122, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -428,7 +428,7 @@ func yaoDataAppJson() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "yao/data/app.json", size: 277, mode: os.FileMode(420), modTime: time.Unix(1645092418, 0)}
info := bindataFileInfo{name: "yao/data/app.json", size: 277, mode: os.FileMode(420), modTime: time.Unix(1655826122, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -448,7 +448,7 @@ func yaoDataIconsDs_store() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "yao/data/icons/.DS_Store", size: 6148, mode: os.FileMode(420), modTime: time.Unix(1645092418, 0)}
info := bindataFileInfo{name: "yao/data/icons/.DS_Store", size: 6148, mode: os.FileMode(420), modTime: time.Unix(1655826122, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -468,7 +468,7 @@ func yaoDataIcons404Png() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "yao/data/icons/404.png", size: 9342, mode: os.FileMode(420), modTime: time.Unix(1645092418, 0)}
info := bindataFileInfo{name: "yao/data/icons/404.png", size: 9342, mode: os.FileMode(420), modTime: time.Unix(1655826122, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -488,7 +488,7 @@ func yaoDataIconsIconIcns() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "yao/data/icons/icon.icns", size: 67465, mode: os.FileMode(420), modTime: time.Unix(1645092418, 0)}
info := bindataFileInfo{name: "yao/data/icons/icon.icns", size: 67465, mode: os.FileMode(420), modTime: time.Unix(1655826122, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -508,7 +508,7 @@ func yaoDataIconsIconIco() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "yao/data/icons/icon.ico", size: 54993, mode: os.FileMode(420), modTime: time.Unix(1645092418, 0)}
info := bindataFileInfo{name: "yao/data/icons/icon.ico", size: 54993, mode: os.FileMode(420), modTime: time.Unix(1655826122, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -528,7 +528,7 @@ func yaoDataIconsIconPng() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "yao/data/icons/icon.png", size: 34558, mode: os.FileMode(420), modTime: time.Unix(1645092418, 0)}
info := bindataFileInfo{name: "yao/data/icons/icon.png", size: 34558, mode: os.FileMode(420), modTime: time.Unix(1655826122, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -548,7 +548,7 @@ func yaoDataIndexHtml() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "yao/data/index.html", size: 282, mode: os.FileMode(420), modTime: time.Unix(1645092418, 0)}
info := bindataFileInfo{name: "yao/data/index.html", size: 282, mode: os.FileMode(420), modTime: time.Unix(1655826122, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -568,7 +568,7 @@ func yaoFlowsReadmeMd() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "yao/flows/README.md", size: 12, mode: os.FileMode(420), modTime: time.Unix(1645092418, 0)}
info := bindataFileInfo{name: "yao/flows/README.md", size: 12, mode: os.FileMode(420), modTime: time.Unix(1655826122, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -588,7 +588,7 @@ func yaoFlowsAdminGuardFlowJson() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "yao/flows/admin/guard.flow.json", size: 559, mode: os.FileMode(420), modTime: time.Unix(1645092418, 0)}
info := bindataFileInfo{name: "yao/flows/admin/guard.flow.json", size: 559, mode: os.FileMode(420), modTime: time.Unix(1655826122, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -608,7 +608,7 @@ func yaoFlowsMenuFlowJson() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "yao/flows/menu.flow.json", size: 1045, mode: os.FileMode(420), modTime: time.Unix(1645092418, 0)}
info := bindataFileInfo{name: "yao/flows/menu.flow.json", size: 1045, mode: os.FileMode(420), modTime: time.Unix(1655826122, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -628,7 +628,7 @@ func yaoFlowsUserFindFlowJson() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "yao/flows/user/find.flow.json", size: 328, mode: os.FileMode(420), modTime: time.Unix(1645092418, 0)}
info := bindataFileInfo{name: "yao/flows/user/find.flow.json", size: 328, mode: os.FileMode(420), modTime: time.Unix(1655826122, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -648,7 +648,7 @@ func yaoLangsEnUsJson() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "yao/langs/en-US.json", size: 66, mode: os.FileMode(420), modTime: time.Unix(1645092418, 0)}
info := bindataFileInfo{name: "yao/langs/en-US.json", size: 66, mode: os.FileMode(420), modTime: time.Unix(1655826122, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -668,7 +668,7 @@ func yaoModelsReadmeMd() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "yao/models/README.md", size: 15, mode: os.FileMode(420), modTime: time.Unix(1645092418, 0)}
info := bindataFileInfo{name: "yao/models/README.md", size: 15, mode: os.FileMode(420), modTime: time.Unix(1655826122, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -688,7 +688,7 @@ func yaoModelsMenuModJson() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "yao/models/menu.mod.json", size: 2378, mode: os.FileMode(420), modTime: time.Unix(1645092418, 0)}
info := bindataFileInfo{name: "yao/models/menu.mod.json", size: 2378, mode: os.FileMode(420), modTime: time.Unix(1655826122, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -708,7 +708,7 @@ func yaoModelsUserModJson() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "yao/models/user.mod.json", size: 8826, mode: os.FileMode(420), modTime: time.Unix(1645092418, 0)}
info := bindataFileInfo{name: "yao/models/user.mod.json", size: 8826, mode: os.FileMode(420), modTime: time.Unix(1655826122, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -728,7 +728,7 @@ func yaoModelsWorkflowModJson() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "yao/models/workflow.mod.json", size: 3333, mode: os.FileMode(420), modTime: time.Unix(1645092418, 0)}
info := bindataFileInfo{name: "yao/models/workflow.mod.json", size: 3333, mode: os.FileMode(420), modTime: time.Unix(1655826122, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -748,7 +748,7 @@ func yaoTablesReadmeMd() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "yao/tables/README.md", size: 15, mode: os.FileMode(420), modTime: time.Unix(1645092418, 0)}
info := bindataFileInfo{name: "yao/tables/README.md", size: 15, mode: os.FileMode(420), modTime: time.Unix(1655826122, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -768,7 +768,7 @@ func yaoTablesMenuTabJson() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "yao/tables/menu.tab.json", size: 10471, mode: os.FileMode(420), modTime: time.Unix(1645092418, 0)}
info := bindataFileInfo{name: "yao/tables/menu.tab.json", size: 10471, mode: os.FileMode(420), modTime: time.Unix(1655826122, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -788,7 +788,7 @@ func yaoTablesUserTabJson() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "yao/tables/user.tab.json", size: 2357, mode: os.FileMode(420), modTime: time.Unix(1645092418, 0)}
info := bindataFileInfo{name: "yao/tables/user.tab.json", size: 2357, mode: os.FileMode(420), modTime: time.Unix(1655826122, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}

View file

@ -20,7 +20,7 @@ ARG ARCH
RUN groupadd -r yao && useradd -r -g yao yao && \
apt-get update && \
apt-get install -y curl sudo procps net-tools
RUN curl -fsSL "https://release-sv.yaoapps.com/archives/yao-${VERSION}-linux-${ARCH}" > /usr/local/bin/yao && \
RUN curl -fsSL "https://release-sv-1252011659.cos.na-siliconvalley.myqcloud.com/archives/yao-${VERSION}-linux-${ARCH}" > /usr/local/bin/yao && \
chmod +x /usr/local/bin/yao && \
mkdir -p /data/app

View file

@ -18,7 +18,7 @@ FROM alpine:latest
ARG VERSION
ARG ARCH
RUN apk --no-cache add curl
RUN curl -fsSL "https://release-sv.yaoapps.com/archives/yao-${VERSION}-linux-${ARCH}" > /usr/local/bin/yao && \
RUN curl -fsSL "https://release-sv-1252011659.cos.na-siliconvalley.myqcloud.com/archives/yao-${VERSION}-linux-${ARCH}" > /usr/local/bin/yao && \
chmod +x /usr/local/bin/yao
RUN mkdir -p /data/app && \
cd /data/app && /usr/local/bin/yao init && \

BIN
docs/architecture.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 288 KiB

BIN
docs/wechat.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

View file

@ -4,12 +4,14 @@ import (
"fmt"
"os"
"path/filepath"
"strings"
"github.com/fatih/color"
"github.com/yaoapp/gou"
"github.com/yaoapp/kun/exception"
"github.com/yaoapp/kun/log"
"github.com/yaoapp/yao/api"
"github.com/yaoapp/yao/app"
"github.com/yaoapp/yao/cert"
"github.com/yaoapp/yao/chart"
"github.com/yaoapp/yao/config"
"github.com/yaoapp/yao/flow"
@ -18,11 +20,15 @@ import (
"github.com/yaoapp/yao/page"
"github.com/yaoapp/yao/plugin"
"github.com/yaoapp/yao/query"
"github.com/yaoapp/yao/schedule"
"github.com/yaoapp/yao/script"
"github.com/yaoapp/yao/server"
"github.com/yaoapp/yao/share"
"github.com/yaoapp/yao/socket"
"github.com/yaoapp/yao/store"
"github.com/yaoapp/yao/table"
"github.com/yaoapp/yao/workflow"
"github.com/yaoapp/yao/task"
"github.com/yaoapp/yao/websocket"
"github.com/yaoapp/yao/widget"
)
// Load 根据配置加载 API, FLow, Model, Plugin
@ -37,9 +43,15 @@ func Load(cfg config.Config) (err error) {
gou.LoadCrypt(fmt.Sprintf(`{"key":"%s"}`, cfg.DB.AESKey), "AES")
gou.LoadCrypt(`{}`, "PASSWORD")
// Load Certs
err = cert.Load(cfg)
if err != nil {
printErr(cfg.Mode, "Cert", err)
}
// 第二步: 建立数据库 & 会话连接
share.DBConnect(cfg.DB) // 创建数据库连接
share.SessionConnect(cfg.Session) // 创建会话服务器链接
share.DBConnect(cfg.DB) // 创建数据库连接
// share.SessionConnect(cfg.Session) // 创建会话服务器链接
// 加载应用引擎
if os.Getenv("YAO_DEV") != "" {
@ -54,51 +66,94 @@ func Load(cfg config.Config) (err error) {
// 第四步: 加载共享库 & JS 处理器
err = share.Load(cfg) // 加载共享库 lib
if err != nil {
log.Warn(err.Error())
printErr(cfg.Mode, "Lib", err)
}
err = script.Load(cfg) // 加载JS处理器 script
if err != nil {
log.Warn(err.Error())
printErr(cfg.Mode, "Script", err)
}
// 第五步: 加载数据模型等
err = model.Load(cfg) // 加载数据模型 model
if err != nil {
log.Warn(err.Error())
printErr(cfg.Mode, "Model", err)
}
err = flow.Load(cfg) // 加载业务逻辑 Flow
if err != nil {
log.Warn(err.Error())
printErr(cfg.Mode, "Flow", err)
}
err = store.Load(cfg) // Load stores
if err != nil {
printErr(cfg.Mode, "Store", err)
}
err = plugin.Load(cfg) // 加载业务插件 plugin
if err != nil {
log.Warn(err.Error())
printErr(cfg.Mode, "Plugin", err)
}
err = table.Load(cfg) // 加载数据表格 table
if err != nil {
log.Warn(err.Error())
printErr(cfg.Mode, "Table", err)
}
err = chart.Load(cfg) // 加载分析图表 chart
if err != nil {
log.Warn(err.Error())
printErr(cfg.Mode, "Chart", err)
}
err = page.Load(cfg) // 加载页面 page 忽略错误
if err != nil {
printErr(cfg.Mode, "Page", err)
}
page.Load(cfg) // 加载页面 page 忽略错误
importer.Load(cfg) // 加载数据导入 imports
workflow.Load(cfg) // 加载工作流 workflow
// workflow.Load(cfg) // 加载工作流 workflow
err = api.Load(cfg) // 加载业务接口 API
if err != nil {
log.Warn(err.Error())
printErr(cfg.Mode, "API", err)
}
err = socket.Load(cfg) // Load sockets
if err != nil {
printErr(cfg.Mode, "Socket", err)
}
err = websocket.Load(cfg) // Load websockets (client)
if err != nil {
printErr(cfg.Mode, "WebSocket", err)
}
err = task.Load(cfg) // Load tasks
if err != nil {
printErr(cfg.Mode, "Task", err)
}
err = schedule.Load(cfg) // Load schedules
if err != nil {
printErr(cfg.Mode, "Schedule", err)
}
err = widget.Load(cfg) // Load widgets
if err != nil {
printErr(cfg.Mode, "Widget", err)
}
server.Load(cfg) // 加载服务
return nil
}
func printErr(mode, widget string, err error) {
message := fmt.Sprintf("[%s] %s", widget, err.Error())
if !strings.Contains(message, "does not exists") && mode == "development" {
color.Red(message)
}
}
// Reload 根据配置重新加载 API, FLow, Model, Plugin
func Reload(cfg config.Config) {
gou.APIs = map[string]*gou.API{}

View file

@ -8,6 +8,7 @@ import (
)
func TestUserAuth(t *testing.T) {
res := user.Auth("email", "xiang@iqka.com", "A123456p+")
assert.True(t, res.Has("user"))
assert.True(t, res.Has("token"))

View file

@ -20,5 +20,5 @@ func check(t *testing.T) {
for key := range gou.Flows {
keys = append(keys, key)
}
assert.Equal(t, 22, len(keys))
assert.Equal(t, 26, len(keys))
}

97
go.mod
View file

@ -1,50 +1,99 @@
module github.com/yaoapp/yao
go 1.16
go 1.18
require (
github.com/aliyun/alibaba-cloud-sdk-go v1.61.1286
github.com/aliyun/aliyun-oss-go-sdk v2.1.10+incompatible
github.com/armon/go-metrics v0.3.10 // indirect
github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f // indirect
github.com/buraksezer/consistent v0.9.0 // indirect
github.com/buraksezer/olric v0.4.2
github.com/caarlos0/env/v6 v6.9.1
github.com/elazarl/go-bindata-assetfs v1.0.1
github.com/fatih/color v1.13.0
github.com/fsnotify/fsnotify v1.5.1
github.com/fsnotify/fsnotify v1.5.4
github.com/gin-gonic/gin v1.7.7
github.com/go-sql-driver/mysql v1.6.0 // indirect
github.com/golang-jwt/jwt v3.2.2+incompatible
github.com/google/btree v1.0.1 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-immutable-radix v1.3.1 // indirect
github.com/hashicorp/go-msgpack v1.1.5 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/golang-lru v0.5.4 // indirect
github.com/hashicorp/memberlist v0.3.0 // indirect
github.com/hashicorp/yamux v0.0.0-20211028200310-0bc27b27de87 // indirect
github.com/jmoiron/sqlx v1.3.4 // indirect
github.com/joho/godotenv v1.3.0
github.com/json-iterator/go v1.1.12
github.com/lib/pq v1.10.4 // indirect
github.com/mattn/go-sqlite3 v1.14.9 // indirect
github.com/miekg/dns v1.1.43 // indirect
github.com/mojocn/base64Captcha v1.3.5
github.com/satori/go.uuid v1.2.0 // indirect
github.com/spf13/afero v1.6.0
github.com/spf13/cobra v1.2.1
github.com/stretchr/testify v1.7.0
github.com/stretchr/testify v1.7.1
github.com/xuri/excelize/v2 v2.5.0
github.com/yaoapp/gou v0.0.0-20211120135538-e5387704eb03
github.com/yaoapp/kun v0.9.0
github.com/yaoapp/xun v0.9.0
golang.org/x/crypto v0.0.0-20220208050332-20e1d8d225ab
)
require (
github.com/TylerBrock/colorjson v0.0.0-20200706003622-8a50f05110d2 // indirect
github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f // indirect
github.com/blang/semver/v4 v4.0.0 // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-errors/errors v1.4.2 // indirect
github.com/go-playground/locales v0.13.0 // indirect
github.com/go-playground/universal-translator v0.17.0 // indirect
github.com/go-playground/validator/v10 v10.4.1 // indirect
github.com/go-redis/redis/v8 v8.11.4 // indirect
github.com/go-sql-driver/mysql v1.6.0 // indirect
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/google/uuid v1.3.0
github.com/gorilla/websocket v1.5.0 // indirect
github.com/hashicorp/go-hclog v1.2.0 // indirect
github.com/hashicorp/go-plugin v1.4.4 // indirect
github.com/hashicorp/golang-lru v0.5.4 // indirect
github.com/hashicorp/yamux v0.0.0-20211028200310-0bc27b27de87 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af // indirect
github.com/jmoiron/sqlx v1.3.4 // indirect
github.com/leodido/go-urn v1.2.0 // indirect
github.com/lib/pq v1.10.4 // indirect
github.com/mattn/go-colorable v0.1.12 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/mattn/go-sqlite3 v1.14.9 // indirect
github.com/miekg/dns v1.1.48 // indirect
github.com/mitchellh/go-testing-interface v1.14.1 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
github.com/oklog/run v1.1.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/richardlehane/mscfb v1.0.3 // indirect
github.com/richardlehane/msoleps v1.0.1 // indirect
github.com/robfig/cron/v3 v3.0.1 // indirect
github.com/satori/go.uuid v1.2.0 // indirect
github.com/sirupsen/logrus v1.8.1 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/takama/daemon v1.0.0 // indirect
github.com/tidwall/btree v1.1.0 // indirect
github.com/tidwall/buntdb v1.2.9 // indirect
github.com/tidwall/gjson v1.12.1 // indirect
github.com/tidwall/grect v0.1.4 // indirect
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.0 // indirect
github.com/tidwall/rtred v0.1.2 // indirect
github.com/tidwall/tinyqueue v0.1.1 // indirect
github.com/ugorji/go/codec v1.1.7 // indirect
github.com/xuri/efp v0.0.0-20210322160811-ab561f5b45e3 // indirect
golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d // indirect
golang.org/x/net v0.0.0-20211118161319-6a13c67c3ce4 // indirect
golang.org/x/mod v0.4.2 // indirect
golang.org/x/net v0.0.0-20220526153639-5463443f8c37 // indirect
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect
google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1 // indirect
google.golang.org/grpc v1.42.0 // indirect
golang.org/x/tools v0.1.7 // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
google.golang.org/genproto v0.0.0-20220526192754-51939a95c655 // indirect
google.golang.org/grpc v1.46.2 // indirect
google.golang.org/protobuf v1.28.0 // indirect
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
gopkg.in/ini.v1 v1.62.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.0 // indirect
rogchap.com/v8go v0.7.0 // indirect
)
// go env -w GOPRIVATE=github.com/yaoapp/*

288
go.sum
View file

@ -39,15 +39,8 @@ cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/TylerBrock/colorjson v0.0.0-20200706003622-8a50f05110d2 h1:ZBbLwSJqkHBuFDA6DUhhse0IGJ7T5bemHyNILUjvOq4=
github.com/TylerBrock/colorjson v0.0.0-20200706003622-8a50f05110d2/go.mod h1:VSw57q4QFiWDbRnjdX8Cb3Ow0SFncRw+bA/ofY6Q83w=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/aliyun/alibaba-cloud-sdk-go v1.61.1286 h1:+Ah33S/QRnOFp4dM6VV2gYDbFu3Cj4HrRy1VpOhBzm8=
github.com/aliyun/alibaba-cloud-sdk-go v1.61.1286/go.mod h1:9CMdKNL3ynIGPpfTcdwTvIm8SGuAZYYC4jFVSSvE1YQ=
github.com/aliyun/aliyun-oss-go-sdk v2.1.10+incompatible h1:D3gwOr9qUUmyyBRDbpnATqu+EkqqmigFd3Od6xO1QUU=
@ -55,52 +48,38 @@ github.com/aliyun/aliyun-oss-go-sdk v2.1.10+incompatible/go.mod h1:T/Aws4fEfogEE
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
github.com/armon/go-metrics v0.3.10 h1:FR+drcQStOe+32sYyJYyZ7FIdgoGGBnwLl+flodp8Uo=
github.com/armon/go-metrics v0.3.10/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f h1:ZNv7On9kyUzm7fvRZumSyy/IUiSC7AzL0I1jKKtwooA=
github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f/go.mod h1:AuiFmCCPBSrqvVMvuqFuk0qogytodnVFVSN5CeJB8Gc=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM=
github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=
github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
github.com/buraksezer/connpool v0.4.0 h1:fNLvWu0FOtJxL7Sqetm6+040mhZWVs8c6vrke14SEKw=
github.com/buraksezer/connpool v0.4.0/go.mod h1:qPiG7gKXo+EjrwG/yqn2StZM4ek6gcYnnGgFIVKN6b0=
github.com/buraksezer/consistent v0.0.0-20191006190839-693edf70fd72/go.mod h1:OEE5igu/CDjGegM1Jn6ZMo7R6LlV/JChAkjfQQIRLpg=
github.com/buraksezer/consistent v0.9.0 h1:Zfs6bX62wbP3QlbPGKUhqDw7SmNkOzY5bHZIYXYpR5g=
github.com/buraksezer/consistent v0.9.0/go.mod h1:6BrVajWq7wbKZlTOUPs/XVfR8c0maujuPowduSpZqmw=
github.com/buraksezer/olric v0.4.2 h1:1W5UCYFYrkeD6OHGyTi6R9YTLJ3vXcVkgas4ZbFQMfw=
github.com/buraksezer/olric v0.4.2/go.mod h1:xNt+/QiiVuqioJzgTMuiV2LynTNu87L1Tp5289XuU78=
github.com/caarlos0/env/v6 v6.9.1 h1:zOkkjM0F6ltnQ5eBX6IPI41UP/KDGEK7rRPwGCNos8k=
github.com/caarlos0/env/v6 v6.9.1/go.mod h1:hvp/ryKXKipEkcuYjs9mI4bBCg+UI0Yhgm5Zu0ddvwc=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/elazarl/go-bindata-assetfs v1.0.1 h1:m0kkaHRKEu7tUIUFVwhGGGYClXvyl4RE03qmvRTNfbw=
github.com/elazarl/go-bindata-assetfs v1.0.1/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
@ -109,16 +88,17 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI=
github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=
github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI=
github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
@ -129,27 +109,21 @@ github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3Bop
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU=
github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho=
github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE=
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
github.com/go-playground/validator/v10 v10.10.0 h1:I7mrTYv78z8k8VXa/qJlOlEXn/nBh+BF8dHX5nt/dr0=
github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos=
github.com/go-redis/redis/v8 v8.11.4 h1:kHoYkfZP6+pe04aFTnhDH6GDROa5yJdHJVNxV3F46Tg=
github.com/go-redis/redis/v8 v8.11.4/go.mod h1:2Z2wHZXdQpCDXEGzqMockDpNyYvi2l4Pxt6RJr792+w=
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A=
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
@ -188,8 +162,6 @@ github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
@ -200,8 +172,9 @@ github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
@ -219,42 +192,30 @@ github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLe
github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-hclog v0.14.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
github.com/hashicorp/go-hclog v0.16.2/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
github.com/hashicorp/go-hclog v1.1.0 h1:QsGcniKx5/LuX2eYoeL+Np3UKYPNaN7YKpTh29h8rbw=
github.com/hashicorp/go-hclog v1.1.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
github.com/hashicorp/go-hclog v1.2.0 h1:La19f8d7WIlm4ogzNHB0JGqs5AUDAZ2UfCY4sJXcJdM=
github.com/hashicorp/go-hclog v1.2.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc=
github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
github.com/hashicorp/go-msgpack v1.1.5 h1:9byZdVjKTe5mce63pRVNP1L7UAmdHOTEMGehn6KvJWs=
github.com/hashicorp/go-msgpack v1.1.5/go.mod h1:gWVc3sv/wbDmR3rQsj1CAktEZzoz1YNK9NfGLXJ69/4=
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
github.com/hashicorp/go-plugin v1.4.2/go.mod h1:5fGEH17QVwTTcR0zV7yhDPLLmFX9YSZ38b18Udy6vYQ=
github.com/hashicorp/go-plugin v1.4.3 h1:DXmvivbWD5qdiBts9TpBC7BYL1Aia5sxbRgQB+v6UZM=
github.com/hashicorp/go-plugin v1.4.3/go.mod h1:5fGEH17QVwTTcR0zV7yhDPLLmFX9YSZ38b18Udy6vYQ=
github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
github.com/hashicorp/go-plugin v1.4.4 h1:NVdrSdFRt3SkZtNckJ6tog7gbpRrcbOjQi/rgF7JYWQ=
github.com/hashicorp/go-plugin v1.4.4/go.mod h1:viDMjcLJuDui6pXb8U4HVfb8AamCWhHGUjr2IrTF67s=
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc=
github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A=
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE=
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
@ -262,35 +223,26 @@ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ
github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y=
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
github.com/hashicorp/memberlist v0.1.5/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
github.com/hashicorp/memberlist v0.3.0 h1:8+567mCcFDnS5ADl7lrpxPMWiFCElyUEeW0gtj34fMA=
github.com/hashicorp/memberlist v0.3.0/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE=
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
github.com/hashicorp/yamux v0.0.0-20210707203944-259a57b3608c/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
github.com/hashicorp/yamux v0.0.0-20211028200310-0bc27b27de87 h1:xixZ2bWeofWV68J+x6AzmKuVM/JWCQwkWm6GW/MUR6I=
github.com/hashicorp/yamux v0.0.0-20211028200310-0bc27b27de87/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
github.com/hokaccha/go-prettyjson v0.0.0-20210113012101-fb4e108d2519 h1:nqAlWFEdqI0ClbTDrhDvE/8LeQ4pftrqKUX9w5k0j3s=
github.com/hokaccha/go-prettyjson v0.0.0-20210113012101-fb4e108d2519/go.mod h1:pFlLw2CfqZiIBOx6BuCeRLCrfxBJipTY0nIOF/VbGcI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jhump/protoreflect v1.6.0 h1:h5jfMVslIg6l29nsMs0D8Wj17RDVdNYti0vDN/PZZoE=
github.com/jhump/protoreflect v1.6.0/go.mod h1:eaTn3RZAmMBcV0fifFvlm6VHNz3wSkYyXYWUh7ymB74=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jmoiron/sqlx v1.3.1/go.mod h1:2BljVx/86SuTyjE+aPYlHCTNvZrnJXghYGpNiXLBMCQ=
github.com/jmoiron/sqlx v1.3.4 h1:wv+0IJZfL5z0uZoUjlpKgHkgaFSYD+r9CfrXjEXsO7w=
github.com/jmoiron/sqlx v1.3.4/go.mod h1:2BljVx/86SuTyjE+aPYlHCTNvZrnJXghYGpNiXLBMCQ=
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
@ -299,57 +251,42 @@ github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.9.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lib/pq v1.10.4 h1:SO9z7FRPzA03QhHKJrH5BXA6HU1rS4V2nIVrrNC1iYk=
github.com/lib/pq v1.10.4/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.11 h1:nQ+aFkoE2TMGc0b68U2OKSexC+eq46+XwZzWXHRmPYs=
github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40=
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/mattn/go-sqlite3 v1.14.9 h1:10HX2Td0ocZpYEjhilsuo6WWtUqttj2Kb0KtD86/KYA=
github.com/mattn/go-sqlite3 v1.14.9/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
github.com/miekg/dns v1.1.31/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
github.com/miekg/dns v1.1.43 h1:JKfpVSCB84vrAmHzyrsxB5NAr5kLoMXZArPSw7Qlgyg=
github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4=
github.com/miekg/dns v1.1.48 h1:Ucfr7IIVyMBz4lRE8qmGUuZ4Wt3/ZGu9hmcMT3Uu4tQ=
github.com/miekg/dns v1.1.48/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU=
github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8=
github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
@ -366,64 +303,49 @@ github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
github.com/mojocn/base64Captcha v1.3.5 h1:Qeilr7Ta6eDtG4S+tQuZ5+hO+QHbiGAJdi4PfoagaA0=
github.com/mojocn/base64Captcha v1.3.5/go.mod h1:/tTTXn4WTpX9CfrmipqRytCpJ27Uw3G6I7NcP2WwcmY=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA=
github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc=
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.16.0 h1:6gjqkI8iiRHMvdccRJM8rVKjCWk6ZIm6FTm3ddIe4/c=
github.com/onsi/gomega v1.16.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY=
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
github.com/richardlehane/mscfb v1.0.3 h1:rD8TBkYWkObWO0oLDFCbwMeZ4KoalxQy+QgniCj3nKI=
github.com/richardlehane/mscfb v1.0.3/go.mod h1:YzVpcZg9czvAuhk9T+a3avCpcFPMUWm7gK3DypaEsUk=
github.com/richardlehane/msoleps v1.0.1 h1:RfrALnSNXzmXLbGct/P2b4xkFz4e8Gmj/0Vj9M9xC1o=
github.com/richardlehane/msoleps v1.0.1/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg=
github.com/robertkrimen/otto v0.0.0-20211024170158-b87d35c0b86f h1:a7clxaGmmqtdNTXyvrp/lVO/Gnkzlhc/+dLs5v965GM=
github.com/robertkrimen/otto v0.0.0-20211024170158-b87d35c0b86f/go.mod h1:/mK7FZ3mFYEn9zvNPhpngTyatyehSwte5bJZ4ehL5Xw=
github.com/robfig/cron v1.2.0 h1:ZjScXvvxeQ63Dbyxy76Fj3AT3Ut0aKsyd2/tl3DTMuQ=
github.com/robfig/cron v1.2.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k=
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 h1:qLC7fQah7D6K1B0ujays3HV9gkFtllcxhzImRR7ArPQ=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.6.0 h1:xoax2sJ2DT8S8xA2paPFjDCScCNeWsg75VG0DLRreiY=
github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
@ -434,24 +356,40 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
github.com/takama/daemon v1.0.0 h1:XS3VLnFKmqw2Z7fQ/dHRarrVjdir9G3z7BEP8osjizQ=
github.com/takama/daemon v1.0.0/go.mod h1:gKlhcjbqtBODg5v9H1nj5dU1a2j2GemtuWSNLD5rxOE=
github.com/tidwall/assert v0.1.0 h1:aWcKyRBUAdLoVebxo95N7+YZVTFF/ASTr7BN4sLP6XI=
github.com/tidwall/assert v0.1.0/go.mod h1:QLYtGyeqse53vuELQheYl9dngGCJQ+mTtlxcktb+Kj8=
github.com/tidwall/btree v1.1.0 h1:5P+9WU8ui5uhmcg3SoPyTwoI0mVyZ1nps7YQzTZFkYM=
github.com/tidwall/btree v1.1.0/go.mod h1:TzIRzen6yHbibdSfK6t8QimqbUnoxUSrZfeW7Uob0q4=
github.com/tidwall/buntdb v1.2.9 h1:XVz684P7X6HCTrdr385yDZWB1zt/n20ZNG3M1iGyFm4=
github.com/tidwall/buntdb v1.2.9/go.mod h1:IwyGSvvDg6hnKSIhtdZ0AqhCZGH8ukdtCAzaP8fI1X4=
github.com/tidwall/gjson v1.12.1 h1:ikuZsLdhr8Ws0IdROXUS1Gi4v9Z4pGqpX/CvJkxvfpo=
github.com/tidwall/gjson v1.12.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/grect v0.1.4 h1:dA3oIgNgWdSspFzn1kS4S/RDpZFLrIxAZOdJKjYapOg=
github.com/tidwall/grect v0.1.4/go.mod h1:9FBsaYRaR0Tcy4UwefBX/UDcDcDy9V5jUcxHzv2jd5Q=
github.com/tidwall/lotsa v1.0.2 h1:dNVBH5MErdaQ/xd9s769R31/n2dXavsQ0Yf4TMEHHw8=
github.com/tidwall/lotsa v1.0.2/go.mod h1:X6NiU+4yHA3fE3Puvpnn1XMDrFZrE9JO2/w+UMuqgR8=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/tidwall/rtred v0.1.2 h1:exmoQtOLvDoO8ud++6LwVsAMTu0KPzLTUrMln8u1yu8=
github.com/tidwall/rtred v0.1.2/go.mod h1:hd69WNXQ5RP9vHd7dqekAz+RIdtfBogmglkZSRxCHFQ=
github.com/tidwall/tinyqueue v0.1.1 h1:SpNEvEggbpyN5DIReaJ2/1ndroY8iyEGxPYxoSaymYE=
github.com/tidwall/tinyqueue v0.1.1/go.mod h1:O/QNHwrnjqr6IHItYrzoHAKYhBkLI67Q096fQP5zMYw=
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
github.com/ugorji/go v1.2.6 h1:tGiWC9HENWE2tqYycIqFTNorMmFRVhNwCpDOpWqnk8E=
github.com/ugorji/go v1.2.6/go.mod h1:anCg0y61KIhDlPZmnH+so+RQbysYVyDko0IMgJv0Nn0=
github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
github.com/ugorji/go/codec v1.2.6 h1:7kbGefxLoDBuYXOms4yD7223OpNMMPNPZxXk5TvFcyQ=
github.com/ugorji/go/codec v1.2.6/go.mod h1:V6TCNZ4PHqoHGFZuSG1W8nrCzzdgA2DozYxWFFpvxTw=
github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI=
github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=
github.com/xuri/efp v0.0.0-20210322160811-ab561f5b45e3 h1:EpI0bqf/eX9SdZDwlMmahKM+CDBgNbsXMhsN28XrM8o=
github.com/xuri/efp v0.0.0-20210322160811-ab561f5b45e3/go.mod h1:ybY/Jr0T0GTCnYjKqmdwxyxn2BQf2RcQIIvex5QldPI=
github.com/xuri/excelize/v2 v2.5.0 h1:nDDVfX0qaDuGjAvb+5zTd0Bxxoqa1Ffv9B4kiE23PTM=
@ -461,7 +399,6 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=
go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ=
@ -476,16 +413,13 @@ go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqe
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201001193750-eb9a90e9f9cb/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220208050332-20e1d8d225ab h1:lnZ4LoV0UMdibeCUfIB2a4uFwRu491WX/VB2reB8xNc=
golang.org/x/crypto v0.0.0-20220208050332-20e1d8d225ab/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
@ -527,12 +461,12 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@ -541,11 +475,9 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@ -556,11 +488,11 @@ golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200930145003-4acb6c075d10/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
@ -569,12 +501,10 @@ golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211118161319-6a13c67c3ce4 h1:DZshvxDdVoeKIbudAdFEKi+f70l51luSy/7b76ibTY0=
golang.org/x/net v0.0.0-20211118161319-6a13c67c3ce4/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220526153639-5463443f8c37 h1:lUkvobShwKsOesNfWWlCS5q7fnbG1MEliIzwu886fn8=
golang.org/x/net v0.0.0-20220526153639-5463443f8c37/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@ -595,32 +525,29 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200930132711-30421366ff76/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -636,15 +563,16 @@ golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200722175500-76b94024e4b6/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -654,13 +582,10 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211004093028-2c5d950f24ef/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220207234003-57398862261d h1:Bm7BNOQt2Qv7ZqysjeLjgCBanX+88Z/OtdvsrEv1Djc=
golang.org/x/sys v0.0.0-20220207234003-57398862261d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@ -684,7 +609,6 @@ golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190424220101-1e8e1cfdf96b/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
@ -692,7 +616,6 @@ golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgw
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
@ -701,7 +624,6 @@ golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtn
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
@ -727,11 +649,13 @@ golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82u
golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.7 h1:6j8CgantCy3yc8JGBqkDLMKWqZ0RDU2g1HVgacojGWQ=
golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@ -766,9 +690,7 @@ google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/genproto v0.0.0-20170818010345-ee236bd376b0/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
@ -810,10 +732,8 @@ google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6D
google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1 h1:b9mVrqYfq3P4bCdaLg1qtBnPzUYgglsIdjZkL/fQVOE=
google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
google.golang.org/genproto v0.0.0-20220526192754-51939a95c655 h1:56rmjc5LUAanErbiNrY+s/Nd47wDQEJkpqS7i43M1I0=
google.golang.org/genproto v0.0.0-20220526192754-51939a95c655/go.mod h1:yKyY4AMRwFiC8yMMNaMi+RkCnjZJt9LoWuvhXjMs+To=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
@ -834,9 +754,8 @@ google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAG
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
google.golang.org/grpc v1.42.0 h1:XT2/MFpuPFsEX2fWh3YQtHkZ+WYZFQRfaUgLZYj/p6A=
google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
google.golang.org/grpc v1.46.2 h1:u+MLGgVf7vRdjEYZ8wDFhAVNmhkbJ5hmrA1LMWK1CAQ=
google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
@ -849,32 +768,31 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU=
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/readline.v1 v1.0.0-20160726135117-62c6fe619375/go.mod h1:lNEQeAhU009zbRxng+XOj5ITVgY24WcbNnQopyfKoYQ=
gopkg.in/sourcemap.v1 v1.0.5 h1:inv58fC9f9J3TK2Y2R1NPntXEn3/wjWHkonhIUODNTI=
gopkg.in/sourcemap.v1 v1.0.5/go.mod h1:2RlvNNSMglmRrcvhfuzp4hQHwOtjxlbjX7UPY/GXb78=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0 h1:hjy8E9ON/egN1tAYqKb61G10WtihqetD4sz2H+8nIeA=
gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

22
helper/hex.process.go Normal file
View file

@ -0,0 +1,22 @@
package helper
import (
"encoding/hex"
"github.com/yaoapp/gou"
"github.com/yaoapp/kun/log"
)
// ProcessHexToString xiang.helper.HexToString
func ProcessHexToString(process *gou.Process) interface{} {
process.ValidateArgNums(1)
switch process.Args[0].(type) {
case string:
return hex.EncodeToString([]byte(process.Args[0].(string)))
case []byte:
return hex.EncodeToString(process.Args[0].([]byte))
}
log.With(log.F{"input": process.Args[0]}).Error("HexToString: type does not support")
return nil
}

61
helper/hex_test.go Normal file
View file

@ -0,0 +1,61 @@
package helper
import (
"os"
"path"
"testing"
"github.com/stretchr/testify/assert"
"github.com/yaoapp/gou"
"github.com/yaoapp/yao/flow"
"github.com/yaoapp/yao/share"
)
func TestProcessHexToString(t *testing.T) {
res, err := gou.NewProcess("xiang.helper.HexToString", []byte{0x0, 0x1}).Exec()
assert.Nil(t, err)
assert.Equal(t, "0001", res)
res, err = gou.NewProcess("xiang.helper.HexToString", string([]byte{0x0, 0x1})).Exec()
assert.Nil(t, err)
assert.Equal(t, "0001", res)
res, err = gou.NewProcess("xiang.helper.HexToString", 1024).Exec()
assert.Nil(t, err)
assert.Nil(t, res)
}
func TestProcessHexToStringInFlow(t *testing.T) {
testflow := path.Join(os.Getenv("YAO_DEV"), "tests", "flows", "helper")
flow.LoadFrom(testflow, "helper.")
res, err := gou.NewProcess("flows.helper.HexToString").Exec()
assert.Nil(t, err)
assert.Equal(t, "6162", res) // ab
}
func TestProcessHexToStringInScript(t *testing.T) {
testscirpt := path.Join(os.Getenv("YAO_DEV"), "tests", "scripts")
share.LoadFrom(testscirpt)
res, err := gou.NewProcess("scripts.helper.HexToStringString").Exec()
assert.Nil(t, err)
assert.Equal(t, "6162", res) // ab
res, err = gou.NewProcess("scripts.helper.HexToStringBytes", []byte{0x0, 0x1}).Exec()
assert.Nil(t, err)
assert.Equal(t, "0001", res) // []byte{0x0, 0x1}
}
func TestProcessBufferInScript(t *testing.T) {
testscirpt := path.Join(os.Getenv("YAO_DEV"), "tests", "scripts")
share.LoadFrom(testscirpt)
res, err := gou.NewProcess("scripts.helper.Buffer").Exec()
assert.Nil(t, err)
str, ok := res.(string)
assert.True(t, ok)
assert.Equal(t, []byte{0x0, 0x1}, []byte(str))
}

View file

@ -27,6 +27,8 @@ func init() {
gou.RegisterProcessHandler("xiang.helper.MapDel", ProcessMapDel)
gou.RegisterProcessHandler("xiang.helper.MapMultiDel", ProcessMapMultiDel)
gou.RegisterProcessHandler("xiang.helper.HexToString", ProcessHexToString)
gou.RegisterProcessHandler("xiang.helper.StrConcat", ProcessStrConcat)
gou.RegisterProcessHandler("xiang.helper.Captcha", ProcessCaptcha)
@ -64,6 +66,7 @@ func init() {
gou.RegisterProcessHandler("xiang.flow.Sleep", ProcessSleep)
gou.AliasProcess("xiang.flow.Sleep", "xiang.sys.Sleep")
}
// ProcessPrint xiang.helper.Print 打印语句

View file

@ -7,6 +7,7 @@ import (
"sort"
"strings"
"github.com/google/uuid"
jsoniter "github.com/json-iterator/go"
"github.com/yaoapp/gou"
"github.com/yaoapp/kun/any"
@ -382,19 +383,22 @@ func (imp *Importer) SaveAsTemplate(src from.Source) {
}
// Run 运行导入
func (imp *Importer) Run(src from.Source, mapping *Mapping) map[string]int {
func (imp *Importer) Run(src from.Source, mapping *Mapping) interface{} {
if mapping == nil {
mapping = imp.AutoMapping(src)
}
id := uuid.NewString()
page := 0
total := 0
failed := 0
ignore := 0
imp.Chunk(src, mapping, func(line int, data [][]interface{}) {
page++
length := len(data)
total = total + length
columns, data := imp.DataClean(data, mapping.Columns)
process, err := gou.ProcessOf(imp.Process, columns, data)
process, err := gou.ProcessOf(imp.Process, columns, data, id, page)
if err != nil {
failed = failed + length
log.With(log.F{"line": line}).Error("导入失败: %s", err.Error())
@ -424,12 +428,24 @@ func (imp *Importer) Run(src from.Source, mapping *Mapping) map[string]int {
log.With(log.F{"line": line, "response": response, "length": length}).Error("导入处理器未返回失败结果")
})
return map[string]int{
output := map[string]int{
"total": total,
"success": total - failed - ignore,
"failure": failed,
"ignore": ignore,
}
if imp.Output != "" {
res, err := gou.NewProcess(imp.Output, output).Exec()
if err != nil {
log.With(log.F{"output": imp.Output}).Error(err.Error())
return output
}
return res
}
return output
}
// Start 运行导入(异步)

View file

@ -89,7 +89,7 @@ func TestRunSimple(t *testing.T) {
imp := Select("order")
mapping := imp.AutoMapping(file)
res := imp.Run(file, mapping)
res := imp.Run(file, mapping).(map[string]int)
assert.Equal(t, 1, res["ignore"])
assert.Equal(t, 1, res["failure"])
assert.Equal(t, 2, res["success"])

View file

@ -13,6 +13,7 @@ const PreviewNever = "never"
type Importer struct {
Title string `json:"title,omitempty"` // 导入名称
Process string `json:"process"` // 处理器名称
Output string `json:"output,omitempty"` // The process import output
Columns []Column `json:"columns"` // 字段列表
Option Option `json:"option,omitempty"` // 导入配置项
Rules map[string]string `json:"rules,omitempty"` // 许可导入规则

View file

@ -95,7 +95,7 @@ func (xlsx *Xlsx) Chunk(size int, axises []string, cb func(line int, data [][]in
row, end := xlsx.readLine(line, axises)
if end {
cb(line, data)
break
return
}
data = append(data, row)

38
lang/lang.go Normal file
View file

@ -0,0 +1,38 @@
package lang
import (
"os"
"path/filepath"
"github.com/yaoapp/gou/lang"
"github.com/yaoapp/kun/log"
"github.com/yaoapp/yao/config"
)
func init() {
lang.RegisterWidget("tables", "table")
lang.RegisterWidget("charts", "chart")
lang.RegisterWidget("kanban", "page")
lang.RegisterWidget("screen", "page")
lang.RegisterWidget("pages", "page")
}
// Load language packs
func Load(cfg config.Config) error {
root := filepath.Join(cfg.Root, "langs")
err := lang.Load(root)
if err != nil {
return err
}
name := os.Getenv("YAO_LANG")
if name != "" {
if _, has := lang.Dicts[name]; !has {
log.Error("The language pack %s does not found", name)
return nil
}
lang.Pick(name).AsDefault()
}
return nil
}

16
lang/lang_test.go Normal file
View file

@ -0,0 +1,16 @@
package lang
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/yaoapp/gou/lang"
"github.com/yaoapp/yao/config"
"github.com/yaoapp/yao/share"
)
func TestLoad(t *testing.T) {
share.DBConnect(config.Conf.DB)
Load(config.Conf)
assert.Len(t, lang.Dicts, 2)
}

View file

@ -2,8 +2,12 @@ package main
import (
"github.com/yaoapp/yao/cmd"
_ "github.com/yaoapp/gou/encoding"
_ "github.com/yaoapp/yao/crypto"
_ "github.com/yaoapp/yao/helper"
_ "github.com/yaoapp/yao/network"
_ "github.com/yaoapp/yao/system"
_ "github.com/yaoapp/yao/user"
_ "github.com/yaoapp/yao/xfs"
)

View file

@ -122,6 +122,7 @@ func TestCommandStop(t *testing.T) {
times++
res, err := request()
if err != nil {
fmt.Println("REQUEST ERROR:", err)
continue
}
assert.Equal(t, 1, any.Of(res.Get("id")).CInt())

View file

@ -5,6 +5,7 @@ import (
"path/filepath"
"github.com/yaoapp/gou"
"github.com/yaoapp/gou/lang"
"github.com/yaoapp/kun/log"
"github.com/yaoapp/yao/config"
"github.com/yaoapp/yao/share"
@ -31,7 +32,14 @@ func LoadFrom(dir string, prefix string) error {
_, err := gou.LoadModelReturn(string(content), name)
if err != nil {
log.With(log.F{"root": root, "file": filename}).Error(err.Error())
return
}
// Apply the language pack
if lang.Default != nil {
lang.Default.Apply(gou.Models[name])
}
})
return err

View file

@ -1,17 +1,23 @@
package model
import (
"os"
"testing"
"github.com/stretchr/testify/assert"
"github.com/yaoapp/gou"
"github.com/yaoapp/yao/config"
"github.com/yaoapp/yao/lang"
"github.com/yaoapp/yao/share"
)
func TestLoad(t *testing.T) {
os.Setenv("YAO_LANG", "zh-hk")
lang.Load(config.Conf)
share.DBConnect(config.Conf.DB)
gou.Models = make(map[string]*gou.Model)
Load(config.Conf)
LoadFrom("not a path", "404.")
@ -23,5 +29,9 @@ func check(t *testing.T) {
for key := range gou.Models {
keys = append(keys, key)
}
assert.Equal(t, 10, len(keys))
assert.Equal(t, 11, len(keys))
demo := gou.Select("demo")
assert.Equal(t, demo.MetaData.Name, "演示")
assert.Equal(t, demo.Columns["action"].Label, "動作")
}

View file

@ -41,7 +41,7 @@ func ProcessIP(process *gou.Process) interface{} {
// FreePort xiang.network.FreePort 获取可用端口
func FreePort() int {
addr, err := net.ResolveTCPAddr("tcp", "localhost:0")
addr, err := net.ResolveTCPAddr("tcp", ":0")
if err != nil {
exception.New("获取可用端口失败 %s", 500, err.Error()).Throw()
return 0
@ -52,6 +52,7 @@ func FreePort() int {
exception.New("获取可用端口失败 %s", 500, err.Error()).Throw()
return 0
}
defer l.Close()
return l.Addr().(*net.TCPAddr).Port
}

View file

@ -9,6 +9,7 @@ import (
"strings"
jsoniter "github.com/json-iterator/go"
"github.com/yaoapp/gou/dns"
)
// Response 请求响应结果
@ -94,16 +95,21 @@ func RequestSend(method string, url string, params map[string]interface{}, data
}
}
// Https
var client *http.Client = &http.Client{}
// Force using system DSN resolver
// var dialer = &net.Dialer{Resolver: &net.Resolver{PreferGo: false}}
var dialContext = dns.DialContext()
var tr = &http.Transport{DialContext: dialContext}
var client *http.Client = &http.Client{Transport: tr}
// SkipVerify false
// Https SkipVerify false
if strings.HasPrefix(url, "https://") {
tr := &http.Transport{
tr = &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
DialContext: dialContext,
}
client = &http.Client{Transport: tr}
}
defer tr.CloseIdleConnections()
resp, err := client.Do(req)
if err != nil {

125
page/lang.go Normal file
View file

@ -0,0 +1,125 @@
package page
// Lang for applying a language pack
func (page *Page) Lang(trans func(widget string, inst string, value *string) bool) {
inst := page.Flow.Name
widget := "page"
trans(widget, inst, &page.Name)
trans(widget, inst, &page.Label)
trans(widget, inst, &page.Description)
trans(widget, inst, &page.Page.Primary)
transMap(widget, inst, page.Page.Layout, trans)
page.Output = transAny(widget, inst, page.Output, trans)
// Filters
for name, filter := range page.Filters {
new := name
trans(widget, inst, &new)
trans(widget, inst, &filter.Label)
delete(page.Filters, name)
// Props
transMap(widget, inst, filter.Input.Props, trans)
page.Filters[new] = filter
}
}
func transAny(widget string, inst string, input interface{}, trans func(widget string, inst string, value *string) bool) interface{} {
switch input.(type) {
case []interface{}:
values := input.([]interface{})
transArr(widget, inst, values, trans)
input = values
break
case map[string]interface{}:
values := input.(map[string]interface{})
for name, value := range values {
new := name
newValue := value
switch value.(type) {
case string:
val := value.(string)
trans(widget, inst, &val)
newValue = val
break
case []interface{}:
vals := value.([]interface{})
transArr(widget, inst, vals, trans)
newValue = vals
break
case map[string]interface{}:
vals := value.(map[string]interface{})
transMap(widget, inst, vals, trans)
newValue = vals
break
}
trans(widget, inst, &new)
delete(values, name)
values[new] = newValue
}
input = values
break
}
return input
}
func transMap(widget string, inst string, values map[string]interface{}, trans func(widget string, inst string, value *string) bool) {
for key, value := range values {
switch value.(type) {
case string:
v := value.(string)
trans(widget, inst, &v)
values[key] = v
break
case []interface{}:
v := value.([]interface{})
transArr(widget, inst, v, trans)
values[key] = v
break
case map[string]interface{}:
v := value.(map[string]interface{})
transMap(widget, inst, v, trans)
values[key] = v
break
}
}
}
func transArr(widget string, inst string, values []interface{}, trans func(widget string, inst string, value *string) bool) {
for key, value := range values {
switch value.(type) {
case string:
v := value.(string)
trans(widget, inst, &v)
values[key] = v
break
case []interface{}:
v := value.([]interface{})
transArr(widget, inst, v, trans)
values[key] = v
break
case map[string]interface{}:
v := value.(map[string]interface{})
transMap(widget, inst, v, trans)
values[key] = v
break
}
}
}

View file

@ -6,6 +6,7 @@ import (
jsoniter "github.com/json-iterator/go"
"github.com/yaoapp/gou"
"github.com/yaoapp/gou/lang"
"github.com/yaoapp/kun/exception"
"github.com/yaoapp/kun/log"
"github.com/yaoapp/yao/config"
@ -24,15 +25,15 @@ func Load(cfg config.Config) error {
var errs error = nil
if err := LoadFrom(path.Join(cfg.Root, "/kanban"), ""); err != nil {
log.Trace("load kanban error: %s ", err.Error())
// log.Trace("load kanban error: %s ", err.Error())
errs = err
}
if err := LoadFrom(path.Join(cfg.Root, "/screen"), ""); err != nil {
log.Trace("load screen error: %s", err.Error())
// log.Trace("load screen error: %s", err.Error())
errs = err
}
if err := LoadFrom(path.Join(cfg.Root, "/pages"), ""); err != nil {
log.Trace("load screen error: %s", err.Error())
// log.Trace("load screen error: %s", err.Error())
errs = err
}
@ -93,6 +94,12 @@ func LoadPage(source []byte, name string) (*Page, error) {
page.Prepare()
page.SetupAPIs()
Pages[name] = page
// Apply a language pack
if lang.Default != nil {
lang.Default.Apply(Pages[name])
}
return page, nil
}

View file

@ -1,16 +1,22 @@
package page
import (
"os"
"testing"
"github.com/stretchr/testify/assert"
"github.com/yaoapp/yao/config"
"github.com/yaoapp/yao/lang"
"github.com/yaoapp/yao/model"
"github.com/yaoapp/yao/query"
"github.com/yaoapp/yao/share"
)
func TestLoad(t *testing.T) {
os.Setenv("YAO_LANG", "zh-hk")
lang.Load(config.Conf)
share.DBConnect(config.Conf.DB)
model.Load(config.Conf)
query.Load(config.Conf)
@ -25,5 +31,15 @@ func check(t *testing.T) {
for key := range Pages {
keys = append(keys, key)
}
assert.Equal(t, 1, len(keys))
assert.Equal(t, 2, len(keys))
lang := Pages["lang"]
output := lang.Output.(map[string]interface{})
assert.Equal(t, "{{$in}}", output["參數"])
filters := lang.Page.Layout["filters"].([]interface{})
begin := filters[0].(map[string]interface{})
assert.Equal(t, "開始時間", begin["name"])
assert.Equal(t, "請選擇開始時間", lang.Filters["開始時間"].Input.Props["placeholder"])
}

View file

@ -33,6 +33,10 @@ func ProcessData(process *gou.Process) interface{} {
}
}
// with Session
page.Flow.WithGlobal(process.Global)
page.Flow.WithSID(process.Sid)
return page.GetData(params)
}

View file

@ -8,7 +8,6 @@ import (
"github.com/stretchr/testify/assert"
"github.com/yaoapp/gou"
"github.com/yaoapp/kun/any"
"github.com/yaoapp/kun/utils"
"github.com/yaoapp/yao/config"
_ "github.com/yaoapp/yao/helper"
"github.com/yaoapp/yao/model"
@ -68,8 +67,6 @@ func TestProcessData(t *testing.T) {
}
process := gou.NewProcess("xiang.page.Data", args...)
response := ProcessData(process)
utils.Dump(response)
assert.NotNil(t, response)
res := any.Of(response).Map().Dot()

View file

@ -2,6 +2,7 @@ package query
import (
"github.com/yaoapp/gou"
"github.com/yaoapp/gou/query"
dsl "github.com/yaoapp/gou/query/gou"
"github.com/yaoapp/kun/log"
"github.com/yaoapp/xun/capsule"
@ -10,21 +11,21 @@ import (
// Load 加载查询引擎
func Load(cfg config.Config) {
XiangQuery()
DefaultQuery()
}
// XiangQuery 注册应用引擎象 xiang
func XiangQuery() {
gou.RegisterEngine("xiang", &dsl.Query{
// DefaultQuery register the default engine
func DefaultQuery() {
query.Register("default", &dsl.Query{
Query: capsule.Query(),
GetTableName: func(s string) string {
if mod, has := gou.Models[s]; has {
return mod.MetaData.Table.Name
}
// exception.New("%s 数据模型尚未加载", 404).Throw()
log.Error("%s model does not load", s)
return s
},
AESKey: config.Conf.DB.AESKey,
})
query.Alias("default", "xiang")
}

36
schedule/schedule.go Normal file
View file

@ -0,0 +1,36 @@
package schedule
import (
"fmt"
"path/filepath"
"github.com/yaoapp/gou"
"github.com/yaoapp/kun/log"
"github.com/yaoapp/yao/config"
"github.com/yaoapp/yao/share"
)
// Load load schedule
func Load(cfg config.Config) error {
var root = filepath.Join(cfg.Root, "schedules")
return LoadFrom(root, "")
}
// LoadFrom load from dir
func LoadFrom(dir string, prefix string) error {
if share.DirNotExists(dir) {
return fmt.Errorf("%s does not exists", dir)
}
err := share.Walk(dir, ".json", func(root, filename string) {
name := prefix + share.SpecName(root, filename)
content := share.ReadFile(filename)
_, err := gou.LoadSchedule(string(content), name)
if err != nil {
log.With(log.F{"root": root, "file": filename}).Error(err.Error())
}
})
return err
}

21
schedule/schedule_test.go Normal file
View file

@ -0,0 +1,21 @@
package schedule
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/yaoapp/gou"
"github.com/yaoapp/yao/config"
"github.com/yaoapp/yao/task"
)
func TestLoad(t *testing.T) {
task.Load(config.Conf)
Load(config.Conf)
LoadFrom("not a path", "404.")
check(t)
}
func check(t *testing.T) {
assert.Equal(t, 2, len(gou.Schedules))
}

View file

@ -5,12 +5,14 @@ import (
"github.com/gin-gonic/gin"
"github.com/yaoapp/yao/helper"
"github.com/yaoapp/yao/table"
)
// Guards 服务中间件
var Guards = map[string]gin.HandlerFunc{
"bearer-jwt": bearerJWT, // JWT 鉴权
"cross-domain": crossDomain, // 跨域许可
"table-guard": table.Guard, // Table Guard
}
// JWT 鉴权
@ -25,7 +27,6 @@ func bearerJWT(c *gin.Context) {
claims := helper.JwtValidate(tokenString)
c.Set("__sid", claims.SID)
c.Next()
}
// crossDomain 跨域访问
@ -39,6 +40,4 @@ func crossDomain(c *gin.Context) {
c.AbortWithStatus(204)
return
}
c.Next()
}

View file

@ -27,7 +27,8 @@ func BinStatic(c *gin.Context) {
length := len(c.Request.URL.Path)
if length >= 5 && c.Request.URL.Path[0:5] == "/api/" { // API接口
if (length >= 5 && c.Request.URL.Path[0:5] == "/api/") ||
(length >= 11 && c.Request.URL.Path[0:11] == "/websocket/") { // API & websocket
c.Next()
return
} else if length >= 7 && c.Request.URL.Path[0:7] == "/xiang/" { // 数据管理后台

View file

@ -1,32 +1,39 @@
package service
import (
"context"
"github.com/yaoapp/gou"
"github.com/yaoapp/kun/log"
"github.com/yaoapp/yao/config"
"github.com/yaoapp/yao/share"
)
var shutdown = make(chan bool)
var shutdownComplete = make(chan bool)
var shutdown = make(chan bool, 1)
var shutdownComplete = make(chan bool, 1)
// Start 启动服务
func Start() {
func Start() error {
if config.Conf.Session.Hosting && config.Conf.Session.IsCLI == false {
share.SessionServerStart()
err := share.SessionStart()
if err != nil {
return err
}
gou.SetHTTPGuards(Guards)
gou.ServeHTTP(
gou.Server{
Host: config.Conf.Host,
Port: config.Conf.Port,
Root: "/api",
Host: config.Conf.Host,
Port: config.Conf.Port,
Root: "/api",
Allows: config.Conf.AllowFrom,
},
&shutdown, func(s gou.Server) {
shutdown, func(s gou.Server) {
shutdownComplete <- true
},
Middlewares...)
return nil
}
// StartWithouttSession 启动服务
@ -35,11 +42,12 @@ func StartWithouttSession() {
gou.SetHTTPGuards(Guards)
gou.ServeHTTP(
gou.Server{
Host: config.Conf.Host,
Port: config.Conf.Port,
Root: "/api",
Host: config.Conf.Host,
Port: config.Conf.Port,
Root: "/api",
Allows: config.Conf.AllowFrom,
},
&shutdown, func(s gou.Server) {
shutdown, func(s gou.Server) {
shutdownComplete <- true
},
Middlewares...)
@ -48,16 +56,31 @@ func StartWithouttSession() {
// StopWithouttSession 关闭服务
func StopWithouttSession(onComplete func()) {
shutdown <- true
<-shutdownComplete
gou.KillPlugins()
onComplete()
select {
case <-shutdownComplete:
onComplete()
}
}
// Stop 关闭服务
func Stop(onComplete func()) {
shutdown <- true
<-shutdownComplete
share.SessionServerStop()
gou.KillPlugins()
onComplete()
select {
case <-shutdownComplete:
share.SessionStop()
onComplete()
}
}
// StopWithContext stop with timeout
func StopWithContext(ctx context.Context, onComplete func()) {
shutdown <- true
select {
case <-ctx.Done():
log.Error("[STOP] canceled (%v)", ctx.Err())
onComplete()
case <-shutdownComplete:
share.SessionStop()
onComplete()
}
}

View file

@ -1,459 +1,268 @@
package service
import (
"context"
"fmt"
"io/fs"
"io/ioutil"
"os"
"os/signal"
"path/filepath"
"strings"
"syscall"
"time"
"github.com/fatih/color"
"github.com/fsnotify/fsnotify"
"github.com/yaoapp/gou"
"github.com/yaoapp/yao/chart"
"github.com/yaoapp/kun/log"
"github.com/yaoapp/yao/config"
"github.com/yaoapp/yao/engine"
"github.com/yaoapp/yao/page"
"github.com/yaoapp/yao/share"
"github.com/yaoapp/yao/table"
"github.com/yaoapp/yao/workflow"
)
// Watch 监听应用目录文件变更
func Watch(cfg config.Config) {
if os.Getenv("YAO_DEV") != "" {
WatchEngine(filepath.Join(os.Getenv("YAO_DEV"), "/yao"))
}
WatchModel(filepath.Join(cfg.Root, "models"), "")
WatchAPI(filepath.Join(cfg.Root, "apis"), "")
WatchFlow(filepath.Join(cfg.Root, "flows"), "")
WatchPlugin(filepath.Join(cfg.Root, "plugins"))
WatchTable(filepath.Join(cfg.Root, "tables"), "")
WatchChart(filepath.Join(cfg.Root, "charts"), "")
WatchPage(filepath.Join(cfg.Root, "pages"), "")
WatchWorkFlow(filepath.Join(cfg.Root, "workflows"), "")
// 看板大屏
WatchPage(filepath.Join(cfg.Root, "kanban"), "")
WatchPage(filepath.Join(cfg.Root, "screen"), "")
// 监听脚本 & libs更新
WatchGlobal(filepath.Join(cfg.Root, "libs"))
WatchGlobal(filepath.Join(cfg.Root, "scripts"))
var watchShutdown = make(chan bool, 1) // shutdown signal
var watchReady = make(chan bool, 1) // ready signal
var excludes = map[string]bool{"ui": true, "db": true, "data": true}
var handlers = map[string]func(root string, file string, event string, cfg config.Config){
"models": watchModel,
}
// WatchEngine 监听监听引擎内建数据变更
func WatchEngine(root string) {
root = share.DirAbs(root)
WatchModel(filepath.Join(root, "models"), "xiang.")
WatchAPI(filepath.Join(root, "apis"), "xiang.")
WatchFlow(filepath.Join(root, "flows"), "xiang.")
WatchTable(filepath.Join(root, "tables"), "xiang.")
// Watch the application code change for hot update
func Watch(cfg config.Config) (err error) {
go func() { err = watchStart(cfg) }()
select {
case <-watchReady:
return nil
}
}
// WatchGlobal 监听通用程序更新
func WatchGlobal(root string) {
if share.DirNotExists(root) {
return
// StopWatch stop watching the code change
func StopWatch() {
watchShutdown <- true
time.Sleep(200 * time.Millisecond)
}
func watchStart(cfg config.Config) error {
root := cfg.Root
// recive interrupt signal
interrupt := make(chan os.Signal, 1)
signal.Notify(interrupt, os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT)
shutdown := make(chan bool, 1)
watcher, err := fsnotify.NewWatcher()
if err != nil {
return err
}
root = share.DirAbs(root)
go share.Watch(root, func(op string, filename string) {
if !strings.HasSuffix(filename, ".json") && !strings.HasSuffix(filename, ".js") {
return
defer watcher.Close()
root, err = filepath.Abs(root)
if err != nil {
return err
}
dirs, err := ioutil.ReadDir(root)
if err != nil {
return err
}
// Add path
for _, dir := range dirs {
if !dir.IsDir() {
continue
}
// 重启服务器
if op == "write" || op == "create" || op == "remove" || op == "rename" {
err := engine.Load(config.Conf)
name := dir.Name()
if _, has := excludes[name]; has {
continue
}
if strings.HasPrefix(name, ".") {
continue
}
filename := filepath.Join(root, name)
err := watcher.Add(filename)
if err != nil {
log.Error("[Watch] %s", err.Error())
return err
}
fmt.Println(color.GreenString("[Watch] Watching %s", name))
log.Info("[Watch] Watching: %s", filename)
// sub dir
depth := 0
err = filepath.WalkDir(filename, func(path string, d fs.DirEntry, err error) error {
depth = depth + 1
if depth == 1 {
return nil
}
if !d.IsDir() {
return nil
}
log.Info("[Watch] Watching: %s", path)
err = watcher.Add(path)
if err != nil {
fmt.Println(color.RedString("Fatal: %s", err.Error()))
return
log.Error("[Watch] %s", err.Error())
}
StopWithouttSession(func() {
fmt.Println(color.GreenString("Service Restarted"))
go StartWithouttSession()
})
}
})
}
return nil
})
// WatchModel 监听业务接口更新
func WatchModel(root string, prefix string) {
if share.DirNotExists(root) {
return
if err != nil {
log.Error("[Watch] %s", err.Error())
}
}
root = share.DirAbs(root)
go share.Watch(root, func(op string, filename string) {
if !strings.HasSuffix(filename, ".json") {
return
}
if op == "write" || op == "create" {
name := prefix + share.SpecName(root, filename)
content := share.ReadFile(filename)
_, err := gou.LoadModelReturn(string(content), name) // Reload
if err != nil {
fmt.Println(color.RedString("Fatal: %s", err.Error()))
// event handler
go func() {
for {
select {
case <-shutdown:
log.Info("[Watch] The event handler exit")
return
}
fmt.Println(color.GreenString("Model %s Reloaded", name))
} else if op == "remove" || op == "rename" {
name := prefix + share.SpecName(root, filename)
if _, has := gou.Models[name]; has {
delete(gou.Models, name)
fmt.Println(color.RedString("Model %s Removed", name))
}
}
})
}
// WatchAPI 监听业务接口更新
func WatchAPI(root string, prefix string) {
if share.DirNotExists(root) {
return
}
root = share.DirAbs(root)
go share.Watch(root, func(op string, filename string) {
case event, ok := <-watcher.Events:
if !ok {
return
}
relpath := strings.TrimPrefix(event.Name, root)
if strings.HasPrefix(relpath, string(os.PathSeparator)) {
relpath = strings.TrimPrefix(relpath, string(os.PathSeparator))
}
if !strings.HasSuffix(filename, ".json") {
return
}
pi := strings.Split(relpath, string(os.PathSeparator))
widget := pi[0]
if op == "write" || op == "create" {
name := prefix + share.SpecName(root, filename)
content := share.ReadFile(filename)
_, err := gou.LoadAPIReturn(string(content), name) // Reload
if err != nil {
fmt.Println(color.RedString("Fatal: %s", err.Error()))
return
}
fmt.Println(color.GreenString("API %s Reloaded", name))
watchHanler := watchReload
if handler, has := handlers[widget]; has {
watchHanler = handler
}
} else if op == "remove" || op == "rename" {
name := prefix + share.SpecName(root, filename)
if _, has := gou.APIs[name]; has {
delete(gou.APIs, name)
fmt.Println(color.RedString("API %s Removed", name))
}
}
// 重启服务器
if op == "write" || op == "create" || op == "remove" || op == "rename" {
StopWithouttSession(func() {
fmt.Println(color.GreenString("Service Restarted"))
go StartWithouttSession()
})
}
})
}
// WatchFlow 监听业务逻辑变更
func WatchFlow(root string, prefix string) {
if share.DirNotExists(root) {
return
}
root = share.DirAbs(root)
go share.Watch(root, func(op string, filename string) {
if !strings.HasSuffix(filename, ".json") && !strings.HasSuffix(filename, ".js") {
return
}
if strings.HasSuffix(filename, ".js") {
name := prefix + share.SpecName(root, filename)
name = strings.ReplaceAll(name, ".", "/")
filename = filepath.Join(root, name+".flow.json")
}
if op == "write" || op == "create" {
name := prefix + share.SpecName(root, filename)
content := share.ReadFile(filename)
flow, err := gou.LoadFlowReturn(string(content), name) // Reload
if err != nil {
fmt.Println(color.RedString("Fatal: %s", err.Error()))
return
}
if flow != nil { // Reload Script
dir := filepath.Dir(filename)
share.Walk(dir, ".js", func(root, filename string) {
script := share.ScriptName(filename)
content := share.ReadFile(filename)
_, err := flow.LoadScriptReturn(string(content), script)
if err != nil {
fmt.Println(color.RedString("Fatal: %s", err.Error()))
return
if _, has := excludes[widget]; !has {
base := filepath.Base(event.Name)
isdir := true
if strings.HasSuffix(base, ".yao") || strings.HasSuffix(base, ".json") || strings.HasSuffix(base, ".js") {
isdir = false
}
})
}
fmt.Println(color.GreenString("Flow %s Reloaded", name))
events := strings.Split(event.Op.String(), "|")
for _, eventType := range events {
} else if op == "remove" || op == "rename" {
name := prefix + share.SpecName(root, filename)
if _, has := gou.Flows[name]; has {
delete(gou.Flows, name)
fmt.Println(color.RedString("Flow %s Removed", name))
}
}
})
}
// ADD / REMOVE Watching dir
if isdir {
switch eventType {
case "CREATE":
log.Info("[Watch] Watching: %s", event.Name)
watcher.Add(event.Name)
break
case "REMOVE":
log.Info("[Watch] Unwatching: %s", event.Name)
watcher.Remove(event.Name)
break
}
continue
}
// WatchPlugin 监听业务插件变更
func WatchPlugin(root string) {
if share.DirNotExists(root) {
return
}
root = share.DirAbs(root)
go share.Watch(root, func(op string, filename string) {
if !strings.HasSuffix(filename, ".so") {
return
}
if op == "write" || op == "create" {
name := share.SpecName(root, filename)
_, err := gou.LoadPluginReturn(filename, name) // Reload
if err != nil {
fmt.Println(color.RedString("Fatal: %s", err.Error()))
return
}
fmt.Println(color.GreenString("Plugin %s Reloaded", name))
} else if op == "remove" || op == "rename" {
name := share.SpecName(root, filename)
if _, has := gou.Plugins[name]; has {
delete(gou.Plugins, name)
fmt.Println(color.RedString("Plugin %s Removed", name))
}
}
})
}
// WatchTable 监听数据表格更新
func WatchTable(root string, prefix string) {
if share.DirNotExists(root) {
return
}
root = share.DirAbs(root)
go share.Watch(root, func(op string, filename string) {
if !strings.HasSuffix(filename, ".json") {
return
}
if op == "write" || op == "create" {
name := prefix + share.SpecName(root, filename)
content := share.ReadFile(filename)
_, err := table.LoadTable(string(content), name) // Reload Table
if err != nil {
fmt.Println(color.RedString("Fatal: %s", err.Error()))
return
}
api, has := gou.APIs["xiang.table"]
if has {
_, err := gou.LoadAPIReturn(api.Source, api.Name)
if err != nil {
fmt.Println(color.RedString("Fatal: %s", err.Error()))
return
}
}
fmt.Println(color.GreenString("Table %s Reloaded", name))
} else if op == "remove" || op == "rename" {
name := prefix + share.SpecName(root, filename)
if _, has := table.Tables[name]; has {
delete(table.Tables, name)
fmt.Println(color.RedString("Table %s Removed", name))
}
}
// 重启服务器
if op == "write" || op == "create" || op == "remove" || op == "rename" {
StopWithouttSession(func() {
fmt.Println(color.GreenString("Service Restarted"))
go StartWithouttSession()
})
}
})
}
// WatchChart 监听分析图表更新
func WatchChart(root string, prefix string) {
if share.DirNotExists(root) {
return
}
root = share.DirAbs(root)
go share.Watch(root, func(op string, filename string) {
if !strings.HasSuffix(filename, ".json") && !strings.HasSuffix(filename, ".js") {
return
}
if strings.HasSuffix(filename, ".js") {
name := prefix + share.SpecName(root, filename)
name = strings.ReplaceAll(name, ".", "/")
filename = filepath.Join(root, name+".chart.json")
}
if op == "write" || op == "create" {
name := prefix + share.SpecName(root, filename)
content := share.ReadFile(filename)
chart, err := chart.LoadChart(content, name) // Relaod
if err != nil {
fmt.Println(color.RedString("Fatal: %s", err.Error()))
return
}
if chart != nil { // Reload Script
dir := filepath.Dir(filename)
share.Walk(dir, ".js", func(root, filename string) {
script := share.ScriptName(filename)
content := share.ReadFile(filename)
_, err := chart.LoadScriptReturn(string(content), script)
if err != nil {
fmt.Println(color.RedString("Fatal: %s", err.Error()))
return
log.Info("[Watch] %s %s", eventType, event.Name)
watchHanler(root, event.Name, eventType, cfg)
}
})
}
}
break
api, has := gou.APIs["xiang.chart"]
if has {
_, err := gou.LoadAPIReturn(api.Source, api.Name)
if err != nil {
fmt.Println(color.RedString("Fatal: %s", err.Error()))
case err, ok := <-watcher.Errors:
if !ok {
return
}
}
fmt.Println(color.GreenString("Chart %s Reloaded", name))
} else if op == "remove" || op == "rename" {
name := prefix + share.SpecName(root, filename)
if _, has := chart.Charts[name]; has {
delete(chart.Charts, name)
fmt.Println(color.RedString("Chart %s Removed", name))
fmt.Println(color.RedString("[Watch] %s", err.Error()))
log.Error("[Watch] %s", err.Error())
break
}
}
}()
// 重启服务器
if op == "write" || op == "create" || op == "remove" || op == "rename" {
StopWithouttSession(func() {
fmt.Println(color.GreenString("Service Restarted"))
go StartWithouttSession()
})
fmt.Println(color.GreenString("[Watch] Started"))
watchReady <- true
for {
select {
case <-watchShutdown:
shutdown <- true
log.Info("[Watch] Stopped")
fmt.Println(color.YellowString("[Watch] Stopped"))
return nil
case <-interrupt:
shutdown <- true
log.Info("[Watch] Stopped")
fmt.Println(color.YellowString("[Watch] Stopped"))
return nil
}
})
}
}
// WatchPage 监听页面更新
func WatchPage(root string, prefix string) {
if share.DirNotExists(root) {
return
}
root = share.DirAbs(root)
go share.Watch(root, func(op string, filename string) {
if !strings.HasSuffix(filename, ".json") && !strings.HasSuffix(filename, ".js") {
func watchModel(root string, file string, event string, cfg config.Config) {
name := share.SpecName(root, file)
switch event {
case "CREATE":
content, err := ioutil.ReadFile(file)
if err != nil {
fmt.Println(color.RedString("[Watch] Model: %s %s", name, err.Error()))
return
}
mod, err := gou.LoadModelReturn(string(content), name)
if err != nil {
fmt.Println(color.RedString("[Watch] Model: %s %s", name, err.Error()))
return
}
if strings.HasSuffix(filename, ".js") {
name := prefix + share.SpecName(root, filename)
name = strings.ReplaceAll(name, ".", "/")
filename = filepath.Join(root, name+".page.json")
}
mod.Migrate(true)
fmt.Println(color.GreenString("[Watch] Model: %s Created", name))
break
if op == "write" || op == "create" {
name := prefix + share.SpecName(root, filename)
content := share.ReadFile(filename)
page, err := page.LoadPage(content, name) // Relaod
if err != nil {
fmt.Println(color.RedString("Fatal: %s", err.Error()))
return
}
if page != nil { // Reload Script
dir := filepath.Dir(filename)
share.Walk(dir, ".js", func(root, filename string) {
script := share.ScriptName(filename)
content := share.ReadFile(filename)
_, err := page.LoadScriptReturn(string(content), script)
if err != nil {
fmt.Println(color.RedString("Fatal: %s", err.Error()))
return
}
})
}
api, has := gou.APIs["xiang.page"]
if has {
_, err := gou.LoadAPIReturn(api.Source, api.Name)
if err != nil {
fmt.Println(color.RedString("Fatal: %s", err.Error()))
return
}
}
fmt.Println(color.GreenString("Page %s Reloaded", name))
} else if op == "remove" || op == "rename" {
name := prefix + share.SpecName(root, filename)
if _, has := page.Pages[name]; has {
delete(page.Pages, name)
fmt.Println(color.RedString("Page %s Removed", name))
}
}
// 重启服务器
if op == "write" || op == "create" || op == "remove" || op == "rename" {
StopWithouttSession(func() {
fmt.Println(color.GreenString("Service Restarted"))
go StartWithouttSession()
})
}
})
}
// WatchWorkFlow 监听工作流更新
func WatchWorkFlow(root string, prefix string) {
if share.DirNotExists(root) {
return
}
root = share.DirAbs(root)
go share.Watch(root, func(op string, filename string) {
if !strings.HasSuffix(filename, ".json") {
case "WRITE":
content, err := ioutil.ReadFile(file)
if err != nil {
fmt.Println(color.RedString("[Watch] Model: %s %s", name, err.Error()))
return
}
if op == "write" || op == "create" {
name := prefix + share.SpecName(root, filename)
content := share.ReadFile(filename)
_, err := workflow.LoadWorkFlow(content, name) // Relaod
if err != nil {
fmt.Println(color.RedString("Fatal: %s", err.Error()))
return
}
api, has := gou.APIs["xiang.workflow."+name]
if has {
_, err := gou.LoadAPIReturn(api.Source, api.Name)
if err != nil {
fmt.Println(color.RedString("Fatal: %s", err.Error()))
return
}
}
fmt.Println(color.GreenString("Workflow %s Reloaded", name))
} else if op == "remove" || op == "rename" {
name := prefix + share.SpecName(root, filename)
if _, has := workflow.WorkFlows[name]; has {
delete(workflow.WorkFlows, name)
fmt.Println(color.RedString("Workflow %s Removed", name))
}
mod, err := gou.LoadModelReturn(string(content), name)
if err != nil {
fmt.Println(color.RedString("[Watch] Model: %s %s", name, err.Error()))
return
}
mod.Migrate(false)
fmt.Println(color.GreenString("[Watch] Model: %s Reloaded", name))
break
// 重启服务器
if op == "write" || op == "create" || op == "remove" || op == "rename" {
StopWithouttSession(func() {
fmt.Println(color.GreenString("Service Restarted"))
go StartWithouttSession()
})
}
})
case "REMOVE", "RENAME":
delete(gou.Models, name)
fmt.Println(color.GreenString("[Watch] Model: %s Removed", name))
break
}
}
func watchReload(root string, file string, event string, cfg config.Config) {
switch event {
case "CREATE", "WRITE", "REMOVE":
err := engine.Load(config.Conf) // 加载脚本等
if err != nil {
fmt.Println(color.RedString("[Watch] Reload: %s", err.Error()))
}
// Restart Server
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
StopWithContext(ctx, func() {
go Start()
fmt.Println(color.GreenString("[Watch] Reload Completed"))
})
}
}

153
service/watch_test.go Normal file
View file

@ -0,0 +1,153 @@
package service
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"testing"
"time"
"github.com/yaoapp/yao/config"
"github.com/yaoapp/yao/share"
)
func TestWatch(t *testing.T) {
share.DBConnect(config.Conf.DB)
err := Watch(config.Conf)
if err != nil {
t.Fatal(err)
}
defer StopWatch()
createDir(t)
renameDir(t)
createModel(t)
changeModel(t)
renameModel(t)
removeModel(t)
createModel(t)
removeDir(t)
}
func TestWatchReload(t *testing.T) {
go Start()
defer Stop(func() {})
share.DBConnect(config.Conf.DB)
watchReload("", "", "", config.Conf)
}
func createDir(t *testing.T) {
root := config.Conf.Root
file := filepath.Join(root, "models", "watch", "test")
fmt.Println("CREATE-DIR", file)
err := os.MkdirAll(file, os.ModePerm)
if err != nil {
t.Fatal(err)
}
time.Sleep(1 * time.Second)
}
func renameDir(t *testing.T) {
root := config.Conf.Root
file := filepath.Join(root, "models", "watch", "test")
new := filepath.Join(root, "models", "watch", "test_new")
fmt.Println("RENAME-DIR", file)
err := os.Rename(file, new)
if err != nil {
t.Fatal(err)
}
time.Sleep(1 * time.Second)
}
func removeDir(t *testing.T) {
root := config.Conf.Root
file := filepath.Join(root, "models", "watch")
fmt.Println("REMOVE-DIR", file)
err := os.RemoveAll(file)
if err != nil {
t.Fatal(err)
}
time.Sleep(1 * time.Second)
}
func createModel(t *testing.T) {
dsl := `
{
"name": "watch-test",
"table": {
"name": "watch_test",
"comment": "WatchTest",
"engine": "InnoDB"
},
"columns": [
{ "name": "id", "type": "ID" },
{ "label": "Name", "name": "name", "type": "string", "index": true }
],
"relations": {},
"option": { "timestamps": true, "soft_deletes": true }
}
`
root := config.Conf.Root
file := filepath.Join(root, "models", "watch", "test_new", "watch.mod.json")
fmt.Println("CREATE", file)
err := ioutil.WriteFile(file, []byte(dsl), 0644)
if err != nil {
t.Fatal(err)
}
time.Sleep(1 * time.Second)
}
func changeModel(t *testing.T) {
dsl := `
{
"name": "watch-test",
"table": {
"name": "watch_test",
"comment": "WatchTest",
"engine": "InnoDB"
},
"columns": [
{ "name": "id", "type": "ID" },
{ "label": "Name", "name": "name", "type": "string", "index": true },
{ "label": "Data", "name": "data", "type": "json", "nullable": true }
],
"relations": {},
"option": { "timestamps": true, "soft_deletes": true }
}
`
root := config.Conf.Root
file := filepath.Join(root, "models", "watch", "test_new", "watch.mod.json")
fmt.Println("CHANGE", file)
err := ioutil.WriteFile(file, []byte(dsl), 0644)
if err != nil {
t.Fatal(err)
}
time.Sleep(1 * time.Second)
}
func renameModel(t *testing.T) {
root := config.Conf.Root
file := filepath.Join(root, "models", "watch", "test_new", "watch.mod.json")
new := filepath.Join(root, "models", "watch", "test_new", "watch_new.mod.json")
fmt.Println("RENAME", new)
err := os.Rename(file, new)
if err != nil {
t.Fatal(err)
}
time.Sleep(1 * time.Second)
}
func removeModel(t *testing.T) {
root := config.Conf.Root
file := filepath.Join(root, "models", "watch", "test_new", "watch_new.mod.json")
fmt.Println("REMOVE", file)
err := os.Remove(file)
if err != nil {
t.Fatal(err)
}
time.Sleep(1 * time.Second)
}

View file

@ -9,8 +9,6 @@ import (
)
func TestGetQueryParam(t *testing.T) {
SessionServerStart()
defer SessionServerStop()
sid := session.ID()
s := session.Global().ID(sid).Expire(5000 * time.Microsecond)
s.MustSet("id", 10086)

View file

@ -1,7 +1,10 @@
package share
// VERSION 版本号
const VERSION = "0.9.1"
const VERSION = "0.10.1"
// PRVERSION PreRelease Version
const PRVERSION = "DEV"
// BUILDIN 打包应用合成一个制品
const BUILDIN = false

View file

@ -1,116 +1,77 @@
package share
import (
"context"
"fmt"
"io"
"log"
"path/filepath"
klog "github.com/yaoapp/kun/log"
"github.com/buraksezer/olric"
"github.com/buraksezer/olric/client"
config_olric "github.com/buraksezer/olric/config"
"github.com/buraksezer/olric/serializer"
"github.com/yaoapp/gou/session"
"github.com/yaoapp/kun/exception"
"github.com/yaoapp/kun/log"
"github.com/yaoapp/yao/config"
"github.com/yaoapp/yao/network"
"github.com/yaoapp/gou/session"
)
var sessServer *olric.Olric
var sessionDB *session.BuntDB
// SessionPort Session 端口
var SessionPort int
func init() {
SessionPort = network.FreePort()
klog.Trace("session port: %d", SessionPort)
// SessionStart start session
func SessionStart() error {
if config.Conf.Session.Store == "file" {
return SessionFile()
} else if config.Conf.Session.Store == "redis" {
return SessionRedis()
}
return fmt.Errorf("Session Store config error %s (file|redis)", config.Conf.Session.Store)
}
// SessionConnect 加载会话信息
func SessionConnect(conf config.SessionConfig) {
var clientConfig = &client.Config{
Servers: []string{fmt.Sprintf("%s:%d", "127.0.0.1", SessionPort)},
Serializer: serializer.NewMsgpackSerializer(),
Client: config_olric.NewClient(),
}
c, err := client.New(clientConfig)
if err != nil {
exception.New("会话服务器连接失败 %s", 500, err.Error()).Throw()
}
dm := c.NewDMap("local-session")
session.MemoryUse(session.ClientDMap{DMap: dm})
}
// SessionServerStop 关闭会话服务器
func SessionServerStop() {
if sessServer != nil {
sessServer.Shutdown(context.Background())
// SessionStop stop session
func SessionStop() {
if sessionDB != nil {
sessionDB.Close()
}
}
// SessionServerStart 启动会话服务器
func SessionServerStart() {
c := &config_olric.Config{
BindAddr: "127.0.0.1",
BindPort: SessionPort,
ReadRepair: false,
ReplicaCount: 1,
WriteQuorum: 1,
ReadQuorum: 1,
MemberCountQuorum: 1,
Peers: []string{},
DMaps: &config_olric.DMaps{},
StorageEngines: config_olric.NewStorageEngine(),
Logger: &log.Logger{},
// SessionRedis Connect redis server
func SessionRedis() error {
args := []string{}
if config.Conf.Session.Port == "" {
config.Conf.Session.Port = "6379"
}
m, err := config_olric.NewMemberlistConfig("local")
if config.Conf.Session.DB == "" {
config.Conf.Session.DB = "1"
}
args = append(args, config.Conf.Session.Port, config.Conf.Session.DB, config.Conf.Session.Password)
rdb, err := session.NewRedis(config.Conf.Session.Host, args...)
if err != nil {
panic(fmt.Sprintf("unable to create a new memberlist config: %v", err))
}
// m.BindAddr = config.Conf.Session.Host
m.BindPort = SessionPort
m.AdvertisePort = SessionPort
c.MemberlistConfig = m
// c.MemberlistConfig.BindAddr = config.Conf.Session.Host
// c.MemberlistConfig.BindPort = 3308
// c := config_olric.New("local")
// c.BindAddr = config.Conf.Session.Host
// c.BindPort = config.Conf.Session.Port
c.Logger.SetOutput(io.Discard) // 暂时关闭日志
ctx, cancel := context.WithCancel(context.Background())
c.Started = func() {
defer cancel()
klog.Trace("[INFO] Olric is ready to accept connections")
return err
}
sessServer, err = olric.New(c)
if err != nil {
klog.Error("Failed to create Olric instance: %v", err)
}
go func() {
err = sessServer.Start() // Call Start at background. It's a blocker call.
if err != nil {
klog.Panic("olric.Start returned an error: %v", err)
}
}()
<-ctx.Done()
dm, err := sessServer.NewDMap("local-session")
if err != nil {
klog.Panic("olric.NewDMap returned an error: %v", err)
}
session.MemoryUse(session.ServerDMap{DMap: dm})
session.Register("redis", rdb)
session.Name = "redis"
log.Trace("Session Store:REDIS HOST:%s PORT:%s DB:%s", config.Conf.Session.Host, config.Conf.Session.Port, config.Conf.Session.DB)
return nil
}
// SessionFile Start session file
func SessionFile() error {
file := config.Conf.Session.File
if file == "" {
file = filepath.Join(config.Conf.Root, "data", ".session.db")
}
file, err := filepath.Abs(file)
if err != nil {
return err
}
burndb, err := session.NewBuntDB(file)
if err != nil {
return err
}
session.Register("file", burndb)
session.Name = "file"
sessionDB = burndb
log.Trace("Session Store: File %s", file)
return nil
}

View file

@ -27,10 +27,11 @@ type ColumnImp Column
// Column 字段呈现方式
type Column struct {
Label string `json:"label"`
View Render `json:"view,omitempty"`
Edit Render `json:"edit,omitempty"`
Form Render `json:"form,omitempty"`
Label string `json:"label"`
Export string `json:"export,omitempty"`
View Render `json:"view,omitempty"`
Edit Render `json:"edit,omitempty"`
Form Render `json:"form,omitempty"`
Importable
}
@ -71,8 +72,7 @@ type Page struct {
// AppInfo 应用信息
type AppInfo struct {
Name string `json:"name,omitempty"`
Lang string `json:"lang,omitempty"`
L map[string]string `json:"-"` // 应用的语言包
L map[string]string `json:"-"`
Short string `json:"short,omitempty"`
Version string `json:"version,omitempty"`
Description string `json:"description,omitempty"`

View file

@ -6,13 +6,14 @@ import (
"log"
"os"
"path/filepath"
"sync"
"github.com/fatih/color"
"github.com/fsnotify/fsnotify"
"github.com/yaoapp/kun/exception"
)
var watchDone = []chan bool{}
var watchGroup sync.WaitGroup
var watchOp = map[fsnotify.Op]string{
fsnotify.Create: "create",
fsnotify.Write: "write",
@ -27,10 +28,12 @@ func Watch(root string, cb func(op string, file string)) {
if err != nil {
log.Fatal(err)
}
defer watcher.Close()
defer func() {
watcher.Close()
watchGroup.Done()
}()
watchDone = append(watchDone, make(chan bool))
last := len(watchDone) - 1
watchGroup.Add(1)
go func() {
for {
select {
@ -86,20 +89,6 @@ func Watch(root string, cb func(op string, file string)) {
return nil
})
select {
case v := <-watchDone[last]:
fmt.Println(color.GreenString("Stop Watching: %s", root))
if v == true {
break
}
}
}
watchGroup.Wait()
// StopWatch 停止监听
func StopWatch() {
for i := range watchDone {
fmt.Println(color.GreenString("Stop: %d", i))
watchDone[i] <- true
}
watchDone = []chan bool{}
}

View file

@ -5,7 +5,6 @@ import (
"os"
"path"
"testing"
"time"
"github.com/stretchr/testify/assert"
)
@ -16,7 +15,5 @@ func TestWatch(t *testing.T) {
go Watch(root, func(op string, file string) {
log.Println(op, file)
})
time.Sleep(time.Second * 2)
defer StopWatch()
})
}

View file

@ -1,4 +1,4 @@
package server
package socket
import (
"fmt"
@ -11,9 +11,9 @@ import (
)
// Load 加载API
func Load(cfg config.Config) {
var root = filepath.Join(cfg.Root, "servers")
LoadFrom(root, "")
func Load(cfg config.Config) error {
var root = filepath.Join(cfg.Root, "sockets")
return LoadFrom(root, "")
}
// LoadFrom 从特定目录加载
@ -26,7 +26,7 @@ func LoadFrom(dir string, prefix string) error {
err := share.Walk(dir, ".sock.json", func(root, filename string) {
name := prefix + share.SpecName(root, filename)
content := share.ReadFile(filename)
_, err := gou.LoadServer(string(content), name)
_, err := gou.LoadSocket(string(content), name)
if err != nil {
log.With(log.F{"root": root, "file": filename}).Error(err.Error())
}

View file

@ -1,4 +1,4 @@
package server
package socket
import (
"testing"
@ -16,8 +16,8 @@ func TestLoad(t *testing.T) {
func check(t *testing.T) {
keys := []string{}
for key := range gou.Servers {
for key := range gou.Sockets {
keys = append(keys, key)
}
assert.Equal(t, 1, len(keys))
assert.Equal(t, 2, len(keys))
}

36
store/store.go Normal file
View file

@ -0,0 +1,36 @@
package store
import (
"fmt"
"path/filepath"
"github.com/yaoapp/gou"
"github.com/yaoapp/kun/log"
"github.com/yaoapp/yao/config"
"github.com/yaoapp/yao/share"
)
// Load load store
func Load(cfg config.Config) error {
var root = filepath.Join(cfg.Root, "stores")
return LoadFrom(root, "")
}
// LoadFrom load from dir
func LoadFrom(dir string, prefix string) error {
if share.DirNotExists(dir) {
return fmt.Errorf("%s does not exists", dir)
}
err := share.Walk(dir, ".json", func(root, filename string) {
name := prefix + share.SpecName(root, filename)
content := share.ReadFile(filename)
_, err := gou.LoadStore(string(content), name)
if err != nil {
log.With(log.F{"root": root, "file": filename}).Error(err.Error())
}
})
return err
}

23
store/store_test.go Normal file
View file

@ -0,0 +1,23 @@
package store
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/yaoapp/gou/kv"
"github.com/yaoapp/yao/config"
)
func TestLoad(t *testing.T) {
Load(config.Conf)
LoadFrom("not a path", "404.")
check(t)
}
func check(t *testing.T) {
keys := []string{}
for key := range kv.Pools {
keys = append(keys, key)
}
assert.Equal(t, 1, len(keys))
}

42
system/process.go Normal file
View file

@ -0,0 +1,42 @@
package system
import (
"fmt"
"os/exec"
"strings"
"github.com/yaoapp/gou"
"github.com/yaoapp/kun/exception"
)
func init() {
gou.RegisterProcessHandler("yao.system.Exec", processExec)
}
// processExec execute the system command
func processExec(process *gou.Process) interface{} {
process.ValidateArgNums(1)
cmd := process.ArgsString(0)
_, err := exec.LookPath(cmd)
if err != nil {
exception.New("command %s not found: %s", 400, cmd, err.Error()).Throw()
return nil
}
args := []string{}
for i, arg := range process.Args {
if i == 0 {
continue
}
args = append(args, fmt.Sprintf("%v", arg))
}
res, err := exec.Command(cmd, args...).Output()
if err != nil {
exception.New("command %s error: %s", 500, cmd, err.Error()).Throw()
return nil
}
return strings.TrimRight(string(res), "\n")
}

16
system/process_test.go Normal file
View file

@ -0,0 +1,16 @@
package system
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/yaoapp/gou"
)
func TestProcessReturn(t *testing.T) {
output, err := gou.NewProcess("yao.system.Exec", "echo", "hello").Exec()
if err != nil {
t.Fatal(err)
}
assert.Equal(t, "hello", output)
}

Some files were not shown because too many files have changed in this diff Show more