mirror of
https://github.com/astaxie/beego.git
synced 2025-07-11 13:51:02 +00:00
Compare commits
222 Commits
Author | SHA1 | Date | |
---|---|---|---|
522b3a4a70 | |||
cd67f13bf9 | |||
0f554d9b1a | |||
3742d1178c | |||
d03285a0ee | |||
e810f2e930 | |||
41aac79ac0 | |||
52f916a28a | |||
bf6bd6b292 | |||
3bb4ca5adc | |||
712df81c99 | |||
3d7ef599cc | |||
453691728a | |||
e7e3ca77ad | |||
d3f3956def | |||
7206214105 | |||
b08ace7532 | |||
d1a2583972 | |||
0cb8de4218 | |||
0cd31a247f | |||
405c170d45 | |||
932019770d | |||
d5c03f5b8f | |||
3d20c0b8f4 | |||
a4fb4c6a03 | |||
a9941b6edc | |||
6a32b048bd | |||
fb04d3cff1 | |||
5c7673e73d | |||
54b05377d9 | |||
f49f3f92ec | |||
b18b94f03b | |||
bf469f0b55 | |||
c12709dbc9 | |||
2808a13f07 | |||
a8d48aa5ea | |||
fdb2660a2a | |||
7c3a997735 | |||
1760f0ceca | |||
c387aeeb36 | |||
83d4385f1f | |||
ae0a75c464 | |||
a05e5a7c09 | |||
75ca7b77b6 | |||
b0e2012a17 | |||
12dff072fa | |||
d956444965 | |||
8dbf9eb0bf | |||
faa981fb6a | |||
1ea3c13ff5 | |||
46d8fef0ad | |||
37c1ffc57a | |||
856fde28dc | |||
3204d7631b | |||
f5fc2edfd3 | |||
21d1267c14 | |||
3e37b97549 | |||
206f736819 | |||
24d4a27842 | |||
46f3ea4f43 | |||
c9cc642d37 | |||
b34853f8cc | |||
d41f4c0a3a | |||
8e46decc8e | |||
323a1c4214 | |||
c5f838e785 | |||
f53e98d11b | |||
c2f7f3efa7 | |||
1e5051e112 | |||
fa44ff54bb | |||
4c6de379e0 | |||
6d997366ed | |||
e0250e2871 | |||
3d629e7320 | |||
74045090cc | |||
49fffe3ebe | |||
50e294be32 | |||
b235b48de4 | |||
28011a5835 | |||
b03b0779dc | |||
393e4c4969 | |||
1d49a4bcbd | |||
fc2c0f4fba | |||
2117aecf65 | |||
8a2b697625 | |||
be586572e0 | |||
4e047bd483 | |||
db67ffbb94 | |||
90b03d34cc | |||
872a964145 | |||
dade92d98b | |||
dfa74faf43 | |||
95562cff41 | |||
9b714a7518 | |||
2a660820c9 | |||
b55e20ac60 | |||
b0dcb5b91d | |||
5c76f62103 | |||
126dbdae2f | |||
24d8290a3f | |||
957c0630c0 | |||
e32d173b0d | |||
fbc9f8e640 | |||
82d2ace3bd | |||
3fa7fc6e41 | |||
d1b58a00ce | |||
6a2ee371a5 | |||
9266ece7a4 | |||
09c405990c | |||
61e694f388 | |||
195c9b24eb | |||
2f6da122fd | |||
f0d1d7149b | |||
96387e9a9b | |||
86f6470fb2 | |||
d77160dafe | |||
caca5e37ba | |||
189c73986b | |||
fe21305bb3 | |||
75ec8d33a2 | |||
d736d0ca87 | |||
99093693c8 | |||
c9c284be27 | |||
fa12dcf792 | |||
b93f5c6f9c | |||
f9791c1221 | |||
8fac2d8d58 | |||
e90f4bee1a | |||
eb50221a15 | |||
fc4801494d | |||
eba6afd6fb | |||
81328b72fa | |||
c0c113036b | |||
b788d74fd1 | |||
90999717dd | |||
a20ccde90d | |||
d548ddeb5b | |||
de99ac5da5 | |||
e2d9d34c75 | |||
bdb525831d | |||
22dba90ec4 | |||
7af6dad58e | |||
5af26446ec | |||
39d40ba8fa | |||
5bc3e30653 | |||
34e2b26b99 | |||
24015e9ace | |||
9ff88f0b35 | |||
27c59a8017 | |||
a74ebaa1eb | |||
b5c29d6143 | |||
0a822209c8 | |||
3baac14095 | |||
ef3655877a | |||
5a8c40710c | |||
b635af5a8c | |||
683e6856ef | |||
8beefc8bfd | |||
e430de3307 | |||
2b442e842e | |||
41633900da | |||
aaf6e775d6 | |||
2f6fc3f62b | |||
84310b1652 | |||
5ceac1dd04 | |||
51b31c6cb0 | |||
5488a5bbd7 | |||
33f7f46670 | |||
3c05eafbc4 | |||
dfa9e03980 | |||
53e996d4c3 | |||
b151a9616e | |||
1effb6ce30 | |||
0be05eb47c | |||
1090ca0154 | |||
a328584238 | |||
f19ad3fdd3 | |||
836be7ab9a | |||
bba04dd864 | |||
9499b3eb90 | |||
9f6bbe3c51 | |||
2d87d4feaf | |||
15a45ccc7b | |||
e0c59fcf0b | |||
083f697c59 | |||
a5a6546b91 | |||
9cafbf6a21 | |||
faba0d7273 | |||
c3116d3601 | |||
868e14b8ba | |||
421bf97b84 | |||
c16507607c | |||
2b7dd85b92 | |||
a58115fed2 | |||
f9b5b0f551 | |||
e53c147129 | |||
da0e6e790d | |||
58ffc6f5f8 | |||
d5fb74aa94 | |||
11247d41a7 | |||
5b21c7cd71 | |||
dd0f05b1f1 | |||
a32241e7d3 | |||
0ef357ebd7 | |||
32dd976620 | |||
fcd8a2024e | |||
30661472c8 | |||
6ced26660e | |||
7d6c45d4c9 | |||
98740fddac | |||
6d3042f5e5 | |||
8b9d6eee1a | |||
11ef5929aa | |||
c697b98006 | |||
7c2e563879 | |||
56aa224a6e | |||
8c37a07adb | |||
161c061376 | |||
aa091cea42 | |||
7df74c0a30 | |||
1e1e900278 | |||
fb2343567b |
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,4 +1,5 @@
|
||||
.idea
|
||||
.vscode
|
||||
.DS_Store
|
||||
*.swp
|
||||
*.swo
|
||||
|
4
.gosimpleignore
Normal file
4
.gosimpleignore
Normal file
@ -0,0 +1,4 @@
|
||||
github.com/astaxie/beego/*/*:S1012
|
||||
github.com/astaxie/beego/*:S1012
|
||||
github.com/astaxie/beego/*/*:S1007
|
||||
github.com/astaxie/beego/*:S1007
|
12
.travis.yml
12
.travis.yml
@ -1,9 +1,9 @@
|
||||
language: go
|
||||
|
||||
go:
|
||||
- 1.6
|
||||
- 1.5.3
|
||||
- 1.4.3
|
||||
- 1.6.4
|
||||
- 1.7.5
|
||||
- 1.8.1
|
||||
services:
|
||||
- redis-server
|
||||
- mysql
|
||||
@ -31,6 +31,10 @@ install:
|
||||
- go get github.com/siddontang/ledisdb/config
|
||||
- go get github.com/siddontang/ledisdb/ledis
|
||||
- go get github.com/ssdb/gossdb/ssdb
|
||||
- go get github.com/cloudflare/golz4
|
||||
- go get github.com/gogo/protobuf/proto
|
||||
- go get -u honnef.co/go/tools/cmd/gosimple
|
||||
- go get -u github.com/mdempsky/unconvert
|
||||
before_script:
|
||||
- psql --version
|
||||
- sh -c "if [ '$ORM_DRIVER' = 'postgres' ]; then psql -c 'create database orm_test;' -U postgres; fi"
|
||||
@ -45,5 +49,7 @@ after_script:
|
||||
- rm -rf ./res/var/*
|
||||
script:
|
||||
- go test -v ./...
|
||||
- gosimple -ignore "$(cat .gosimpleignore)" $(go list ./... | grep -v /vendor/)
|
||||
- unconvert $(go list ./... | grep -v /vendor/)
|
||||
addons:
|
||||
postgresql: "9.4"
|
||||
|
31
README.md
31
README.md
@ -1,19 +1,17 @@
|
||||
## Beego
|
||||
|
||||
[](https://travis-ci.org/astaxie/beego)
|
||||
[](http://godoc.org/github.com/astaxie/beego)
|
||||
# Beego [](https://travis-ci.org/astaxie/beego) [](http://godoc.org/github.com/astaxie/beego) [](http://golangfoundation.org)
|
||||
|
||||
beego is used for rapid development of RESTful APIs, web apps and backend services in Go.
|
||||
It is inspired by Tornado, Sinatra and Flask. beego has some Go-specific features such as interfaces and struct embedding.
|
||||
|
||||
More info [beego.me](http://beego.me)
|
||||
###### More info at [beego.me](http://beego.me).
|
||||
|
||||
##Quick Start
|
||||
######Download and install
|
||||
## Quick Start
|
||||
|
||||
#### Download and install
|
||||
|
||||
go get github.com/astaxie/beego
|
||||
|
||||
######Create file `hello.go`
|
||||
#### Create file `hello.go`
|
||||
```go
|
||||
package main
|
||||
|
||||
@ -23,15 +21,16 @@ func main(){
|
||||
beego.Run()
|
||||
}
|
||||
```
|
||||
######Build and run
|
||||
```bash
|
||||
#### Build and run
|
||||
|
||||
go build hello.go
|
||||
./hello
|
||||
```
|
||||
######Congratulations!
|
||||
You just built your first beego app.
|
||||
Open your browser and visit `http://localhost:8080`.
|
||||
Please see [Documentation](http://beego.me/docs) for more.
|
||||
|
||||
#### Go to [http://localhost:8080](http://localhost:8080)
|
||||
|
||||
Congratulations! You've just built your first **beego** app.
|
||||
|
||||
###### Please see [Documentation](http://beego.me/docs) for more.
|
||||
|
||||
## Features
|
||||
|
||||
@ -55,7 +54,7 @@ Please see [Documentation](http://beego.me/docs) for more.
|
||||
* [http://beego.me/community](http://beego.me/community)
|
||||
* Welcome to join us in Slack: [https://beego.slack.com](https://beego.slack.com), you can get invited from [here](https://github.com/beego/beedoc/issues/232)
|
||||
|
||||
## LICENSE
|
||||
## License
|
||||
|
||||
beego source code is licensed under the Apache Licence, Version 2.0
|
||||
(http://www.apache.org/licenses/LICENSE-2.0.html).
|
||||
|
32
admin.go
32
admin.go
@ -157,8 +157,8 @@ func listConf(rw http.ResponseWriter, r *http.Request) {
|
||||
resultList := new([][]string)
|
||||
for _, f := range bf {
|
||||
var result = []string{
|
||||
fmt.Sprintf("%s", f.pattern),
|
||||
fmt.Sprintf("%s", utils.GetFuncName(f.filterFunc)),
|
||||
f.pattern,
|
||||
utils.GetFuncName(f.filterFunc),
|
||||
}
|
||||
*resultList = append(*resultList, result)
|
||||
}
|
||||
@ -208,12 +208,12 @@ func printTree(resultList *[][]string, t *Tree) {
|
||||
printTree(resultList, t.wildcard)
|
||||
}
|
||||
for _, l := range t.leaves {
|
||||
if v, ok := l.runObject.(*controllerInfo); ok {
|
||||
if v, ok := l.runObject.(*ControllerInfo); ok {
|
||||
if v.routerType == routerTypeBeego {
|
||||
var result = []string{
|
||||
v.pattern,
|
||||
fmt.Sprintf("%s", v.methods),
|
||||
fmt.Sprintf("%s", v.controllerType),
|
||||
v.controllerType.String(),
|
||||
}
|
||||
*resultList = append(*resultList, result)
|
||||
} else if v.routerType == routerTypeRESTFul {
|
||||
@ -276,8 +276,8 @@ func profIndex(rw http.ResponseWriter, r *http.Request) {
|
||||
// it's in "/healthcheck" pattern in admin module.
|
||||
func healthcheck(rw http.ResponseWriter, req *http.Request) {
|
||||
var (
|
||||
result []string
|
||||
data = make(map[interface{}]interface{})
|
||||
result = []string{}
|
||||
resultList = new([][]string)
|
||||
content = map[string]interface{}{
|
||||
"Fields": []string{"Name", "Message", "Status"},
|
||||
@ -287,21 +287,20 @@ func healthcheck(rw http.ResponseWriter, req *http.Request) {
|
||||
for name, h := range toolbox.AdminCheckList {
|
||||
if err := h.Check(); err != nil {
|
||||
result = []string{
|
||||
fmt.Sprintf("error"),
|
||||
fmt.Sprintf("%s", name),
|
||||
fmt.Sprintf("%s", err.Error()),
|
||||
"error",
|
||||
name,
|
||||
err.Error(),
|
||||
}
|
||||
|
||||
} else {
|
||||
result = []string{
|
||||
fmt.Sprintf("success"),
|
||||
fmt.Sprintf("%s", name),
|
||||
fmt.Sprintf("OK"),
|
||||
"success",
|
||||
name,
|
||||
"OK",
|
||||
}
|
||||
|
||||
}
|
||||
*resultList = append(*resultList, result)
|
||||
}
|
||||
|
||||
content["Data"] = resultList
|
||||
data["Content"] = content
|
||||
data["Title"] = "Health Check"
|
||||
@ -330,7 +329,6 @@ func taskStatus(rw http.ResponseWriter, req *http.Request) {
|
||||
// List Tasks
|
||||
content := make(map[string]interface{})
|
||||
resultList := new([][]string)
|
||||
var result = []string{}
|
||||
var fields = []string{
|
||||
"Task Name",
|
||||
"Task Spec",
|
||||
@ -339,10 +337,10 @@ func taskStatus(rw http.ResponseWriter, req *http.Request) {
|
||||
"",
|
||||
}
|
||||
for tname, tk := range toolbox.AdminTaskList {
|
||||
result = []string{
|
||||
result := []string{
|
||||
tname,
|
||||
fmt.Sprintf("%s", tk.GetSpec()),
|
||||
fmt.Sprintf("%s", tk.GetStatus()),
|
||||
tk.GetSpec(),
|
||||
tk.GetStatus(),
|
||||
tk.GetPrev().String(),
|
||||
}
|
||||
*resultList = append(*resultList, result)
|
||||
|
@ -65,6 +65,7 @@ func oldMap() map[string]interface{} {
|
||||
m["BConfig.WebConfig.Session.SessionCookieLifeTime"] = BConfig.WebConfig.Session.SessionCookieLifeTime
|
||||
m["BConfig.WebConfig.Session.SessionAutoSetCookie"] = BConfig.WebConfig.Session.SessionAutoSetCookie
|
||||
m["BConfig.WebConfig.Session.SessionDomain"] = BConfig.WebConfig.Session.SessionDomain
|
||||
m["BConfig.WebConfig.Session.SessionDisableHTTPOnly"] = BConfig.WebConfig.Session.SessionDisableHTTPOnly
|
||||
m["BConfig.Log.AccessLogs"] = BConfig.Log.AccessLogs
|
||||
m["BConfig.Log.FileLineNum"] = BConfig.Log.FileLineNum
|
||||
m["BConfig.Log.Outputs"] = BConfig.Log.Outputs
|
||||
|
13
adminui.go
13
adminui.go
@ -78,13 +78,14 @@ var qpsTpl = `{{define "content"}}
|
||||
{{range $i, $elem := .Content.Data}}
|
||||
|
||||
<tr>
|
||||
{{range $elem}}
|
||||
<td>
|
||||
{{.}}
|
||||
</td>
|
||||
{{end}}
|
||||
<td>{{index $elem 0}}</td>
|
||||
<td>{{index $elem 1}}</td>
|
||||
<td>{{index $elem 2}}</td>
|
||||
<td data-order="{{index $elem 3}}">{{index $elem 4}}</td>
|
||||
<td data-order="{{index $elem 5}}">{{index $elem 6}}</td>
|
||||
<td data-order="{{index $elem 7}}">{{index $elem 8}}</td>
|
||||
<td data-order="{{index $elem 9}}">{{index $elem 10}}</td>
|
||||
</tr>
|
||||
|
||||
{{end}}
|
||||
</tbody>
|
||||
|
||||
|
6
app.go
6
app.go
@ -348,9 +348,9 @@ func Any(rootpath string, f FilterFunc) *App {
|
||||
|
||||
// Handler used to register a Handler router
|
||||
// usage:
|
||||
// beego.Handler("/api", func(ctx *context.Context){
|
||||
// ctx.Output.Body("hello world")
|
||||
// })
|
||||
// beego.Handler("/api", http.HandlerFunc(func (w http.ResponseWriter, r *http.Request) {
|
||||
// fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path))
|
||||
// }))
|
||||
func Handler(rootpath string, h http.Handler, options ...interface{}) *App {
|
||||
BeeApp.Handlers.Handler(rootpath, h, options...)
|
||||
return BeeApp
|
||||
|
9
beego.go
9
beego.go
@ -23,7 +23,7 @@ import (
|
||||
|
||||
const (
|
||||
// VERSION represent beego web framework version.
|
||||
VERSION = "1.7.0"
|
||||
VERSION = "1.8.1"
|
||||
|
||||
// DEV is for develop
|
||||
DEV = "dev"
|
||||
@ -85,8 +85,13 @@ func initBeforeHTTPRun() {
|
||||
|
||||
// TestBeegoInit is for test package init
|
||||
func TestBeegoInit(ap string) {
|
||||
appConfigPath = filepath.Join(ap, "conf", "app.conf")
|
||||
path := filepath.Join(ap, "conf", "app.conf")
|
||||
os.Chdir(ap)
|
||||
InitBeegoBeforeTest(path)
|
||||
}
|
||||
|
||||
// InitBeegoBeforeTest is for test package init
|
||||
func InitBeegoBeforeTest(appConfigPath string) {
|
||||
if err := LoadAppConfig(appConfigProvider, appConfigPath); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
6
cache/conv_test.go
vendored
6
cache/conv_test.go
vendored
@ -118,14 +118,14 @@ func TestGetFloat64(t *testing.T) {
|
||||
|
||||
func TestGetBool(t *testing.T) {
|
||||
var t1 = true
|
||||
if true != GetBool(t1) {
|
||||
if !GetBool(t1) {
|
||||
t.Error("get bool from bool error")
|
||||
}
|
||||
var t2 = "true"
|
||||
if true != GetBool(t2) {
|
||||
if !GetBool(t2) {
|
||||
t.Error("get bool from string error")
|
||||
}
|
||||
if false != GetBool(nil) {
|
||||
if GetBool(nil) {
|
||||
t.Error("get bool from nil error")
|
||||
}
|
||||
}
|
||||
|
25
cache/file.go
vendored
25
cache/file.go
vendored
@ -22,6 +22,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
@ -222,33 +223,13 @@ func exists(path string) (bool, error) {
|
||||
// FileGetContents Get bytes to file.
|
||||
// if non-exist, create this file.
|
||||
func FileGetContents(filename string) (data []byte, e error) {
|
||||
f, e := os.OpenFile(filename, os.O_RDWR|os.O_CREATE, os.ModePerm)
|
||||
if e != nil {
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
stat, e := f.Stat()
|
||||
if e != nil {
|
||||
return
|
||||
}
|
||||
data = make([]byte, stat.Size())
|
||||
result, e := f.Read(data)
|
||||
if e != nil || int64(result) != stat.Size() {
|
||||
return nil, e
|
||||
}
|
||||
return
|
||||
return ioutil.ReadFile(filename)
|
||||
}
|
||||
|
||||
// FilePutContents Put bytes to file.
|
||||
// if non-exist, create this file.
|
||||
func FilePutContents(filename string, content []byte) error {
|
||||
fp, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE, os.ModePerm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer fp.Close()
|
||||
_, err = fp.Write(content)
|
||||
return err
|
||||
return ioutil.WriteFile(filename, content, os.ModePerm)
|
||||
}
|
||||
|
||||
// GobEncode Gob encodes file cache item.
|
||||
|
26
cache/memcache/memcache.go
vendored
26
cache/memcache/memcache.go
vendored
@ -33,12 +33,10 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"github.com/bradfitz/gomemcache/memcache"
|
||||
|
||||
"time"
|
||||
|
||||
"github.com/astaxie/beego/cache"
|
||||
"github.com/bradfitz/gomemcache/memcache"
|
||||
)
|
||||
|
||||
// Cache Memcache adapter.
|
||||
@ -60,7 +58,7 @@ func (rc *Cache) Get(key string) interface{} {
|
||||
}
|
||||
}
|
||||
if item, err := rc.conn.Get(key); err == nil {
|
||||
return string(item.Value)
|
||||
return item.Value
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@ -80,7 +78,7 @@ func (rc *Cache) GetMulti(keys []string) []interface{} {
|
||||
mv, err := rc.conn.GetMulti(keys)
|
||||
if err == nil {
|
||||
for _, v := range mv {
|
||||
rv = append(rv, string(v.Value))
|
||||
rv = append(rv, v.Value)
|
||||
}
|
||||
return rv
|
||||
}
|
||||
@ -90,18 +88,21 @@ func (rc *Cache) GetMulti(keys []string) []interface{} {
|
||||
return rv
|
||||
}
|
||||
|
||||
// Put put value to memcache. only support string.
|
||||
// Put put value to memcache.
|
||||
func (rc *Cache) Put(key string, val interface{}, timeout time.Duration) error {
|
||||
if rc.conn == nil {
|
||||
if err := rc.connectInit(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
v, ok := val.(string)
|
||||
if !ok {
|
||||
return errors.New("val must string")
|
||||
item := memcache.Item{Key: key, Expiration: int32(timeout / time.Second)}
|
||||
if v, ok := val.([]byte); ok {
|
||||
item.Value = v
|
||||
} else if str, ok := val.(string); ok {
|
||||
item.Value = []byte(str)
|
||||
} else {
|
||||
return errors.New("val only support string and []byte")
|
||||
}
|
||||
item := memcache.Item{Key: key, Value: []byte(v), Expiration: int32(timeout / time.Second)}
|
||||
return rc.conn.Set(&item)
|
||||
}
|
||||
|
||||
@ -145,10 +146,7 @@ func (rc *Cache) IsExist(key string) bool {
|
||||
}
|
||||
}
|
||||
_, err := rc.conn.Get(key)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
return !(err != nil)
|
||||
}
|
||||
|
||||
// ClearAll clear all cached in memcache.
|
||||
|
12
cache/memcache/memcache_test.go
vendored
12
cache/memcache/memcache_test.go
vendored
@ -46,7 +46,7 @@ func TestMemcacheCache(t *testing.T) {
|
||||
t.Error("set Error", err)
|
||||
}
|
||||
|
||||
if v, err := strconv.Atoi(bm.Get("astaxie").(string)); err != nil || v != 1 {
|
||||
if v, err := strconv.Atoi(string(bm.Get("astaxie").([]byte))); err != nil || v != 1 {
|
||||
t.Error("get err")
|
||||
}
|
||||
|
||||
@ -54,7 +54,7 @@ func TestMemcacheCache(t *testing.T) {
|
||||
t.Error("Incr Error", err)
|
||||
}
|
||||
|
||||
if v, err := strconv.Atoi(bm.Get("astaxie").(string)); err != nil || v != 2 {
|
||||
if v, err := strconv.Atoi(string(bm.Get("astaxie").([]byte))); err != nil || v != 2 {
|
||||
t.Error("get err")
|
||||
}
|
||||
|
||||
@ -62,7 +62,7 @@ func TestMemcacheCache(t *testing.T) {
|
||||
t.Error("Decr Error", err)
|
||||
}
|
||||
|
||||
if v, err := strconv.Atoi(bm.Get("astaxie").(string)); err != nil || v != 1 {
|
||||
if v, err := strconv.Atoi(string(bm.Get("astaxie").([]byte))); err != nil || v != 1 {
|
||||
t.Error("get err")
|
||||
}
|
||||
bm.Delete("astaxie")
|
||||
@ -78,7 +78,7 @@ func TestMemcacheCache(t *testing.T) {
|
||||
t.Error("check err")
|
||||
}
|
||||
|
||||
if v := bm.Get("astaxie").(string); v != "author" {
|
||||
if v := bm.Get("astaxie").([]byte); string(v) != "author" {
|
||||
t.Error("get err")
|
||||
}
|
||||
|
||||
@ -94,10 +94,10 @@ func TestMemcacheCache(t *testing.T) {
|
||||
if len(vv) != 2 {
|
||||
t.Error("GetMulti ERROR")
|
||||
}
|
||||
if vv[0].(string) != "author" && vv[0].(string) != "author1" {
|
||||
if string(vv[0].([]byte)) != "author" && string(vv[0].([]byte)) != "author1" {
|
||||
t.Error("GetMulti ERROR")
|
||||
}
|
||||
if vv[1].(string) != "author1" && vv[1].(string) != "author" {
|
||||
if string(vv[1].([]byte)) != "author1" && string(vv[1].([]byte)) != "author" {
|
||||
t.Error("GetMulti ERROR")
|
||||
}
|
||||
|
||||
|
2
cache/redis/redis.go
vendored
2
cache/redis/redis.go
vendored
@ -137,7 +137,7 @@ func (rc *Cache) IsExist(key string) bool {
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if v == false {
|
||||
if !v {
|
||||
if _, err = rc.do("HDEL", rc.key, key); err != nil {
|
||||
return false
|
||||
}
|
||||
|
21
cache/ssdb/ssdb.go
vendored
21
cache/ssdb/ssdb.go
vendored
@ -53,7 +53,7 @@ func (rc *Cache) GetMulti(keys []string) []interface{} {
|
||||
resSize := len(res)
|
||||
if err == nil {
|
||||
for i := 1; i < resSize; i += 2 {
|
||||
values = append(values, string(res[i+1]))
|
||||
values = append(values, res[i+1])
|
||||
}
|
||||
return values
|
||||
}
|
||||
@ -71,10 +71,7 @@ func (rc *Cache) DelMulti(keys []string) error {
|
||||
}
|
||||
}
|
||||
_, err := rc.conn.Do("multi_del", keys)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return err
|
||||
}
|
||||
|
||||
// Put put value to memcache. only support string.
|
||||
@ -113,10 +110,7 @@ func (rc *Cache) Delete(key string) error {
|
||||
}
|
||||
}
|
||||
_, err := rc.conn.Del(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return err
|
||||
}
|
||||
|
||||
// Incr increase counter.
|
||||
@ -152,7 +146,7 @@ func (rc *Cache) IsExist(key string) bool {
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if resp[1] == "1" {
|
||||
if len(resp) == 2 && resp[1] == "1" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
@ -175,7 +169,7 @@ func (rc *Cache) ClearAll() error {
|
||||
}
|
||||
keys := []string{}
|
||||
for i := 1; i < size; i += 2 {
|
||||
keys = append(keys, string(resp[i]))
|
||||
keys = append(keys, resp[i])
|
||||
}
|
||||
_, e := rc.conn.Do("multi_del", keys)
|
||||
if e != nil {
|
||||
@ -229,10 +223,7 @@ func (rc *Cache) connectInit() error {
|
||||
}
|
||||
var err error
|
||||
rc.conn, err = ssdb.Connect(host, port)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return err
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
96
config.go
96
config.go
@ -19,9 +19,11 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/astaxie/beego/config"
|
||||
"github.com/astaxie/beego/context"
|
||||
"github.com/astaxie/beego/logs"
|
||||
"github.com/astaxie/beego/session"
|
||||
"github.com/astaxie/beego/utils"
|
||||
@ -34,10 +36,12 @@ type Config struct {
|
||||
RouterCaseSensitive bool
|
||||
ServerName string
|
||||
RecoverPanic bool
|
||||
RecoverFunc func(*context.Context)
|
||||
CopyRequestBody bool
|
||||
EnableGzip bool
|
||||
MaxMemory int64
|
||||
EnableErrorsShow bool
|
||||
EnableErrorsRender bool
|
||||
Listen Listen
|
||||
WebConfig WebConfig
|
||||
Log LogConfig
|
||||
@ -83,17 +87,18 @@ type WebConfig struct {
|
||||
|
||||
// SessionConfig holds session related config
|
||||
type SessionConfig struct {
|
||||
SessionOn bool
|
||||
SessionProvider string
|
||||
SessionName string
|
||||
SessionGCMaxLifetime int64
|
||||
SessionProviderConfig string
|
||||
SessionCookieLifeTime int
|
||||
SessionAutoSetCookie bool
|
||||
SessionDomain string
|
||||
EnableSidInHttpHeader bool // enable store/get the sessionId into/from http headers
|
||||
SessionNameInHttpHeader string
|
||||
EnableSidInUrlQuery bool // enable get the sessionId from Url Query params
|
||||
SessionOn bool
|
||||
SessionProvider string
|
||||
SessionName string
|
||||
SessionGCMaxLifetime int64
|
||||
SessionProviderConfig string
|
||||
SessionCookieLifeTime int
|
||||
SessionAutoSetCookie bool
|
||||
SessionDomain string
|
||||
SessionDisableHTTPOnly bool // used to allow for cross domain cookies/javascript cookies.
|
||||
SessionEnableSidInHTTPHeader bool // enable store/get the sessionId into/from http headers
|
||||
SessionNameInHTTPHeader string
|
||||
SessionEnableSidInURLQuery bool // enable get the sessionId from Url Query params
|
||||
}
|
||||
|
||||
// LogConfig holds Log related config
|
||||
@ -142,6 +147,37 @@ func init() {
|
||||
}
|
||||
}
|
||||
|
||||
func recoverPanic(ctx *context.Context) {
|
||||
if err := recover(); err != nil {
|
||||
if err == ErrAbort {
|
||||
return
|
||||
}
|
||||
if !BConfig.RecoverPanic {
|
||||
panic(err)
|
||||
}
|
||||
if BConfig.EnableErrorsShow {
|
||||
if _, ok := ErrorMaps[fmt.Sprint(err)]; ok {
|
||||
exception(fmt.Sprint(err), ctx)
|
||||
return
|
||||
}
|
||||
}
|
||||
var stack string
|
||||
logs.Critical("the request url is ", ctx.Input.URL())
|
||||
logs.Critical("Handler crashed with error", err)
|
||||
for i := 1; ; i++ {
|
||||
_, file, line, ok := runtime.Caller(i)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
logs.Critical(fmt.Sprintf("%s:%d", file, line))
|
||||
stack = stack + fmt.Sprintln(fmt.Sprintf("%s:%d", file, line))
|
||||
}
|
||||
if BConfig.RunMode == DEV && BConfig.EnableErrorsRender {
|
||||
showErr(err, ctx, stack)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func newBConfig() *Config {
|
||||
return &Config{
|
||||
AppName: "beego",
|
||||
@ -149,10 +185,12 @@ func newBConfig() *Config {
|
||||
RouterCaseSensitive: true,
|
||||
ServerName: "beegoServer:" + VERSION,
|
||||
RecoverPanic: true,
|
||||
RecoverFunc: recoverPanic,
|
||||
CopyRequestBody: false,
|
||||
EnableGzip: false,
|
||||
MaxMemory: 1 << 26, //64MB
|
||||
EnableErrorsShow: true,
|
||||
EnableErrorsRender: true,
|
||||
Listen: Listen{
|
||||
Graceful: false,
|
||||
ServerTimeOut: 0,
|
||||
@ -186,17 +224,18 @@ func newBConfig() *Config {
|
||||
XSRFKey: "beegoxsrf",
|
||||
XSRFExpire: 0,
|
||||
Session: SessionConfig{
|
||||
SessionOn: false,
|
||||
SessionProvider: "memory",
|
||||
SessionName: "beegosessionID",
|
||||
SessionGCMaxLifetime: 3600,
|
||||
SessionProviderConfig: "",
|
||||
SessionCookieLifeTime: 0, //set cookie default is the browser life
|
||||
SessionAutoSetCookie: true,
|
||||
SessionDomain: "",
|
||||
EnableSidInHttpHeader: false, // enable store/get the sessionId into/from http headers
|
||||
SessionNameInHttpHeader: "Beegosessionid",
|
||||
EnableSidInUrlQuery: false, // enable get the sessionId from Url Query params
|
||||
SessionOn: false,
|
||||
SessionProvider: "memory",
|
||||
SessionName: "beegosessionID",
|
||||
SessionGCMaxLifetime: 3600,
|
||||
SessionProviderConfig: "",
|
||||
SessionDisableHTTPOnly: false,
|
||||
SessionCookieLifeTime: 0, //set cookie default is the browser life
|
||||
SessionAutoSetCookie: true,
|
||||
SessionDomain: "",
|
||||
SessionEnableSidInHTTPHeader: false, // enable store/get the sessionId into/from http headers
|
||||
SessionNameInHTTPHeader: "Beegosessionid",
|
||||
SessionEnableSidInURLQuery: false, // enable get the sessionId from Url Query params
|
||||
},
|
||||
},
|
||||
Log: LogConfig{
|
||||
@ -217,6 +256,9 @@ func parseConfig(appConfigPath string) (err error) {
|
||||
}
|
||||
|
||||
func assignConfig(ac config.Configer) error {
|
||||
for _, i := range []interface{}{BConfig, &BConfig.Listen, &BConfig.WebConfig, &BConfig.Log, &BConfig.WebConfig.Session} {
|
||||
assignSingleConfig(i, ac)
|
||||
}
|
||||
// set the run mode first
|
||||
if envRunMode := os.Getenv("BEEGO_RUNMODE"); envRunMode != "" {
|
||||
BConfig.RunMode = envRunMode
|
||||
@ -224,10 +266,6 @@ func assignConfig(ac config.Configer) error {
|
||||
BConfig.RunMode = runMode
|
||||
}
|
||||
|
||||
for _, i := range []interface{}{BConfig, &BConfig.Listen, &BConfig.WebConfig, &BConfig.Log, &BConfig.WebConfig.Session} {
|
||||
assignSingleConfig(i, ac)
|
||||
}
|
||||
|
||||
if sd := ac.String("StaticDir"); sd != "" {
|
||||
BConfig.WebConfig.StaticDir = map[string]string{}
|
||||
sds := strings.Fields(sd)
|
||||
@ -259,6 +297,10 @@ func assignConfig(ac config.Configer) error {
|
||||
}
|
||||
|
||||
if lo := ac.String("LogOutputs"); lo != "" {
|
||||
// if lo is not nil or empty
|
||||
// means user has set his own LogOutputs
|
||||
// clear the default setting to BConfig.Log.Outputs
|
||||
BConfig.Log.Outputs = make(map[string]string)
|
||||
los := strings.Split(lo, ";")
|
||||
for _, v := range los {
|
||||
if logType2Config := strings.SplitN(v, ",", 2); len(logType2Config) == 2 {
|
||||
@ -303,7 +345,7 @@ func assignSingleConfig(p interface{}, ac config.Configer) {
|
||||
case reflect.String:
|
||||
pf.SetString(ac.DefaultString(name, pf.String()))
|
||||
case reflect.Int, reflect.Int64:
|
||||
pf.SetInt(int64(ac.DefaultInt64(name, pf.Int())))
|
||||
pf.SetInt(ac.DefaultInt64(name, pf.Int()))
|
||||
case reflect.Bool:
|
||||
pf.SetBool(ac.DefaultBool(name, pf.Bool()))
|
||||
case reflect.Struct:
|
||||
|
@ -43,6 +43,8 @@ package config
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"reflect"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Configer defines how to get and set value from configuration raw data.
|
||||
@ -204,3 +206,37 @@ func ParseBool(val interface{}) (value bool, err error) {
|
||||
}
|
||||
return false, fmt.Errorf("parsing <nil>: invalid syntax")
|
||||
}
|
||||
|
||||
// ToString converts values of any type to string.
|
||||
func ToString(x interface{}) string {
|
||||
switch y := x.(type) {
|
||||
|
||||
// Handle dates with special logic
|
||||
// This needs to come above the fmt.Stringer
|
||||
// test since time.Time's have a .String()
|
||||
// method
|
||||
case time.Time:
|
||||
return y.Format("A Monday")
|
||||
|
||||
// Handle type string
|
||||
case string:
|
||||
return y
|
||||
|
||||
// Handle type with .String() method
|
||||
case fmt.Stringer:
|
||||
return y.String()
|
||||
|
||||
// Handle type with .Error() method
|
||||
case error:
|
||||
return y.Error()
|
||||
|
||||
}
|
||||
|
||||
// Handle named string type
|
||||
if v := reflect.ValueOf(x); v.Kind() == reflect.String {
|
||||
return v.String()
|
||||
}
|
||||
|
||||
// Fallback to fmt package for anything else like numeric types
|
||||
return fmt.Sprint(x)
|
||||
}
|
||||
|
85
config/env/env.go
vendored
Normal file
85
config/env/env.go
vendored
Normal file
@ -0,0 +1,85 @@
|
||||
// Copyright 2014 beego Author. All Rights Reserved.
|
||||
// Copyright 2017 Faissal Elamraoui. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
package env
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/astaxie/beego/utils"
|
||||
)
|
||||
|
||||
var env *utils.BeeMap
|
||||
|
||||
func init() {
|
||||
env = utils.NewBeeMap()
|
||||
for _, e := range os.Environ() {
|
||||
splits := strings.Split(e, "=")
|
||||
env.Set(splits[0], os.Getenv(splits[0]))
|
||||
}
|
||||
}
|
||||
|
||||
// Get returns a value by key.
|
||||
// If the key does not exist, the default value will be returned.
|
||||
func Get(key string, defVal string) string {
|
||||
if val := env.Get(key); val != nil {
|
||||
return val.(string)
|
||||
}
|
||||
return defVal
|
||||
}
|
||||
|
||||
// MustGet returns a value by key.
|
||||
// If the key does not exist, it will return an error.
|
||||
func MustGet(key string) (string, error) {
|
||||
if val := env.Get(key); val != nil {
|
||||
return val.(string), nil
|
||||
}
|
||||
return "", fmt.Errorf("no env variable with %s", key)
|
||||
}
|
||||
|
||||
// Set sets a value in the ENV copy.
|
||||
// This does not affect the child process environment.
|
||||
func Set(key string, value string) {
|
||||
env.Set(key, value)
|
||||
}
|
||||
|
||||
// MustSet sets a value in the ENV copy and the child process environment.
|
||||
// It returns an error in case the set operation failed.
|
||||
func MustSet(key string, value string) error {
|
||||
err := os.Setenv(key, value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
env.Set(key, value)
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetAll returns all keys/values in the current child process environment.
|
||||
func GetAll() map[string]string {
|
||||
items := env.Items()
|
||||
envs := make(map[string]string, env.Count())
|
||||
|
||||
for key, val := range items {
|
||||
switch key := key.(type) {
|
||||
case string:
|
||||
switch val := val.(type) {
|
||||
case string:
|
||||
envs[key] = val
|
||||
}
|
||||
}
|
||||
}
|
||||
return envs
|
||||
}
|
75
config/env/env_test.go
vendored
Normal file
75
config/env/env_test.go
vendored
Normal file
@ -0,0 +1,75 @@
|
||||
// Copyright 2014 beego Author. All Rights Reserved.
|
||||
// Copyright 2017 Faissal Elamraoui. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
package env
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestEnvGet(t *testing.T) {
|
||||
gopath := Get("GOPATH", "")
|
||||
if gopath != os.Getenv("GOPATH") {
|
||||
t.Error("expected GOPATH not empty.")
|
||||
}
|
||||
|
||||
noExistVar := Get("NOEXISTVAR", "foo")
|
||||
if noExistVar != "foo" {
|
||||
t.Errorf("expected NOEXISTVAR to equal foo, got %s.", noExistVar)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnvMustGet(t *testing.T) {
|
||||
gopath, err := MustGet("GOPATH")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if gopath != os.Getenv("GOPATH") {
|
||||
t.Errorf("expected GOPATH to be the same, got %s.", gopath)
|
||||
}
|
||||
|
||||
_, err = MustGet("NOEXISTVAR")
|
||||
if err == nil {
|
||||
t.Error("expected error to be non-nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnvSet(t *testing.T) {
|
||||
Set("MYVAR", "foo")
|
||||
myVar := Get("MYVAR", "bar")
|
||||
if myVar != "foo" {
|
||||
t.Errorf("expected MYVAR to equal foo, got %s.", myVar)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnvMustSet(t *testing.T) {
|
||||
err := MustSet("FOO", "bar")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
fooVar := os.Getenv("FOO")
|
||||
if fooVar != "bar" {
|
||||
t.Errorf("expected FOO variable to equal bar, got %s.", fooVar)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnvGetAll(t *testing.T) {
|
||||
envMap := GetAll()
|
||||
if len(envMap) == 0 {
|
||||
t.Error("expected environment not empty.")
|
||||
}
|
||||
}
|
@ -18,15 +18,14 @@ import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -51,24 +50,26 @@ func (ini *IniConfig) Parse(name string) (Configer, error) {
|
||||
}
|
||||
|
||||
func (ini *IniConfig) parseFile(name string) (*IniConfigContainer, error) {
|
||||
file, err := os.Open(name)
|
||||
data, err := ioutil.ReadFile(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ini.parseData(filepath.Dir(name), data)
|
||||
}
|
||||
|
||||
func (ini *IniConfig) parseData(dir string, data []byte) (*IniConfigContainer, error) {
|
||||
cfg := &IniConfigContainer{
|
||||
file.Name(),
|
||||
make(map[string]map[string]string),
|
||||
make(map[string]string),
|
||||
make(map[string]string),
|
||||
sync.RWMutex{},
|
||||
data: make(map[string]map[string]string),
|
||||
sectionComment: make(map[string]string),
|
||||
keyComment: make(map[string]string),
|
||||
RWMutex: sync.RWMutex{},
|
||||
}
|
||||
cfg.Lock()
|
||||
defer cfg.Unlock()
|
||||
defer file.Close()
|
||||
|
||||
var comment bytes.Buffer
|
||||
buf := bufio.NewReader(file)
|
||||
buf := bufio.NewReader(bytes.NewBuffer(data))
|
||||
// check the BOM
|
||||
head, err := buf.Peek(3)
|
||||
if err == nil && head[0] == 239 && head[1] == 187 && head[2] == 191 {
|
||||
@ -129,16 +130,20 @@ func (ini *IniConfig) parseFile(name string) (*IniConfigContainer, error) {
|
||||
|
||||
// handle include "other.conf"
|
||||
if len(keyValue) == 1 && strings.HasPrefix(key, "include") {
|
||||
|
||||
includefiles := strings.Fields(key)
|
||||
if includefiles[0] == "include" && len(includefiles) == 2 {
|
||||
|
||||
otherfile := strings.Trim(includefiles[1], "\"")
|
||||
if !path.IsAbs(otherfile) {
|
||||
otherfile = path.Join(path.Dir(name), otherfile)
|
||||
if !filepath.IsAbs(otherfile) {
|
||||
otherfile = filepath.Join(dir, otherfile)
|
||||
}
|
||||
|
||||
i, err := ini.parseFile(otherfile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for sec, dt := range i.data {
|
||||
if _, ok := cfg.data[sec]; !ok {
|
||||
cfg.data[sec] = make(map[string]string)
|
||||
@ -147,12 +152,15 @@ func (ini *IniConfig) parseFile(name string) (*IniConfigContainer, error) {
|
||||
cfg.data[sec][k] = v
|
||||
}
|
||||
}
|
||||
|
||||
for sec, comm := range i.sectionComment {
|
||||
cfg.sectionComment[sec] = comm
|
||||
}
|
||||
|
||||
for k, comm := range i.keyComment {
|
||||
cfg.keyComment[k] = comm
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
}
|
||||
@ -176,20 +184,25 @@ func (ini *IniConfig) parseFile(name string) (*IniConfigContainer, error) {
|
||||
}
|
||||
|
||||
// ParseData parse ini the data
|
||||
// When include other.conf,other.conf is either absolute directory
|
||||
// or under beego in default temporary directory(/tmp/beego[-username]).
|
||||
func (ini *IniConfig) ParseData(data []byte) (Configer, error) {
|
||||
// Save memory data to temporary file
|
||||
tmpName := path.Join(os.TempDir(), "beego", fmt.Sprintf("%d", time.Now().Nanosecond()))
|
||||
os.MkdirAll(path.Dir(tmpName), os.ModePerm)
|
||||
if err := ioutil.WriteFile(tmpName, data, 0655); err != nil {
|
||||
dir := "beego"
|
||||
currentUser, err := user.Current()
|
||||
if err == nil {
|
||||
dir = "beego-" + currentUser.Username
|
||||
}
|
||||
dir = filepath.Join(os.TempDir(), dir)
|
||||
if err = os.MkdirAll(dir, os.ModePerm); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ini.Parse(tmpName)
|
||||
|
||||
return ini.parseData(dir, data)
|
||||
}
|
||||
|
||||
// IniConfigContainer A Config represents the ini configuration.
|
||||
// When set and get value, support key as section:name type.
|
||||
type IniConfigContainer struct {
|
||||
filename string
|
||||
data map[string]map[string]string // section=> key:val
|
||||
sectionComment map[string]string // section : comment
|
||||
keyComment map[string]string // id: []{comment, key...}; id 1 is for main comment.
|
||||
@ -296,7 +309,7 @@ func (c *IniConfigContainer) GetSection(section string) (map[string]string, erro
|
||||
if v, ok := c.data[section]; ok {
|
||||
return v, nil
|
||||
}
|
||||
return nil, errors.New("not exist setction")
|
||||
return nil, errors.New("not exist section")
|
||||
}
|
||||
|
||||
// SaveConfigFile save the config into file.
|
||||
@ -392,11 +405,8 @@ func (c *IniConfigContainer) SaveConfigFile(filename string) (err error) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if _, err = buf.WriteTo(f); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
_, err = buf.WriteTo(f)
|
||||
return err
|
||||
}
|
||||
|
||||
// Set writes a new value for key.
|
||||
@ -411,7 +421,7 @@ func (c *IniConfigContainer) Set(key, value string) error {
|
||||
|
||||
var (
|
||||
section, k string
|
||||
sectionKey = strings.Split(key, "::")
|
||||
sectionKey = strings.Split(strings.ToLower(key), "::")
|
||||
)
|
||||
|
||||
if len(sectionKey) >= 2 {
|
||||
|
@ -181,7 +181,7 @@ name=mysql
|
||||
cfgData := string(data)
|
||||
datas := strings.Split(saveResult, "\n")
|
||||
for _, line := range datas {
|
||||
if strings.Contains(cfgData, line+"\n") == false {
|
||||
if !strings.Contains(cfgData, line+"\n") {
|
||||
t.Fatalf("different after save ini config file. need contains %q", line)
|
||||
}
|
||||
}
|
||||
|
@ -35,11 +35,9 @@ import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/astaxie/beego/config"
|
||||
"github.com/beego/x2j"
|
||||
@ -52,36 +50,26 @@ type Config struct{}
|
||||
|
||||
// Parse returns a ConfigContainer with parsed xml config map.
|
||||
func (xc *Config) Parse(filename string) (config.Configer, error) {
|
||||
file, err := os.Open(filename)
|
||||
context, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
return xc.ParseData(context)
|
||||
}
|
||||
|
||||
// ParseData xml data
|
||||
func (xc *Config) ParseData(data []byte) (config.Configer, error) {
|
||||
x := &ConfigContainer{data: make(map[string]interface{})}
|
||||
content, err := ioutil.ReadAll(file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
d, err := x2j.DocToMap(string(content))
|
||||
d, err := x2j.DocToMap(string(data))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
x.data = config.ExpandValueEnvForMap(d["config"].(map[string]interface{}))
|
||||
return x, nil
|
||||
}
|
||||
|
||||
// ParseData xml data
|
||||
func (xc *Config) ParseData(data []byte) (config.Configer, error) {
|
||||
// Save memory data to temporary file
|
||||
tmpName := path.Join(os.TempDir(), "beego", fmt.Sprintf("%d", time.Now().Nanosecond()))
|
||||
os.MkdirAll(path.Dir(tmpName), os.ModePerm)
|
||||
if err := ioutil.WriteFile(tmpName, data, 0655); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return xc.Parse(tmpName)
|
||||
return x, nil
|
||||
}
|
||||
|
||||
// ConfigContainer A Config represents the xml configuration.
|
||||
@ -193,10 +181,14 @@ func (c *ConfigContainer) DefaultStrings(key string, defaultval []string) []stri
|
||||
|
||||
// GetSection returns map for the given section
|
||||
func (c *ConfigContainer) GetSection(section string) (map[string]string, error) {
|
||||
if v, ok := c.data[section]; ok {
|
||||
return v.(map[string]string), nil
|
||||
if v, ok := c.data[section].(map[string]interface{}); ok {
|
||||
mapstr := make(map[string]string)
|
||||
for k, val := range v {
|
||||
mapstr[k] = config.ToString(val)
|
||||
}
|
||||
return mapstr, nil
|
||||
}
|
||||
return nil, errors.New("not exist setction")
|
||||
return nil, fmt.Errorf("section '%s' not found", section)
|
||||
}
|
||||
|
||||
// SaveConfigFile save the config into file
|
||||
|
@ -37,6 +37,10 @@ func TestXML(t *testing.T) {
|
||||
<copyrequestbody>true</copyrequestbody>
|
||||
<path1>${GOPATH}</path1>
|
||||
<path2>${GOPATH||/home/go}</path2>
|
||||
<mysection>
|
||||
<id>1</id>
|
||||
<name>MySection</name>
|
||||
</mysection>
|
||||
</config>
|
||||
`
|
||||
keyValue = map[string]interface{}{
|
||||
@ -65,11 +69,22 @@ func TestXML(t *testing.T) {
|
||||
}
|
||||
f.Close()
|
||||
defer os.Remove("testxml.conf")
|
||||
|
||||
xmlconf, err := config.NewConfig("xml", "testxml.conf")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
var xmlsection map[string]string
|
||||
xmlsection, err = xmlconf.GetSection("mysection")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(xmlsection) == 0 {
|
||||
t.Error("section should not be empty")
|
||||
}
|
||||
|
||||
for k, v := range keyValue {
|
||||
|
||||
var (
|
||||
|
@ -37,10 +37,8 @@ import (
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/astaxie/beego/config"
|
||||
"github.com/beego/goyaml2"
|
||||
@ -63,26 +61,30 @@ func (yaml *Config) Parse(filename string) (y config.Configer, err error) {
|
||||
|
||||
// ParseData parse yaml data
|
||||
func (yaml *Config) ParseData(data []byte) (config.Configer, error) {
|
||||
// Save memory data to temporary file
|
||||
tmpName := path.Join(os.TempDir(), "beego", fmt.Sprintf("%d", time.Now().Nanosecond()))
|
||||
os.MkdirAll(path.Dir(tmpName), os.ModePerm)
|
||||
if err := ioutil.WriteFile(tmpName, data, 0655); err != nil {
|
||||
cnf, err := parseYML(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return yaml.Parse(tmpName)
|
||||
|
||||
return &ConfigContainer{
|
||||
data: cnf,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ReadYmlReader Read yaml file to map.
|
||||
// if json like, use json package, unless goyaml2 package.
|
||||
func ReadYmlReader(path string) (cnf map[string]interface{}, err error) {
|
||||
f, err := os.Open(path)
|
||||
buf, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
buf, err := ioutil.ReadAll(f)
|
||||
if err != nil || len(buf) < 3 {
|
||||
return parseYML(buf)
|
||||
}
|
||||
|
||||
// parseYML parse yaml formatted []byte to map.
|
||||
func parseYML(buf []byte) (cnf map[string]interface{}, err error) {
|
||||
if len(buf) < 3 {
|
||||
return
|
||||
}
|
||||
|
||||
@ -250,7 +252,7 @@ func (c *ConfigContainer) GetSection(section string) (map[string]string, error)
|
||||
if v, ok := c.data[section]; ok {
|
||||
return v.(map[string]string), nil
|
||||
}
|
||||
return nil, errors.New("not exist setction")
|
||||
return nil, errors.New("not exist section")
|
||||
}
|
||||
|
||||
// SaveConfigFile save the config into file
|
||||
|
@ -40,12 +40,14 @@ var (
|
||||
// BeegoInput operates the http request header, data, cookie and body.
|
||||
// it also contains router params and current session.
|
||||
type BeegoInput struct {
|
||||
Context *Context
|
||||
CruSession session.Store
|
||||
pnames []string
|
||||
pvalues []string
|
||||
data map[interface{}]interface{} // store some values in this context when calling context in filter or controller.
|
||||
RequestBody []byte
|
||||
Context *Context
|
||||
CruSession session.Store
|
||||
pnames []string
|
||||
pvalues []string
|
||||
data map[interface{}]interface{} // store some values in this context when calling context in filter or controller.
|
||||
RequestBody []byte
|
||||
RunMethod string
|
||||
RunController reflect.Type
|
||||
}
|
||||
|
||||
// NewInput return BeegoInput generated by Context.
|
||||
@ -411,7 +413,13 @@ func (input *BeegoInput) Bind(dest interface{}, key string) error {
|
||||
if !value.CanSet() {
|
||||
return errors.New("beego: non-settable variable passed to Bind: " + key)
|
||||
}
|
||||
rv := input.bind(key, value.Type())
|
||||
typ := value.Type()
|
||||
// Get real type if dest define with interface{}.
|
||||
// e.g var dest interface{} dest=1.0
|
||||
if value.Kind() == reflect.Interface {
|
||||
typ = value.Elem().Type()
|
||||
}
|
||||
rv := input.bind(key, typ)
|
||||
if !rv.IsValid() {
|
||||
return errors.New("beego: reflect value is empty")
|
||||
}
|
||||
@ -420,6 +428,9 @@ func (input *BeegoInput) Bind(dest interface{}, key string) error {
|
||||
}
|
||||
|
||||
func (input *BeegoInput) bind(key string, typ reflect.Type) reflect.Value {
|
||||
if input.Context.Request.Form == nil {
|
||||
input.Context.Request.ParseForm()
|
||||
}
|
||||
rv := reflect.Zero(typ)
|
||||
switch typ.Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
|
@ -15,81 +15,97 @@
|
||||
package context
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestParse(t *testing.T) {
|
||||
r, _ := http.NewRequest("GET", "/?id=123&isok=true&ft=1.2&ol[0]=1&ol[1]=2&ul[]=str&ul[]=array&user.Name=astaxie", nil)
|
||||
beegoInput := NewInput()
|
||||
beegoInput.Context = NewContext()
|
||||
beegoInput.Context.Reset(httptest.NewRecorder(), r)
|
||||
beegoInput.ParseFormOrMulitForm(1 << 20)
|
||||
func TestBind(t *testing.T) {
|
||||
type testItem struct {
|
||||
field string
|
||||
empty interface{}
|
||||
want interface{}
|
||||
}
|
||||
type Human struct {
|
||||
ID int
|
||||
Nick string
|
||||
Pwd string
|
||||
Ms bool
|
||||
}
|
||||
|
||||
var id int
|
||||
err := beegoInput.Bind(&id, "id")
|
||||
if id != 123 || err != nil {
|
||||
t.Fatal("id should has int value")
|
||||
}
|
||||
fmt.Println(id)
|
||||
cases := []struct {
|
||||
request string
|
||||
valueGp []testItem
|
||||
}{
|
||||
{"/?p=str", []testItem{{"p", interface{}(""), interface{}("str")}}},
|
||||
|
||||
var isok bool
|
||||
err = beegoInput.Bind(&isok, "isok")
|
||||
if !isok || err != nil {
|
||||
t.Fatal("isok should be true")
|
||||
}
|
||||
fmt.Println(isok)
|
||||
{"/?p=", []testItem{{"p", "", ""}}},
|
||||
{"/?p=str", []testItem{{"p", "", "str"}}},
|
||||
|
||||
var float float64
|
||||
err = beegoInput.Bind(&float, "ft")
|
||||
if float != 1.2 || err != nil {
|
||||
t.Fatal("float should be equal to 1.2")
|
||||
}
|
||||
fmt.Println(float)
|
||||
{"/?p=123", []testItem{{"p", 0, 123}}},
|
||||
{"/?p=123", []testItem{{"p", uint(0), uint(123)}}},
|
||||
|
||||
ol := make([]int, 0, 2)
|
||||
err = beegoInput.Bind(&ol, "ol")
|
||||
if len(ol) != 2 || err != nil || ol[0] != 1 || ol[1] != 2 {
|
||||
t.Fatal("ol should has two elements")
|
||||
}
|
||||
fmt.Println(ol)
|
||||
{"/?p=1.0", []testItem{{"p", 0.0, 1.0}}},
|
||||
{"/?p=1", []testItem{{"p", false, true}}},
|
||||
|
||||
ul := make([]string, 0, 2)
|
||||
err = beegoInput.Bind(&ul, "ul")
|
||||
if len(ul) != 2 || err != nil || ul[0] != "str" || ul[1] != "array" {
|
||||
t.Fatal("ul should has two elements")
|
||||
}
|
||||
fmt.Println(ul)
|
||||
{"/?p=true", []testItem{{"p", false, true}}},
|
||||
{"/?p=ON", []testItem{{"p", false, true}}},
|
||||
{"/?p=on", []testItem{{"p", false, true}}},
|
||||
{"/?p=1", []testItem{{"p", false, true}}},
|
||||
{"/?p=2", []testItem{{"p", false, false}}},
|
||||
{"/?p=false", []testItem{{"p", false, false}}},
|
||||
|
||||
type User struct {
|
||||
Name string
|
||||
}
|
||||
user := User{}
|
||||
err = beegoInput.Bind(&user, "user")
|
||||
if err != nil || user.Name != "astaxie" {
|
||||
t.Fatal("user should has name")
|
||||
}
|
||||
fmt.Println(user)
|
||||
}
|
||||
{"/?p[a]=1&p[b]=2&p[c]=3", []testItem{{"p", map[string]int{}, map[string]int{"a": 1, "b": 2, "c": 3}}}},
|
||||
{"/?p[a]=v1&p[b]=v2&p[c]=v3", []testItem{{"p", map[string]string{}, map[string]string{"a": "v1", "b": "v2", "c": "v3"}}}},
|
||||
|
||||
func TestParse2(t *testing.T) {
|
||||
r, _ := http.NewRequest("GET", "/?user[0][Username]=Raph&user[1].Username=Leo&user[0].Password=123456&user[1][Password]=654321", nil)
|
||||
beegoInput := NewInput()
|
||||
beegoInput.Context = NewContext()
|
||||
beegoInput.Context.Reset(httptest.NewRecorder(), r)
|
||||
beegoInput.ParseFormOrMulitForm(1 << 20)
|
||||
type User struct {
|
||||
Username string
|
||||
Password string
|
||||
{"/?p[]=8&p[]=9&p[]=10", []testItem{{"p", []int{}, []int{8, 9, 10}}}},
|
||||
{"/?p[0]=8&p[1]=9&p[2]=10", []testItem{{"p", []int{}, []int{8, 9, 10}}}},
|
||||
{"/?p[0]=8&p[1]=9&p[2]=10&p[5]=14", []testItem{{"p", []int{}, []int{8, 9, 10, 0, 0, 14}}}},
|
||||
{"/?p[0]=8.0&p[1]=9.0&p[2]=10.0", []testItem{{"p", []float64{}, []float64{8.0, 9.0, 10.0}}}},
|
||||
|
||||
{"/?p[]=10&p[]=9&p[]=8", []testItem{{"p", []string{}, []string{"10", "9", "8"}}}},
|
||||
{"/?p[0]=8&p[1]=9&p[2]=10", []testItem{{"p", []string{}, []string{"8", "9", "10"}}}},
|
||||
|
||||
{"/?p[0]=true&p[1]=false&p[2]=true&p[5]=1&p[6]=ON&p[7]=other", []testItem{{"p", []bool{}, []bool{true, false, true, false, false, true, true, false}}}},
|
||||
|
||||
{"/?human.Nick=astaxie", []testItem{{"human", Human{}, Human{Nick: "astaxie"}}}},
|
||||
{"/?human.ID=888&human.Nick=astaxie&human.Ms=true&human[Pwd]=pass", []testItem{{"human", Human{}, Human{ID: 888, Nick: "astaxie", Ms: true, Pwd: "pass"}}}},
|
||||
{"/?human[0].ID=888&human[0].Nick=astaxie&human[0].Ms=true&human[0][Pwd]=pass01&human[1].ID=999&human[1].Nick=ysqi&human[1].Ms=On&human[1].Pwd=pass02",
|
||||
[]testItem{{"human", []Human{}, []Human{
|
||||
{ID: 888, Nick: "astaxie", Ms: true, Pwd: "pass01"},
|
||||
{ID: 999, Nick: "ysqi", Ms: true, Pwd: "pass02"},
|
||||
}}}},
|
||||
|
||||
{
|
||||
"/?id=123&isok=true&ft=1.2&ol[0]=1&ol[1]=2&ul[]=str&ul[]=array&human.Nick=astaxie",
|
||||
[]testItem{
|
||||
{"id", 0, 123},
|
||||
{"isok", false, true},
|
||||
{"ft", 0.0, 1.2},
|
||||
{"ol", []int{}, []int{1, 2}},
|
||||
{"ul", []string{}, []string{"str", "array"}},
|
||||
{"human", Human{}, Human{Nick: "astaxie"}},
|
||||
},
|
||||
},
|
||||
}
|
||||
var users []User
|
||||
err := beegoInput.Bind(&users, "user")
|
||||
fmt.Println(users)
|
||||
if err != nil || users[0].Username != "Raph" || users[0].Password != "123456" || users[1].Username != "Leo" || users[1].Password != "654321" {
|
||||
t.Fatal("users info wrong")
|
||||
for _, c := range cases {
|
||||
r, _ := http.NewRequest("GET", c.request, nil)
|
||||
beegoInput := NewInput()
|
||||
beegoInput.Context = NewContext()
|
||||
beegoInput.Context.Reset(httptest.NewRecorder(), r)
|
||||
|
||||
for _, item := range c.valueGp {
|
||||
got := item.empty
|
||||
err := beegoInput.Bind(&got, item.field)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !reflect.DeepEqual(got, item.want) {
|
||||
t.Fatalf("Bind %q error,should be:\n%#v \ngot:\n%#v", item.field, item.want, got)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -67,6 +67,7 @@ func (output *BeegoOutput) Body(content []byte) error {
|
||||
}
|
||||
if b, n, _ := WriteBody(encoding, buf, content); b {
|
||||
output.Header("Content-Encoding", n)
|
||||
output.Header("Content-Length", strconv.Itoa(buf.Len()))
|
||||
} else {
|
||||
output.Header("Content-Length", strconv.Itoa(len(content)))
|
||||
}
|
||||
@ -104,7 +105,7 @@ func (output *BeegoOutput) Cookie(name string, value string, others ...interface
|
||||
switch {
|
||||
case maxAge > 0:
|
||||
fmt.Fprintf(&b, "; Expires=%s; Max-Age=%d", time.Now().Add(time.Duration(maxAge)*time.Second).UTC().Format(time.RFC1123), maxAge)
|
||||
case maxAge < 0:
|
||||
case maxAge <= 0:
|
||||
fmt.Fprintf(&b, "; Max-Age=0")
|
||||
}
|
||||
}
|
||||
@ -330,16 +331,17 @@ func (output *BeegoOutput) IsServerError() bool {
|
||||
|
||||
func stringsToJSON(str string) string {
|
||||
rs := []rune(str)
|
||||
jsons := ""
|
||||
var jsons bytes.Buffer
|
||||
for _, r := range rs {
|
||||
rint := int(r)
|
||||
if rint < 128 {
|
||||
jsons += string(r)
|
||||
jsons.WriteRune(r)
|
||||
} else {
|
||||
jsons += "\\u" + strconv.FormatInt(int64(rint), 16) // json
|
||||
jsons.WriteString("\\u")
|
||||
jsons.WriteString(strconv.FormatInt(int64(rint), 16))
|
||||
}
|
||||
}
|
||||
return jsons
|
||||
return jsons.String()
|
||||
}
|
||||
|
||||
// Session sets session item value with given key.
|
||||
|
@ -69,6 +69,7 @@ type Controller struct {
|
||||
|
||||
// template data
|
||||
TplName string
|
||||
ViewPath string
|
||||
Layout string
|
||||
LayoutSections map[string]string // the key is the section name and the value is the template name
|
||||
TplPrefix string
|
||||
@ -185,7 +186,11 @@ func (c *Controller) Render() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.Ctx.Output.Header("Content-Type", "text/html; charset=utf-8")
|
||||
|
||||
if c.Ctx.ResponseWriter.Header().Get("Content-Type") == "" {
|
||||
c.Ctx.Output.Header("Content-Type", "text/html; charset=utf-8")
|
||||
}
|
||||
|
||||
return c.Ctx.Output.Body(rb)
|
||||
}
|
||||
|
||||
@ -209,7 +214,7 @@ func (c *Controller) RenderBytes() ([]byte, error) {
|
||||
continue
|
||||
}
|
||||
buf.Reset()
|
||||
err = ExecuteTemplate(&buf, sectionTpl, c.Data)
|
||||
err = ExecuteViewPathTemplate(&buf, sectionTpl, c.viewPath(), c.Data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -218,7 +223,7 @@ func (c *Controller) RenderBytes() ([]byte, error) {
|
||||
}
|
||||
|
||||
buf.Reset()
|
||||
ExecuteTemplate(&buf, c.Layout, c.Data)
|
||||
ExecuteViewPathTemplate(&buf, c.Layout, c.viewPath(), c.Data)
|
||||
}
|
||||
return buf.Bytes(), err
|
||||
}
|
||||
@ -244,9 +249,16 @@ func (c *Controller) renderTemplate() (bytes.Buffer, error) {
|
||||
}
|
||||
}
|
||||
}
|
||||
BuildTemplate(BConfig.WebConfig.ViewsPath, buildFiles...)
|
||||
BuildTemplate(c.viewPath(), buildFiles...)
|
||||
}
|
||||
return buf, ExecuteTemplate(&buf, c.TplName, c.Data)
|
||||
return buf, ExecuteViewPathTemplate(&buf, c.TplName, c.viewPath(), c.Data)
|
||||
}
|
||||
|
||||
func (c *Controller) viewPath() string {
|
||||
if c.ViewPath == "" {
|
||||
return BConfig.WebConfig.ViewsPath
|
||||
}
|
||||
return c.ViewPath
|
||||
}
|
||||
|
||||
// Redirect sends the redirection response to url with status code.
|
||||
@ -302,7 +314,7 @@ func (c *Controller) ServeJSON(encoding ...bool) {
|
||||
if BConfig.RunMode == PROD {
|
||||
hasIndent = false
|
||||
}
|
||||
if len(encoding) > 0 && encoding[0] == true {
|
||||
if len(encoding) > 0 && encoding[0] {
|
||||
hasEncoding = true
|
||||
}
|
||||
c.Ctx.Output.JSON(c.Data["json"], hasIndent, hasEncoding)
|
||||
@ -399,6 +411,16 @@ func (c *Controller) GetInt8(key string, def ...int8) (int8, error) {
|
||||
return int8(i64), err
|
||||
}
|
||||
|
||||
// GetUint8 return input as an uint8 or the default value while it's present and input is blank
|
||||
func (c *Controller) GetUint8(key string, def ...uint8) (uint8, error) {
|
||||
strv := c.Ctx.Input.Query(key)
|
||||
if len(strv) == 0 && len(def) > 0 {
|
||||
return def[0], nil
|
||||
}
|
||||
u64, err := strconv.ParseUint(strv, 10, 8)
|
||||
return uint8(u64), err
|
||||
}
|
||||
|
||||
// GetInt16 returns input as an int16 or the default value while it's present and input is blank
|
||||
func (c *Controller) GetInt16(key string, def ...int16) (int16, error) {
|
||||
strv := c.Ctx.Input.Query(key)
|
||||
@ -409,6 +431,16 @@ func (c *Controller) GetInt16(key string, def ...int16) (int16, error) {
|
||||
return int16(i64), err
|
||||
}
|
||||
|
||||
// GetUint16 returns input as an uint16 or the default value while it's present and input is blank
|
||||
func (c *Controller) GetUint16(key string, def ...uint16) (uint16, error) {
|
||||
strv := c.Ctx.Input.Query(key)
|
||||
if len(strv) == 0 && len(def) > 0 {
|
||||
return def[0], nil
|
||||
}
|
||||
u64, err := strconv.ParseUint(strv, 10, 16)
|
||||
return uint16(u64), err
|
||||
}
|
||||
|
||||
// GetInt32 returns input as an int32 or the default value while it's present and input is blank
|
||||
func (c *Controller) GetInt32(key string, def ...int32) (int32, error) {
|
||||
strv := c.Ctx.Input.Query(key)
|
||||
@ -419,6 +451,16 @@ func (c *Controller) GetInt32(key string, def ...int32) (int32, error) {
|
||||
return int32(i64), err
|
||||
}
|
||||
|
||||
// GetUint32 returns input as an uint32 or the default value while it's present and input is blank
|
||||
func (c *Controller) GetUint32(key string, def ...uint32) (uint32, error) {
|
||||
strv := c.Ctx.Input.Query(key)
|
||||
if len(strv) == 0 && len(def) > 0 {
|
||||
return def[0], nil
|
||||
}
|
||||
u64, err := strconv.ParseUint(strv, 10, 32)
|
||||
return uint32(u64), err
|
||||
}
|
||||
|
||||
// GetInt64 returns input value as int64 or the default value while it's present and input is blank.
|
||||
func (c *Controller) GetInt64(key string, def ...int64) (int64, error) {
|
||||
strv := c.Ctx.Input.Query(key)
|
||||
@ -428,6 +470,15 @@ func (c *Controller) GetInt64(key string, def ...int64) (int64, error) {
|
||||
return strconv.ParseInt(strv, 10, 64)
|
||||
}
|
||||
|
||||
// GetUint64 returns input value as uint64 or the default value while it's present and input is blank.
|
||||
func (c *Controller) GetUint64(key string, def ...uint64) (uint64, error) {
|
||||
strv := c.Ctx.Input.Query(key)
|
||||
if len(strv) == 0 && len(def) > 0 {
|
||||
return def[0], nil
|
||||
}
|
||||
return strconv.ParseUint(strv, 10, 64)
|
||||
}
|
||||
|
||||
// GetBool returns input value as bool or the default value while it's present and input is blank.
|
||||
func (c *Controller) GetBool(key string, def ...bool) (bool, error) {
|
||||
strv := c.Ctx.Input.Query(key)
|
||||
@ -453,7 +504,7 @@ func (c *Controller) GetFile(key string) (multipart.File, *multipart.FileHeader,
|
||||
}
|
||||
|
||||
// GetFiles return multi-upload files
|
||||
// files, err:=c.Getfiles("myfiles")
|
||||
// files, err:=c.GetFiles("myfiles")
|
||||
// if err != nil {
|
||||
// http.Error(w, err.Error(), http.StatusNoContent)
|
||||
// return
|
||||
|
@ -15,9 +15,13 @@
|
||||
package beego
|
||||
|
||||
import (
|
||||
"math"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/astaxie/beego/context"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
func TestGetInt(t *testing.T) {
|
||||
@ -75,3 +79,103 @@ func TestGetInt64(t *testing.T) {
|
||||
t.Errorf("TestGeetInt64 expect 40,get %T,%v", val, val)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetUint8(t *testing.T) {
|
||||
i := context.NewInput()
|
||||
i.SetParam("age", strconv.FormatUint(math.MaxUint8, 10))
|
||||
ctx := &context.Context{Input: i}
|
||||
ctrlr := Controller{Ctx: ctx}
|
||||
val, _ := ctrlr.GetUint8("age")
|
||||
if val != math.MaxUint8 {
|
||||
t.Errorf("TestGetUint8 expect %v,get %T,%v", math.MaxUint8, val, val)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetUint16(t *testing.T) {
|
||||
i := context.NewInput()
|
||||
i.SetParam("age", strconv.FormatUint(math.MaxUint16, 10))
|
||||
ctx := &context.Context{Input: i}
|
||||
ctrlr := Controller{Ctx: ctx}
|
||||
val, _ := ctrlr.GetUint16("age")
|
||||
if val != math.MaxUint16 {
|
||||
t.Errorf("TestGetUint16 expect %v,get %T,%v", math.MaxUint16, val, val)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetUint32(t *testing.T) {
|
||||
i := context.NewInput()
|
||||
i.SetParam("age", strconv.FormatUint(math.MaxUint32, 10))
|
||||
ctx := &context.Context{Input: i}
|
||||
ctrlr := Controller{Ctx: ctx}
|
||||
val, _ := ctrlr.GetUint32("age")
|
||||
if val != math.MaxUint32 {
|
||||
t.Errorf("TestGetUint32 expect %v,get %T,%v", math.MaxUint32, val, val)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetUint64(t *testing.T) {
|
||||
i := context.NewInput()
|
||||
i.SetParam("age", strconv.FormatUint(math.MaxUint64, 10))
|
||||
ctx := &context.Context{Input: i}
|
||||
ctrlr := Controller{Ctx: ctx}
|
||||
val, _ := ctrlr.GetUint64("age")
|
||||
if val != math.MaxUint64 {
|
||||
t.Errorf("TestGetUint64 expect %v,get %T,%v", uint64(math.MaxUint64), val, val)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAdditionalViewPaths(t *testing.T) {
|
||||
dir1 := "_beeTmp"
|
||||
dir2 := "_beeTmp2"
|
||||
defer os.RemoveAll(dir1)
|
||||
defer os.RemoveAll(dir2)
|
||||
|
||||
dir1file := "file1.tpl"
|
||||
dir2file := "file2.tpl"
|
||||
|
||||
genFile := func(dir string, name string, content string) {
|
||||
os.MkdirAll(filepath.Dir(filepath.Join(dir, name)), 0777)
|
||||
if f, err := os.Create(filepath.Join(dir, name)); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
defer f.Close()
|
||||
f.WriteString(content)
|
||||
f.Close()
|
||||
}
|
||||
|
||||
}
|
||||
genFile(dir1, dir1file, `<div>{{.Content}}</div>`)
|
||||
genFile(dir2, dir2file, `<html>{{.Content}}</html>`)
|
||||
|
||||
AddViewPath(dir1)
|
||||
AddViewPath(dir2)
|
||||
|
||||
ctrl := Controller{
|
||||
TplName: "file1.tpl",
|
||||
ViewPath: dir1,
|
||||
}
|
||||
ctrl.Data = map[interface{}]interface{}{
|
||||
"Content": "value2",
|
||||
}
|
||||
if result, err := ctrl.RenderString(); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
if result != "<div>value2</div>" {
|
||||
t.Fatalf("TestAdditionalViewPaths expect %s got %s", "<div>value2</div>", result)
|
||||
}
|
||||
}
|
||||
|
||||
func() {
|
||||
ctrl.TplName = "file2.tpl"
|
||||
defer func() {
|
||||
if r := recover(); r == nil {
|
||||
t.Fatal("TestAdditionalViewPaths expected error")
|
||||
}
|
||||
}()
|
||||
ctrl.RenderString()
|
||||
}()
|
||||
|
||||
ctrl.TplName = "file2.tpl"
|
||||
ctrl.ViewPath = dir2
|
||||
ctrl.RenderString()
|
||||
}
|
||||
|
6
error.go
6
error.go
@ -93,7 +93,11 @@ func showErr(err interface{}, ctx *context.Context, stack string) {
|
||||
"BeegoVersion": VERSION,
|
||||
"GoVersion": runtime.Version(),
|
||||
}
|
||||
ctx.ResponseWriter.WriteHeader(500)
|
||||
if ctx.Output.Status != 0 {
|
||||
ctx.ResponseWriter.WriteHeader(ctx.Output.Status)
|
||||
} else {
|
||||
ctx.ResponseWriter.WriteHeader(500)
|
||||
}
|
||||
t.Execute(ctx.ResponseWriter, data)
|
||||
}
|
||||
|
||||
|
@ -48,7 +48,7 @@ func TestFlashHeader(t *testing.T) {
|
||||
// match for the expected header
|
||||
res := strings.Contains(sc, "BEEGO_FLASH=%00notice%23BEEGOFLASH%23TestFlashString%00")
|
||||
// validate the assertion
|
||||
if res != true {
|
||||
if !res {
|
||||
t.Errorf("TestFlashHeader() unable to validate flash message")
|
||||
}
|
||||
}
|
||||
|
@ -85,23 +85,31 @@ var (
|
||||
|
||||
isChild bool
|
||||
socketOrder string
|
||||
once sync.Once
|
||||
|
||||
hookableSignals []os.Signal
|
||||
)
|
||||
|
||||
func onceInit() {
|
||||
regLock = &sync.Mutex{}
|
||||
func init() {
|
||||
flag.BoolVar(&isChild, "graceful", false, "listen on open fd (after forking)")
|
||||
flag.StringVar(&socketOrder, "socketorder", "", "previous initialization order - used when more than one listener was started")
|
||||
|
||||
regLock = &sync.Mutex{}
|
||||
runningServers = make(map[string]*Server)
|
||||
runningServersOrder = []string{}
|
||||
socketPtrOffsetMap = make(map[string]uint)
|
||||
|
||||
hookableSignals = []os.Signal{
|
||||
syscall.SIGHUP,
|
||||
syscall.SIGINT,
|
||||
syscall.SIGTERM,
|
||||
}
|
||||
}
|
||||
|
||||
// NewServer returns a new graceServer.
|
||||
func NewServer(addr string, handler http.Handler) (srv *Server) {
|
||||
once.Do(onceInit)
|
||||
regLock.Lock()
|
||||
defer regLock.Unlock()
|
||||
|
||||
if !flag.Parsed() {
|
||||
flag.Parse()
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ func newGraceListener(l net.Listener, srv *Server) (el *graceListener) {
|
||||
server: srv,
|
||||
}
|
||||
go func() {
|
||||
_ = <-el.stop
|
||||
<-el.stop
|
||||
el.stopped = true
|
||||
el.stop <- el.Listener.Close()
|
||||
}()
|
||||
|
@ -162,9 +162,7 @@ func (srv *Server) handleSignals() {
|
||||
|
||||
signal.Notify(
|
||||
srv.sigChan,
|
||||
syscall.SIGHUP,
|
||||
syscall.SIGINT,
|
||||
syscall.SIGTERM,
|
||||
hookableSignals...,
|
||||
)
|
||||
|
||||
pid := syscall.Getpid()
|
||||
@ -198,7 +196,6 @@ func (srv *Server) signalHooks(ppFlag int, sig os.Signal) {
|
||||
for _, f := range srv.SignalHooks[ppFlag][sig] {
|
||||
f()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// shutdown closes the listener so that no new connections are accepted. it also
|
||||
@ -290,3 +287,19 @@ func (srv *Server) fork() (err error) {
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// RegisterSignalHook registers a function to be run PreSignal or PostSignal for a given signal.
|
||||
func (srv *Server) RegisterSignalHook(ppFlag int, sig os.Signal, f func()) (err error) {
|
||||
if ppFlag != PreSignal && ppFlag != PostSignal {
|
||||
err = fmt.Errorf("Invalid ppFlag argument. Must be either grace.PreSignal or grace.PostSignal.")
|
||||
return
|
||||
}
|
||||
for _, s := range hookableSignals {
|
||||
if s == sig {
|
||||
srv.SignalHooks[ppFlag][sig] = append(srv.SignalHooks[ppFlag][sig], f)
|
||||
return
|
||||
}
|
||||
}
|
||||
err = fmt.Errorf("Signal '%v' is not supported.", sig)
|
||||
return
|
||||
}
|
||||
|
10
hooks.go
10
hooks.go
@ -53,10 +53,11 @@ func registerSession() error {
|
||||
conf.Secure = BConfig.Listen.EnableHTTPS
|
||||
conf.CookieLifeTime = BConfig.WebConfig.Session.SessionCookieLifeTime
|
||||
conf.ProviderConfig = filepath.ToSlash(BConfig.WebConfig.Session.SessionProviderConfig)
|
||||
conf.DisableHTTPOnly = BConfig.WebConfig.Session.SessionDisableHTTPOnly
|
||||
conf.Domain = BConfig.WebConfig.Session.SessionDomain
|
||||
conf.EnableSidInHttpHeader = BConfig.WebConfig.Session.EnableSidInHttpHeader
|
||||
conf.SessionNameInHttpHeader = BConfig.WebConfig.Session.SessionNameInHttpHeader
|
||||
conf.EnableSidInUrlQuery = BConfig.WebConfig.Session.EnableSidInUrlQuery
|
||||
conf.EnableSidInHttpHeader = BConfig.WebConfig.Session.SessionEnableSidInHTTPHeader
|
||||
conf.SessionNameInHttpHeader = BConfig.WebConfig.Session.SessionNameInHTTPHeader
|
||||
conf.EnableSidInUrlQuery = BConfig.WebConfig.Session.SessionEnableSidInURLQuery
|
||||
} else {
|
||||
if err = json.Unmarshal([]byte(sessionConfig), conf); err != nil {
|
||||
return err
|
||||
@ -71,7 +72,8 @@ func registerSession() error {
|
||||
}
|
||||
|
||||
func registerTemplate() error {
|
||||
if err := BuildTemplate(BConfig.WebConfig.ViewsPath); err != nil {
|
||||
defer lockViewPaths()
|
||||
if err := AddViewPath(BConfig.WebConfig.ViewsPath); err != nil {
|
||||
if BConfig.RunMode == DEV {
|
||||
logs.Warn(err)
|
||||
}
|
||||
|
@ -136,9 +136,11 @@ type BeegoHTTPSettings struct {
|
||||
TLSClientConfig *tls.Config
|
||||
Proxy func(*http.Request) (*url.URL, error)
|
||||
Transport http.RoundTripper
|
||||
CheckRedirect func(req *http.Request, via []*http.Request) error
|
||||
EnableCookie bool
|
||||
Gzip bool
|
||||
DumpBody bool
|
||||
Retries int // if set to -1 means will retry forever
|
||||
}
|
||||
|
||||
// BeegoHTTPRequest provides more useful methods for requesting one url than http.Request.
|
||||
@ -188,6 +190,15 @@ func (b *BeegoHTTPRequest) Debug(isdebug bool) *BeegoHTTPRequest {
|
||||
return b
|
||||
}
|
||||
|
||||
// Retries sets Retries times.
|
||||
// default is 0 means no retried.
|
||||
// -1 means retried forever.
|
||||
// others means retried times.
|
||||
func (b *BeegoHTTPRequest) Retries(times int) *BeegoHTTPRequest {
|
||||
b.setting.Retries = times
|
||||
return b
|
||||
}
|
||||
|
||||
// DumpBody setting whether need to Dump the Body.
|
||||
func (b *BeegoHTTPRequest) DumpBody(isdump bool) *BeegoHTTPRequest {
|
||||
b.setting.DumpBody = isdump
|
||||
@ -265,6 +276,15 @@ func (b *BeegoHTTPRequest) SetProxy(proxy func(*http.Request) (*url.URL, error))
|
||||
return b
|
||||
}
|
||||
|
||||
// SetCheckRedirect specifies the policy for handling redirects.
|
||||
//
|
||||
// If CheckRedirect is nil, the Client uses its default policy,
|
||||
// which is to stop after 10 consecutive requests.
|
||||
func (b *BeegoHTTPRequest) SetCheckRedirect(redirect func(req *http.Request, via []*http.Request) error) *BeegoHTTPRequest {
|
||||
b.setting.CheckRedirect = redirect
|
||||
return b
|
||||
}
|
||||
|
||||
// Param adds query param in to request.
|
||||
// params build query string as ?key1=value1&key2=value2...
|
||||
func (b *BeegoHTTPRequest) Param(key, value string) *BeegoHTTPRequest {
|
||||
@ -315,7 +335,7 @@ func (b *BeegoHTTPRequest) JSONBody(obj interface{}) (*BeegoHTTPRequest, error)
|
||||
func (b *BeegoHTTPRequest) buildURL(paramBody string) {
|
||||
// build GET url with query string
|
||||
if b.req.Method == "GET" && len(paramBody) > 0 {
|
||||
if strings.Index(b.url, "?") != -1 {
|
||||
if strings.Contains(b.url, "?") {
|
||||
b.url += "&" + paramBody
|
||||
} else {
|
||||
b.url = b.url + "?" + paramBody
|
||||
@ -324,7 +344,7 @@ func (b *BeegoHTTPRequest) buildURL(paramBody string) {
|
||||
}
|
||||
|
||||
// build POST/PUT/PATCH url and body
|
||||
if (b.req.Method == "POST" || b.req.Method == "PUT" || b.req.Method == "PATCH") && b.req.Body == nil {
|
||||
if (b.req.Method == "POST" || b.req.Method == "PUT" || b.req.Method == "PATCH" || b.req.Method == "DELETE") && b.req.Body == nil {
|
||||
// with files
|
||||
if len(b.files) > 0 {
|
||||
pr, pw := io.Pipe()
|
||||
@ -380,7 +400,7 @@ func (b *BeegoHTTPRequest) getResponse() (*http.Response, error) {
|
||||
}
|
||||
|
||||
// DoRequest will do the client.Do
|
||||
func (b *BeegoHTTPRequest) DoRequest() (*http.Response, error) {
|
||||
func (b *BeegoHTTPRequest) DoRequest() (resp *http.Response, err error) {
|
||||
var paramBody string
|
||||
if len(b.params) > 0 {
|
||||
var buf bytes.Buffer
|
||||
@ -446,6 +466,10 @@ func (b *BeegoHTTPRequest) DoRequest() (*http.Response, error) {
|
||||
b.req.Header.Set("User-Agent", b.setting.UserAgent)
|
||||
}
|
||||
|
||||
if b.setting.CheckRedirect != nil {
|
||||
client.CheckRedirect = b.setting.CheckRedirect
|
||||
}
|
||||
|
||||
if b.setting.ShowDebug {
|
||||
dump, err := httputil.DumpRequest(b.req, b.setting.DumpBody)
|
||||
if err != nil {
|
||||
@ -453,7 +477,16 @@ func (b *BeegoHTTPRequest) DoRequest() (*http.Response, error) {
|
||||
}
|
||||
b.dump = dump
|
||||
}
|
||||
return client.Do(b.req)
|
||||
// retries default value is 0, it will run once.
|
||||
// retries equal to -1, it will run forever until success
|
||||
// retries is setted, it will retries fixed times.
|
||||
for i := 0; b.setting.Retries == -1 || i <= b.setting.Retries; i++ {
|
||||
resp, err = client.Do(b.req)
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
return resp, err
|
||||
}
|
||||
|
||||
// String returns the body string in response.
|
||||
|
@ -102,6 +102,14 @@ func TestSimpleDelete(t *testing.T) {
|
||||
t.Log(str)
|
||||
}
|
||||
|
||||
func TestSimpleDeleteParam(t *testing.T) {
|
||||
str, err := Delete("http://httpbin.org/delete").Param("key", "val").String()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log(str)
|
||||
}
|
||||
|
||||
func TestWithCookie(t *testing.T) {
|
||||
v := "smallfish"
|
||||
str, err := Get("http://httpbin.org/cookies/set?k1=" + v).SetEnableCookie(true).String()
|
||||
|
192
logs/alils/alils.go
Normal file
192
logs/alils/alils.go
Normal file
@ -0,0 +1,192 @@
|
||||
package alils
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/astaxie/beego/logs"
|
||||
"github.com/gogo/protobuf/proto"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
CacheSize int = 64
|
||||
Delimiter string = "##"
|
||||
)
|
||||
|
||||
type AliLSConfig struct {
|
||||
Project string `json:"project"`
|
||||
Endpoint string `json:"endpoint"`
|
||||
KeyID string `json:"key_id"`
|
||||
KeySecret string `json:"key_secret"`
|
||||
LogStore string `json:"log_store"`
|
||||
Topics []string `json:"topics"`
|
||||
Source string `json:"source"`
|
||||
Level int `json:"level"`
|
||||
FlushWhen int `json:"flush_when"`
|
||||
}
|
||||
|
||||
// aliLSWriter implements LoggerInterface.
|
||||
// it writes messages in keep-live tcp connection.
|
||||
type aliLSWriter struct {
|
||||
store *LogStore
|
||||
group []*LogGroup
|
||||
withMap bool
|
||||
groupMap map[string]*LogGroup
|
||||
lock *sync.Mutex
|
||||
AliLSConfig
|
||||
}
|
||||
|
||||
// 创建提供Logger接口的日志服务
|
||||
func NewAliLS() logs.Logger {
|
||||
alils := new(aliLSWriter)
|
||||
alils.Level = logs.LevelTrace
|
||||
return alils
|
||||
}
|
||||
|
||||
// 读取配置
|
||||
// 初始化必要的数据结构
|
||||
func (c *aliLSWriter) Init(jsonConfig string) (err error) {
|
||||
|
||||
json.Unmarshal([]byte(jsonConfig), c)
|
||||
|
||||
if c.FlushWhen > CacheSize {
|
||||
c.FlushWhen = CacheSize
|
||||
}
|
||||
|
||||
// 初始化Project
|
||||
prj := &LogProject{
|
||||
Name: c.Project,
|
||||
Endpoint: c.Endpoint,
|
||||
AccessKeyId: c.KeyID,
|
||||
AccessKeySecret: c.KeySecret,
|
||||
}
|
||||
|
||||
// 获取logstore
|
||||
c.store, err = prj.GetLogStore(c.LogStore)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 创建默认Log Group
|
||||
c.group = append(c.group, &LogGroup{
|
||||
Topic: proto.String(""),
|
||||
Source: proto.String(c.Source),
|
||||
Logs: make([]*Log, 0, c.FlushWhen),
|
||||
})
|
||||
|
||||
// 创建其它Log Group
|
||||
c.groupMap = make(map[string]*LogGroup)
|
||||
for _, topic := range c.Topics {
|
||||
|
||||
lg := &LogGroup{
|
||||
Topic: proto.String(topic),
|
||||
Source: proto.String(c.Source),
|
||||
Logs: make([]*Log, 0, c.FlushWhen),
|
||||
}
|
||||
|
||||
c.group = append(c.group, lg)
|
||||
c.groupMap[topic] = lg
|
||||
}
|
||||
|
||||
if len(c.group) == 1 {
|
||||
c.withMap = false
|
||||
} else {
|
||||
c.withMap = true
|
||||
}
|
||||
|
||||
c.lock = &sync.Mutex{}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteMsg write message in connection.
|
||||
// if connection is down, try to re-connect.
|
||||
func (c *aliLSWriter) WriteMsg(when time.Time, msg string, level int) (err error) {
|
||||
|
||||
if level > c.Level {
|
||||
return nil
|
||||
}
|
||||
|
||||
var topic string
|
||||
var content string
|
||||
var lg *LogGroup
|
||||
if c.withMap {
|
||||
|
||||
// 解析出Topic,并匹配LogGroup
|
||||
strs := strings.SplitN(msg, Delimiter, 2)
|
||||
if len(strs) == 2 {
|
||||
pos := strings.LastIndex(strs[0], " ")
|
||||
topic = strs[0][pos+1 : len(strs[0])]
|
||||
content = strs[0][0:pos] + strs[1]
|
||||
lg = c.groupMap[topic]
|
||||
}
|
||||
|
||||
// 默认发到空Topic
|
||||
if lg == nil {
|
||||
topic = ""
|
||||
content = msg
|
||||
lg = c.group[0]
|
||||
}
|
||||
} else {
|
||||
topic = ""
|
||||
content = msg
|
||||
lg = c.group[0]
|
||||
}
|
||||
|
||||
// 生成日志
|
||||
c1 := &Log_Content{
|
||||
Key: proto.String("msg"),
|
||||
Value: proto.String(content),
|
||||
}
|
||||
|
||||
l := &Log{
|
||||
Time: proto.Uint32(uint32(when.Unix())), // 填写日志时间
|
||||
Contents: []*Log_Content{
|
||||
c1,
|
||||
},
|
||||
}
|
||||
|
||||
c.lock.Lock()
|
||||
lg.Logs = append(lg.Logs, l)
|
||||
c.lock.Unlock()
|
||||
|
||||
// 满足条件则Flush
|
||||
if len(lg.Logs) >= c.FlushWhen {
|
||||
c.flush(lg)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Flush implementing method. empty.
|
||||
func (c *aliLSWriter) Flush() {
|
||||
|
||||
// flush所有group
|
||||
for _, lg := range c.group {
|
||||
c.flush(lg)
|
||||
}
|
||||
}
|
||||
|
||||
// Destroy destroy connection writer and close tcp listener.
|
||||
func (c *aliLSWriter) Destroy() {
|
||||
}
|
||||
|
||||
func (c *aliLSWriter) flush(lg *LogGroup) {
|
||||
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
|
||||
// 把以上的LogGroup推送到SLS服务器,
|
||||
// SLS服务器会根据该logstore的shard个数自动进行负载均衡。
|
||||
err := c.store.PutLogs(lg)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
lg.Logs = make([]*Log, 0, c.FlushWhen)
|
||||
}
|
||||
|
||||
func init() {
|
||||
logs.Register(logs.AdapterAliLS, NewAliLS)
|
||||
}
|
13
logs/alils/config.go
Executable file
13
logs/alils/config.go
Executable file
@ -0,0 +1,13 @@
|
||||
package alils
|
||||
|
||||
const (
|
||||
version = "0.5.0" // SDK version
|
||||
signatureMethod = "hmac-sha1" // Signature method
|
||||
|
||||
// OffsetNewest stands for the log head offset, i.e. the offset that will be
|
||||
// assigned to the next message that will be produced to the shard.
|
||||
OffsetNewest = "end"
|
||||
// OffsetOldest stands for the oldest offset available on the logstore for a
|
||||
// shard.
|
||||
OffsetOldest = "begin"
|
||||
)
|
984
logs/alils/log.pb.go
Executable file
984
logs/alils/log.pb.go
Executable file
@ -0,0 +1,984 @@
|
||||
package alils
|
||||
|
||||
import "github.com/gogo/protobuf/proto"
|
||||
import "fmt"
|
||||
import "math"
|
||||
|
||||
// discarding unused import gogoproto "."
|
||||
|
||||
import github_com_gogo_protobuf_proto "github.com/gogo/protobuf/proto"
|
||||
|
||||
import "io"
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
var _ = fmt.Errorf
|
||||
var _ = math.Inf
|
||||
|
||||
type Log struct {
|
||||
Time *uint32 `protobuf:"varint,1,req,name=Time" json:"Time,omitempty"`
|
||||
Contents []*Log_Content `protobuf:"bytes,2,rep,name=Contents" json:"Contents,omitempty"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
}
|
||||
|
||||
func (m *Log) Reset() { *m = Log{} }
|
||||
func (m *Log) String() string { return proto.CompactTextString(m) }
|
||||
func (*Log) ProtoMessage() {}
|
||||
|
||||
func (m *Log) GetTime() uint32 {
|
||||
if m != nil && m.Time != nil {
|
||||
return *m.Time
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (m *Log) GetContents() []*Log_Content {
|
||||
if m != nil {
|
||||
return m.Contents
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type Log_Content struct {
|
||||
Key *string `protobuf:"bytes,1,req,name=Key" json:"Key,omitempty"`
|
||||
Value *string `protobuf:"bytes,2,req,name=Value" json:"Value,omitempty"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
}
|
||||
|
||||
func (m *Log_Content) Reset() { *m = Log_Content{} }
|
||||
func (m *Log_Content) String() string { return proto.CompactTextString(m) }
|
||||
func (*Log_Content) ProtoMessage() {}
|
||||
|
||||
func (m *Log_Content) GetKey() string {
|
||||
if m != nil && m.Key != nil {
|
||||
return *m.Key
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *Log_Content) GetValue() string {
|
||||
if m != nil && m.Value != nil {
|
||||
return *m.Value
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type LogGroup struct {
|
||||
Logs []*Log `protobuf:"bytes,1,rep,name=Logs" json:"Logs,omitempty"`
|
||||
Reserved *string `protobuf:"bytes,2,opt,name=Reserved" json:"Reserved,omitempty"`
|
||||
Topic *string `protobuf:"bytes,3,opt,name=Topic" json:"Topic,omitempty"`
|
||||
Source *string `protobuf:"bytes,4,opt,name=Source" json:"Source,omitempty"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
}
|
||||
|
||||
func (m *LogGroup) Reset() { *m = LogGroup{} }
|
||||
func (m *LogGroup) String() string { return proto.CompactTextString(m) }
|
||||
func (*LogGroup) ProtoMessage() {}
|
||||
|
||||
func (m *LogGroup) GetLogs() []*Log {
|
||||
if m != nil {
|
||||
return m.Logs
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *LogGroup) GetReserved() string {
|
||||
if m != nil && m.Reserved != nil {
|
||||
return *m.Reserved
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *LogGroup) GetTopic() string {
|
||||
if m != nil && m.Topic != nil {
|
||||
return *m.Topic
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *LogGroup) GetSource() string {
|
||||
if m != nil && m.Source != nil {
|
||||
return *m.Source
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type LogGroupList struct {
|
||||
LogGroups []*LogGroup `protobuf:"bytes,1,rep,name=logGroups" json:"logGroups,omitempty"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
}
|
||||
|
||||
func (m *LogGroupList) Reset() { *m = LogGroupList{} }
|
||||
func (m *LogGroupList) String() string { return proto.CompactTextString(m) }
|
||||
func (*LogGroupList) ProtoMessage() {}
|
||||
|
||||
func (m *LogGroupList) GetLogGroups() []*LogGroup {
|
||||
if m != nil {
|
||||
return m.LogGroups
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Log) Marshal() (data []byte, err error) {
|
||||
size := m.Size()
|
||||
data = make([]byte, size)
|
||||
n, err := m.MarshalTo(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return data[:n], nil
|
||||
}
|
||||
|
||||
func (m *Log) MarshalTo(data []byte) (int, error) {
|
||||
var i int
|
||||
_ = i
|
||||
var l int
|
||||
_ = l
|
||||
if m.Time == nil {
|
||||
return 0, github_com_gogo_protobuf_proto.NewRequiredNotSetError("Time")
|
||||
} else {
|
||||
data[i] = 0x8
|
||||
i++
|
||||
i = encodeVarintLog(data, i, uint64(*m.Time))
|
||||
}
|
||||
if len(m.Contents) > 0 {
|
||||
for _, msg := range m.Contents {
|
||||
data[i] = 0x12
|
||||
i++
|
||||
i = encodeVarintLog(data, i, uint64(msg.Size()))
|
||||
n, err := msg.MarshalTo(data[i:])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
i += n
|
||||
}
|
||||
}
|
||||
if m.XXX_unrecognized != nil {
|
||||
i += copy(data[i:], m.XXX_unrecognized)
|
||||
}
|
||||
return i, nil
|
||||
}
|
||||
|
||||
func (m *Log_Content) Marshal() (data []byte, err error) {
|
||||
size := m.Size()
|
||||
data = make([]byte, size)
|
||||
n, err := m.MarshalTo(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return data[:n], nil
|
||||
}
|
||||
|
||||
func (m *Log_Content) MarshalTo(data []byte) (int, error) {
|
||||
var i int
|
||||
_ = i
|
||||
var l int
|
||||
_ = l
|
||||
if m.Key == nil {
|
||||
return 0, github_com_gogo_protobuf_proto.NewRequiredNotSetError("Key")
|
||||
} else {
|
||||
data[i] = 0xa
|
||||
i++
|
||||
i = encodeVarintLog(data, i, uint64(len(*m.Key)))
|
||||
i += copy(data[i:], *m.Key)
|
||||
}
|
||||
if m.Value == nil {
|
||||
return 0, github_com_gogo_protobuf_proto.NewRequiredNotSetError("Value")
|
||||
} else {
|
||||
data[i] = 0x12
|
||||
i++
|
||||
i = encodeVarintLog(data, i, uint64(len(*m.Value)))
|
||||
i += copy(data[i:], *m.Value)
|
||||
}
|
||||
if m.XXX_unrecognized != nil {
|
||||
i += copy(data[i:], m.XXX_unrecognized)
|
||||
}
|
||||
return i, nil
|
||||
}
|
||||
|
||||
func (m *LogGroup) Marshal() (data []byte, err error) {
|
||||
size := m.Size()
|
||||
data = make([]byte, size)
|
||||
n, err := m.MarshalTo(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return data[:n], nil
|
||||
}
|
||||
|
||||
func (m *LogGroup) MarshalTo(data []byte) (int, error) {
|
||||
var i int
|
||||
_ = i
|
||||
var l int
|
||||
_ = l
|
||||
if len(m.Logs) > 0 {
|
||||
for _, msg := range m.Logs {
|
||||
data[i] = 0xa
|
||||
i++
|
||||
i = encodeVarintLog(data, i, uint64(msg.Size()))
|
||||
n, err := msg.MarshalTo(data[i:])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
i += n
|
||||
}
|
||||
}
|
||||
if m.Reserved != nil {
|
||||
data[i] = 0x12
|
||||
i++
|
||||
i = encodeVarintLog(data, i, uint64(len(*m.Reserved)))
|
||||
i += copy(data[i:], *m.Reserved)
|
||||
}
|
||||
if m.Topic != nil {
|
||||
data[i] = 0x1a
|
||||
i++
|
||||
i = encodeVarintLog(data, i, uint64(len(*m.Topic)))
|
||||
i += copy(data[i:], *m.Topic)
|
||||
}
|
||||
if m.Source != nil {
|
||||
data[i] = 0x22
|
||||
i++
|
||||
i = encodeVarintLog(data, i, uint64(len(*m.Source)))
|
||||
i += copy(data[i:], *m.Source)
|
||||
}
|
||||
if m.XXX_unrecognized != nil {
|
||||
i += copy(data[i:], m.XXX_unrecognized)
|
||||
}
|
||||
return i, nil
|
||||
}
|
||||
|
||||
func (m *LogGroupList) Marshal() (data []byte, err error) {
|
||||
size := m.Size()
|
||||
data = make([]byte, size)
|
||||
n, err := m.MarshalTo(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return data[:n], nil
|
||||
}
|
||||
|
||||
func (m *LogGroupList) MarshalTo(data []byte) (int, error) {
|
||||
var i int
|
||||
_ = i
|
||||
var l int
|
||||
_ = l
|
||||
if len(m.LogGroups) > 0 {
|
||||
for _, msg := range m.LogGroups {
|
||||
data[i] = 0xa
|
||||
i++
|
||||
i = encodeVarintLog(data, i, uint64(msg.Size()))
|
||||
n, err := msg.MarshalTo(data[i:])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
i += n
|
||||
}
|
||||
}
|
||||
if m.XXX_unrecognized != nil {
|
||||
i += copy(data[i:], m.XXX_unrecognized)
|
||||
}
|
||||
return i, nil
|
||||
}
|
||||
|
||||
func encodeFixed64Log(data []byte, offset int, v uint64) int {
|
||||
data[offset] = uint8(v)
|
||||
data[offset+1] = uint8(v >> 8)
|
||||
data[offset+2] = uint8(v >> 16)
|
||||
data[offset+3] = uint8(v >> 24)
|
||||
data[offset+4] = uint8(v >> 32)
|
||||
data[offset+5] = uint8(v >> 40)
|
||||
data[offset+6] = uint8(v >> 48)
|
||||
data[offset+7] = uint8(v >> 56)
|
||||
return offset + 8
|
||||
}
|
||||
func encodeFixed32Log(data []byte, offset int, v uint32) int {
|
||||
data[offset] = uint8(v)
|
||||
data[offset+1] = uint8(v >> 8)
|
||||
data[offset+2] = uint8(v >> 16)
|
||||
data[offset+3] = uint8(v >> 24)
|
||||
return offset + 4
|
||||
}
|
||||
func encodeVarintLog(data []byte, offset int, v uint64) int {
|
||||
for v >= 1<<7 {
|
||||
data[offset] = uint8(v&0x7f | 0x80)
|
||||
v >>= 7
|
||||
offset++
|
||||
}
|
||||
data[offset] = uint8(v)
|
||||
return offset + 1
|
||||
}
|
||||
func (m *Log) Size() (n int) {
|
||||
var l int
|
||||
_ = l
|
||||
if m.Time != nil {
|
||||
n += 1 + sovLog(uint64(*m.Time))
|
||||
}
|
||||
if len(m.Contents) > 0 {
|
||||
for _, e := range m.Contents {
|
||||
l = e.Size()
|
||||
n += 1 + l + sovLog(uint64(l))
|
||||
}
|
||||
}
|
||||
if m.XXX_unrecognized != nil {
|
||||
n += len(m.XXX_unrecognized)
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func (m *Log_Content) Size() (n int) {
|
||||
var l int
|
||||
_ = l
|
||||
if m.Key != nil {
|
||||
l = len(*m.Key)
|
||||
n += 1 + l + sovLog(uint64(l))
|
||||
}
|
||||
if m.Value != nil {
|
||||
l = len(*m.Value)
|
||||
n += 1 + l + sovLog(uint64(l))
|
||||
}
|
||||
if m.XXX_unrecognized != nil {
|
||||
n += len(m.XXX_unrecognized)
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func (m *LogGroup) Size() (n int) {
|
||||
var l int
|
||||
_ = l
|
||||
if len(m.Logs) > 0 {
|
||||
for _, e := range m.Logs {
|
||||
l = e.Size()
|
||||
n += 1 + l + sovLog(uint64(l))
|
||||
}
|
||||
}
|
||||
if m.Reserved != nil {
|
||||
l = len(*m.Reserved)
|
||||
n += 1 + l + sovLog(uint64(l))
|
||||
}
|
||||
if m.Topic != nil {
|
||||
l = len(*m.Topic)
|
||||
n += 1 + l + sovLog(uint64(l))
|
||||
}
|
||||
if m.Source != nil {
|
||||
l = len(*m.Source)
|
||||
n += 1 + l + sovLog(uint64(l))
|
||||
}
|
||||
if m.XXX_unrecognized != nil {
|
||||
n += len(m.XXX_unrecognized)
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func (m *LogGroupList) Size() (n int) {
|
||||
var l int
|
||||
_ = l
|
||||
if len(m.LogGroups) > 0 {
|
||||
for _, e := range m.LogGroups {
|
||||
l = e.Size()
|
||||
n += 1 + l + sovLog(uint64(l))
|
||||
}
|
||||
}
|
||||
if m.XXX_unrecognized != nil {
|
||||
n += len(m.XXX_unrecognized)
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func sovLog(x uint64) (n int) {
|
||||
for {
|
||||
n++
|
||||
x >>= 7
|
||||
if x == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return n
|
||||
}
|
||||
func sozLog(x uint64) (n int) {
|
||||
return sovLog((x << 1) ^ (x >> 63))
|
||||
}
|
||||
func (m *Log) Unmarshal(data []byte) error {
|
||||
var hasFields [1]uint64
|
||||
l := len(data)
|
||||
iNdEx := 0
|
||||
for iNdEx < l {
|
||||
preIndex := iNdEx
|
||||
var wire uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowLog
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := data[iNdEx]
|
||||
iNdEx++
|
||||
wire |= (uint64(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
fieldNum := int32(wire >> 3)
|
||||
wireType := int(wire & 0x7)
|
||||
if wireType == 4 {
|
||||
return fmt.Errorf("proto: Log: wiretype end group for non-group")
|
||||
}
|
||||
if fieldNum <= 0 {
|
||||
return fmt.Errorf("proto: Log: illegal tag %d (wire type %d)", fieldNum, wire)
|
||||
}
|
||||
switch fieldNum {
|
||||
case 1:
|
||||
if wireType != 0 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Time", wireType)
|
||||
}
|
||||
var v uint32
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowLog
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := data[iNdEx]
|
||||
iNdEx++
|
||||
v |= (uint32(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
m.Time = &v
|
||||
hasFields[0] |= uint64(0x00000001)
|
||||
case 2:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Contents", wireType)
|
||||
}
|
||||
var msglen int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowLog
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := data[iNdEx]
|
||||
iNdEx++
|
||||
msglen |= (int(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if msglen < 0 {
|
||||
return ErrInvalidLengthLog
|
||||
}
|
||||
postIndex := iNdEx + msglen
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.Contents = append(m.Contents, &Log_Content{})
|
||||
if err := m.Contents[len(m.Contents)-1].Unmarshal(data[iNdEx:postIndex]); err != nil {
|
||||
return err
|
||||
}
|
||||
iNdEx = postIndex
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skipLog(data[iNdEx:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthLog
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.XXX_unrecognized = append(m.XXX_unrecognized, data[iNdEx:iNdEx+skippy]...)
|
||||
iNdEx += skippy
|
||||
}
|
||||
}
|
||||
if hasFields[0]&uint64(0x00000001) == 0 {
|
||||
return github_com_gogo_protobuf_proto.NewRequiredNotSetError("Time")
|
||||
}
|
||||
|
||||
if iNdEx > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (m *Log_Content) Unmarshal(data []byte) error {
|
||||
var hasFields [1]uint64
|
||||
l := len(data)
|
||||
iNdEx := 0
|
||||
for iNdEx < l {
|
||||
preIndex := iNdEx
|
||||
var wire uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowLog
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := data[iNdEx]
|
||||
iNdEx++
|
||||
wire |= (uint64(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
fieldNum := int32(wire >> 3)
|
||||
wireType := int(wire & 0x7)
|
||||
if wireType == 4 {
|
||||
return fmt.Errorf("proto: Content: wiretype end group for non-group")
|
||||
}
|
||||
if fieldNum <= 0 {
|
||||
return fmt.Errorf("proto: Content: illegal tag %d (wire type %d)", fieldNum, wire)
|
||||
}
|
||||
switch fieldNum {
|
||||
case 1:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Key", wireType)
|
||||
}
|
||||
var stringLen uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowLog
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := data[iNdEx]
|
||||
iNdEx++
|
||||
stringLen |= (uint64(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
intStringLen := int(stringLen)
|
||||
if intStringLen < 0 {
|
||||
return ErrInvalidLengthLog
|
||||
}
|
||||
postIndex := iNdEx + intStringLen
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
s := string(data[iNdEx:postIndex])
|
||||
m.Key = &s
|
||||
iNdEx = postIndex
|
||||
hasFields[0] |= uint64(0x00000001)
|
||||
case 2:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Value", wireType)
|
||||
}
|
||||
var stringLen uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowLog
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := data[iNdEx]
|
||||
iNdEx++
|
||||
stringLen |= (uint64(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
intStringLen := int(stringLen)
|
||||
if intStringLen < 0 {
|
||||
return ErrInvalidLengthLog
|
||||
}
|
||||
postIndex := iNdEx + intStringLen
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
s := string(data[iNdEx:postIndex])
|
||||
m.Value = &s
|
||||
iNdEx = postIndex
|
||||
hasFields[0] |= uint64(0x00000002)
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skipLog(data[iNdEx:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthLog
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.XXX_unrecognized = append(m.XXX_unrecognized, data[iNdEx:iNdEx+skippy]...)
|
||||
iNdEx += skippy
|
||||
}
|
||||
}
|
||||
if hasFields[0]&uint64(0x00000001) == 0 {
|
||||
return github_com_gogo_protobuf_proto.NewRequiredNotSetError("Key")
|
||||
}
|
||||
if hasFields[0]&uint64(0x00000002) == 0 {
|
||||
return github_com_gogo_protobuf_proto.NewRequiredNotSetError("Value")
|
||||
}
|
||||
|
||||
if iNdEx > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (m *LogGroup) Unmarshal(data []byte) error {
|
||||
l := len(data)
|
||||
iNdEx := 0
|
||||
for iNdEx < l {
|
||||
preIndex := iNdEx
|
||||
var wire uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowLog
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := data[iNdEx]
|
||||
iNdEx++
|
||||
wire |= (uint64(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
fieldNum := int32(wire >> 3)
|
||||
wireType := int(wire & 0x7)
|
||||
if wireType == 4 {
|
||||
return fmt.Errorf("proto: LogGroup: wiretype end group for non-group")
|
||||
}
|
||||
if fieldNum <= 0 {
|
||||
return fmt.Errorf("proto: LogGroup: illegal tag %d (wire type %d)", fieldNum, wire)
|
||||
}
|
||||
switch fieldNum {
|
||||
case 1:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Logs", wireType)
|
||||
}
|
||||
var msglen int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowLog
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := data[iNdEx]
|
||||
iNdEx++
|
||||
msglen |= (int(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if msglen < 0 {
|
||||
return ErrInvalidLengthLog
|
||||
}
|
||||
postIndex := iNdEx + msglen
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.Logs = append(m.Logs, &Log{})
|
||||
if err := m.Logs[len(m.Logs)-1].Unmarshal(data[iNdEx:postIndex]); err != nil {
|
||||
return err
|
||||
}
|
||||
iNdEx = postIndex
|
||||
case 2:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Reserved", wireType)
|
||||
}
|
||||
var stringLen uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowLog
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := data[iNdEx]
|
||||
iNdEx++
|
||||
stringLen |= (uint64(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
intStringLen := int(stringLen)
|
||||
if intStringLen < 0 {
|
||||
return ErrInvalidLengthLog
|
||||
}
|
||||
postIndex := iNdEx + intStringLen
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
s := string(data[iNdEx:postIndex])
|
||||
m.Reserved = &s
|
||||
iNdEx = postIndex
|
||||
case 3:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Topic", wireType)
|
||||
}
|
||||
var stringLen uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowLog
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := data[iNdEx]
|
||||
iNdEx++
|
||||
stringLen |= (uint64(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
intStringLen := int(stringLen)
|
||||
if intStringLen < 0 {
|
||||
return ErrInvalidLengthLog
|
||||
}
|
||||
postIndex := iNdEx + intStringLen
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
s := string(data[iNdEx:postIndex])
|
||||
m.Topic = &s
|
||||
iNdEx = postIndex
|
||||
case 4:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Source", wireType)
|
||||
}
|
||||
var stringLen uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowLog
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := data[iNdEx]
|
||||
iNdEx++
|
||||
stringLen |= (uint64(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
intStringLen := int(stringLen)
|
||||
if intStringLen < 0 {
|
||||
return ErrInvalidLengthLog
|
||||
}
|
||||
postIndex := iNdEx + intStringLen
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
s := string(data[iNdEx:postIndex])
|
||||
m.Source = &s
|
||||
iNdEx = postIndex
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skipLog(data[iNdEx:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthLog
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.XXX_unrecognized = append(m.XXX_unrecognized, data[iNdEx:iNdEx+skippy]...)
|
||||
iNdEx += skippy
|
||||
}
|
||||
}
|
||||
|
||||
if iNdEx > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (m *LogGroupList) Unmarshal(data []byte) error {
|
||||
l := len(data)
|
||||
iNdEx := 0
|
||||
for iNdEx < l {
|
||||
preIndex := iNdEx
|
||||
var wire uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowLog
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := data[iNdEx]
|
||||
iNdEx++
|
||||
wire |= (uint64(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
fieldNum := int32(wire >> 3)
|
||||
wireType := int(wire & 0x7)
|
||||
if wireType == 4 {
|
||||
return fmt.Errorf("proto: LogGroupList: wiretype end group for non-group")
|
||||
}
|
||||
if fieldNum <= 0 {
|
||||
return fmt.Errorf("proto: LogGroupList: illegal tag %d (wire type %d)", fieldNum, wire)
|
||||
}
|
||||
switch fieldNum {
|
||||
case 1:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field LogGroups", wireType)
|
||||
}
|
||||
var msglen int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowLog
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := data[iNdEx]
|
||||
iNdEx++
|
||||
msglen |= (int(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if msglen < 0 {
|
||||
return ErrInvalidLengthLog
|
||||
}
|
||||
postIndex := iNdEx + msglen
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.LogGroups = append(m.LogGroups, &LogGroup{})
|
||||
if err := m.LogGroups[len(m.LogGroups)-1].Unmarshal(data[iNdEx:postIndex]); err != nil {
|
||||
return err
|
||||
}
|
||||
iNdEx = postIndex
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skipLog(data[iNdEx:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthLog
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.XXX_unrecognized = append(m.XXX_unrecognized, data[iNdEx:iNdEx+skippy]...)
|
||||
iNdEx += skippy
|
||||
}
|
||||
}
|
||||
|
||||
if iNdEx > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func skipLog(data []byte) (n int, err error) {
|
||||
l := len(data)
|
||||
iNdEx := 0
|
||||
for iNdEx < l {
|
||||
var wire uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return 0, ErrIntOverflowLog
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return 0, io.ErrUnexpectedEOF
|
||||
}
|
||||
b := data[iNdEx]
|
||||
iNdEx++
|
||||
wire |= (uint64(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
wireType := int(wire & 0x7)
|
||||
switch wireType {
|
||||
case 0:
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return 0, ErrIntOverflowLog
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return 0, io.ErrUnexpectedEOF
|
||||
}
|
||||
iNdEx++
|
||||
if data[iNdEx-1] < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return iNdEx, nil
|
||||
case 1:
|
||||
iNdEx += 8
|
||||
return iNdEx, nil
|
||||
case 2:
|
||||
var length int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return 0, ErrIntOverflowLog
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return 0, io.ErrUnexpectedEOF
|
||||
}
|
||||
b := data[iNdEx]
|
||||
iNdEx++
|
||||
length |= (int(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
iNdEx += length
|
||||
if length < 0 {
|
||||
return 0, ErrInvalidLengthLog
|
||||
}
|
||||
return iNdEx, nil
|
||||
case 3:
|
||||
for {
|
||||
var innerWire uint64
|
||||
var start int = iNdEx
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return 0, ErrIntOverflowLog
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return 0, io.ErrUnexpectedEOF
|
||||
}
|
||||
b := data[iNdEx]
|
||||
iNdEx++
|
||||
innerWire |= (uint64(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
innerWireType := int(innerWire & 0x7)
|
||||
if innerWireType == 4 {
|
||||
break
|
||||
}
|
||||
next, err := skipLog(data[start:])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
iNdEx = start + next
|
||||
}
|
||||
return iNdEx, nil
|
||||
case 4:
|
||||
return iNdEx, nil
|
||||
case 5:
|
||||
iNdEx += 4
|
||||
return iNdEx, nil
|
||||
default:
|
||||
return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
|
||||
}
|
||||
}
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
var (
|
||||
ErrInvalidLengthLog = fmt.Errorf("proto: negative length found during unmarshaling")
|
||||
ErrIntOverflowLog = fmt.Errorf("proto: integer overflow")
|
||||
)
|
39
logs/alils/log_config.go
Executable file
39
logs/alils/log_config.go
Executable file
@ -0,0 +1,39 @@
|
||||
package alils
|
||||
|
||||
type InputDetail struct {
|
||||
LogType string `json:"logType"`
|
||||
LogPath string `json:"logPath"`
|
||||
FilePattern string `json:"filePattern"`
|
||||
LocalStorage bool `json:"localStorage"`
|
||||
TimeFormat string `json:"timeFormat"`
|
||||
LogBeginRegex string `json:"logBeginRegex"`
|
||||
Regex string `json:"regex"`
|
||||
Keys []string `json:"key"`
|
||||
FilterKeys []string `json:"filterKey"`
|
||||
FilterRegex []string `json:"filterRegex"`
|
||||
TopicFormat string `json:"topicFormat"`
|
||||
}
|
||||
|
||||
type OutputDetail struct {
|
||||
Endpoint string `json:"endpoint"`
|
||||
LogStoreName string `json:"logstoreName"`
|
||||
}
|
||||
|
||||
type LogConfig struct {
|
||||
Name string `json:"configName"`
|
||||
InputType string `json:"inputType"`
|
||||
InputDetail InputDetail `json:"inputDetail"`
|
||||
OutputType string `json:"outputType"`
|
||||
OutputDetail OutputDetail `json:"outputDetail"`
|
||||
|
||||
CreateTime uint32
|
||||
LastModifyTime uint32
|
||||
|
||||
project *LogProject
|
||||
}
|
||||
|
||||
// GetAppliedMachineGroup returns applied machine group of this config.
|
||||
func (c *LogConfig) GetAppliedMachineGroup(confName string) (groupNames []string, err error) {
|
||||
groupNames, err = c.project.GetAppliedMachineGroups(c.Name)
|
||||
return
|
||||
}
|
818
logs/alils/log_project.go
Executable file
818
logs/alils/log_project.go
Executable file
@ -0,0 +1,818 @@
|
||||
/*
|
||||
Package sls implements the SDK(v0.5.0) of Simple Log Service(abbr. SLS).
|
||||
|
||||
For more description about SLS, please read this article:
|
||||
http://gitlab.alibaba-inc.com/sls/doc.
|
||||
*/
|
||||
package alils
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
)
|
||||
|
||||
// Error message in SLS HTTP response.
|
||||
type errorMessage struct {
|
||||
Code string `json:"errorCode"`
|
||||
Message string `json:"errorMessage"`
|
||||
}
|
||||
|
||||
type LogProject struct {
|
||||
Name string // Project name
|
||||
Endpoint string // IP or hostname of SLS endpoint
|
||||
AccessKeyId string
|
||||
AccessKeySecret string
|
||||
}
|
||||
|
||||
// NewLogProject creates a new SLS project.
|
||||
func NewLogProject(name, endpoint, accessKeyId, accessKeySecret string) (p *LogProject, err error) {
|
||||
p = &LogProject{
|
||||
Name: name,
|
||||
Endpoint: endpoint,
|
||||
AccessKeyId: accessKeyId,
|
||||
AccessKeySecret: accessKeySecret,
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// ListLogStore returns all logstore names of project p.
|
||||
func (p *LogProject) ListLogStore() (storeNames []string, err error) {
|
||||
h := map[string]string{
|
||||
"x-sls-bodyrawsize": "0",
|
||||
}
|
||||
|
||||
uri := fmt.Sprintf("/logstores")
|
||||
r, err := request(p, "GET", uri, h, nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
buf, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if r.StatusCode != http.StatusOK {
|
||||
errMsg := &errorMessage{}
|
||||
err = json.Unmarshal(buf, errMsg)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("failed to list logstore")
|
||||
dump, _ := httputil.DumpResponse(r, true)
|
||||
fmt.Printf("%s\n", dump)
|
||||
return
|
||||
}
|
||||
err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
|
||||
return
|
||||
}
|
||||
|
||||
type Body struct {
|
||||
Count int
|
||||
LogStores []string
|
||||
}
|
||||
body := &Body{}
|
||||
|
||||
err = json.Unmarshal(buf, body)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
storeNames = body.LogStores
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// GetLogStore returns logstore according by logstore name.
|
||||
func (p *LogProject) GetLogStore(name string) (s *LogStore, err error) {
|
||||
h := map[string]string{
|
||||
"x-sls-bodyrawsize": "0",
|
||||
}
|
||||
|
||||
r, err := request(p, "GET", "/logstores/"+name, h, nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
buf, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if r.StatusCode != http.StatusOK {
|
||||
errMsg := &errorMessage{}
|
||||
err = json.Unmarshal(buf, errMsg)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("failed to get logstore")
|
||||
dump, _ := httputil.DumpResponse(r, true)
|
||||
fmt.Printf("%s\n", dump)
|
||||
return
|
||||
}
|
||||
err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
|
||||
return
|
||||
}
|
||||
|
||||
s = &LogStore{}
|
||||
err = json.Unmarshal(buf, s)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
s.project = p
|
||||
return
|
||||
}
|
||||
|
||||
// CreateLogStore creates a new logstore in SLS,
|
||||
// where name is logstore name,
|
||||
// and ttl is time-to-live(in day) of logs,
|
||||
// and shardCnt is the number of shards.
|
||||
func (p *LogProject) CreateLogStore(name string, ttl, shardCnt int) (err error) {
|
||||
|
||||
type Body struct {
|
||||
Name string `json:"logstoreName"`
|
||||
TTL int `json:"ttl"`
|
||||
ShardCount int `json:"shardCount"`
|
||||
}
|
||||
|
||||
store := &Body{
|
||||
Name: name,
|
||||
TTL: ttl,
|
||||
ShardCount: shardCnt,
|
||||
}
|
||||
|
||||
body, err := json.Marshal(store)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
h := map[string]string{
|
||||
"x-sls-bodyrawsize": fmt.Sprintf("%v", len(body)),
|
||||
"Content-Type": "application/json",
|
||||
"Accept-Encoding": "deflate", // TODO: support lz4
|
||||
}
|
||||
|
||||
r, err := request(p, "POST", "/logstores", h, body)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
body, err = ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if r.StatusCode != http.StatusOK {
|
||||
errMsg := &errorMessage{}
|
||||
err = json.Unmarshal(body, errMsg)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("failed to create logstore")
|
||||
dump, _ := httputil.DumpResponse(r, true)
|
||||
fmt.Printf("%s\n", dump)
|
||||
return
|
||||
}
|
||||
err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// DeleteLogStore deletes a logstore according by logstore name.
|
||||
func (p *LogProject) DeleteLogStore(name string) (err error) {
|
||||
h := map[string]string{
|
||||
"x-sls-bodyrawsize": "0",
|
||||
}
|
||||
|
||||
r, err := request(p, "DELETE", "/logstores/"+name, h, nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
body, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if r.StatusCode != http.StatusOK {
|
||||
errMsg := &errorMessage{}
|
||||
err = json.Unmarshal(body, errMsg)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("failed to delete logstore")
|
||||
dump, _ := httputil.DumpResponse(r, true)
|
||||
fmt.Printf("%s\n", dump)
|
||||
return
|
||||
}
|
||||
err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// UpdateLogStore updates a logstore according by logstore name,
|
||||
// obviously we can't modify the logstore name itself.
|
||||
func (p *LogProject) UpdateLogStore(name string, ttl, shardCnt int) (err error) {
|
||||
|
||||
type Body struct {
|
||||
Name string `json:"logstoreName"`
|
||||
TTL int `json:"ttl"`
|
||||
ShardCount int `json:"shardCount"`
|
||||
}
|
||||
|
||||
store := &Body{
|
||||
Name: name,
|
||||
TTL: ttl,
|
||||
ShardCount: shardCnt,
|
||||
}
|
||||
|
||||
body, err := json.Marshal(store)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
h := map[string]string{
|
||||
"x-sls-bodyrawsize": fmt.Sprintf("%v", len(body)),
|
||||
"Content-Type": "application/json",
|
||||
"Accept-Encoding": "deflate", // TODO: support lz4
|
||||
}
|
||||
|
||||
r, err := request(p, "PUT", "/logstores", h, body)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
body, err = ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if r.StatusCode != http.StatusOK {
|
||||
errMsg := &errorMessage{}
|
||||
err = json.Unmarshal(body, errMsg)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("failed to update logstore")
|
||||
dump, _ := httputil.DumpResponse(r, true)
|
||||
fmt.Printf("%s\n", dump)
|
||||
return
|
||||
}
|
||||
err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// ListMachineGroup returns machine group name list and the total number of machine groups.
|
||||
// The offset starts from 0 and the size is the max number of machine groups could be returned.
|
||||
func (p *LogProject) ListMachineGroup(offset, size int) (m []string, total int, err error) {
|
||||
h := map[string]string{
|
||||
"x-sls-bodyrawsize": "0",
|
||||
}
|
||||
|
||||
if size <= 0 {
|
||||
size = 500
|
||||
}
|
||||
|
||||
uri := fmt.Sprintf("/machinegroups?offset=%v&size=%v", offset, size)
|
||||
r, err := request(p, "GET", uri, h, nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
buf, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if r.StatusCode != http.StatusOK {
|
||||
errMsg := &errorMessage{}
|
||||
err = json.Unmarshal(buf, errMsg)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("failed to list machine group")
|
||||
dump, _ := httputil.DumpResponse(r, true)
|
||||
fmt.Printf("%s\n", dump)
|
||||
return
|
||||
}
|
||||
err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
|
||||
return
|
||||
}
|
||||
|
||||
type Body struct {
|
||||
MachineGroups []string
|
||||
Count int
|
||||
Total int
|
||||
}
|
||||
body := &Body{}
|
||||
|
||||
err = json.Unmarshal(buf, body)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
m = body.MachineGroups
|
||||
total = body.Total
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// GetMachineGroup retruns machine group according by machine group name.
|
||||
func (p *LogProject) GetMachineGroup(name string) (m *MachineGroup, err error) {
|
||||
h := map[string]string{
|
||||
"x-sls-bodyrawsize": "0",
|
||||
}
|
||||
|
||||
r, err := request(p, "GET", "/machinegroups/"+name, h, nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
buf, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if r.StatusCode != http.StatusOK {
|
||||
errMsg := &errorMessage{}
|
||||
err = json.Unmarshal(buf, errMsg)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("failed to get machine group:%v", name)
|
||||
dump, _ := httputil.DumpResponse(r, true)
|
||||
fmt.Printf("%s\n", dump)
|
||||
return
|
||||
}
|
||||
err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
|
||||
return
|
||||
}
|
||||
|
||||
m = &MachineGroup{}
|
||||
err = json.Unmarshal(buf, m)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
m.project = p
|
||||
return
|
||||
}
|
||||
|
||||
// CreateMachineGroup creates a new machine group in SLS.
|
||||
func (p *LogProject) CreateMachineGroup(m *MachineGroup) (err error) {
|
||||
|
||||
body, err := json.Marshal(m)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
h := map[string]string{
|
||||
"x-sls-bodyrawsize": fmt.Sprintf("%v", len(body)),
|
||||
"Content-Type": "application/json",
|
||||
"Accept-Encoding": "deflate", // TODO: support lz4
|
||||
}
|
||||
|
||||
r, err := request(p, "POST", "/machinegroups", h, body)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
body, err = ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if r.StatusCode != http.StatusOK {
|
||||
errMsg := &errorMessage{}
|
||||
err = json.Unmarshal(body, errMsg)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("failed to create machine group")
|
||||
dump, _ := httputil.DumpResponse(r, true)
|
||||
fmt.Printf("%s\n", dump)
|
||||
return
|
||||
}
|
||||
err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// UpdateMachineGroup updates a machine group.
|
||||
func (p *LogProject) UpdateMachineGroup(m *MachineGroup) (err error) {
|
||||
|
||||
body, err := json.Marshal(m)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
h := map[string]string{
|
||||
"x-sls-bodyrawsize": fmt.Sprintf("%v", len(body)),
|
||||
"Content-Type": "application/json",
|
||||
"Accept-Encoding": "deflate", // TODO: support lz4
|
||||
}
|
||||
|
||||
r, err := request(p, "PUT", "/machinegroups/"+m.Name, h, body)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
body, err = ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if r.StatusCode != http.StatusOK {
|
||||
errMsg := &errorMessage{}
|
||||
err = json.Unmarshal(body, errMsg)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("failed to update machine group")
|
||||
dump, _ := httputil.DumpResponse(r, true)
|
||||
fmt.Printf("%s\n", dump)
|
||||
return
|
||||
}
|
||||
err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// DeleteMachineGroup deletes machine group according machine group name.
|
||||
func (p *LogProject) DeleteMachineGroup(name string) (err error) {
|
||||
h := map[string]string{
|
||||
"x-sls-bodyrawsize": "0",
|
||||
}
|
||||
|
||||
r, err := request(p, "DELETE", "/machinegroups/"+name, h, nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
body, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if r.StatusCode != http.StatusOK {
|
||||
errMsg := &errorMessage{}
|
||||
err = json.Unmarshal(body, errMsg)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("failed to delete machine group")
|
||||
dump, _ := httputil.DumpResponse(r, true)
|
||||
fmt.Printf("%s\n", dump)
|
||||
return
|
||||
}
|
||||
err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ListConfig returns config names list and the total number of configs.
|
||||
// The offset starts from 0 and the size is the max number of configs could be returned.
|
||||
func (p *LogProject) ListConfig(offset, size int) (cfgNames []string, total int, err error) {
|
||||
h := map[string]string{
|
||||
"x-sls-bodyrawsize": "0",
|
||||
}
|
||||
|
||||
if size <= 0 {
|
||||
size = 100
|
||||
}
|
||||
|
||||
uri := fmt.Sprintf("/configs?offset=%v&size=%v", offset, size)
|
||||
r, err := request(p, "GET", uri, h, nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
buf, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if r.StatusCode != http.StatusOK {
|
||||
errMsg := &errorMessage{}
|
||||
err = json.Unmarshal(buf, errMsg)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("failed to delete machine group")
|
||||
dump, _ := httputil.DumpResponse(r, true)
|
||||
fmt.Printf("%s\n", dump)
|
||||
return
|
||||
}
|
||||
err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
|
||||
return
|
||||
}
|
||||
|
||||
type Body struct {
|
||||
Total int
|
||||
Configs []string
|
||||
}
|
||||
body := &Body{}
|
||||
|
||||
err = json.Unmarshal(buf, body)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
cfgNames = body.Configs
|
||||
total = body.Total
|
||||
return
|
||||
}
|
||||
|
||||
// GetConfig returns config according by config name.
|
||||
func (p *LogProject) GetConfig(name string) (c *LogConfig, err error) {
|
||||
h := map[string]string{
|
||||
"x-sls-bodyrawsize": "0",
|
||||
}
|
||||
|
||||
r, err := request(p, "GET", "/configs/"+name, h, nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
buf, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if r.StatusCode != http.StatusOK {
|
||||
errMsg := &errorMessage{}
|
||||
err = json.Unmarshal(buf, errMsg)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("failed to delete config")
|
||||
dump, _ := httputil.DumpResponse(r, true)
|
||||
fmt.Printf("%s\n", dump)
|
||||
return
|
||||
}
|
||||
err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
|
||||
return
|
||||
}
|
||||
|
||||
c = &LogConfig{}
|
||||
err = json.Unmarshal(buf, c)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
c.project = p
|
||||
return
|
||||
}
|
||||
|
||||
// UpdateConfig updates a config.
|
||||
func (p *LogProject) UpdateConfig(c *LogConfig) (err error) {
|
||||
|
||||
body, err := json.Marshal(c)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
h := map[string]string{
|
||||
"x-sls-bodyrawsize": fmt.Sprintf("%v", len(body)),
|
||||
"Content-Type": "application/json",
|
||||
"Accept-Encoding": "deflate", // TODO: support lz4
|
||||
}
|
||||
|
||||
r, err := request(p, "PUT", "/configs/"+c.Name, h, body)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
body, err = ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if r.StatusCode != http.StatusOK {
|
||||
errMsg := &errorMessage{}
|
||||
err = json.Unmarshal(body, errMsg)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("failed to update config")
|
||||
dump, _ := httputil.DumpResponse(r, true)
|
||||
fmt.Printf("%s\n", dump)
|
||||
return
|
||||
}
|
||||
err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// CreateConfig creates a new config in SLS.
|
||||
func (p *LogProject) CreateConfig(c *LogConfig) (err error) {
|
||||
|
||||
body, err := json.Marshal(c)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
h := map[string]string{
|
||||
"x-sls-bodyrawsize": fmt.Sprintf("%v", len(body)),
|
||||
"Content-Type": "application/json",
|
||||
"Accept-Encoding": "deflate", // TODO: support lz4
|
||||
}
|
||||
|
||||
r, err := request(p, "POST", "/configs", h, body)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
body, err = ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if r.StatusCode != http.StatusOK {
|
||||
errMsg := &errorMessage{}
|
||||
err = json.Unmarshal(body, errMsg)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("failed to update config")
|
||||
dump, _ := httputil.DumpResponse(r, true)
|
||||
fmt.Printf("%s\n", dump)
|
||||
return
|
||||
}
|
||||
err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// DeleteConfig deletes a config according by config name.
|
||||
func (p *LogProject) DeleteConfig(name string) (err error) {
|
||||
h := map[string]string{
|
||||
"x-sls-bodyrawsize": "0",
|
||||
}
|
||||
|
||||
r, err := request(p, "DELETE", "/configs/"+name, h, nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
body, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if r.StatusCode != http.StatusOK {
|
||||
errMsg := &errorMessage{}
|
||||
err = json.Unmarshal(body, errMsg)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("failed to delete config")
|
||||
dump, _ := httputil.DumpResponse(r, true)
|
||||
fmt.Printf("%s\n", dump)
|
||||
return
|
||||
}
|
||||
err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// GetAppliedMachineGroups returns applied machine group names list according config name.
|
||||
func (p *LogProject) GetAppliedMachineGroups(confName string) (groupNames []string, err error) {
|
||||
h := map[string]string{
|
||||
"x-sls-bodyrawsize": "0",
|
||||
}
|
||||
|
||||
uri := fmt.Sprintf("/configs/%v/machinegroups", confName)
|
||||
r, err := request(p, "GET", uri, h, nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
buf, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if r.StatusCode != http.StatusOK {
|
||||
errMsg := &errorMessage{}
|
||||
err = json.Unmarshal(buf, errMsg)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("failed to get applied machine groups")
|
||||
dump, _ := httputil.DumpResponse(r, true)
|
||||
fmt.Printf("%s\n", dump)
|
||||
return
|
||||
}
|
||||
err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
|
||||
return
|
||||
}
|
||||
|
||||
type Body struct {
|
||||
Count int
|
||||
Machinegroups []string
|
||||
}
|
||||
|
||||
body := &Body{}
|
||||
err = json.Unmarshal(buf, body)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
groupNames = body.Machinegroups
|
||||
return
|
||||
}
|
||||
|
||||
// GetAppliedConfigs returns applied config names list according machine group name groupName.
|
||||
func (p *LogProject) GetAppliedConfigs(groupName string) (confNames []string, err error) {
|
||||
h := map[string]string{
|
||||
"x-sls-bodyrawsize": "0",
|
||||
}
|
||||
|
||||
uri := fmt.Sprintf("/machinegroups/%v/configs", groupName)
|
||||
r, err := request(p, "GET", uri, h, nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
buf, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if r.StatusCode != http.StatusOK {
|
||||
errMsg := &errorMessage{}
|
||||
err = json.Unmarshal(buf, errMsg)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("failed to applied configs")
|
||||
dump, _ := httputil.DumpResponse(r, true)
|
||||
fmt.Printf("%s\n", dump)
|
||||
return
|
||||
}
|
||||
err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
|
||||
return
|
||||
}
|
||||
|
||||
type Cfg struct {
|
||||
Count int `json:"count"`
|
||||
Configs []string `json:"configs"`
|
||||
}
|
||||
|
||||
body := &Cfg{}
|
||||
err = json.Unmarshal(buf, body)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
confNames = body.Configs
|
||||
return
|
||||
}
|
||||
|
||||
// ApplyConfigToMachineGroup applies config to machine group.
|
||||
func (p *LogProject) ApplyConfigToMachineGroup(confName, groupName string) (err error) {
|
||||
h := map[string]string{
|
||||
"x-sls-bodyrawsize": "0",
|
||||
}
|
||||
|
||||
uri := fmt.Sprintf("/machinegroups/%v/configs/%v", groupName, confName)
|
||||
r, err := request(p, "PUT", uri, h, nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
buf, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if r.StatusCode != http.StatusOK {
|
||||
errMsg := &errorMessage{}
|
||||
err = json.Unmarshal(buf, errMsg)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("failed to apply config to machine group")
|
||||
dump, _ := httputil.DumpResponse(r, true)
|
||||
fmt.Printf("%s\n", dump)
|
||||
return
|
||||
}
|
||||
err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// RemoveConfigFromMachineGroup removes config from machine group.
|
||||
func (p *LogProject) RemoveConfigFromMachineGroup(confName, groupName string) (err error) {
|
||||
h := map[string]string{
|
||||
"x-sls-bodyrawsize": "0",
|
||||
}
|
||||
|
||||
uri := fmt.Sprintf("/machinegroups/%v/configs/%v", groupName, confName)
|
||||
r, err := request(p, "DELETE", uri, h, nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
buf, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if r.StatusCode != http.StatusOK {
|
||||
errMsg := &errorMessage{}
|
||||
err = json.Unmarshal(buf, errMsg)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("failed to remove config from machine group")
|
||||
dump, _ := httputil.DumpResponse(r, true)
|
||||
fmt.Printf("%s\n", dump)
|
||||
return
|
||||
}
|
||||
err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
269
logs/alils/log_store.go
Executable file
269
logs/alils/log_store.go
Executable file
@ -0,0 +1,269 @@
|
||||
package alils
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"strconv"
|
||||
|
||||
lz4 "github.com/cloudflare/golz4"
|
||||
"github.com/gogo/protobuf/proto"
|
||||
)
|
||||
|
||||
type LogStore struct {
|
||||
Name string `json:"logstoreName"`
|
||||
TTL int
|
||||
ShardCount int
|
||||
|
||||
CreateTime uint32
|
||||
LastModifyTime uint32
|
||||
|
||||
project *LogProject
|
||||
}
|
||||
|
||||
type Shard struct {
|
||||
ShardID int `json:"shardID"`
|
||||
}
|
||||
|
||||
// ListShards returns shard id list of this logstore.
|
||||
func (s *LogStore) ListShards() (shardIDs []int, err error) {
|
||||
h := map[string]string{
|
||||
"x-sls-bodyrawsize": "0",
|
||||
}
|
||||
|
||||
uri := fmt.Sprintf("/logstores/%v/shards", s.Name)
|
||||
r, err := request(s.project, "GET", uri, h, nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
buf, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if r.StatusCode != http.StatusOK {
|
||||
errMsg := &errorMessage{}
|
||||
err = json.Unmarshal(buf, errMsg)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("failed to list logstore")
|
||||
dump, _ := httputil.DumpResponse(r, true)
|
||||
fmt.Println(dump)
|
||||
return
|
||||
}
|
||||
err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
|
||||
return
|
||||
}
|
||||
|
||||
var shards []*Shard
|
||||
err = json.Unmarshal(buf, &shards)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
for _, v := range shards {
|
||||
shardIDs = append(shardIDs, v.ShardID)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// PutLogs put logs into logstore.
|
||||
// The callers should transform user logs into LogGroup.
|
||||
func (s *LogStore) PutLogs(lg *LogGroup) (err error) {
|
||||
body, err := proto.Marshal(lg)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Compresse body with lz4
|
||||
out := make([]byte, lz4.CompressBound(body))
|
||||
n, err := lz4.Compress(body, out)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
h := map[string]string{
|
||||
"x-sls-compresstype": "lz4",
|
||||
"x-sls-bodyrawsize": fmt.Sprintf("%v", len(body)),
|
||||
"Content-Type": "application/x-protobuf",
|
||||
}
|
||||
|
||||
uri := fmt.Sprintf("/logstores/%v", s.Name)
|
||||
r, err := request(s.project, "POST", uri, h, out[:n])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
buf, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if r.StatusCode != http.StatusOK {
|
||||
errMsg := &errorMessage{}
|
||||
err = json.Unmarshal(buf, errMsg)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("failed to put logs")
|
||||
dump, _ := httputil.DumpResponse(r, true)
|
||||
fmt.Println(dump)
|
||||
return
|
||||
}
|
||||
err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// GetCursor gets log cursor of one shard specified by shardId.
|
||||
// The from can be in three form: a) unix timestamp in seccond, b) "begin", c) "end".
|
||||
// For more detail please read: http://gitlab.alibaba-inc.com/sls/doc/blob/master/api/shard.md#logstore
|
||||
func (s *LogStore) GetCursor(shardId int, from string) (cursor string, err error) {
|
||||
h := map[string]string{
|
||||
"x-sls-bodyrawsize": "0",
|
||||
}
|
||||
|
||||
uri := fmt.Sprintf("/logstores/%v/shards/%v?type=cursor&from=%v",
|
||||
s.Name, shardId, from)
|
||||
|
||||
r, err := request(s.project, "GET", uri, h, nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
buf, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if r.StatusCode != http.StatusOK {
|
||||
errMsg := &errorMessage{}
|
||||
err = json.Unmarshal(buf, errMsg)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("failed to get cursor")
|
||||
dump, _ := httputil.DumpResponse(r, true)
|
||||
fmt.Println(dump)
|
||||
return
|
||||
}
|
||||
err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
|
||||
return
|
||||
}
|
||||
|
||||
type Body struct {
|
||||
Cursor string
|
||||
}
|
||||
body := &Body{}
|
||||
|
||||
err = json.Unmarshal(buf, body)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
cursor = body.Cursor
|
||||
return
|
||||
}
|
||||
|
||||
// GetLogsBytes gets logs binary data from shard specified by shardId according cursor.
|
||||
// The logGroupMaxCount is the max number of logGroup could be returned.
|
||||
// The nextCursor is the next curosr can be used to read logs at next time.
|
||||
func (s *LogStore) GetLogsBytes(shardId int, cursor string,
|
||||
logGroupMaxCount int) (out []byte, nextCursor string, err error) {
|
||||
|
||||
h := map[string]string{
|
||||
"x-sls-bodyrawsize": "0",
|
||||
"Accept": "application/x-protobuf",
|
||||
"Accept-Encoding": "lz4",
|
||||
}
|
||||
|
||||
uri := fmt.Sprintf("/logstores/%v/shards/%v?type=logs&cursor=%v&count=%v",
|
||||
s.Name, shardId, cursor, logGroupMaxCount)
|
||||
|
||||
r, err := request(s.project, "GET", uri, h, nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
buf, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if r.StatusCode != http.StatusOK {
|
||||
errMsg := &errorMessage{}
|
||||
err = json.Unmarshal(buf, errMsg)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("failed to get cursor")
|
||||
dump, _ := httputil.DumpResponse(r, true)
|
||||
fmt.Println(dump)
|
||||
return
|
||||
}
|
||||
err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
|
||||
return
|
||||
}
|
||||
|
||||
v, ok := r.Header["X-Sls-Compresstype"]
|
||||
if !ok || len(v) == 0 {
|
||||
err = fmt.Errorf("can't find 'x-sls-compresstype' header")
|
||||
return
|
||||
}
|
||||
if v[0] != "lz4" {
|
||||
err = fmt.Errorf("unexpected compress type:%v", v[0])
|
||||
return
|
||||
}
|
||||
|
||||
v, ok = r.Header["X-Sls-Cursor"]
|
||||
if !ok || len(v) == 0 {
|
||||
err = fmt.Errorf("can't find 'x-sls-cursor' header")
|
||||
return
|
||||
}
|
||||
nextCursor = v[0]
|
||||
|
||||
v, ok = r.Header["X-Sls-Bodyrawsize"]
|
||||
if !ok || len(v) == 0 {
|
||||
err = fmt.Errorf("can't find 'x-sls-bodyrawsize' header")
|
||||
return
|
||||
}
|
||||
bodyRawSize, err := strconv.Atoi(v[0])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
out = make([]byte, bodyRawSize)
|
||||
err = lz4.Uncompress(buf, out)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// LogsBytesDecode decodes logs binary data retruned by GetLogsBytes API
|
||||
func LogsBytesDecode(data []byte) (gl *LogGroupList, err error) {
|
||||
|
||||
gl = &LogGroupList{}
|
||||
err = proto.Unmarshal(data, gl)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// GetLogs gets logs from shard specified by shardId according cursor.
|
||||
// The logGroupMaxCount is the max number of logGroup could be returned.
|
||||
// The nextCursor is the next curosr can be used to read logs at next time.
|
||||
func (s *LogStore) GetLogs(shardId int, cursor string,
|
||||
logGroupMaxCount int) (gl *LogGroupList, nextCursor string, err error) {
|
||||
|
||||
out, nextCursor, err := s.GetLogsBytes(shardId, cursor, logGroupMaxCount)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
gl, err = LogsBytesDecode(out)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
87
logs/alils/machine_group.go
Executable file
87
logs/alils/machine_group.go
Executable file
@ -0,0 +1,87 @@
|
||||
package alils
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
)
|
||||
|
||||
type MachinGroupAttribute struct {
|
||||
ExternalName string `json:"externalName"`
|
||||
TopicName string `json:"groupTopic"`
|
||||
}
|
||||
|
||||
type MachineGroup struct {
|
||||
Name string `json:"groupName"`
|
||||
Type string `json:"groupType"`
|
||||
MachineIdType string `json:"machineIdentifyType"`
|
||||
MachineIdList []string `json:"machineList"`
|
||||
|
||||
Attribute MachinGroupAttribute `json:"groupAttribute"`
|
||||
|
||||
CreateTime uint32
|
||||
LastModifyTime uint32
|
||||
|
||||
project *LogProject
|
||||
}
|
||||
|
||||
type Machine struct {
|
||||
IP string
|
||||
UniqueId string `json:"machine-uniqueid"`
|
||||
UserdefinedId string `json:"userdefined-id"`
|
||||
}
|
||||
|
||||
type MachineList struct {
|
||||
Total int
|
||||
Machines []*Machine
|
||||
}
|
||||
|
||||
// ListMachines returns machine list of this machine group.
|
||||
func (m *MachineGroup) ListMachines() (ms []*Machine, total int, err error) {
|
||||
h := map[string]string{
|
||||
"x-sls-bodyrawsize": "0",
|
||||
}
|
||||
|
||||
uri := fmt.Sprintf("/machinegroups/%v/machines", m.Name)
|
||||
r, err := request(m.project, "GET", uri, h, nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
buf, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if r.StatusCode != http.StatusOK {
|
||||
errMsg := &errorMessage{}
|
||||
err = json.Unmarshal(buf, errMsg)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("failed to remove config from machine group")
|
||||
dump, _ := httputil.DumpResponse(r, true)
|
||||
fmt.Println(dump)
|
||||
return
|
||||
}
|
||||
err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
|
||||
return
|
||||
}
|
||||
|
||||
body := &MachineList{}
|
||||
err = json.Unmarshal(buf, body)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
ms = body.Machines
|
||||
total = body.Total
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// GetAppliedConfigs returns applied configs of this machine group.
|
||||
func (m *MachineGroup) GetAppliedConfigs() (confNames []string, err error) {
|
||||
confNames, err = m.project.GetAppliedConfigs(m.Name)
|
||||
return
|
||||
}
|
62
logs/alils/request.go
Executable file
62
logs/alils/request.go
Executable file
@ -0,0 +1,62 @@
|
||||
package alils
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/md5"
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// request sends a request to SLS.
|
||||
func request(project *LogProject, method, uri string, headers map[string]string,
|
||||
body []byte) (resp *http.Response, err error) {
|
||||
|
||||
// The caller should provide 'x-sls-bodyrawsize' header
|
||||
if _, ok := headers["x-sls-bodyrawsize"]; !ok {
|
||||
err = fmt.Errorf("Can't find 'x-sls-bodyrawsize' header")
|
||||
return
|
||||
}
|
||||
|
||||
// SLS public request headers
|
||||
headers["Host"] = project.Name + "." + project.Endpoint
|
||||
headers["Date"] = nowRFC1123()
|
||||
headers["x-sls-apiversion"] = version
|
||||
headers["x-sls-signaturemethod"] = signatureMethod
|
||||
if body != nil {
|
||||
bodyMD5 := fmt.Sprintf("%X", md5.Sum(body))
|
||||
headers["Content-MD5"] = bodyMD5
|
||||
|
||||
if _, ok := headers["Content-Type"]; !ok {
|
||||
err = fmt.Errorf("Can't find 'Content-Type' header")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Calc Authorization
|
||||
// Authorization = "SLS <AccessKeyId>:<Signature>"
|
||||
digest, err := signature(project, method, uri, headers)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
auth := fmt.Sprintf("SLS %v:%v", project.AccessKeyId, digest)
|
||||
headers["Authorization"] = auth
|
||||
|
||||
// Initialize http request
|
||||
reader := bytes.NewReader(body)
|
||||
urlStr := fmt.Sprintf("http://%v.%v%v", project.Name, project.Endpoint, uri)
|
||||
req, err := http.NewRequest(method, urlStr, reader)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for k, v := range headers {
|
||||
req.Header.Add(k, v)
|
||||
}
|
||||
|
||||
// Get ready to do request
|
||||
resp, err = http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
111
logs/alils/signature.go
Executable file
111
logs/alils/signature.go
Executable file
@ -0,0 +1,111 @@
|
||||
package alils
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha1"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// GMT location
|
||||
var gmtLoc = time.FixedZone("GMT", 0)
|
||||
|
||||
// NowRFC1123 returns now time in RFC1123 format with GMT timezone,
|
||||
// eg. "Mon, 02 Jan 2006 15:04:05 GMT".
|
||||
func nowRFC1123() string {
|
||||
return time.Now().In(gmtLoc).Format(time.RFC1123)
|
||||
}
|
||||
|
||||
// signature calculates a request's signature digest.
|
||||
func signature(project *LogProject, method, uri string,
|
||||
headers map[string]string) (digest string, err error) {
|
||||
var contentMD5, contentType, date, canoHeaders, canoResource string
|
||||
var slsHeaderKeys sort.StringSlice
|
||||
|
||||
// SignString = VERB + "\n"
|
||||
// + CONTENT-MD5 + "\n"
|
||||
// + CONTENT-TYPE + "\n"
|
||||
// + DATE + "\n"
|
||||
// + CanonicalizedSLSHeaders + "\n"
|
||||
// + CanonicalizedResource
|
||||
|
||||
if val, ok := headers["Content-MD5"]; ok {
|
||||
contentMD5 = val
|
||||
}
|
||||
|
||||
if val, ok := headers["Content-Type"]; ok {
|
||||
contentType = val
|
||||
}
|
||||
|
||||
date, ok := headers["Date"]
|
||||
if !ok {
|
||||
err = fmt.Errorf("Can't find 'Date' header")
|
||||
return
|
||||
}
|
||||
|
||||
// Calc CanonicalizedSLSHeaders
|
||||
slsHeaders := make(map[string]string, len(headers))
|
||||
for k, v := range headers {
|
||||
l := strings.TrimSpace(strings.ToLower(k))
|
||||
if strings.HasPrefix(l, "x-sls-") {
|
||||
slsHeaders[l] = strings.TrimSpace(v)
|
||||
slsHeaderKeys = append(slsHeaderKeys, l)
|
||||
}
|
||||
}
|
||||
|
||||
sort.Sort(slsHeaderKeys)
|
||||
for i, k := range slsHeaderKeys {
|
||||
canoHeaders += k + ":" + slsHeaders[k]
|
||||
if i+1 < len(slsHeaderKeys) {
|
||||
canoHeaders += "\n"
|
||||
}
|
||||
}
|
||||
|
||||
// Calc CanonicalizedResource
|
||||
u, err := url.Parse(uri)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
canoResource += url.QueryEscape(u.Path)
|
||||
if u.RawQuery != "" {
|
||||
var keys sort.StringSlice
|
||||
|
||||
vals := u.Query()
|
||||
for k := range vals {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
|
||||
sort.Sort(keys)
|
||||
canoResource += "?"
|
||||
for i, k := range keys {
|
||||
if i > 0 {
|
||||
canoResource += "&"
|
||||
}
|
||||
|
||||
for _, v := range vals[k] {
|
||||
canoResource += k + "=" + v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
signStr := method + "\n" +
|
||||
contentMD5 + "\n" +
|
||||
contentType + "\n" +
|
||||
date + "\n" +
|
||||
canoHeaders + "\n" +
|
||||
canoResource
|
||||
|
||||
// Signature = base64(hmac-sha1(UTF8-Encoding-Of(SignString),AccessKeySecret))
|
||||
mac := hmac.New(sha1.New, []byte(project.AccessKeySecret))
|
||||
_, err = mac.Write([]byte(signStr))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
digest = base64.StdEncoding.EncodeToString(mac.Sum(nil))
|
||||
return
|
||||
}
|
@ -41,7 +41,7 @@ var colors = []brush{
|
||||
newBrush("1;33"), // Warning yellow
|
||||
newBrush("1;32"), // Notice green
|
||||
newBrush("1;34"), // Informational blue
|
||||
newBrush("1;34"), // Debug blue
|
||||
newBrush("1;44"), // Debug Background blue
|
||||
}
|
||||
|
||||
// consoleWriter implements LoggerInterface and writes messages to terminal.
|
||||
|
15
logs/file.go
15
logs/file.go
@ -193,16 +193,14 @@ func (w *fileLogWriter) dailyRotate(openTime time.Time) {
|
||||
y, m, d := openTime.Add(24 * time.Hour).Date()
|
||||
nextDay := time.Date(y, m, d, 0, 0, 0, 0, openTime.Location())
|
||||
tm := time.NewTimer(time.Duration(nextDay.UnixNano() - openTime.UnixNano() + 100))
|
||||
select {
|
||||
case <-tm.C:
|
||||
w.Lock()
|
||||
if w.needRotate(0, time.Now().Day()) {
|
||||
if err := w.doRotate(time.Now()); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.Filename, err)
|
||||
}
|
||||
<-tm.C
|
||||
w.Lock()
|
||||
if w.needRotate(0, time.Now().Day()) {
|
||||
if err := w.doRotate(time.Now()); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.Filename, err)
|
||||
}
|
||||
w.Unlock()
|
||||
}
|
||||
w.Unlock()
|
||||
}
|
||||
|
||||
func (w *fileLogWriter) lines() (int, error) {
|
||||
@ -270,6 +268,7 @@ func (w *fileLogWriter) doRotate(logTime time.Time) error {
|
||||
// Rename the file to its new found name
|
||||
// even if occurs error,we MUST guarantee to restart new logger
|
||||
err = os.Rename(w.Filename, fName)
|
||||
err = os.Chmod(fName, os.FileMode(0440))
|
||||
// re-start logger
|
||||
RESTART_LOGGER:
|
||||
|
||||
|
@ -162,7 +162,27 @@ func TestFileRotate_05(t *testing.T) {
|
||||
testFileDailyRotate(t, fn1, fn2)
|
||||
os.Remove(fn)
|
||||
}
|
||||
|
||||
func TestFileRotate_06(t *testing.T) { //test file mode
|
||||
log := NewLogger(10000)
|
||||
log.SetLogger("file", `{"filename":"test3.log","maxlines":4}`)
|
||||
log.Debug("debug")
|
||||
log.Info("info")
|
||||
log.Notice("notice")
|
||||
log.Warning("warning")
|
||||
log.Error("error")
|
||||
log.Alert("alert")
|
||||
log.Critical("critical")
|
||||
log.Emergency("emergency")
|
||||
rotateName := "test3" + fmt.Sprintf(".%s.%03d", time.Now().Format("2006-01-02"), 1) + ".log"
|
||||
s, _ := os.Lstat(rotateName)
|
||||
if s.Mode() != 0440 {
|
||||
os.Remove(rotateName)
|
||||
os.Remove("test3.log")
|
||||
t.Fatal("rotate file mode error")
|
||||
}
|
||||
os.Remove(rotateName)
|
||||
os.Remove("test3.log")
|
||||
}
|
||||
func testFileRotate(t *testing.T, fn1, fn2 string) {
|
||||
fw := &fileLogWriter{
|
||||
Daily: true,
|
||||
|
72
logs/jianliao.go
Normal file
72
logs/jianliao.go
Normal file
@ -0,0 +1,72 @@
|
||||
package logs
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
)
|
||||
|
||||
// JLWriter implements beego LoggerInterface and is used to send jiaoliao webhook
|
||||
type JLWriter struct {
|
||||
AuthorName string `json:"authorname"`
|
||||
Title string `json:"title"`
|
||||
WebhookURL string `json:"webhookurl"`
|
||||
RedirectURL string `json:"redirecturl,omitempty"`
|
||||
ImageURL string `json:"imageurl,omitempty"`
|
||||
Level int `json:"level"`
|
||||
}
|
||||
|
||||
// newJLWriter create jiaoliao writer.
|
||||
func newJLWriter() Logger {
|
||||
return &JLWriter{Level: LevelTrace}
|
||||
}
|
||||
|
||||
// Init JLWriter with json config string
|
||||
func (s *JLWriter) Init(jsonconfig string) error {
|
||||
return json.Unmarshal([]byte(jsonconfig), s)
|
||||
}
|
||||
|
||||
// WriteMsg write message in smtp writer.
|
||||
// it will send an email with subject and only this message.
|
||||
func (s *JLWriter) WriteMsg(when time.Time, msg string, level int) error {
|
||||
if level > s.Level {
|
||||
return nil
|
||||
}
|
||||
|
||||
text := fmt.Sprintf("%s %s", when.Format("2006-01-02 15:04:05"), msg)
|
||||
|
||||
form := url.Values{}
|
||||
form.Add("authorName", s.AuthorName)
|
||||
form.Add("title", s.Title)
|
||||
form.Add("text", text)
|
||||
if s.RedirectURL != "" {
|
||||
form.Add("redirectUrl", s.RedirectURL)
|
||||
}
|
||||
if s.ImageURL != "" {
|
||||
form.Add("imageUrl", s.ImageURL)
|
||||
}
|
||||
|
||||
resp, err := http.PostForm(s.WebhookURL, form)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return fmt.Errorf("Post webhook failed %s %d", resp.Status, resp.StatusCode)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Flush implementing method. empty.
|
||||
func (s *JLWriter) Flush() {
|
||||
}
|
||||
|
||||
// Destroy implementing method. empty.
|
||||
func (s *JLWriter) Destroy() {
|
||||
}
|
||||
|
||||
func init() {
|
||||
Register(AdapterJianLiao, newJLWriter)
|
||||
}
|
28
logs/log.go
28
logs/log.go
@ -66,9 +66,12 @@ const (
|
||||
AdapterConsole = "console"
|
||||
AdapterFile = "file"
|
||||
AdapterMultiFile = "multifile"
|
||||
AdapterMail = "stmp"
|
||||
AdapterMail = "smtp"
|
||||
AdapterConn = "conn"
|
||||
AdapterEs = "es"
|
||||
AdapterJianLiao = "jianliao"
|
||||
AdapterSlack = "slack"
|
||||
AdapterAliLS = "alils"
|
||||
)
|
||||
|
||||
// Legacy log level constants to ensure backwards compatibility.
|
||||
@ -272,7 +275,7 @@ func (bl *BeeLogger) writeMsg(logLevel int, msg string, v ...interface{}) error
|
||||
line = 0
|
||||
}
|
||||
_, filename := path.Split(file)
|
||||
msg = "[" + filename + ":" + strconv.FormatInt(int64(line), 10) + "] " + msg
|
||||
msg = "[" + filename + ":" + strconv.Itoa(line) + "] " + msg
|
||||
}
|
||||
|
||||
//set level info in front of filename info
|
||||
@ -378,7 +381,10 @@ func (bl *BeeLogger) Error(format string, v ...interface{}) {
|
||||
|
||||
// Warning Log WARNING level message.
|
||||
func (bl *BeeLogger) Warning(format string, v ...interface{}) {
|
||||
bl.Warn(format, v...)
|
||||
if LevelWarn > bl.level {
|
||||
return
|
||||
}
|
||||
bl.writeMsg(LevelWarn, format, v...)
|
||||
}
|
||||
|
||||
// Notice Log NOTICE level message.
|
||||
@ -391,7 +397,10 @@ func (bl *BeeLogger) Notice(format string, v ...interface{}) {
|
||||
|
||||
// Informational Log INFORMATIONAL level message.
|
||||
func (bl *BeeLogger) Informational(format string, v ...interface{}) {
|
||||
bl.Info(format, v...)
|
||||
if LevelInfo > bl.level {
|
||||
return
|
||||
}
|
||||
bl.writeMsg(LevelInfo, format, v...)
|
||||
}
|
||||
|
||||
// Debug Log DEBUG level message.
|
||||
@ -423,7 +432,10 @@ func (bl *BeeLogger) Info(format string, v ...interface{}) {
|
||||
// Trace Log TRACE level message.
|
||||
// compatibility alias for Debug()
|
||||
func (bl *BeeLogger) Trace(format string, v ...interface{}) {
|
||||
bl.Debug(format, v...)
|
||||
if LevelDebug > bl.level {
|
||||
return
|
||||
}
|
||||
bl.writeMsg(LevelDebug, format, v...)
|
||||
}
|
||||
|
||||
// Flush flush all chan data.
|
||||
@ -549,11 +561,7 @@ func SetLogFuncCallDepth(d int) {
|
||||
|
||||
// SetLogger sets a new logger.
|
||||
func SetLogger(adapter string, config ...string) error {
|
||||
err := beeLogger.SetLogger(adapter, config...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return beeLogger.SetLogger(adapter, config...)
|
||||
}
|
||||
|
||||
// Emergency logs a message at emergency level.
|
||||
|
60
logs/slack.go
Normal file
60
logs/slack.go
Normal file
@ -0,0 +1,60 @@
|
||||
package logs
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
)
|
||||
|
||||
// SLACKWriter implements beego LoggerInterface and is used to send jiaoliao webhook
|
||||
type SLACKWriter struct {
|
||||
WebhookURL string `json:"webhookurl"`
|
||||
Level int `json:"level"`
|
||||
}
|
||||
|
||||
// newSLACKWriter create jiaoliao writer.
|
||||
func newSLACKWriter() Logger {
|
||||
return &SLACKWriter{Level: LevelTrace}
|
||||
}
|
||||
|
||||
// Init SLACKWriter with json config string
|
||||
func (s *SLACKWriter) Init(jsonconfig string) error {
|
||||
return json.Unmarshal([]byte(jsonconfig), s)
|
||||
}
|
||||
|
||||
// WriteMsg write message in smtp writer.
|
||||
// it will send an email with subject and only this message.
|
||||
func (s *SLACKWriter) WriteMsg(when time.Time, msg string, level int) error {
|
||||
if level > s.Level {
|
||||
return nil
|
||||
}
|
||||
|
||||
text := fmt.Sprintf("{\"text\": \"%s %s\"}", when.Format("2006-01-02 15:04:05"), msg)
|
||||
|
||||
form := url.Values{}
|
||||
form.Add("payload", text)
|
||||
|
||||
resp, err := http.PostForm(s.WebhookURL, form)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return fmt.Errorf("Post webhook failed %s %d", resp.Status, resp.StatusCode)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Flush implementing method. empty.
|
||||
func (s *SLACKWriter) Flush() {
|
||||
}
|
||||
|
||||
// Destroy implementing method. empty.
|
||||
func (s *SLACKWriter) Destroy() {
|
||||
}
|
||||
|
||||
func init() {
|
||||
Register(AdapterSlack, newSLACKWriter)
|
||||
}
|
17
logs/smtp.go
17
logs/smtp.go
@ -52,11 +52,7 @@ func newSMTPWriter() Logger {
|
||||
// "level":LevelError
|
||||
// }
|
||||
func (s *SMTPWriter) Init(jsonconfig string) error {
|
||||
err := json.Unmarshal([]byte(jsonconfig), s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return json.Unmarshal([]byte(jsonconfig), s)
|
||||
}
|
||||
|
||||
func (s *SMTPWriter) getSMTPAuth(host string) smtp.Auth {
|
||||
@ -106,7 +102,7 @@ func (s *SMTPWriter) sendMail(hostAddressWithPort string, auth smtp.Auth, fromAd
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = w.Write([]byte(msgContent))
|
||||
_, err = w.Write(msgContent)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -116,12 +112,7 @@ func (s *SMTPWriter) sendMail(hostAddressWithPort string, auth smtp.Auth, fromAd
|
||||
return err
|
||||
}
|
||||
|
||||
err = client.Quit()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
return client.Quit()
|
||||
}
|
||||
|
||||
// WriteMsg write message in smtp writer.
|
||||
@ -147,12 +138,10 @@ func (s *SMTPWriter) WriteMsg(when time.Time, msg string, level int) error {
|
||||
|
||||
// Flush implementing method. empty.
|
||||
func (s *SMTPWriter) Flush() {
|
||||
return
|
||||
}
|
||||
|
||||
// Destroy implementing method. empty.
|
||||
func (s *SMTPWriter) Destroy() {
|
||||
return
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
11
namespace.go
11
namespace.go
@ -267,13 +267,12 @@ func addPrefix(t *Tree, prefix string) {
|
||||
addPrefix(t.wildcard, prefix)
|
||||
}
|
||||
for _, l := range t.leaves {
|
||||
if c, ok := l.runObject.(*controllerInfo); ok {
|
||||
if c, ok := l.runObject.(*ControllerInfo); ok {
|
||||
if !strings.HasPrefix(c.pattern, prefix) {
|
||||
c.pattern = prefix + c.pattern
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// NSCond is Namespace Condition
|
||||
@ -284,16 +283,16 @@ func NSCond(cond namespaceCond) LinkNamespace {
|
||||
}
|
||||
|
||||
// NSBefore Namespace BeforeRouter filter
|
||||
func NSBefore(filiterList ...FilterFunc) LinkNamespace {
|
||||
func NSBefore(filterList ...FilterFunc) LinkNamespace {
|
||||
return func(ns *Namespace) {
|
||||
ns.Filter("before", filiterList...)
|
||||
ns.Filter("before", filterList...)
|
||||
}
|
||||
}
|
||||
|
||||
// NSAfter add Namespace FinishRouter filter
|
||||
func NSAfter(filiterList ...FilterFunc) LinkNamespace {
|
||||
func NSAfter(filterList ...FilterFunc) LinkNamespace {
|
||||
return func(ns *Namespace) {
|
||||
ns.Filter("after", filiterList...)
|
||||
ns.Filter("after", filterList...)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -139,10 +139,7 @@ func TestNamespaceCond(t *testing.T) {
|
||||
|
||||
ns := NewNamespace("/v2")
|
||||
ns.Cond(func(ctx *context.Context) bool {
|
||||
if ctx.Input.Domain() == "beego.me" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
return ctx.Input.Domain() == "beego.me"
|
||||
}).
|
||||
AutoRouter(&TestController{})
|
||||
AddNamespace(ns)
|
||||
|
@ -150,7 +150,7 @@ func (d *commandSyncDb) Run() error {
|
||||
}
|
||||
|
||||
for _, fi := range mi.fields.fieldsDB {
|
||||
if _, ok := columns[fi.column]; ok == false {
|
||||
if _, ok := columns[fi.column]; !ok {
|
||||
fields = append(fields, fi)
|
||||
}
|
||||
}
|
||||
@ -175,7 +175,7 @@ func (d *commandSyncDb) Run() error {
|
||||
}
|
||||
|
||||
for _, idx := range indexes[mi.table] {
|
||||
if d.al.DbBaser.IndexExists(db, idx.Table, idx.Name) == false {
|
||||
if !d.al.DbBaser.IndexExists(db, idx.Table, idx.Name) {
|
||||
if !d.noInfo {
|
||||
fmt.Printf("create index `%s` for table `%s`\n", idx.Name, idx.Table)
|
||||
}
|
||||
|
@ -89,7 +89,7 @@ checkColumn:
|
||||
col = T["float64"]
|
||||
case TypeDecimalField:
|
||||
s := T["float64-decimal"]
|
||||
if strings.Index(s, "%d") == -1 {
|
||||
if !strings.Contains(s, "%d") {
|
||||
col = s
|
||||
} else {
|
||||
col = fmt.Sprintf(s, fi.digits, fi.decimals)
|
||||
@ -120,7 +120,7 @@ func getColumnAddQuery(al *alias, fi *fieldInfo) string {
|
||||
Q := al.DbBaser.TableQuote()
|
||||
typ := getColumnTyp(al, fi)
|
||||
|
||||
if fi.null == false {
|
||||
if !fi.null {
|
||||
typ += " " + "NOT NULL"
|
||||
}
|
||||
|
||||
@ -172,7 +172,7 @@ func getDbCreateSQL(al *alias) (sqls []string, tableIndexes map[string][]dbIndex
|
||||
} else {
|
||||
column += col
|
||||
|
||||
if fi.null == false {
|
||||
if !fi.null {
|
||||
column += " " + "NOT NULL"
|
||||
}
|
||||
|
||||
@ -192,7 +192,7 @@ func getDbCreateSQL(al *alias) (sqls []string, tableIndexes map[string][]dbIndex
|
||||
}
|
||||
}
|
||||
|
||||
if strings.Index(column, "%COL%") != -1 {
|
||||
if strings.Contains(column, "%COL%") {
|
||||
column = strings.Replace(column, "%COL%", fi.column, -1)
|
||||
}
|
||||
|
||||
|
68
orm/db.go
68
orm/db.go
@ -48,6 +48,7 @@ var (
|
||||
"lte": true,
|
||||
"eq": true,
|
||||
"nq": true,
|
||||
"ne": true,
|
||||
"startswith": true,
|
||||
"endswith": true,
|
||||
"istartswith": true,
|
||||
@ -86,7 +87,7 @@ func (d *dbBase) collectValues(mi *modelInfo, ind reflect.Value, cols []string,
|
||||
} else {
|
||||
panic(fmt.Errorf("wrong db field/column name `%s` for model `%s`", column, mi.fullName))
|
||||
}
|
||||
if fi.dbcol == false || fi.auto && skipAuto {
|
||||
if !fi.dbcol || fi.auto && skipAuto {
|
||||
continue
|
||||
}
|
||||
value, err := d.collectFieldValue(mi, fi, ind, insert, tz)
|
||||
@ -223,7 +224,7 @@ func (d *dbBase) collectFieldValue(mi *modelInfo, fi *fieldInfo, ind reflect.Val
|
||||
value = nil
|
||||
}
|
||||
}
|
||||
if fi.null == false && value == nil {
|
||||
if !fi.null && value == nil {
|
||||
return nil, fmt.Errorf("field `%s` cannot be NULL", fi.fullName)
|
||||
}
|
||||
}
|
||||
@ -270,7 +271,7 @@ func (d *dbBase) PrepareInsert(q dbQuerier, mi *modelInfo) (stmtQuerier, string,
|
||||
dbcols := make([]string, 0, len(mi.fields.dbcols))
|
||||
marks := make([]string, 0, len(mi.fields.dbcols))
|
||||
for _, fi := range mi.fields.fieldsDB {
|
||||
if fi.auto == false {
|
||||
if !fi.auto {
|
||||
dbcols = append(dbcols, fi.column)
|
||||
marks = append(marks, "?")
|
||||
}
|
||||
@ -310,7 +311,7 @@ func (d *dbBase) InsertStmt(stmt stmtQuerier, mi *modelInfo, ind reflect.Value,
|
||||
}
|
||||
|
||||
// query sql ,read records and persist in dbBaser.
|
||||
func (d *dbBase) Read(q dbQuerier, mi *modelInfo, ind reflect.Value, tz *time.Location, cols []string) error {
|
||||
func (d *dbBase) Read(q dbQuerier, mi *modelInfo, ind reflect.Value, tz *time.Location, cols []string, isForUpdate bool) error {
|
||||
var whereCols []string
|
||||
var args []interface{}
|
||||
|
||||
@ -325,7 +326,7 @@ func (d *dbBase) Read(q dbQuerier, mi *modelInfo, ind reflect.Value, tz *time.Lo
|
||||
} else {
|
||||
// default use pk value as where condtion.
|
||||
pkColumn, pkValue, ok := getExistPk(mi, ind)
|
||||
if ok == false {
|
||||
if !ok {
|
||||
return ErrMissPK
|
||||
}
|
||||
whereCols = []string{pkColumn}
|
||||
@ -341,7 +342,12 @@ func (d *dbBase) Read(q dbQuerier, mi *modelInfo, ind reflect.Value, tz *time.Lo
|
||||
sep = fmt.Sprintf("%s = ? AND %s", Q, Q)
|
||||
wheres := strings.Join(whereCols, sep)
|
||||
|
||||
query := fmt.Sprintf("SELECT %s%s%s FROM %s%s%s WHERE %s%s%s = ?", Q, sels, Q, Q, mi.table, Q, Q, wheres, Q)
|
||||
forUpdate := ""
|
||||
if isForUpdate {
|
||||
forUpdate = "FOR UPDATE"
|
||||
}
|
||||
|
||||
query := fmt.Sprintf("SELECT %s%s%s FROM %s%s%s WHERE %s%s%s = ? %s", Q, sels, Q, Q, mi.table, Q, Q, wheres, Q, forUpdate)
|
||||
|
||||
refs := make([]interface{}, colsNum)
|
||||
for i := range refs {
|
||||
@ -586,7 +592,7 @@ func (d *dbBase) InsertOrUpdate(q dbQuerier, mi *modelInfo, ind reflect.Value, a
|
||||
row := q.QueryRow(query, values...)
|
||||
var id int64
|
||||
err = row.Scan(&id)
|
||||
if err.Error() == `pq: syntax error at or near "ON"` {
|
||||
if err != nil && err.Error() == `pq: syntax error at or near "ON"` {
|
||||
err = fmt.Errorf("postgres version must 9.5 or higher")
|
||||
}
|
||||
return id, err
|
||||
@ -595,7 +601,7 @@ func (d *dbBase) InsertOrUpdate(q dbQuerier, mi *modelInfo, ind reflect.Value, a
|
||||
// execute update sql dbQuerier with given struct reflect.Value.
|
||||
func (d *dbBase) Update(q dbQuerier, mi *modelInfo, ind reflect.Value, tz *time.Location, cols []string) (int64, error) {
|
||||
pkName, pkValue, ok := getExistPk(mi, ind)
|
||||
if ok == false {
|
||||
if !ok {
|
||||
return 0, ErrMissPK
|
||||
}
|
||||
|
||||
@ -634,18 +640,36 @@ func (d *dbBase) Update(q dbQuerier, mi *modelInfo, ind reflect.Value, tz *time.
|
||||
|
||||
// execute delete sql dbQuerier with given struct reflect.Value.
|
||||
// delete index is pk.
|
||||
func (d *dbBase) Delete(q dbQuerier, mi *modelInfo, ind reflect.Value, tz *time.Location) (int64, error) {
|
||||
pkName, pkValue, ok := getExistPk(mi, ind)
|
||||
if ok == false {
|
||||
return 0, ErrMissPK
|
||||
func (d *dbBase) Delete(q dbQuerier, mi *modelInfo, ind reflect.Value, tz *time.Location, cols []string) (int64, error) {
|
||||
var whereCols []string
|
||||
var args []interface{}
|
||||
// if specify cols length > 0, then use it for where condition.
|
||||
if len(cols) > 0 {
|
||||
var err error
|
||||
whereCols = make([]string, 0, len(cols))
|
||||
args, _, err = d.collectValues(mi, ind, cols, false, false, &whereCols, tz)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
} else {
|
||||
// default use pk value as where condtion.
|
||||
pkColumn, pkValue, ok := getExistPk(mi, ind)
|
||||
if !ok {
|
||||
return 0, ErrMissPK
|
||||
}
|
||||
whereCols = []string{pkColumn}
|
||||
args = append(args, pkValue)
|
||||
}
|
||||
|
||||
Q := d.ins.TableQuote()
|
||||
|
||||
query := fmt.Sprintf("DELETE FROM %s%s%s WHERE %s%s%s = ?", Q, mi.table, Q, Q, pkName, Q)
|
||||
sep := fmt.Sprintf("%s = ? AND %s", Q, Q)
|
||||
wheres := strings.Join(whereCols, sep)
|
||||
|
||||
query := fmt.Sprintf("DELETE FROM %s%s%s WHERE %s%s%s = ?", Q, mi.table, Q, Q, wheres, Q)
|
||||
|
||||
d.ins.ReplaceMarks(&query)
|
||||
res, err := q.Exec(query, pkValue)
|
||||
res, err := q.Exec(query, args...)
|
||||
if err == nil {
|
||||
num, err := res.RowsAffected()
|
||||
if err != nil {
|
||||
@ -659,7 +683,7 @@ func (d *dbBase) Delete(q dbQuerier, mi *modelInfo, ind reflect.Value, tz *time.
|
||||
ind.FieldByIndex(mi.fields.pk.fieldIndex).SetInt(0)
|
||||
}
|
||||
}
|
||||
err := d.deleteRels(q, mi, []interface{}{pkValue}, tz)
|
||||
err := d.deleteRels(q, mi, args, tz)
|
||||
if err != nil {
|
||||
return num, err
|
||||
}
|
||||
@ -675,7 +699,7 @@ func (d *dbBase) UpdateBatch(q dbQuerier, qs *querySet, mi *modelInfo, cond *Con
|
||||
columns := make([]string, 0, len(params))
|
||||
values := make([]interface{}, 0, len(params))
|
||||
for col, val := range params {
|
||||
if fi, ok := mi.fields.GetByAny(col); ok == false || fi.dbcol == false {
|
||||
if fi, ok := mi.fields.GetByAny(col); !ok || !fi.dbcol {
|
||||
panic(fmt.Errorf("wrong field/column name `%s`", col))
|
||||
} else {
|
||||
columns = append(columns, fi.column)
|
||||
@ -905,7 +929,7 @@ func (d *dbBase) ReadBatch(q dbQuerier, qs *querySet, mi *modelInfo, cond *Condi
|
||||
if hasRel {
|
||||
for _, fi := range mi.fields.fieldsDB {
|
||||
if fi.fieldType&IsRelField > 0 {
|
||||
if maps[fi.column] == false {
|
||||
if !maps[fi.column] {
|
||||
tCols = append(tCols, fi.column)
|
||||
}
|
||||
}
|
||||
@ -963,7 +987,7 @@ func (d *dbBase) ReadBatch(q dbQuerier, qs *querySet, mi *modelInfo, cond *Condi
|
||||
|
||||
var cnt int64
|
||||
for rs.Next() {
|
||||
if one && cnt == 0 || one == false {
|
||||
if one && cnt == 0 || !one {
|
||||
if err := rs.Scan(refs...); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@ -1043,7 +1067,7 @@ func (d *dbBase) ReadBatch(q dbQuerier, qs *querySet, mi *modelInfo, cond *Condi
|
||||
cnt++
|
||||
}
|
||||
|
||||
if one == false {
|
||||
if !one {
|
||||
if cnt > 0 {
|
||||
ind.Set(slice)
|
||||
} else {
|
||||
@ -1333,7 +1357,7 @@ end:
|
||||
func (d *dbBase) setFieldValue(fi *fieldInfo, value interface{}, field reflect.Value) (interface{}, error) {
|
||||
|
||||
fieldType := fi.fieldType
|
||||
isNative := fi.isFielder == false
|
||||
isNative := !fi.isFielder
|
||||
|
||||
setValue:
|
||||
switch {
|
||||
@ -1509,7 +1533,7 @@ setValue:
|
||||
}
|
||||
}
|
||||
|
||||
if isNative == false {
|
||||
if !isNative {
|
||||
fd := field.Addr().Interface().(Fielder)
|
||||
err := fd.SetRaw(value)
|
||||
if err != nil {
|
||||
@ -1570,7 +1594,7 @@ func (d *dbBase) ReadValues(q dbQuerier, qs *querySet, mi *modelInfo, cond *Cond
|
||||
infos = make([]*fieldInfo, 0, len(exprs))
|
||||
for _, ex := range exprs {
|
||||
index, name, fi, suc := tables.parseExprs(mi, strings.Split(ex, ExprSep))
|
||||
if suc == false {
|
||||
if !suc {
|
||||
panic(fmt.Errorf("unknown field/column name `%s`", ex))
|
||||
}
|
||||
cols = append(cols, fmt.Sprintf("%s.%s%s%s %s%s%s", index, Q, fi.column, Q, Q, name, Q))
|
||||
|
@ -60,6 +60,8 @@ var (
|
||||
"sqlite3": DRSqlite,
|
||||
"tidb": DRTiDB,
|
||||
"oracle": DROracle,
|
||||
"oci8": DROracle, // github.com/mattn/go-oci8
|
||||
"ora": DROracle, //https://github.com/rana/ora
|
||||
}
|
||||
dbBasers = map[DriverType]dbBaser{
|
||||
DRMySQL: newdbBaseMysql(),
|
||||
@ -80,7 +82,7 @@ type _dbCache struct {
|
||||
func (ac *_dbCache) add(name string, al *alias) (added bool) {
|
||||
ac.mux.Lock()
|
||||
defer ac.mux.Unlock()
|
||||
if _, ok := ac.cache[name]; ok == false {
|
||||
if _, ok := ac.cache[name]; !ok {
|
||||
ac.cache[name] = al
|
||||
added = true
|
||||
}
|
||||
@ -186,7 +188,7 @@ func addAliasWthDB(aliasName, driverName string, db *sql.DB) (*alias, error) {
|
||||
return nil, fmt.Errorf("register db Ping `%s`, %s", aliasName, err.Error())
|
||||
}
|
||||
|
||||
if dataBaseCache.add(aliasName, al) == false {
|
||||
if !dataBaseCache.add(aliasName, al) {
|
||||
return nil, fmt.Errorf("DataBase alias name `%s` already registered, cannot reuse", aliasName)
|
||||
}
|
||||
|
||||
@ -244,7 +246,7 @@ end:
|
||||
|
||||
// RegisterDriver Register a database driver use specify driver name, this can be definition the driver is which database type.
|
||||
func RegisterDriver(driverName string, typ DriverType) error {
|
||||
if t, ok := drivers[driverName]; ok == false {
|
||||
if t, ok := drivers[driverName]; !ok {
|
||||
drivers[driverName] = typ
|
||||
} else {
|
||||
if t != typ {
|
||||
|
@ -16,6 +16,8 @@ package orm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// mysql operators.
|
||||
@ -96,6 +98,83 @@ func (d *dbBaseMysql) IndexExists(db dbQuerier, table string, name string) bool
|
||||
return cnt > 0
|
||||
}
|
||||
|
||||
// InsertOrUpdate a row
|
||||
// If your primary key or unique column conflict will update
|
||||
// If no will insert
|
||||
// Add "`" for mysql sql building
|
||||
func (d *dbBaseMysql) InsertOrUpdate(q dbQuerier, mi *modelInfo, ind reflect.Value, a *alias, args ...string) (int64, error) {
|
||||
|
||||
iouStr := ""
|
||||
argsMap := map[string]string{}
|
||||
|
||||
iouStr = "ON DUPLICATE KEY UPDATE"
|
||||
|
||||
//Get on the key-value pairs
|
||||
for _, v := range args {
|
||||
kv := strings.Split(v, "=")
|
||||
if len(kv) == 2 {
|
||||
argsMap[strings.ToLower(kv[0])] = kv[1]
|
||||
}
|
||||
}
|
||||
|
||||
isMulti := false
|
||||
names := make([]string, 0, len(mi.fields.dbcols)-1)
|
||||
Q := d.ins.TableQuote()
|
||||
values, _, err := d.collectValues(mi, ind, mi.fields.dbcols, true, true, &names, a.TZ)
|
||||
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
marks := make([]string, len(names))
|
||||
updateValues := make([]interface{}, 0)
|
||||
updates := make([]string, len(names))
|
||||
|
||||
for i, v := range names {
|
||||
marks[i] = "?"
|
||||
valueStr := argsMap[strings.ToLower(v)]
|
||||
if valueStr != "" {
|
||||
updates[i] = "`" + v + "`" + "=" + valueStr
|
||||
} else {
|
||||
updates[i] = "`" + v + "`" + "=?"
|
||||
updateValues = append(updateValues, values[i])
|
||||
}
|
||||
}
|
||||
|
||||
values = append(values, updateValues...)
|
||||
|
||||
sep := fmt.Sprintf("%s, %s", Q, Q)
|
||||
qmarks := strings.Join(marks, ", ")
|
||||
qupdates := strings.Join(updates, ", ")
|
||||
columns := strings.Join(names, sep)
|
||||
|
||||
multi := len(values) / len(names)
|
||||
|
||||
if isMulti {
|
||||
qmarks = strings.Repeat(qmarks+"), (", multi-1) + qmarks
|
||||
}
|
||||
//conflitValue maybe is a int,can`t use fmt.Sprintf
|
||||
query := fmt.Sprintf("INSERT INTO %s%s%s (%s%s%s) VALUES (%s) %s "+qupdates, Q, mi.table, Q, Q, columns, Q, qmarks, iouStr)
|
||||
|
||||
d.ins.ReplaceMarks(&query)
|
||||
|
||||
if isMulti || !d.ins.HasReturningID(mi, &query) {
|
||||
res, err := q.Exec(query, values...)
|
||||
if err == nil {
|
||||
if isMulti {
|
||||
return res.RowsAffected()
|
||||
}
|
||||
return res.LastInsertId()
|
||||
}
|
||||
return 0, err
|
||||
}
|
||||
|
||||
row := q.QueryRow(query, values...)
|
||||
var id int64
|
||||
err = row.Scan(&id)
|
||||
return id, err
|
||||
}
|
||||
|
||||
// create new mysql dbBaser.
|
||||
func newdbBaseMysql() dbBaser {
|
||||
b := new(dbBaseMysql)
|
||||
|
@ -134,7 +134,7 @@ func (d *dbBaseSqlite) IndexExists(db dbQuerier, table string, name string) bool
|
||||
defer rows.Close()
|
||||
for rows.Next() {
|
||||
var tmp, index sql.NullString
|
||||
rows.Scan(&tmp, &index, &tmp)
|
||||
rows.Scan(&tmp, &index, &tmp, &tmp, &tmp)
|
||||
if name == index.String {
|
||||
return true
|
||||
}
|
||||
|
@ -63,7 +63,7 @@ func (t *dbTables) set(names []string, mi *modelInfo, fi *fieldInfo, inner bool)
|
||||
// add table info to collection.
|
||||
func (t *dbTables) add(names []string, mi *modelInfo, fi *fieldInfo, inner bool) (*dbTable, bool) {
|
||||
name := strings.Join(names, ExprSep)
|
||||
if _, ok := t.tablesM[name]; ok == false {
|
||||
if _, ok := t.tablesM[name]; !ok {
|
||||
i := len(t.tables) + 1
|
||||
jt := &dbTable{i, fmt.Sprintf("T%d", i), name, names, false, inner, mi, fi, nil}
|
||||
t.tablesM[name] = jt
|
||||
@ -261,7 +261,7 @@ loopFor:
|
||||
fiN, okN = mmi.fields.GetByAny(exprs[i+1])
|
||||
}
|
||||
|
||||
if isRel && (fi.mi.isThrough == false || num != i) {
|
||||
if isRel && (!fi.mi.isThrough || num != i) {
|
||||
if fi.null || t.skipEnd {
|
||||
inner = false
|
||||
}
|
||||
@ -364,7 +364,7 @@ func (t *dbTables) getCondSQL(cond *Condition, sub bool, tz *time.Location) (whe
|
||||
}
|
||||
|
||||
index, _, fi, suc := t.parseExprs(mi, exprs)
|
||||
if suc == false {
|
||||
if !suc {
|
||||
panic(fmt.Errorf("unknown field/column name `%s`", strings.Join(p.exprs, ExprSep)))
|
||||
}
|
||||
|
||||
@ -383,7 +383,7 @@ func (t *dbTables) getCondSQL(cond *Condition, sub bool, tz *time.Location) (whe
|
||||
}
|
||||
}
|
||||
|
||||
if sub == false && where != "" {
|
||||
if !sub && where != "" {
|
||||
where = "WHERE " + where
|
||||
}
|
||||
|
||||
@ -403,7 +403,7 @@ func (t *dbTables) getGroupSQL(groups []string) (groupSQL string) {
|
||||
exprs := strings.Split(group, ExprSep)
|
||||
|
||||
index, _, fi, suc := t.parseExprs(t.mi, exprs)
|
||||
if suc == false {
|
||||
if !suc {
|
||||
panic(fmt.Errorf("unknown field/column name `%s`", strings.Join(exprs, ExprSep)))
|
||||
}
|
||||
|
||||
@ -432,7 +432,7 @@ func (t *dbTables) getOrderSQL(orders []string) (orderSQL string) {
|
||||
exprs := strings.Split(order, ExprSep)
|
||||
|
||||
index, _, fi, suc := t.parseExprs(t.mi, exprs)
|
||||
if suc == false {
|
||||
if !suc {
|
||||
panic(fmt.Errorf("unknown field/column name `%s`", strings.Join(exprs, ExprSep)))
|
||||
}
|
||||
|
||||
|
@ -41,6 +41,8 @@ func getExistPk(mi *modelInfo, ind reflect.Value) (column string, value interfac
|
||||
vu := v.Int()
|
||||
exist = true
|
||||
value = vu
|
||||
} else if fi.fieldType&IsRelField > 0 {
|
||||
_, value, exist = getExistPk(fi.relModelInfo, reflect.Indirect(v))
|
||||
} else {
|
||||
vu := v.String()
|
||||
exist = vu != ""
|
||||
@ -145,16 +147,18 @@ outFor:
|
||||
if v, ok := arg.(time.Time); ok {
|
||||
if fi != nil && fi.fieldType == TypeDateField {
|
||||
arg = v.In(tz).Format(formatDate)
|
||||
} else if fi.fieldType == TypeDateTimeField {
|
||||
} else if fi != nil && fi.fieldType == TypeDateTimeField {
|
||||
arg = v.In(tz).Format(formatDateTime)
|
||||
} else {
|
||||
} else if fi != nil && fi.fieldType == TypeTimeField {
|
||||
arg = v.In(tz).Format(formatTime)
|
||||
} else {
|
||||
arg = v.In(tz).Format(formatDateTime)
|
||||
}
|
||||
} else {
|
||||
typ := val.Type()
|
||||
name := getFullName(typ)
|
||||
var value interface{}
|
||||
if mmi, ok := modelCache.getByFN(name); ok {
|
||||
if mmi, ok := modelCache.getByFullName(name); ok {
|
||||
if _, vu, exist := getExistPk(mmi, val); exist {
|
||||
value = vu
|
||||
}
|
||||
|
@ -29,39 +29,18 @@ const (
|
||||
|
||||
var (
|
||||
modelCache = &_modelCache{
|
||||
cache: make(map[string]*modelInfo),
|
||||
cacheByFN: make(map[string]*modelInfo),
|
||||
}
|
||||
supportTag = map[string]int{
|
||||
"-": 1,
|
||||
"null": 1,
|
||||
"index": 1,
|
||||
"unique": 1,
|
||||
"pk": 1,
|
||||
"auto": 1,
|
||||
"auto_now": 1,
|
||||
"auto_now_add": 1,
|
||||
"size": 2,
|
||||
"column": 2,
|
||||
"default": 2,
|
||||
"rel": 2,
|
||||
"reverse": 2,
|
||||
"rel_table": 2,
|
||||
"rel_through": 2,
|
||||
"digits": 2,
|
||||
"decimals": 2,
|
||||
"on_delete": 2,
|
||||
"type": 2,
|
||||
cache: make(map[string]*modelInfo),
|
||||
cacheByFullName: make(map[string]*modelInfo),
|
||||
}
|
||||
)
|
||||
|
||||
// model info collection
|
||||
type _modelCache struct {
|
||||
sync.RWMutex
|
||||
orders []string
|
||||
cache map[string]*modelInfo
|
||||
cacheByFN map[string]*modelInfo
|
||||
done bool
|
||||
sync.RWMutex // only used outsite for bootStrap
|
||||
orders []string
|
||||
cache map[string]*modelInfo
|
||||
cacheByFullName map[string]*modelInfo
|
||||
done bool
|
||||
}
|
||||
|
||||
// get all model info
|
||||
@ -88,9 +67,9 @@ func (mc *_modelCache) get(table string) (mi *modelInfo, ok bool) {
|
||||
return
|
||||
}
|
||||
|
||||
// get model info by field name
|
||||
func (mc *_modelCache) getByFN(name string) (mi *modelInfo, ok bool) {
|
||||
mi, ok = mc.cacheByFN[name]
|
||||
// get model info by full name
|
||||
func (mc *_modelCache) getByFullName(name string) (mi *modelInfo, ok bool) {
|
||||
mi, ok = mc.cacheByFullName[name]
|
||||
return
|
||||
}
|
||||
|
||||
@ -98,7 +77,7 @@ func (mc *_modelCache) getByFN(name string) (mi *modelInfo, ok bool) {
|
||||
func (mc *_modelCache) set(table string, mi *modelInfo) *modelInfo {
|
||||
mii := mc.cache[table]
|
||||
mc.cache[table] = mi
|
||||
mc.cacheByFN[mi.fullName] = mi
|
||||
mc.cacheByFullName[mi.fullName] = mi
|
||||
if mii == nil {
|
||||
mc.orders = append(mc.orders, table)
|
||||
}
|
||||
@ -109,7 +88,7 @@ func (mc *_modelCache) set(table string, mi *modelInfo) *modelInfo {
|
||||
func (mc *_modelCache) clean() {
|
||||
mc.orders = make([]string, 0)
|
||||
mc.cache = make(map[string]*modelInfo)
|
||||
mc.cacheByFN = make(map[string]*modelInfo)
|
||||
mc.cacheByFullName = make(map[string]*modelInfo)
|
||||
mc.done = false
|
||||
}
|
||||
|
||||
|
@ -15,7 +15,6 @@
|
||||
package orm
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"reflect"
|
||||
@ -23,24 +22,34 @@ import (
|
||||
)
|
||||
|
||||
// register models.
|
||||
// prefix means table name prefix.
|
||||
func registerModel(prefix string, model interface{}) {
|
||||
// PrefixOrSuffix means table name prefix or suffix.
|
||||
// isPrefix whether the prefix is prefix or suffix
|
||||
func registerModel(PrefixOrSuffix string, model interface{}, isPrefix bool) {
|
||||
val := reflect.ValueOf(model)
|
||||
ind := reflect.Indirect(val)
|
||||
typ := ind.Type()
|
||||
typ := reflect.Indirect(val).Type()
|
||||
|
||||
if val.Kind() != reflect.Ptr {
|
||||
panic(fmt.Errorf("<orm.RegisterModel> cannot use non-ptr model struct `%s`", getFullName(typ)))
|
||||
}
|
||||
// For this case:
|
||||
// u := &User{}
|
||||
// registerModel(&u)
|
||||
if typ.Kind() == reflect.Ptr {
|
||||
panic(fmt.Errorf("<orm.RegisterModel> only allow ptr model struct, it looks you use two reference to the struct `%s`", typ))
|
||||
}
|
||||
|
||||
table := getTableName(val)
|
||||
|
||||
if prefix != "" {
|
||||
table = prefix + table
|
||||
if PrefixOrSuffix != "" {
|
||||
if isPrefix {
|
||||
table = PrefixOrSuffix + table
|
||||
} else {
|
||||
table = table + PrefixOrSuffix
|
||||
}
|
||||
}
|
||||
|
||||
// models's fullname is pkgpath + struct name
|
||||
name := getFullName(typ)
|
||||
if _, ok := modelCache.getByFN(name); ok {
|
||||
if _, ok := modelCache.getByFullName(name); ok {
|
||||
fmt.Printf("<orm.RegisterModel> model `%s` repeat register, must be unique\n", name)
|
||||
os.Exit(2)
|
||||
}
|
||||
@ -50,34 +59,34 @@ func registerModel(prefix string, model interface{}) {
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
info := newModelInfo(val)
|
||||
if info.fields.pk == nil {
|
||||
mi := newModelInfo(val)
|
||||
if mi.fields.pk == nil {
|
||||
outFor:
|
||||
for _, fi := range info.fields.fieldsDB {
|
||||
for _, fi := range mi.fields.fieldsDB {
|
||||
if strings.ToLower(fi.name) == "id" {
|
||||
switch fi.addrValue.Elem().Kind() {
|
||||
case reflect.Int, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint32, reflect.Uint64:
|
||||
fi.auto = true
|
||||
fi.pk = true
|
||||
info.fields.pk = fi
|
||||
mi.fields.pk = fi
|
||||
break outFor
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if info.fields.pk == nil {
|
||||
if mi.fields.pk == nil {
|
||||
fmt.Printf("<orm.RegisterModel> `%s` need a primary key field, default use 'id' if not set\n", name)
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
info.table = table
|
||||
info.pkg = typ.PkgPath()
|
||||
info.model = model
|
||||
info.manual = true
|
||||
mi.table = table
|
||||
mi.pkg = typ.PkgPath()
|
||||
mi.model = model
|
||||
mi.manual = true
|
||||
|
||||
modelCache.set(table, info)
|
||||
modelCache.set(table, mi)
|
||||
}
|
||||
|
||||
// boostrap models
|
||||
@ -85,31 +94,30 @@ func bootStrap() {
|
||||
if modelCache.done {
|
||||
return
|
||||
}
|
||||
|
||||
var (
|
||||
err error
|
||||
models map[string]*modelInfo
|
||||
)
|
||||
|
||||
if dataBaseCache.getDefault() == nil {
|
||||
err = fmt.Errorf("must have one register DataBase alias named `default`")
|
||||
goto end
|
||||
}
|
||||
|
||||
// set rel and reverse model
|
||||
// RelManyToMany set the relTable
|
||||
models = modelCache.all()
|
||||
for _, mi := range models {
|
||||
for _, fi := range mi.fields.columns {
|
||||
if fi.rel || fi.reverse {
|
||||
elm := fi.addrValue.Type().Elem()
|
||||
switch fi.fieldType {
|
||||
case RelReverseMany, RelManyToMany:
|
||||
if fi.fieldType == RelReverseMany || fi.fieldType == RelManyToMany {
|
||||
elm = elm.Elem()
|
||||
}
|
||||
|
||||
// check the rel or reverse model already register
|
||||
name := getFullName(elm)
|
||||
mii, ok := modelCache.getByFN(name)
|
||||
if ok == false || mii.pkg != elm.PkgPath() {
|
||||
err = fmt.Errorf("can not found rel in field `%s`, `%s` may be miss register", fi.fullName, elm.String())
|
||||
mii, ok := modelCache.getByFullName(name)
|
||||
if !ok || mii.pkg != elm.PkgPath() {
|
||||
err = fmt.Errorf("can not find rel in field `%s`, `%s` may be miss register", fi.fullName, elm.String())
|
||||
goto end
|
||||
}
|
||||
fi.relModelInfo = mii
|
||||
@ -117,20 +125,17 @@ func bootStrap() {
|
||||
switch fi.fieldType {
|
||||
case RelManyToMany:
|
||||
if fi.relThrough != "" {
|
||||
msg := fmt.Sprintf("field `%s` wrong rel_through value `%s`", fi.fullName, fi.relThrough)
|
||||
if i := strings.LastIndex(fi.relThrough, "."); i != -1 && len(fi.relThrough) > (i+1) {
|
||||
pn := fi.relThrough[:i]
|
||||
rmi, ok := modelCache.getByFN(fi.relThrough)
|
||||
if ok == false || pn != rmi.pkg {
|
||||
err = errors.New(msg + " cannot find table")
|
||||
rmi, ok := modelCache.getByFullName(fi.relThrough)
|
||||
if !ok || pn != rmi.pkg {
|
||||
err = fmt.Errorf("field `%s` wrong rel_through value `%s` cannot find table", fi.fullName, fi.relThrough)
|
||||
goto end
|
||||
}
|
||||
|
||||
fi.relThroughModelInfo = rmi
|
||||
fi.relTable = rmi.table
|
||||
|
||||
} else {
|
||||
err = errors.New(msg)
|
||||
err = fmt.Errorf("field `%s` wrong rel_through value `%s`", fi.fullName, fi.relThrough)
|
||||
goto end
|
||||
}
|
||||
} else {
|
||||
@ -138,7 +143,6 @@ func bootStrap() {
|
||||
if fi.relTable != "" {
|
||||
i.table = fi.relTable
|
||||
}
|
||||
|
||||
if v := modelCache.set(i.table, i); v != nil {
|
||||
err = fmt.Errorf("the rel table name `%s` already registered, cannot be use, please change one", fi.relTable)
|
||||
goto end
|
||||
@ -153,6 +157,8 @@ func bootStrap() {
|
||||
}
|
||||
}
|
||||
|
||||
// check the rel filed while the relModelInfo also has filed point to current model
|
||||
// if not exist, add a new field to the relModelInfo
|
||||
models = modelCache.all()
|
||||
for _, mi := range models {
|
||||
for _, fi := range mi.fields.fieldsRel {
|
||||
@ -165,8 +171,7 @@ func bootStrap() {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if inModel == false {
|
||||
if !inModel {
|
||||
rmi := fi.relModelInfo
|
||||
ffi := new(fieldInfo)
|
||||
ffi.name = mi.name
|
||||
@ -180,7 +185,7 @@ func bootStrap() {
|
||||
} else {
|
||||
ffi.fieldType = RelReverseMany
|
||||
}
|
||||
if rmi.fields.Add(ffi) == false {
|
||||
if !rmi.fields.Add(ffi) {
|
||||
added := false
|
||||
for cnt := 0; cnt < 5; cnt++ {
|
||||
ffi.name = fmt.Sprintf("%s%d", mi.name, cnt)
|
||||
@ -190,7 +195,7 @@ func bootStrap() {
|
||||
break
|
||||
}
|
||||
}
|
||||
if added == false {
|
||||
if !added {
|
||||
panic(fmt.Errorf("cannot generate auto reverse field info `%s` to `%s`", fi.fullName, ffi.fullName))
|
||||
}
|
||||
}
|
||||
@ -216,7 +221,6 @@ func bootStrap() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if fi.reverseFieldInfoTwo == nil {
|
||||
err = fmt.Errorf("can not find m2m field for m2m model `%s`, ensure your m2m model defined correct",
|
||||
fi.relThroughModelInfo.fullName)
|
||||
@ -244,7 +248,7 @@ func bootStrap() {
|
||||
break mForA
|
||||
}
|
||||
}
|
||||
if found == false {
|
||||
if !found {
|
||||
err = fmt.Errorf("reverse field `%s` not found in model `%s`", fi.fullName, fi.relModelInfo.fullName)
|
||||
goto end
|
||||
}
|
||||
@ -263,7 +267,7 @@ func bootStrap() {
|
||||
break mForB
|
||||
}
|
||||
}
|
||||
if found == false {
|
||||
if !found {
|
||||
mForC:
|
||||
for _, ffi := range fi.relModelInfo.fields.fieldsByType[RelManyToMany] {
|
||||
conditions := fi.relThrough != "" && fi.relThrough == ffi.relThrough ||
|
||||
@ -283,7 +287,7 @@ func bootStrap() {
|
||||
}
|
||||
}
|
||||
}
|
||||
if found == false {
|
||||
if !found {
|
||||
err = fmt.Errorf("reverse field for `%s` not found in model `%s`", fi.fullName, fi.relModelInfo.fullName)
|
||||
goto end
|
||||
}
|
||||
@ -300,17 +304,31 @@ end:
|
||||
|
||||
// RegisterModel register models
|
||||
func RegisterModel(models ...interface{}) {
|
||||
if modelCache.done {
|
||||
panic(fmt.Errorf("RegisterModel must be run before BootStrap"))
|
||||
}
|
||||
RegisterModelWithPrefix("", models...)
|
||||
}
|
||||
|
||||
// RegisterModelWithPrefix register models with a prefix
|
||||
func RegisterModelWithPrefix(prefix string, models ...interface{}) {
|
||||
if modelCache.done {
|
||||
panic(fmt.Errorf("RegisterModel must be run before BootStrap"))
|
||||
panic(fmt.Errorf("RegisterModelWithPrefix must be run before BootStrap"))
|
||||
}
|
||||
|
||||
for _, model := range models {
|
||||
registerModel(prefix, model)
|
||||
registerModel(prefix, model, true)
|
||||
}
|
||||
}
|
||||
|
||||
// RegisterModelWithSuffix register models with a suffix
|
||||
func RegisterModelWithSuffix(suffix string, models ...interface{}) {
|
||||
if modelCache.done {
|
||||
panic(fmt.Errorf("RegisterModelWithSuffix must be run before BootStrap"))
|
||||
}
|
||||
|
||||
for _, model := range models {
|
||||
registerModel(suffix, model, false)
|
||||
}
|
||||
}
|
||||
|
||||
@ -320,7 +338,6 @@ func BootStrap() {
|
||||
if modelCache.done {
|
||||
return
|
||||
}
|
||||
|
||||
modelCache.Lock()
|
||||
defer modelCache.Unlock()
|
||||
bootStrap()
|
||||
|
@ -47,7 +47,7 @@ func (f *fields) Add(fi *fieldInfo) (added bool) {
|
||||
} else {
|
||||
return
|
||||
}
|
||||
if _, ok := f.fieldsByType[fi.fieldType]; ok == false {
|
||||
if _, ok := f.fieldsByType[fi.fieldType]; !ok {
|
||||
f.fieldsByType[fi.fieldType] = make([]*fieldInfo, 0)
|
||||
}
|
||||
f.fieldsByType[fi.fieldType] = append(f.fieldsByType[fi.fieldType], fi)
|
||||
@ -104,7 +104,7 @@ type fieldInfo struct {
|
||||
mi *modelInfo
|
||||
fieldIndex []int
|
||||
fieldType int
|
||||
dbcol bool
|
||||
dbcol bool // table column fk and onetoone
|
||||
inModel bool
|
||||
name string
|
||||
fullName string
|
||||
@ -116,13 +116,13 @@ type fieldInfo struct {
|
||||
null bool
|
||||
index bool
|
||||
unique bool
|
||||
colDefault bool
|
||||
initial StrTo
|
||||
colDefault bool // whether has default tag
|
||||
initial StrTo // store the default value
|
||||
size int
|
||||
toText bool
|
||||
autoNow bool
|
||||
autoNowAdd bool
|
||||
rel bool
|
||||
rel bool // if type equal to RelForeignKey, RelOneToOne, RelManyToMany then true
|
||||
reverse bool
|
||||
reverseField string
|
||||
reverseFieldInfo *fieldInfo
|
||||
@ -134,7 +134,7 @@ type fieldInfo struct {
|
||||
relModelInfo *modelInfo
|
||||
digits int
|
||||
decimals int
|
||||
isFielder bool
|
||||
isFielder bool // implement Fielder interface
|
||||
onDelete string
|
||||
}
|
||||
|
||||
@ -143,7 +143,7 @@ func newFieldInfo(mi *modelInfo, field reflect.Value, sf reflect.StructField, mN
|
||||
var (
|
||||
tag string
|
||||
tagValue string
|
||||
initial StrTo
|
||||
initial StrTo // store the default value
|
||||
fieldType int
|
||||
attrs map[string]bool
|
||||
tags map[string]string
|
||||
@ -152,6 +152,10 @@ func newFieldInfo(mi *modelInfo, field reflect.Value, sf reflect.StructField, mN
|
||||
|
||||
fi = new(fieldInfo)
|
||||
|
||||
// if field which CanAddr is the follow type
|
||||
// A value is addressable if it is an element of a slice,
|
||||
// an element of an addressable array, a field of an
|
||||
// addressable struct, or the result of dereferencing a pointer.
|
||||
addrField = field
|
||||
if field.CanAddr() && field.Kind() != reflect.Ptr {
|
||||
addrField = field.Addr()
|
||||
@ -162,7 +166,7 @@ func newFieldInfo(mi *modelInfo, field reflect.Value, sf reflect.StructField, mN
|
||||
}
|
||||
}
|
||||
|
||||
parseStructTag(sf.Tag.Get(defaultStructTagName), &attrs, &tags)
|
||||
attrs, tags = parseStructTag(sf.Tag.Get(defaultStructTagName))
|
||||
|
||||
if _, ok := attrs["-"]; ok {
|
||||
return nil, errSkipField
|
||||
@ -188,7 +192,7 @@ checkType:
|
||||
}
|
||||
fieldType = f.FieldType()
|
||||
if fieldType&IsRelField > 0 {
|
||||
err = fmt.Errorf("unsupport rel type custom field")
|
||||
err = fmt.Errorf("unsupport type custom field, please refer to https://github.com/astaxie/beego/blob/master/orm/models_fields.go#L24-L42")
|
||||
goto end
|
||||
}
|
||||
default:
|
||||
@ -211,7 +215,7 @@ checkType:
|
||||
}
|
||||
break checkType
|
||||
default:
|
||||
err = fmt.Errorf("error")
|
||||
err = fmt.Errorf("rel only allow these value: fk, one, m2m")
|
||||
goto wrongTag
|
||||
}
|
||||
}
|
||||
@ -231,7 +235,7 @@ checkType:
|
||||
}
|
||||
break checkType
|
||||
default:
|
||||
err = fmt.Errorf("error")
|
||||
err = fmt.Errorf("reverse only allow these value: one, many")
|
||||
goto wrongTag
|
||||
}
|
||||
}
|
||||
@ -261,6 +265,9 @@ checkType:
|
||||
}
|
||||
}
|
||||
|
||||
// check the rel and reverse type
|
||||
// rel should Ptr
|
||||
// reverse should slice []*struct
|
||||
switch fieldType {
|
||||
case RelForeignKey, RelOneToOne, RelReverseOne:
|
||||
if field.Kind() != reflect.Ptr {
|
||||
@ -327,12 +334,12 @@ checkType:
|
||||
switch onDelete {
|
||||
case odCascade, odDoNothing:
|
||||
case odSetDefault:
|
||||
if initial.Exist() == false {
|
||||
if !initial.Exist() {
|
||||
err = errors.New("on_delete: set_default need set field a default value")
|
||||
goto end
|
||||
}
|
||||
case odSetNULL:
|
||||
if fi.null == false {
|
||||
if !fi.null {
|
||||
err = errors.New("on_delete: set_null need set field null")
|
||||
goto end
|
||||
}
|
||||
@ -399,14 +406,12 @@ checkType:
|
||||
|
||||
if fi.auto || fi.pk {
|
||||
if fi.auto {
|
||||
|
||||
switch addrField.Elem().Kind() {
|
||||
case reflect.Int, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint32, reflect.Uint64:
|
||||
default:
|
||||
err = fmt.Errorf("auto primary key only support int, int32, int64, uint, uint32, uint64 but found `%s`", addrField.Elem().Kind())
|
||||
goto end
|
||||
}
|
||||
|
||||
fi.pk = true
|
||||
}
|
||||
fi.null = false
|
||||
@ -418,8 +423,8 @@ checkType:
|
||||
fi.index = false
|
||||
}
|
||||
|
||||
// can not set default for these type
|
||||
if fi.auto || fi.pk || fi.unique || fieldType == TypeTimeField || fieldType == TypeDateField || fieldType == TypeDateTimeField {
|
||||
// can not set default
|
||||
initial.Clear()
|
||||
}
|
||||
|
||||
|
@ -29,31 +29,25 @@ type modelInfo struct {
|
||||
model interface{}
|
||||
fields *fields
|
||||
manual bool
|
||||
addrField reflect.Value
|
||||
addrField reflect.Value //store the original struct value
|
||||
uniques []string
|
||||
isThrough bool
|
||||
}
|
||||
|
||||
// new model info
|
||||
func newModelInfo(val reflect.Value) (info *modelInfo) {
|
||||
|
||||
info = &modelInfo{}
|
||||
info.fields = newFields()
|
||||
|
||||
func newModelInfo(val reflect.Value) (mi *modelInfo) {
|
||||
mi = &modelInfo{}
|
||||
mi.fields = newFields()
|
||||
ind := reflect.Indirect(val)
|
||||
typ := ind.Type()
|
||||
|
||||
info.addrField = val
|
||||
|
||||
info.name = typ.Name()
|
||||
info.fullName = getFullName(typ)
|
||||
|
||||
addModelFields(info, ind, "", []int{})
|
||||
|
||||
mi.addrField = val
|
||||
mi.name = ind.Type().Name()
|
||||
mi.fullName = getFullName(ind.Type())
|
||||
addModelFields(mi, ind, "", []int{})
|
||||
return
|
||||
}
|
||||
|
||||
func addModelFields(info *modelInfo, ind reflect.Value, mName string, index []int) {
|
||||
// index: FieldByIndex returns the nested field corresponding to index
|
||||
func addModelFields(mi *modelInfo, ind reflect.Value, mName string, index []int) {
|
||||
var (
|
||||
err error
|
||||
fi *fieldInfo
|
||||
@ -63,43 +57,39 @@ func addModelFields(info *modelInfo, ind reflect.Value, mName string, index []in
|
||||
for i := 0; i < ind.NumField(); i++ {
|
||||
field := ind.Field(i)
|
||||
sf = ind.Type().Field(i)
|
||||
// if the field is unexported skip
|
||||
if sf.PkgPath != "" {
|
||||
continue
|
||||
}
|
||||
// add anonymous struct fields
|
||||
if sf.Anonymous {
|
||||
addModelFields(info, field, mName+"."+sf.Name, append(index, i))
|
||||
addModelFields(mi, field, mName+"."+sf.Name, append(index, i))
|
||||
continue
|
||||
}
|
||||
|
||||
fi, err = newFieldInfo(info, field, sf, mName)
|
||||
|
||||
if err != nil {
|
||||
if err == errSkipField {
|
||||
err = nil
|
||||
continue
|
||||
}
|
||||
fi, err = newFieldInfo(mi, field, sf, mName)
|
||||
if err == errSkipField {
|
||||
err = nil
|
||||
continue
|
||||
} else if err != nil {
|
||||
break
|
||||
}
|
||||
|
||||
added := info.fields.Add(fi)
|
||||
if added == false {
|
||||
//record current field index
|
||||
fi.fieldIndex = append(index, i)
|
||||
fi.mi = mi
|
||||
fi.inModel = true
|
||||
if !mi.fields.Add(fi) {
|
||||
err = fmt.Errorf("duplicate column name: %s", fi.column)
|
||||
break
|
||||
}
|
||||
|
||||
if fi.pk {
|
||||
if info.fields.pk != nil {
|
||||
if mi.fields.pk != nil {
|
||||
err = fmt.Errorf("one model must have one pk field only")
|
||||
break
|
||||
} else {
|
||||
info.fields.pk = fi
|
||||
mi.fields.pk = fi
|
||||
}
|
||||
}
|
||||
|
||||
fi.fieldIndex = append(index, i)
|
||||
fi.mi = info
|
||||
fi.inModel = true
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
@ -110,23 +100,23 @@ func addModelFields(info *modelInfo, ind reflect.Value, mName string, index []in
|
||||
|
||||
// combine related model info to new model info.
|
||||
// prepare for relation models query.
|
||||
func newM2MModelInfo(m1, m2 *modelInfo) (info *modelInfo) {
|
||||
info = new(modelInfo)
|
||||
info.fields = newFields()
|
||||
info.table = m1.table + "_" + m2.table + "s"
|
||||
info.name = camelString(info.table)
|
||||
info.fullName = m1.pkg + "." + info.name
|
||||
func newM2MModelInfo(m1, m2 *modelInfo) (mi *modelInfo) {
|
||||
mi = new(modelInfo)
|
||||
mi.fields = newFields()
|
||||
mi.table = m1.table + "_" + m2.table + "s"
|
||||
mi.name = camelString(mi.table)
|
||||
mi.fullName = m1.pkg + "." + mi.name
|
||||
|
||||
fa := new(fieldInfo)
|
||||
f1 := new(fieldInfo)
|
||||
f2 := new(fieldInfo)
|
||||
fa := new(fieldInfo) // pk
|
||||
f1 := new(fieldInfo) // m1 table RelForeignKey
|
||||
f2 := new(fieldInfo) // m2 table RelForeignKey
|
||||
fa.fieldType = TypeBigIntegerField
|
||||
fa.auto = true
|
||||
fa.pk = true
|
||||
fa.dbcol = true
|
||||
fa.name = "Id"
|
||||
fa.column = "id"
|
||||
fa.fullName = info.fullName + "." + fa.name
|
||||
fa.fullName = mi.fullName + "." + fa.name
|
||||
|
||||
f1.dbcol = true
|
||||
f2.dbcol = true
|
||||
@ -134,8 +124,8 @@ func newM2MModelInfo(m1, m2 *modelInfo) (info *modelInfo) {
|
||||
f2.fieldType = RelForeignKey
|
||||
f1.name = camelString(m1.table)
|
||||
f2.name = camelString(m2.table)
|
||||
f1.fullName = info.fullName + "." + f1.name
|
||||
f2.fullName = info.fullName + "." + f2.name
|
||||
f1.fullName = mi.fullName + "." + f1.name
|
||||
f2.fullName = mi.fullName + "." + f2.name
|
||||
f1.column = m1.table + "_id"
|
||||
f2.column = m2.table + "_id"
|
||||
f1.rel = true
|
||||
@ -144,14 +134,14 @@ func newM2MModelInfo(m1, m2 *modelInfo) (info *modelInfo) {
|
||||
f2.relTable = m2.table
|
||||
f1.relModelInfo = m1
|
||||
f2.relModelInfo = m2
|
||||
f1.mi = info
|
||||
f2.mi = info
|
||||
f1.mi = mi
|
||||
f2.mi = mi
|
||||
|
||||
info.fields.Add(fa)
|
||||
info.fields.Add(f1)
|
||||
info.fields.Add(f2)
|
||||
info.fields.pk = fa
|
||||
mi.fields.Add(fa)
|
||||
mi.fields.Add(f1)
|
||||
mi.fields.Add(f2)
|
||||
mi.fields.pk = fa
|
||||
|
||||
info.uniques = []string{f1.column, f2.column}
|
||||
mi.uniques = []string{f1.column, f2.column}
|
||||
return
|
||||
}
|
||||
|
@ -406,6 +406,11 @@ type UintPk struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
type PtrPk struct {
|
||||
ID *IntegerPk `orm:"pk;rel(one)"`
|
||||
Positive bool
|
||||
}
|
||||
|
||||
var DBARGS = struct {
|
||||
Driver string
|
||||
Source string
|
||||
|
@ -22,25 +22,47 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// 1 is attr
|
||||
// 2 is tag
|
||||
var supportTag = map[string]int{
|
||||
"-": 1,
|
||||
"null": 1,
|
||||
"index": 1,
|
||||
"unique": 1,
|
||||
"pk": 1,
|
||||
"auto": 1,
|
||||
"auto_now": 1,
|
||||
"auto_now_add": 1,
|
||||
"size": 2,
|
||||
"column": 2,
|
||||
"default": 2,
|
||||
"rel": 2,
|
||||
"reverse": 2,
|
||||
"rel_table": 2,
|
||||
"rel_through": 2,
|
||||
"digits": 2,
|
||||
"decimals": 2,
|
||||
"on_delete": 2,
|
||||
"type": 2,
|
||||
}
|
||||
|
||||
// get reflect.Type name with package path.
|
||||
func getFullName(typ reflect.Type) string {
|
||||
return typ.PkgPath() + "." + typ.Name()
|
||||
}
|
||||
|
||||
// get table name. method, or field name. auto snaked.
|
||||
// getTableName get struct table name.
|
||||
// If the struct implement the TableName, then get the result as tablename
|
||||
// else use the struct name which will apply snakeString.
|
||||
func getTableName(val reflect.Value) string {
|
||||
ind := reflect.Indirect(val)
|
||||
fun := val.MethodByName("TableName")
|
||||
if fun.IsValid() {
|
||||
if fun := val.MethodByName("TableName"); fun.IsValid() {
|
||||
vals := fun.Call([]reflect.Value{})
|
||||
if len(vals) > 0 {
|
||||
val := vals[0]
|
||||
if val.Kind() == reflect.String {
|
||||
return val.String()
|
||||
}
|
||||
// has return and the first val is string
|
||||
if len(vals) > 0 && vals[0].Kind() == reflect.String {
|
||||
return vals[0].String()
|
||||
}
|
||||
}
|
||||
return snakeString(ind.Type().Name())
|
||||
return snakeString(reflect.Indirect(val).Type().Name())
|
||||
}
|
||||
|
||||
// get table engine, mysiam or innodb.
|
||||
@ -48,11 +70,8 @@ func getTableEngine(val reflect.Value) string {
|
||||
fun := val.MethodByName("TableEngine")
|
||||
if fun.IsValid() {
|
||||
vals := fun.Call([]reflect.Value{})
|
||||
if len(vals) > 0 {
|
||||
val := vals[0]
|
||||
if val.Kind() == reflect.String {
|
||||
return val.String()
|
||||
}
|
||||
if len(vals) > 0 && vals[0].Kind() == reflect.String {
|
||||
return vals[0].String()
|
||||
}
|
||||
}
|
||||
return ""
|
||||
@ -63,12 +82,9 @@ func getTableIndex(val reflect.Value) [][]string {
|
||||
fun := val.MethodByName("TableIndex")
|
||||
if fun.IsValid() {
|
||||
vals := fun.Call([]reflect.Value{})
|
||||
if len(vals) > 0 {
|
||||
val := vals[0]
|
||||
if val.CanInterface() {
|
||||
if d, ok := val.Interface().([][]string); ok {
|
||||
return d
|
||||
}
|
||||
if len(vals) > 0 && vals[0].CanInterface() {
|
||||
if d, ok := vals[0].Interface().([][]string); ok {
|
||||
return d
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -80,12 +96,9 @@ func getTableUnique(val reflect.Value) [][]string {
|
||||
fun := val.MethodByName("TableUnique")
|
||||
if fun.IsValid() {
|
||||
vals := fun.Call([]reflect.Value{})
|
||||
if len(vals) > 0 {
|
||||
val := vals[0]
|
||||
if val.CanInterface() {
|
||||
if d, ok := val.Interface().([][]string); ok {
|
||||
return d
|
||||
}
|
||||
if len(vals) > 0 && vals[0].CanInterface() {
|
||||
if d, ok := vals[0].Interface().([][]string); ok {
|
||||
return d
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -189,21 +202,25 @@ func getFieldType(val reflect.Value) (ft int, err error) {
|
||||
}
|
||||
|
||||
// parse struct tag string
|
||||
func parseStructTag(data string, attrs *map[string]bool, tags *map[string]string) {
|
||||
attr := make(map[string]bool)
|
||||
tag := make(map[string]string)
|
||||
func parseStructTag(data string) (attrs map[string]bool, tags map[string]string) {
|
||||
attrs = make(map[string]bool)
|
||||
tags = make(map[string]string)
|
||||
for _, v := range strings.Split(data, defaultStructTagDelim) {
|
||||
if v == "" {
|
||||
continue
|
||||
}
|
||||
v = strings.TrimSpace(v)
|
||||
if t := strings.ToLower(v); supportTag[t] == 1 {
|
||||
attr[t] = true
|
||||
attrs[t] = true
|
||||
} else if i := strings.Index(v, "("); i > 0 && strings.Index(v, ")") == len(v)-1 {
|
||||
name := t[:i]
|
||||
if supportTag[name] == 2 {
|
||||
v = v[i+1 : len(v)-1]
|
||||
tag[name] = v
|
||||
tags[name] = v
|
||||
}
|
||||
} else {
|
||||
DebugLog.Println("unsupport orm tag", v)
|
||||
}
|
||||
}
|
||||
*attrs = attr
|
||||
*tags = tag
|
||||
return
|
||||
}
|
||||
|
39
orm/orm.go
39
orm/orm.go
@ -68,7 +68,7 @@ const (
|
||||
// Define common vars
|
||||
var (
|
||||
Debug = false
|
||||
DebugLog = NewLog(os.Stderr)
|
||||
DebugLog = NewLog(os.Stdout)
|
||||
DefaultRowsLimit = 1000
|
||||
DefaultRelsDepth = 2
|
||||
DefaultTimeLoc = time.Local
|
||||
@ -104,7 +104,7 @@ func (o *orm) getMiInd(md interface{}, needPtr bool) (mi *modelInfo, ind reflect
|
||||
panic(fmt.Errorf("<Ormer> cannot use non-ptr model struct `%s`", getFullName(typ)))
|
||||
}
|
||||
name := getFullName(typ)
|
||||
if mi, ok := modelCache.getByFN(name); ok {
|
||||
if mi, ok := modelCache.getByFullName(name); ok {
|
||||
return mi, ind
|
||||
}
|
||||
panic(fmt.Errorf("<Ormer> table: `%s` not found, maybe not RegisterModel", name))
|
||||
@ -122,18 +122,20 @@ func (o *orm) getFieldInfo(mi *modelInfo, name string) *fieldInfo {
|
||||
// read data to model
|
||||
func (o *orm) Read(md interface{}, cols ...string) error {
|
||||
mi, ind := o.getMiInd(md, true)
|
||||
err := o.alias.DbBaser.Read(o.db, mi, ind, o.alias.TZ, cols)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return o.alias.DbBaser.Read(o.db, mi, ind, o.alias.TZ, cols, false)
|
||||
}
|
||||
|
||||
// read data to model, like Read(), but use "SELECT FOR UPDATE" form
|
||||
func (o *orm) ReadForUpdate(md interface{}, cols ...string) error {
|
||||
mi, ind := o.getMiInd(md, true)
|
||||
return o.alias.DbBaser.Read(o.db, mi, ind, o.alias.TZ, cols, true)
|
||||
}
|
||||
|
||||
// Try to read a row from the database, or insert one if it doesn't exist
|
||||
func (o *orm) ReadOrCreate(md interface{}, col1 string, cols ...string) (bool, int64, error) {
|
||||
cols = append([]string{col1}, cols...)
|
||||
mi, ind := o.getMiInd(md, true)
|
||||
err := o.alias.DbBaser.Read(o.db, mi, ind, o.alias.TZ, cols)
|
||||
err := o.alias.DbBaser.Read(o.db, mi, ind, o.alias.TZ, cols, false)
|
||||
if err == ErrNoRows {
|
||||
// Create
|
||||
id, err := o.Insert(md)
|
||||
@ -143,6 +145,8 @@ func (o *orm) ReadOrCreate(md interface{}, col1 string, cols ...string) (bool, i
|
||||
id, vid := int64(0), ind.FieldByIndex(mi.fields.pk.fieldIndex)
|
||||
if mi.fields.pk.fieldType&IsPositiveIntegerField > 0 {
|
||||
id = int64(vid.Uint())
|
||||
} else if mi.fields.pk.rel {
|
||||
return o.ReadOrCreate(vid.Interface(), mi.fields.pk.relModelInfo.fields.pk.name)
|
||||
} else {
|
||||
id = vid.Int()
|
||||
}
|
||||
@ -226,17 +230,14 @@ func (o *orm) InsertOrUpdate(md interface{}, colConflitAndArgs ...string) (int64
|
||||
// cols set the columns those want to update.
|
||||
func (o *orm) Update(md interface{}, cols ...string) (int64, error) {
|
||||
mi, ind := o.getMiInd(md, true)
|
||||
num, err := o.alias.DbBaser.Update(o.db, mi, ind, o.alias.TZ, cols)
|
||||
if err != nil {
|
||||
return num, err
|
||||
}
|
||||
return num, nil
|
||||
return o.alias.DbBaser.Update(o.db, mi, ind, o.alias.TZ, cols)
|
||||
}
|
||||
|
||||
// delete model in database
|
||||
func (o *orm) Delete(md interface{}) (int64, error) {
|
||||
// cols shows the delete conditions values read from. default is pk
|
||||
func (o *orm) Delete(md interface{}, cols ...string) (int64, error) {
|
||||
mi, ind := o.getMiInd(md, true)
|
||||
num, err := o.alias.DbBaser.Delete(o.db, mi, ind, o.alias.TZ)
|
||||
num, err := o.alias.DbBaser.Delete(o.db, mi, ind, o.alias.TZ, cols)
|
||||
if err != nil {
|
||||
return num, err
|
||||
}
|
||||
@ -348,7 +349,7 @@ func (o *orm) queryRelated(md interface{}, name string) (*modelInfo, *fieldInfo,
|
||||
fi := o.getFieldInfo(mi, name)
|
||||
|
||||
_, _, exist := getExistPk(mi, ind)
|
||||
if exist == false {
|
||||
if !exist {
|
||||
panic(ErrMissPK)
|
||||
}
|
||||
|
||||
@ -427,7 +428,7 @@ func (o *orm) QueryTable(ptrStructOrTableName interface{}) (qs QuerySeter) {
|
||||
}
|
||||
} else {
|
||||
name = getFullName(indirectType(reflect.TypeOf(ptrStructOrTableName)))
|
||||
if mi, ok := modelCache.getByFN(name); ok {
|
||||
if mi, ok := modelCache.getByFullName(name); ok {
|
||||
qs = newQuerySet(o, mi)
|
||||
}
|
||||
}
|
||||
@ -476,7 +477,7 @@ func (o *orm) Begin() error {
|
||||
|
||||
// commit transaction
|
||||
func (o *orm) Commit() error {
|
||||
if o.isTx == false {
|
||||
if !o.isTx {
|
||||
return ErrTxDone
|
||||
}
|
||||
err := o.db.(txEnder).Commit()
|
||||
@ -491,7 +492,7 @@ func (o *orm) Commit() error {
|
||||
|
||||
// rollback transaction
|
||||
func (o *orm) Rollback() error {
|
||||
if o.isTx == false {
|
||||
if !o.isTx {
|
||||
return ErrTxDone
|
||||
}
|
||||
err := o.db.(txEnder).Rollback()
|
||||
|
@ -75,6 +75,19 @@ func (c *Condition) AndCond(cond *Condition) *Condition {
|
||||
return c
|
||||
}
|
||||
|
||||
// AndNotCond combine a AND NOT condition to current condition
|
||||
func (c *Condition) AndNotCond(cond *Condition) *Condition {
|
||||
c = c.clone()
|
||||
if c == cond {
|
||||
panic(fmt.Errorf("<Condition.AndNotCond> cannot use self as sub cond"))
|
||||
}
|
||||
|
||||
if cond != nil {
|
||||
c.params = append(c.params, condValue{cond: cond, isCond: true, isNot: true})
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
// Or add OR expression to condition
|
||||
func (c Condition) Or(expr string, args ...interface{}) *Condition {
|
||||
if expr == "" || len(args) == 0 {
|
||||
@ -105,6 +118,19 @@ func (c *Condition) OrCond(cond *Condition) *Condition {
|
||||
return c
|
||||
}
|
||||
|
||||
// OrNotCond combine a OR NOT condition to current condition
|
||||
func (c *Condition) OrNotCond(cond *Condition) *Condition {
|
||||
c = c.clone()
|
||||
if c == cond {
|
||||
panic(fmt.Errorf("<Condition.OrNotCond> cannot use self as sub cond"))
|
||||
}
|
||||
|
||||
if cond != nil {
|
||||
c.params = append(c.params, condValue{cond: cond, isCond: true, isNot: true, isOr: true})
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
// IsEmpty check the condition arguments are empty or not.
|
||||
func (c *Condition) IsEmpty() bool {
|
||||
return len(c.params) == 0
|
||||
|
@ -42,7 +42,7 @@ func debugLogQueies(alias *alias, operaton, query string, t time.Time, err error
|
||||
if err != nil {
|
||||
flag = "FAIL"
|
||||
}
|
||||
con := fmt.Sprintf(" - %s - [Queries/%s] - [%s / %11s / %7.1fms] - [%s]", t.Format(formatDateTime), alias.Name, flag, operaton, elsp, query)
|
||||
con := fmt.Sprintf(" -[Queries/%s] - [%s / %11s / %7.1fms] - [%s]", alias.Name, flag, operaton, elsp, query)
|
||||
cons := make([]string, 0, len(args))
|
||||
for _, arg := range args {
|
||||
cons = append(cons, fmt.Sprintf("%v", arg))
|
||||
|
@ -72,7 +72,7 @@ func (o *queryM2M) Add(mds ...interface{}) (int64, error) {
|
||||
}
|
||||
|
||||
_, v1, exist := getExistPk(o.mi, o.ind)
|
||||
if exist == false {
|
||||
if !exist {
|
||||
panic(ErrMissPK)
|
||||
}
|
||||
|
||||
@ -87,7 +87,7 @@ func (o *queryM2M) Add(mds ...interface{}) (int64, error) {
|
||||
v2 = ind.Interface()
|
||||
} else {
|
||||
_, v2, exist = getExistPk(fi.relModelInfo, ind)
|
||||
if exist == false {
|
||||
if !exist {
|
||||
panic(ErrMissPK)
|
||||
}
|
||||
}
|
||||
@ -104,11 +104,7 @@ func (o *queryM2M) Remove(mds ...interface{}) (int64, error) {
|
||||
fi := o.fi
|
||||
qs := o.qs.Filter(fi.reverseFieldInfo.name, o.md)
|
||||
|
||||
nums, err := qs.Filter(fi.reverseFieldInfoTwo.name+ExprSep+"in", mds).Delete()
|
||||
if err != nil {
|
||||
return nums, err
|
||||
}
|
||||
return nums, nil
|
||||
return qs.Filter(fi.reverseFieldInfoTwo.name+ExprSep+"in", mds).Delete()
|
||||
}
|
||||
|
||||
// check model is existed in relationship of origin model
|
||||
|
@ -153,6 +153,11 @@ func (o querySet) SetCond(cond *Condition) QuerySeter {
|
||||
return &o
|
||||
}
|
||||
|
||||
// get condition from QuerySeter
|
||||
func (o querySet) GetCond() *Condition {
|
||||
return o.cond
|
||||
}
|
||||
|
||||
// return QuerySeter execution result number
|
||||
func (o *querySet) Count() (int64, error) {
|
||||
return o.orm.alias.DbBaser.Count(o.orm.db, o, o.mi, o.cond, o.orm.alias.TZ)
|
||||
|
@ -286,7 +286,7 @@ func (o *rawSet) QueryRow(containers ...interface{}) error {
|
||||
|
||||
structMode = true
|
||||
fn := getFullName(typ)
|
||||
if mi, ok := modelCache.getByFN(fn); ok {
|
||||
if mi, ok := modelCache.getByFullName(fn); ok {
|
||||
sMi = mi
|
||||
}
|
||||
} else {
|
||||
@ -355,12 +355,9 @@ func (o *rawSet) QueryRow(containers ...interface{}) error {
|
||||
for i := 0; i < ind.NumField(); i++ {
|
||||
f := ind.Field(i)
|
||||
fe := ind.Type().Field(i)
|
||||
|
||||
var attrs map[string]bool
|
||||
var tags map[string]string
|
||||
parseStructTag(fe.Tag.Get("orm"), &attrs, &tags)
|
||||
_, tags := parseStructTag(fe.Tag.Get(defaultStructTagName))
|
||||
var col string
|
||||
if col = tags["column"]; len(col) == 0 {
|
||||
if col = tags["column"]; col == "" {
|
||||
col = snakeString(fe.Name)
|
||||
}
|
||||
if v, ok := columnsMp[col]; ok {
|
||||
@ -422,7 +419,7 @@ func (o *rawSet) QueryRows(containers ...interface{}) (int64, error) {
|
||||
|
||||
structMode = true
|
||||
fn := getFullName(typ)
|
||||
if mi, ok := modelCache.getByFN(fn); ok {
|
||||
if mi, ok := modelCache.getByFullName(fn); ok {
|
||||
sMi = mi
|
||||
}
|
||||
} else {
|
||||
@ -499,12 +496,9 @@ func (o *rawSet) QueryRows(containers ...interface{}) (int64, error) {
|
||||
for i := 0; i < ind.NumField(); i++ {
|
||||
f := ind.Field(i)
|
||||
fe := ind.Type().Field(i)
|
||||
|
||||
var attrs map[string]bool
|
||||
var tags map[string]string
|
||||
parseStructTag(fe.Tag.Get("orm"), &attrs, &tags)
|
||||
_, tags := parseStructTag(fe.Tag.Get(defaultStructTagName))
|
||||
var col string
|
||||
if col = tags["column"]; len(col) == 0 {
|
||||
if col = tags["column"]; col == "" {
|
||||
col = snakeString(fe.Name)
|
||||
}
|
||||
if v, ok := columnsMp[col]; ok {
|
||||
|
@ -93,14 +93,14 @@ wrongArg:
|
||||
}
|
||||
|
||||
func AssertIs(a interface{}, args ...interface{}) error {
|
||||
if ok, err := ValuesCompare(true, a, args...); ok == false {
|
||||
if ok, err := ValuesCompare(true, a, args...); !ok {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func AssertNot(a interface{}, args ...interface{}) error {
|
||||
if ok, err := ValuesCompare(false, a, args...); ok == false {
|
||||
if ok, err := ValuesCompare(false, a, args...); !ok {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
@ -193,6 +193,7 @@ func TestSyncDb(t *testing.T) {
|
||||
RegisterModel(new(InLineOneToOne))
|
||||
RegisterModel(new(IntegerPk))
|
||||
RegisterModel(new(UintPk))
|
||||
RegisterModel(new(PtrPk))
|
||||
|
||||
err := RunSyncdb("default", true, Debug)
|
||||
throwFail(t, err)
|
||||
@ -216,6 +217,7 @@ func TestRegisterModels(t *testing.T) {
|
||||
RegisterModel(new(InLineOneToOne))
|
||||
RegisterModel(new(IntegerPk))
|
||||
RegisterModel(new(UintPk))
|
||||
RegisterModel(new(PtrPk))
|
||||
|
||||
BootStrap()
|
||||
|
||||
@ -227,7 +229,7 @@ func TestModelSyntax(t *testing.T) {
|
||||
user := &User{}
|
||||
ind := reflect.ValueOf(user).Elem()
|
||||
fn := getFullName(ind.Type())
|
||||
mi, ok := modelCache.getByFN(fn)
|
||||
mi, ok := modelCache.getByFullName(fn)
|
||||
throwFail(t, AssertIs(ok, true))
|
||||
|
||||
mi, ok = modelCache.get("user")
|
||||
@ -577,6 +579,10 @@ func TestCRUD(t *testing.T) {
|
||||
err = dORM.Read(&ub)
|
||||
throwFail(t, err)
|
||||
throwFail(t, AssertIs(ub.Name, "name"))
|
||||
|
||||
num, err = dORM.Delete(&ub, "name")
|
||||
throwFail(t, err)
|
||||
throwFail(t, AssertIs(num, 1))
|
||||
}
|
||||
|
||||
func TestInsertTestData(t *testing.T) {
|
||||
@ -905,6 +911,16 @@ func TestSetCond(t *testing.T) {
|
||||
num, err = qs.SetCond(cond2).Count()
|
||||
throwFail(t, err)
|
||||
throwFail(t, AssertIs(num, 2))
|
||||
|
||||
cond3 := cond.AndNotCond(cond.And("status__in", 1))
|
||||
num, err = qs.SetCond(cond3).Count()
|
||||
throwFail(t, err)
|
||||
throwFail(t, AssertIs(num, 2))
|
||||
|
||||
cond4 := cond.And("user_name", "slene").OrNotCond(cond.And("user_name", "slene"))
|
||||
num, err = qs.SetCond(cond4).Count()
|
||||
throwFail(t, err)
|
||||
throwFail(t, AssertIs(num, 3))
|
||||
}
|
||||
|
||||
func TestLimit(t *testing.T) {
|
||||
@ -2130,6 +2146,48 @@ func TestUintPk(t *testing.T) {
|
||||
dORM.Delete(u)
|
||||
}
|
||||
|
||||
func TestPtrPk(t *testing.T) {
|
||||
parent := &IntegerPk{ID: 10, Value: "10"}
|
||||
|
||||
id, _ := dORM.Insert(parent)
|
||||
if !IsMysql {
|
||||
// MySql does not support last_insert_id in this case: see #2382
|
||||
throwFail(t, AssertIs(id, 10))
|
||||
}
|
||||
|
||||
ptr := PtrPk{ID: parent, Positive: true}
|
||||
num, err := dORM.InsertMulti(2, []PtrPk{ptr})
|
||||
throwFail(t, err)
|
||||
throwFail(t, AssertIs(num, 1))
|
||||
throwFail(t, AssertIs(ptr.ID, parent))
|
||||
|
||||
nptr := &PtrPk{ID: parent}
|
||||
created, pk, err := dORM.ReadOrCreate(nptr, "ID")
|
||||
throwFail(t, err)
|
||||
throwFail(t, AssertIs(created, false))
|
||||
throwFail(t, AssertIs(pk, 10))
|
||||
throwFail(t, AssertIs(nptr.ID, parent))
|
||||
throwFail(t, AssertIs(nptr.Positive, true))
|
||||
|
||||
nptr = &PtrPk{Positive: true}
|
||||
created, pk, err = dORM.ReadOrCreate(nptr, "Positive")
|
||||
throwFail(t, err)
|
||||
throwFail(t, AssertIs(created, false))
|
||||
throwFail(t, AssertIs(pk, 10))
|
||||
throwFail(t, AssertIs(nptr.ID, parent))
|
||||
|
||||
nptr.Positive = false
|
||||
num, err = dORM.Update(nptr)
|
||||
throwFail(t, err)
|
||||
throwFail(t, AssertIs(num, 1))
|
||||
throwFail(t, AssertIs(nptr.ID, parent))
|
||||
throwFail(t, AssertIs(nptr.Positive, false))
|
||||
|
||||
num, err = dORM.Delete(nptr)
|
||||
throwFail(t, err)
|
||||
throwFail(t, AssertIs(num, 1))
|
||||
}
|
||||
|
||||
func TestSnake(t *testing.T) {
|
||||
cases := map[string]string{
|
||||
"i": "i",
|
||||
|
@ -19,6 +19,7 @@ import "errors"
|
||||
// QueryBuilder is the Query builder interface
|
||||
type QueryBuilder interface {
|
||||
Select(fields ...string) QueryBuilder
|
||||
ForUpdate() QueryBuilder
|
||||
From(tables ...string) QueryBuilder
|
||||
InnerJoin(table string) QueryBuilder
|
||||
LeftJoin(table string) QueryBuilder
|
||||
|
@ -34,6 +34,12 @@ func (qb *MySQLQueryBuilder) Select(fields ...string) QueryBuilder {
|
||||
return qb
|
||||
}
|
||||
|
||||
// ForUpdate add the FOR UPDATE clause
|
||||
func (qb *MySQLQueryBuilder) ForUpdate() QueryBuilder {
|
||||
qb.Tokens = append(qb.Tokens, "FOR UPDATE")
|
||||
return qb
|
||||
}
|
||||
|
||||
// From join the tables
|
||||
func (qb *MySQLQueryBuilder) From(tables ...string) QueryBuilder {
|
||||
qb.Tokens = append(qb.Tokens, "FROM", strings.Join(tables, CommaSpace))
|
||||
|
@ -31,6 +31,12 @@ func (qb *TiDBQueryBuilder) Select(fields ...string) QueryBuilder {
|
||||
return qb
|
||||
}
|
||||
|
||||
// ForUpdate add the FOR UPDATE clause
|
||||
func (qb *TiDBQueryBuilder) ForUpdate() QueryBuilder {
|
||||
qb.Tokens = append(qb.Tokens, "FOR UPDATE")
|
||||
return qb
|
||||
}
|
||||
|
||||
// From join the tables
|
||||
func (qb *TiDBQueryBuilder) From(tables ...string) QueryBuilder {
|
||||
qb.Tokens = append(qb.Tokens, "FROM", strings.Join(tables, CommaSpace))
|
||||
|
19
orm/types.go
19
orm/types.go
@ -45,6 +45,9 @@ type Ormer interface {
|
||||
// u = &User{UserName: "astaxie", Password: "pass"}
|
||||
// err = Ormer.Read(u, "UserName")
|
||||
Read(md interface{}, cols ...string) error
|
||||
// Like Read(), but with "FOR UPDATE" clause, useful in transaction.
|
||||
// Some databases are not support this feature.
|
||||
ReadForUpdate(md interface{}, cols ...string) error
|
||||
// Try to read a row from the database, or insert one if it doesn't exist
|
||||
ReadOrCreate(md interface{}, col1 string, cols ...string) (bool, int64, error)
|
||||
// insert model data to database
|
||||
@ -71,7 +74,7 @@ type Ormer interface {
|
||||
// num, err = Ormer.Update(&user, "Langs", "Extra")
|
||||
Update(md interface{}, cols ...string) (int64, error)
|
||||
// delete model in database
|
||||
Delete(md interface{}) (int64, error)
|
||||
Delete(md interface{}, cols ...string) (int64, error)
|
||||
// load related models to md model.
|
||||
// args are limit, offset int and order string.
|
||||
//
|
||||
@ -142,6 +145,16 @@ type QuerySeter interface {
|
||||
// //sql-> WHERE T0.`profile_id` IS NOT NULL AND NOT T0.`Status` IN (?) OR T1.`age` > 2000
|
||||
// num, err := qs.SetCond(cond1).Count()
|
||||
SetCond(*Condition) QuerySeter
|
||||
// get condition from QuerySeter.
|
||||
// sql's where condition
|
||||
// cond := orm.NewCondition()
|
||||
// cond = cond.And("profile__isnull", false).AndNot("status__in", 1)
|
||||
// qs = qs.SetCond(cond)
|
||||
// cond = qs.GetCond()
|
||||
// cond := cond.Or("profile__age__gt", 2000)
|
||||
// //sql-> WHERE T0.`profile_id` IS NOT NULL AND NOT T0.`Status` IN (?) OR T1.`age` > 2000
|
||||
// num, err := qs.SetCond(cond).Count()
|
||||
GetCond() *Condition
|
||||
// add LIMIT value.
|
||||
// args[0] means offset, e.g. LIMIT num,offset.
|
||||
// if Limit <= 0 then Limit will be set to default limit ,eg 1000
|
||||
@ -394,14 +407,14 @@ type txEnder interface {
|
||||
|
||||
// base database struct
|
||||
type dbBaser interface {
|
||||
Read(dbQuerier, *modelInfo, reflect.Value, *time.Location, []string) error
|
||||
Read(dbQuerier, *modelInfo, reflect.Value, *time.Location, []string, bool) error
|
||||
Insert(dbQuerier, *modelInfo, reflect.Value, *time.Location) (int64, error)
|
||||
InsertOrUpdate(dbQuerier, *modelInfo, reflect.Value, *alias, ...string) (int64, error)
|
||||
InsertMulti(dbQuerier, *modelInfo, reflect.Value, int, *time.Location) (int64, error)
|
||||
InsertValue(dbQuerier, *modelInfo, bool, []string, []interface{}) (int64, error)
|
||||
InsertStmt(stmtQuerier, *modelInfo, reflect.Value, *time.Location) (int64, error)
|
||||
Update(dbQuerier, *modelInfo, reflect.Value, *time.Location, []string) (int64, error)
|
||||
Delete(dbQuerier, *modelInfo, reflect.Value, *time.Location) (int64, error)
|
||||
Delete(dbQuerier, *modelInfo, reflect.Value, *time.Location, []string) (int64, error)
|
||||
ReadBatch(dbQuerier, *querySet, *modelInfo, *Condition, interface{}, *time.Location, []string) (int64, error)
|
||||
SupportUpdateJoin() bool
|
||||
UpdateBatch(dbQuerier, *querySet, *modelInfo, *Condition, Params, *time.Location) (int64, error)
|
||||
|
42
orm/utils.go
42
orm/utils.go
@ -16,6 +16,7 @@ package orm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
@ -87,7 +88,15 @@ func (f StrTo) Int32() (int32, error) {
|
||||
// Int64 string to int64
|
||||
func (f StrTo) Int64() (int64, error) {
|
||||
v, err := strconv.ParseInt(f.String(), 10, 64)
|
||||
return int64(v), err
|
||||
if err != nil {
|
||||
i := new(big.Int)
|
||||
ni, ok := i.SetString(f.String(), 10) // octal
|
||||
if !ok {
|
||||
return v, err
|
||||
}
|
||||
return ni.Int64(), nil
|
||||
}
|
||||
return v, err
|
||||
}
|
||||
|
||||
// Uint string to uint
|
||||
@ -117,7 +126,15 @@ func (f StrTo) Uint32() (uint32, error) {
|
||||
// Uint64 string to uint64
|
||||
func (f StrTo) Uint64() (uint64, error) {
|
||||
v, err := strconv.ParseUint(f.String(), 10, 64)
|
||||
return uint64(v), err
|
||||
if err != nil {
|
||||
i := new(big.Int)
|
||||
ni, ok := i.SetString(f.String(), 10)
|
||||
if !ok {
|
||||
return v, err
|
||||
}
|
||||
return ni.Uint64(), nil
|
||||
}
|
||||
return v, err
|
||||
}
|
||||
|
||||
// String string to string
|
||||
@ -202,22 +219,17 @@ func snakeString(s string) string {
|
||||
// camel string, xx_yy to XxYy
|
||||
func camelString(s string) string {
|
||||
data := make([]byte, 0, len(s))
|
||||
j := false
|
||||
k := false
|
||||
num := len(s) - 1
|
||||
flag, num := true, len(s)-1
|
||||
for i := 0; i <= num; i++ {
|
||||
d := s[i]
|
||||
if k == false && d >= 'A' && d <= 'Z' {
|
||||
k = true
|
||||
}
|
||||
if d >= 'a' && d <= 'z' && (j || k == false) {
|
||||
d = d - 32
|
||||
j = false
|
||||
k = true
|
||||
}
|
||||
if k && d == '_' && num > i && s[i+1] >= 'a' && s[i+1] <= 'z' {
|
||||
j = true
|
||||
if d == '_' {
|
||||
flag = true
|
||||
continue
|
||||
} else if flag {
|
||||
if d >= 'a' && d <= 'z' {
|
||||
d = d - 32
|
||||
}
|
||||
flag = false
|
||||
}
|
||||
data = append(data, d)
|
||||
}
|
||||
|
36
orm/utils_test.go
Normal file
36
orm/utils_test.go
Normal file
@ -0,0 +1,36 @@
|
||||
// Copyright 2014 beego Author. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package orm
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCamelString(t *testing.T) {
|
||||
snake := []string{"pic_url", "hello_world_", "hello__World", "_HelLO_Word", "pic_url_1", "pic_url__1"}
|
||||
camel := []string{"PicUrl", "HelloWorld", "HelloWorld", "HelLOWord", "PicUrl1", "PicUrl1"}
|
||||
|
||||
answer := make(map[string]string)
|
||||
for i, v := range snake {
|
||||
answer[v] = camel[i]
|
||||
}
|
||||
|
||||
for _, v := range snake {
|
||||
res := camelString(v)
|
||||
if res != answer[v] {
|
||||
t.Error("Unit Test Fail:", v, res, answer[v])
|
||||
}
|
||||
}
|
||||
}
|
@ -49,7 +49,7 @@ var (
|
||||
genInfoList map[string][]ControllerComments
|
||||
)
|
||||
|
||||
const coomentPrefix = "commentsRouter_"
|
||||
const commentPrefix = "commentsRouter_"
|
||||
|
||||
func init() {
|
||||
pkgLastupdate = make(map[string]int64)
|
||||
@ -58,7 +58,7 @@ func init() {
|
||||
func parserPkg(pkgRealpath, pkgpath string) error {
|
||||
rep := strings.NewReplacer("\\", "_", "/", "_", ".", "_")
|
||||
commentFilename, _ = filepath.Rel(AppPath, pkgRealpath)
|
||||
commentFilename = coomentPrefix + rep.Replace(commentFilename) + ".go"
|
||||
commentFilename = commentPrefix + rep.Replace(commentFilename) + ".go"
|
||||
if !compareFile(pkgRealpath) {
|
||||
logs.Info(pkgRealpath + " no changed")
|
||||
return nil
|
||||
|
@ -56,6 +56,7 @@
|
||||
package apiauth
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
@ -119,7 +120,7 @@ func APISecretAuth(f AppIDToAppSecret, timeout int) beego.FilterFunc {
|
||||
return
|
||||
}
|
||||
if ctx.Input.Query("signature") !=
|
||||
Signature(appsecret, ctx.Input.Method(), ctx.Request.Form, ctx.Input.URI()) {
|
||||
Signature(appsecret, ctx.Input.Method(), ctx.Request.Form, ctx.Input.URL()) {
|
||||
ctx.ResponseWriter.WriteHeader(403)
|
||||
ctx.WriteString("auth failed")
|
||||
}
|
||||
@ -127,54 +128,33 @@ func APISecretAuth(f AppIDToAppSecret, timeout int) beego.FilterFunc {
|
||||
}
|
||||
|
||||
// Signature used to generate signature with the appsecret/method/params/RequestURI
|
||||
func Signature(appsecret, method string, params url.Values, RequestURI string) (result string) {
|
||||
var query string
|
||||
func Signature(appsecret, method string, params url.Values, RequestURL string) (result string) {
|
||||
var b bytes.Buffer
|
||||
keys := make([]string, len(params))
|
||||
pa := make(map[string]string)
|
||||
for k, v := range params {
|
||||
pa[k] = v[0]
|
||||
keys = append(keys, k)
|
||||
}
|
||||
vs := mapSorter(pa)
|
||||
vs.Sort()
|
||||
for i := 0; i < vs.Len(); i++ {
|
||||
if vs.Keys[i] == "signature" {
|
||||
|
||||
sort.Strings(keys)
|
||||
|
||||
for _, key := range keys {
|
||||
if key == "signature" {
|
||||
continue
|
||||
}
|
||||
if vs.Keys[i] != "" && vs.Vals[i] != "" {
|
||||
query = fmt.Sprintf("%v%v%v", query, vs.Keys[i], vs.Vals[i])
|
||||
|
||||
val := pa[key]
|
||||
if key != "" && val != "" {
|
||||
b.WriteString(key)
|
||||
b.WriteString(val)
|
||||
}
|
||||
}
|
||||
stringToSign := fmt.Sprintf("%v\n%v\n%v\n", method, query, RequestURI)
|
||||
|
||||
stringToSign := fmt.Sprintf("%v\n%v\n%v\n", method, b.String(), RequestURL)
|
||||
|
||||
sha256 := sha256.New
|
||||
hash := hmac.New(sha256, []byte(appsecret))
|
||||
hash.Write([]byte(stringToSign))
|
||||
return base64.StdEncoding.EncodeToString(hash.Sum(nil))
|
||||
}
|
||||
|
||||
type valSorter struct {
|
||||
Keys []string
|
||||
Vals []string
|
||||
}
|
||||
|
||||
func mapSorter(m map[string]string) *valSorter {
|
||||
vs := &valSorter{
|
||||
Keys: make([]string, 0, len(m)),
|
||||
Vals: make([]string, 0, len(m)),
|
||||
}
|
||||
for k, v := range m {
|
||||
vs.Keys = append(vs.Keys, k)
|
||||
vs.Vals = append(vs.Vals, v)
|
||||
}
|
||||
return vs
|
||||
}
|
||||
|
||||
func (vs *valSorter) Sort() {
|
||||
sort.Sort(vs)
|
||||
}
|
||||
|
||||
func (vs *valSorter) Len() int { return len(vs.Keys) }
|
||||
func (vs *valSorter) Less(i, j int) bool { return vs.Keys[i] < vs.Keys[j] }
|
||||
func (vs *valSorter) Swap(i, j int) {
|
||||
vs.Vals[i], vs.Vals[j] = vs.Vals[j], vs.Vals[i]
|
||||
vs.Keys[i], vs.Keys[j] = vs.Keys[j], vs.Keys[i]
|
||||
}
|
||||
|
20
plugins/apiauth/apiauth_test.go
Normal file
20
plugins/apiauth/apiauth_test.go
Normal file
@ -0,0 +1,20 @@
|
||||
package apiauth
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSignature(t *testing.T) {
|
||||
appsecret := "beego secret"
|
||||
method := "GET"
|
||||
RequestURL := "http://localhost/test/url"
|
||||
params := make(url.Values)
|
||||
params.Add("arg1", "hello")
|
||||
params.Add("arg2", "beego")
|
||||
|
||||
signature := "mFdpvLh48ca4mDVEItE9++AKKQ/IVca7O/ZyyB8hR58="
|
||||
if Signature(appsecret, method, params, RequestURL) != signature {
|
||||
t.Error("Signature error")
|
||||
}
|
||||
}
|
97
policy.go
Normal file
97
policy.go
Normal file
@ -0,0 +1,97 @@
|
||||
// Copyright 2016 beego authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package beego
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/astaxie/beego/context"
|
||||
)
|
||||
|
||||
// PolicyFunc defines a policy function which is invoked before the controller handler is executed.
|
||||
type PolicyFunc func(*context.Context)
|
||||
|
||||
// FindRouter Find Router info for URL
|
||||
func (p *ControllerRegister) FindPolicy(cont *context.Context) []PolicyFunc {
|
||||
var urlPath = cont.Input.URL()
|
||||
if !BConfig.RouterCaseSensitive {
|
||||
urlPath = strings.ToLower(urlPath)
|
||||
}
|
||||
httpMethod := cont.Input.Method()
|
||||
isWildcard := false
|
||||
// Find policy for current method
|
||||
t, ok := p.policies[httpMethod]
|
||||
// If not found - find policy for whole controller
|
||||
if !ok {
|
||||
t, ok = p.policies["*"]
|
||||
isWildcard = true
|
||||
}
|
||||
if ok {
|
||||
runObjects := t.Match(urlPath, cont)
|
||||
if r, ok := runObjects.([]PolicyFunc); ok {
|
||||
return r
|
||||
} else if !isWildcard {
|
||||
// If no policies found and we checked not for "*" method - try to find it
|
||||
t, ok = p.policies["*"]
|
||||
if ok {
|
||||
runObjects = t.Match(urlPath, cont)
|
||||
if r, ok = runObjects.([]PolicyFunc); ok {
|
||||
return r
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *ControllerRegister) addToPolicy(method, pattern string, r ...PolicyFunc) {
|
||||
method = strings.ToUpper(method)
|
||||
p.enablePolicy = true
|
||||
if !BConfig.RouterCaseSensitive {
|
||||
pattern = strings.ToLower(pattern)
|
||||
}
|
||||
if t, ok := p.policies[method]; ok {
|
||||
t.AddRouter(pattern, r)
|
||||
} else {
|
||||
t := NewTree()
|
||||
t.AddRouter(pattern, r)
|
||||
p.policies[method] = t
|
||||
}
|
||||
}
|
||||
|
||||
// Register new policy in beego
|
||||
func Policy(pattern, method string, policy ...PolicyFunc) {
|
||||
BeeApp.Handlers.addToPolicy(method, pattern, policy...)
|
||||
}
|
||||
|
||||
// Find policies and execute if were found
|
||||
func (p *ControllerRegister) execPolicy(cont *context.Context, urlPath string) (started bool) {
|
||||
if !p.enablePolicy {
|
||||
return false
|
||||
}
|
||||
// Find Policy for method
|
||||
policyList := p.FindPolicy(cont)
|
||||
if len(policyList) > 0 {
|
||||
// Run policies
|
||||
for _, runPolicy := range policyList {
|
||||
runPolicy(cont)
|
||||
if cont.ResponseWriter.Started {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
return false
|
||||
}
|
153
router.go
153
router.go
@ -17,7 +17,6 @@ package beego
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
@ -51,15 +50,22 @@ const (
|
||||
var (
|
||||
// HTTPMETHOD list the supported http methods.
|
||||
HTTPMETHOD = map[string]string{
|
||||
"GET": "GET",
|
||||
"POST": "POST",
|
||||
"PUT": "PUT",
|
||||
"DELETE": "DELETE",
|
||||
"PATCH": "PATCH",
|
||||
"OPTIONS": "OPTIONS",
|
||||
"HEAD": "HEAD",
|
||||
"TRACE": "TRACE",
|
||||
"CONNECT": "CONNECT",
|
||||
"GET": "GET",
|
||||
"POST": "POST",
|
||||
"PUT": "PUT",
|
||||
"DELETE": "DELETE",
|
||||
"PATCH": "PATCH",
|
||||
"OPTIONS": "OPTIONS",
|
||||
"HEAD": "HEAD",
|
||||
"TRACE": "TRACE",
|
||||
"CONNECT": "CONNECT",
|
||||
"MKCOL": "MKCOL",
|
||||
"COPY": "COPY",
|
||||
"MOVE": "MOVE",
|
||||
"PROPFIND": "PROPFIND",
|
||||
"PROPPATCH": "PROPPATCH",
|
||||
"LOCK": "LOCK",
|
||||
"UNLOCK": "UNLOCK",
|
||||
}
|
||||
// these beego.Controller's methods shouldn't reflect to AutoRouter
|
||||
exceptMethod = []string{"Init", "Prepare", "Finish", "Render", "RenderString",
|
||||
@ -102,7 +108,8 @@ func ExceptMethodAppend(action string) {
|
||||
exceptMethod = append(exceptMethod, action)
|
||||
}
|
||||
|
||||
type controllerInfo struct {
|
||||
// ControllerInfo holds information about the controller.
|
||||
type ControllerInfo struct {
|
||||
pattern string
|
||||
controllerType reflect.Type
|
||||
methods map[string]string
|
||||
@ -114,6 +121,8 @@ type controllerInfo struct {
|
||||
// ControllerRegister containers registered router rules, controller handlers and filters.
|
||||
type ControllerRegister struct {
|
||||
routers map[string]*Tree
|
||||
enablePolicy bool
|
||||
policies map[string]*Tree
|
||||
enableFilter bool
|
||||
filters [FinishRouter + 1][]*FilterRouter
|
||||
pool sync.Pool
|
||||
@ -122,7 +131,8 @@ type ControllerRegister struct {
|
||||
// NewControllerRegister returns a new ControllerRegister.
|
||||
func NewControllerRegister() *ControllerRegister {
|
||||
cr := &ControllerRegister{
|
||||
routers: make(map[string]*Tree),
|
||||
routers: make(map[string]*Tree),
|
||||
policies: make(map[string]*Tree),
|
||||
}
|
||||
cr.pool.New = func() interface{} {
|
||||
return beecontext.NewContext()
|
||||
@ -166,7 +176,7 @@ func (p *ControllerRegister) Add(pattern string, c ControllerInterface, mappingM
|
||||
}
|
||||
}
|
||||
|
||||
route := &controllerInfo{}
|
||||
route := &ControllerInfo{}
|
||||
route.pattern = pattern
|
||||
route.methods = methods
|
||||
route.routerType = routerTypeBeego
|
||||
@ -188,7 +198,7 @@ func (p *ControllerRegister) Add(pattern string, c ControllerInterface, mappingM
|
||||
}
|
||||
}
|
||||
|
||||
func (p *ControllerRegister) addToRouter(method, pattern string, r *controllerInfo) {
|
||||
func (p *ControllerRegister) addToRouter(method, pattern string, r *ControllerInfo) {
|
||||
if !BConfig.RouterCaseSensitive {
|
||||
pattern = strings.ToLower(pattern)
|
||||
}
|
||||
@ -209,13 +219,11 @@ func (p *ControllerRegister) Include(cList ...ControllerInterface) {
|
||||
for _, c := range cList {
|
||||
reflectVal := reflect.ValueOf(c)
|
||||
t := reflect.Indirect(reflectVal).Type()
|
||||
gopath := os.Getenv("GOPATH")
|
||||
if gopath == "" {
|
||||
wgopath := utils.GetGOPATHs()
|
||||
if len(wgopath) == 0 {
|
||||
panic("you are in dev mode. So please set gopath")
|
||||
}
|
||||
pkgpath := ""
|
||||
|
||||
wgopath := filepath.SplitList(gopath)
|
||||
for _, wg := range wgopath {
|
||||
wg, _ = filepath.EvalSymlinks(filepath.Join(wg, "src", t.PkgPath()))
|
||||
if utils.FileExists(wg) {
|
||||
@ -325,7 +333,7 @@ func (p *ControllerRegister) AddMethod(method, pattern string, f FilterFunc) {
|
||||
if _, ok := HTTPMETHOD[method]; method != "*" && !ok {
|
||||
panic("not support http method: " + method)
|
||||
}
|
||||
route := &controllerInfo{}
|
||||
route := &ControllerInfo{}
|
||||
route.pattern = pattern
|
||||
route.routerType = routerTypeRESTFul
|
||||
route.runFunction = f
|
||||
@ -351,7 +359,7 @@ func (p *ControllerRegister) AddMethod(method, pattern string, f FilterFunc) {
|
||||
|
||||
// Handler add user defined Handler
|
||||
func (p *ControllerRegister) Handler(pattern string, h http.Handler, options ...interface{}) {
|
||||
route := &controllerInfo{}
|
||||
route := &ControllerInfo{}
|
||||
route.pattern = pattern
|
||||
route.routerType = routerTypeHandler
|
||||
route.handler = h
|
||||
@ -386,7 +394,7 @@ func (p *ControllerRegister) AddAutoPrefix(prefix string, c ControllerInterface)
|
||||
controllerName := strings.TrimSuffix(ct.Name(), "Controller")
|
||||
for i := 0; i < rt.NumMethod(); i++ {
|
||||
if !utils.InSlice(rt.Method(i).Name, exceptMethod) {
|
||||
route := &controllerInfo{}
|
||||
route := &ControllerInfo{}
|
||||
route.routerType = routerTypeBeego
|
||||
route.methods = map[string]string{"*": rt.Method(i).Name}
|
||||
route.controllerType = ct
|
||||
@ -492,7 +500,7 @@ func (p *ControllerRegister) geturl(t *Tree, url, controllName, methodName strin
|
||||
}
|
||||
}
|
||||
for _, l := range t.leaves {
|
||||
if c, ok := l.runObject.(*controllerInfo); ok {
|
||||
if c, ok := l.runObject.(*ControllerInfo); ok {
|
||||
if c.routerType == routerTypeBeego &&
|
||||
strings.HasSuffix(path.Join(c.controllerType.PkgPath(), c.controllerType.Name()), controllName) {
|
||||
find := false
|
||||
@ -619,14 +627,16 @@ func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request)
|
||||
runRouter reflect.Type
|
||||
findRouter bool
|
||||
runMethod string
|
||||
routerInfo *controllerInfo
|
||||
routerInfo *ControllerInfo
|
||||
isRunnable bool
|
||||
)
|
||||
context := p.pool.Get().(*beecontext.Context)
|
||||
context.Reset(rw, r)
|
||||
|
||||
defer p.pool.Put(context)
|
||||
defer p.recoverPanic(context)
|
||||
if BConfig.RecoverFunc != nil {
|
||||
defer BConfig.RecoverFunc(context)
|
||||
}
|
||||
|
||||
context.Output.EnableGzip = BConfig.EnableGzip
|
||||
|
||||
@ -658,7 +668,7 @@ func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request)
|
||||
goto Admin
|
||||
}
|
||||
|
||||
if r.Method != "GET" && r.Method != "HEAD" {
|
||||
if r.Method != http.MethodGet && r.Method != http.MethodHead {
|
||||
if BConfig.CopyRequestBody && !context.Input.IsUpload() {
|
||||
context.Input.CopyBody(BConfig.MaxMemory)
|
||||
}
|
||||
@ -683,8 +693,16 @@ func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request)
|
||||
if len(p.filters[BeforeRouter]) > 0 && p.execFilter(context, urlPath, BeforeRouter) {
|
||||
goto Admin
|
||||
}
|
||||
// User can define RunController and RunMethod in filter
|
||||
if context.Input.RunController != nil && context.Input.RunMethod != "" {
|
||||
findRouter = true
|
||||
isRunnable = true
|
||||
runMethod = context.Input.RunMethod
|
||||
runRouter = context.Input.RunController
|
||||
} else {
|
||||
routerInfo, findRouter = p.FindRouter(context)
|
||||
}
|
||||
|
||||
routerInfo, findRouter = p.FindRouter(context)
|
||||
//if no matches to url, throw a not found exception
|
||||
if !findRouter {
|
||||
exception("404", context)
|
||||
@ -696,15 +714,19 @@ func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request)
|
||||
}
|
||||
}
|
||||
|
||||
//store router pattern into context
|
||||
context.Input.SetData("RouterPattern", routerInfo.pattern)
|
||||
|
||||
//execute middleware filters
|
||||
if len(p.filters[BeforeExec]) > 0 && p.execFilter(context, urlPath, BeforeExec) {
|
||||
goto Admin
|
||||
}
|
||||
|
||||
//check policies
|
||||
if p.execPolicy(context, urlPath) {
|
||||
goto Admin
|
||||
}
|
||||
|
||||
if routerInfo != nil {
|
||||
//store router pattern into context
|
||||
context.Input.SetData("RouterPattern", routerInfo.pattern)
|
||||
if routerInfo.routerType == routerTypeRESTFul {
|
||||
if _, ok := routerInfo.methods[r.Method]; ok {
|
||||
isRunnable = true
|
||||
@ -719,11 +741,11 @@ func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request)
|
||||
} else {
|
||||
runRouter = routerInfo.controllerType
|
||||
method := r.Method
|
||||
if r.Method == "POST" && context.Input.Query("_method") == "PUT" {
|
||||
method = "PUT"
|
||||
if r.Method == http.MethodPost && context.Input.Query("_method") == http.MethodPost {
|
||||
method = http.MethodPut
|
||||
}
|
||||
if r.Method == "POST" && context.Input.Query("_method") == "DELETE" {
|
||||
method = "DELETE"
|
||||
if r.Method == http.MethodPost && context.Input.Query("_method") == http.MethodDelete {
|
||||
method = http.MethodDelete
|
||||
}
|
||||
if m, ok := routerInfo.methods[method]; ok {
|
||||
runMethod = m
|
||||
@ -753,8 +775,8 @@ func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request)
|
||||
//if XSRF is Enable then check cookie where there has any cookie in the request's cookie _csrf
|
||||
if BConfig.WebConfig.EnableXSRF {
|
||||
execController.XSRFToken()
|
||||
if r.Method == "POST" || r.Method == "DELETE" || r.Method == "PUT" ||
|
||||
(r.Method == "POST" && (context.Input.Query("_method") == "DELETE" || context.Input.Query("_method") == "PUT")) {
|
||||
if r.Method == http.MethodPost || r.Method == http.MethodDelete || r.Method == http.MethodPut ||
|
||||
(r.Method == http.MethodPost && (context.Input.Query("_method") == http.MethodDelete || context.Input.Query("_method") == http.MethodPut)) {
|
||||
execController.CheckXSRFCookie()
|
||||
}
|
||||
}
|
||||
@ -764,19 +786,19 @@ func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request)
|
||||
if !context.ResponseWriter.Started {
|
||||
//exec main logic
|
||||
switch runMethod {
|
||||
case "GET":
|
||||
case http.MethodGet:
|
||||
execController.Get()
|
||||
case "POST":
|
||||
case http.MethodPost:
|
||||
execController.Post()
|
||||
case "DELETE":
|
||||
case http.MethodDelete:
|
||||
execController.Delete()
|
||||
case "PUT":
|
||||
case http.MethodPut:
|
||||
execController.Put()
|
||||
case "HEAD":
|
||||
case http.MethodHead:
|
||||
execController.Head()
|
||||
case "PATCH":
|
||||
case http.MethodPatch:
|
||||
execController.Patch()
|
||||
case "OPTIONS":
|
||||
case http.MethodOptions:
|
||||
execController.Options()
|
||||
default:
|
||||
if !execController.HandlerFunc(runMethod) {
|
||||
@ -838,16 +860,16 @@ Admin:
|
||||
|
||||
if findRouter {
|
||||
if routerInfo != nil {
|
||||
devInfo = fmt.Sprintf("|%s %3d %s|%13s|%8s|%s %s %-7s %-3s r:%s", statusColor, statusCode,
|
||||
resetColor, timeDur.String(), "match", methodColor, resetColor, r.Method, r.URL.Path,
|
||||
devInfo = fmt.Sprintf("|%15s|%s %3d %s|%13s|%8s|%s %-7s %s %-3s r:%s", context.Input.IP(), statusColor, statusCode,
|
||||
resetColor, timeDur.String(), "match", methodColor, r.Method, resetColor, r.URL.Path,
|
||||
routerInfo.pattern)
|
||||
} else {
|
||||
devInfo = fmt.Sprintf("|%s %3d %s|%13s|%8s|%s %s %-7s %-3s", statusColor, statusCode, resetColor,
|
||||
timeDur.String(), "match", methodColor, resetColor, r.Method, r.URL.Path)
|
||||
devInfo = fmt.Sprintf("|%15s|%s %3d %s|%13s|%8s|%s %-7s %s %-3s", context.Input.IP(), statusColor, statusCode, resetColor,
|
||||
timeDur.String(), "match", methodColor, r.Method, resetColor, r.URL.Path)
|
||||
}
|
||||
} else {
|
||||
devInfo = fmt.Sprintf("|%s %3d %s|%13s|%8s|%s %s %-7s %-3s", statusColor, statusCode, resetColor,
|
||||
timeDur.String(), "nomatch", methodColor, resetColor, r.Method, r.URL.Path)
|
||||
devInfo = fmt.Sprintf("|%15s|%s %3d %s|%13s|%8s|%s %-7s %s %-3s", context.Input.IP(), statusColor, statusCode, resetColor,
|
||||
timeDur.String(), "nomatch", methodColor, r.Method, resetColor, r.URL.Path)
|
||||
}
|
||||
if iswin {
|
||||
logs.W32Debug(devInfo)
|
||||
@ -863,7 +885,7 @@ Admin:
|
||||
}
|
||||
|
||||
// FindRouter Find Router info for URL
|
||||
func (p *ControllerRegister) FindRouter(context *beecontext.Context) (routerInfo *controllerInfo, isFind bool) {
|
||||
func (p *ControllerRegister) FindRouter(context *beecontext.Context) (routerInfo *ControllerInfo, isFind bool) {
|
||||
var urlPath = context.Input.URL()
|
||||
if !BConfig.RouterCaseSensitive {
|
||||
urlPath = strings.ToLower(urlPath)
|
||||
@ -871,44 +893,13 @@ func (p *ControllerRegister) FindRouter(context *beecontext.Context) (routerInfo
|
||||
httpMethod := context.Input.Method()
|
||||
if t, ok := p.routers[httpMethod]; ok {
|
||||
runObject := t.Match(urlPath, context)
|
||||
if r, ok := runObject.(*controllerInfo); ok {
|
||||
if r, ok := runObject.(*ControllerInfo); ok {
|
||||
return r, true
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (p *ControllerRegister) recoverPanic(context *beecontext.Context) {
|
||||
if err := recover(); err != nil {
|
||||
if err == ErrAbort {
|
||||
return
|
||||
}
|
||||
if !BConfig.RecoverPanic {
|
||||
panic(err)
|
||||
}
|
||||
if BConfig.EnableErrorsShow {
|
||||
if _, ok := ErrorMaps[fmt.Sprint(err)]; ok {
|
||||
exception(fmt.Sprint(err), context)
|
||||
return
|
||||
}
|
||||
}
|
||||
var stack string
|
||||
logs.Critical("the request url is ", context.Input.URL())
|
||||
logs.Critical("Handler crashed with error", err)
|
||||
for i := 1; ; i++ {
|
||||
_, file, line, ok := runtime.Caller(i)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
logs.Critical(fmt.Sprintf("%s:%d", file, line))
|
||||
stack = stack + fmt.Sprintln(fmt.Sprintf("%s:%d", file, line))
|
||||
}
|
||||
if BConfig.RunMode == DEV {
|
||||
showErr(err, context, stack)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func toURL(params map[string]string) string {
|
||||
if len(params) == 0 {
|
||||
return ""
|
||||
|
@ -502,10 +502,10 @@ func TestFilterBeforeRouter(t *testing.T) {
|
||||
rw, r := testRequest("GET", url)
|
||||
mux.ServeHTTP(rw, r)
|
||||
|
||||
if strings.Contains(rw.Body.String(), "BeforeRouter1") == false {
|
||||
if !strings.Contains(rw.Body.String(), "BeforeRouter1") {
|
||||
t.Errorf(testName + " BeforeRouter did not run")
|
||||
}
|
||||
if strings.Contains(rw.Body.String(), "hello") == true {
|
||||
if strings.Contains(rw.Body.String(), "hello") {
|
||||
t.Errorf(testName + " BeforeRouter did not return properly")
|
||||
}
|
||||
}
|
||||
@ -525,13 +525,13 @@ func TestFilterBeforeExec(t *testing.T) {
|
||||
rw, r := testRequest("GET", url)
|
||||
mux.ServeHTTP(rw, r)
|
||||
|
||||
if strings.Contains(rw.Body.String(), "BeforeExec1") == false {
|
||||
if !strings.Contains(rw.Body.String(), "BeforeExec1") {
|
||||
t.Errorf(testName + " BeforeExec did not run")
|
||||
}
|
||||
if strings.Contains(rw.Body.String(), "hello") == true {
|
||||
if strings.Contains(rw.Body.String(), "hello") {
|
||||
t.Errorf(testName + " BeforeExec did not return properly")
|
||||
}
|
||||
if strings.Contains(rw.Body.String(), "BeforeRouter") == true {
|
||||
if strings.Contains(rw.Body.String(), "BeforeRouter") {
|
||||
t.Errorf(testName + " BeforeRouter ran in error")
|
||||
}
|
||||
}
|
||||
@ -552,16 +552,16 @@ func TestFilterAfterExec(t *testing.T) {
|
||||
rw, r := testRequest("GET", url)
|
||||
mux.ServeHTTP(rw, r)
|
||||
|
||||
if strings.Contains(rw.Body.String(), "AfterExec1") == false {
|
||||
if !strings.Contains(rw.Body.String(), "AfterExec1") {
|
||||
t.Errorf(testName + " AfterExec did not run")
|
||||
}
|
||||
if strings.Contains(rw.Body.String(), "hello") == false {
|
||||
if !strings.Contains(rw.Body.String(), "hello") {
|
||||
t.Errorf(testName + " handler did not run properly")
|
||||
}
|
||||
if strings.Contains(rw.Body.String(), "BeforeRouter") == true {
|
||||
if strings.Contains(rw.Body.String(), "BeforeRouter") {
|
||||
t.Errorf(testName + " BeforeRouter ran in error")
|
||||
}
|
||||
if strings.Contains(rw.Body.String(), "BeforeExec") == true {
|
||||
if strings.Contains(rw.Body.String(), "BeforeExec") {
|
||||
t.Errorf(testName + " BeforeExec ran in error")
|
||||
}
|
||||
}
|
||||
@ -583,19 +583,19 @@ func TestFilterFinishRouter(t *testing.T) {
|
||||
rw, r := testRequest("GET", url)
|
||||
mux.ServeHTTP(rw, r)
|
||||
|
||||
if strings.Contains(rw.Body.String(), "FinishRouter1") == true {
|
||||
if strings.Contains(rw.Body.String(), "FinishRouter1") {
|
||||
t.Errorf(testName + " FinishRouter did not run")
|
||||
}
|
||||
if strings.Contains(rw.Body.String(), "hello") == false {
|
||||
if !strings.Contains(rw.Body.String(), "hello") {
|
||||
t.Errorf(testName + " handler did not run properly")
|
||||
}
|
||||
if strings.Contains(rw.Body.String(), "AfterExec1") == true {
|
||||
if strings.Contains(rw.Body.String(), "AfterExec1") {
|
||||
t.Errorf(testName + " AfterExec ran in error")
|
||||
}
|
||||
if strings.Contains(rw.Body.String(), "BeforeRouter") == true {
|
||||
if strings.Contains(rw.Body.String(), "BeforeRouter") {
|
||||
t.Errorf(testName + " BeforeRouter ran in error")
|
||||
}
|
||||
if strings.Contains(rw.Body.String(), "BeforeExec") == true {
|
||||
if strings.Contains(rw.Body.String(), "BeforeExec") {
|
||||
t.Errorf(testName + " BeforeExec ran in error")
|
||||
}
|
||||
}
|
||||
@ -615,14 +615,14 @@ func TestFilterFinishRouterMultiFirstOnly(t *testing.T) {
|
||||
rw, r := testRequest("GET", url)
|
||||
mux.ServeHTTP(rw, r)
|
||||
|
||||
if strings.Contains(rw.Body.String(), "FinishRouter1") == false {
|
||||
if !strings.Contains(rw.Body.String(), "FinishRouter1") {
|
||||
t.Errorf(testName + " FinishRouter1 did not run")
|
||||
}
|
||||
if strings.Contains(rw.Body.String(), "hello") == false {
|
||||
if !strings.Contains(rw.Body.String(), "hello") {
|
||||
t.Errorf(testName + " handler did not run properly")
|
||||
}
|
||||
// not expected in body
|
||||
if strings.Contains(rw.Body.String(), "FinishRouter2") == true {
|
||||
if strings.Contains(rw.Body.String(), "FinishRouter2") {
|
||||
t.Errorf(testName + " FinishRouter2 did run")
|
||||
}
|
||||
}
|
||||
@ -642,44 +642,52 @@ func TestFilterFinishRouterMulti(t *testing.T) {
|
||||
rw, r := testRequest("GET", url)
|
||||
mux.ServeHTTP(rw, r)
|
||||
|
||||
if strings.Contains(rw.Body.String(), "FinishRouter1") == false {
|
||||
if !strings.Contains(rw.Body.String(), "FinishRouter1") {
|
||||
t.Errorf(testName + " FinishRouter1 did not run")
|
||||
}
|
||||
if strings.Contains(rw.Body.String(), "hello") == false {
|
||||
if !strings.Contains(rw.Body.String(), "hello") {
|
||||
t.Errorf(testName + " handler did not run properly")
|
||||
}
|
||||
if strings.Contains(rw.Body.String(), "FinishRouter2") == false {
|
||||
if !strings.Contains(rw.Body.String(), "FinishRouter2") {
|
||||
t.Errorf(testName + " FinishRouter2 did not run properly")
|
||||
}
|
||||
}
|
||||
|
||||
func beegoFilterNoOutput(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
func beegoBeforeRouter1(ctx *context.Context) {
|
||||
ctx.WriteString("|BeforeRouter1")
|
||||
}
|
||||
|
||||
func beegoBeforeRouter2(ctx *context.Context) {
|
||||
ctx.WriteString("|BeforeRouter2")
|
||||
}
|
||||
|
||||
func beegoBeforeExec1(ctx *context.Context) {
|
||||
ctx.WriteString("|BeforeExec1")
|
||||
}
|
||||
|
||||
func beegoBeforeExec2(ctx *context.Context) {
|
||||
ctx.WriteString("|BeforeExec2")
|
||||
}
|
||||
|
||||
func beegoAfterExec1(ctx *context.Context) {
|
||||
ctx.WriteString("|AfterExec1")
|
||||
}
|
||||
|
||||
func beegoAfterExec2(ctx *context.Context) {
|
||||
ctx.WriteString("|AfterExec2")
|
||||
}
|
||||
|
||||
func beegoFinishRouter1(ctx *context.Context) {
|
||||
ctx.WriteString("|FinishRouter1")
|
||||
}
|
||||
|
||||
func beegoFinishRouter2(ctx *context.Context) {
|
||||
ctx.WriteString("|FinishRouter2")
|
||||
}
|
||||
|
||||
func beegoResetParams(ctx *context.Context) {
|
||||
ctx.ResponseWriter.Header().Set("splat", ctx.Input.Param(":splat"))
|
||||
}
|
||||
|
@ -155,10 +155,13 @@ func (cp *Provider) SessionInit(maxlifetime int64, savePath string) error {
|
||||
func (cp *Provider) SessionRead(sid string) (session.Store, error) {
|
||||
cp.b = cp.getBucket()
|
||||
|
||||
var doc []byte
|
||||
var (
|
||||
kv map[interface{}]interface{}
|
||||
err error
|
||||
doc []byte
|
||||
)
|
||||
|
||||
err := cp.b.Get(sid, &doc)
|
||||
var kv map[interface{}]interface{}
|
||||
err = cp.b.Get(sid, &doc)
|
||||
if doc == nil {
|
||||
kv = make(map[interface{}]interface{})
|
||||
} else {
|
||||
@ -230,7 +233,6 @@ func (cp *Provider) SessionDestroy(sid string) error {
|
||||
|
||||
// SessionGC Recycle
|
||||
func (cp *Provider) SessionGC() {
|
||||
return
|
||||
}
|
||||
|
||||
// SessionAll return all active session
|
||||
|
@ -12,8 +12,10 @@ import (
|
||||
"github.com/siddontang/ledisdb/ledis"
|
||||
)
|
||||
|
||||
var ledispder = &Provider{}
|
||||
var c *ledis.DB
|
||||
var (
|
||||
ledispder = &Provider{}
|
||||
c *ledis.DB
|
||||
)
|
||||
|
||||
// SessionStore ledis session store
|
||||
type SessionStore struct {
|
||||
@ -97,27 +99,36 @@ func (lp *Provider) SessionInit(maxlifetime int64, savePath string) error {
|
||||
}
|
||||
cfg := new(config.Config)
|
||||
cfg.DataDir = lp.savePath
|
||||
nowLedis, err := ledis.Open(cfg)
|
||||
c, err = nowLedis.Select(lp.db)
|
||||
|
||||
var ledisInstance *ledis.Ledis
|
||||
ledisInstance, err = ledis.Open(cfg)
|
||||
if err != nil {
|
||||
println(err)
|
||||
return nil
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
c, err = ledisInstance.Select(lp.db)
|
||||
return err
|
||||
}
|
||||
|
||||
// SessionRead read ledis session by sid
|
||||
func (lp *Provider) SessionRead(sid string) (session.Store, error) {
|
||||
kvs, err := c.Get([]byte(sid))
|
||||
var kv map[interface{}]interface{}
|
||||
var (
|
||||
kv map[interface{}]interface{}
|
||||
kvs []byte
|
||||
err error
|
||||
)
|
||||
|
||||
if kvs, err = c.Get([]byte(sid)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(kvs) == 0 {
|
||||
kv = make(map[interface{}]interface{})
|
||||
} else {
|
||||
kv, err = session.DecodeGob(kvs)
|
||||
if err != nil {
|
||||
if kv, err = session.DecodeGob(kvs); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
ls := &SessionStore{sid: sid, values: kv, maxlifetime: lp.maxlifetime}
|
||||
return ls, nil
|
||||
}
|
||||
@ -125,10 +136,7 @@ func (lp *Provider) SessionRead(sid string) (session.Store, error) {
|
||||
// SessionExist check ledis session exist by sid
|
||||
func (lp *Provider) SessionExist(sid string) bool {
|
||||
count, _ := c.Exists([]byte(sid))
|
||||
if count == 0 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
return !(count == 0)
|
||||
}
|
||||
|
||||
// SessionRegenerate generate new sid for ledis session
|
||||
@ -145,18 +153,7 @@ func (lp *Provider) SessionRegenerate(oldsid, sid string) (session.Store, error)
|
||||
c.Set([]byte(sid), data)
|
||||
c.Expire([]byte(sid), lp.maxlifetime)
|
||||
}
|
||||
kvs, err := c.Get([]byte(sid))
|
||||
var kv map[interface{}]interface{}
|
||||
if len(kvs) == 0 {
|
||||
kv = make(map[interface{}]interface{})
|
||||
} else {
|
||||
kv, err = session.DecodeGob([]byte(kvs))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
ls := &SessionStore{sid: sid, values: kv, maxlifetime: lp.maxlifetime}
|
||||
return ls, nil
|
||||
return lp.SessionRead(sid)
|
||||
}
|
||||
|
||||
// SessionDestroy delete ledis session by id
|
||||
@ -167,7 +164,6 @@ func (lp *Provider) SessionDestroy(sid string) error {
|
||||
|
||||
// SessionGC Impelment method, no used.
|
||||
func (lp *Provider) SessionGC() {
|
||||
return
|
||||
}
|
||||
|
||||
// SessionAll return all active session
|
||||
|
@ -205,11 +205,7 @@ func (rp *MemProvider) SessionDestroy(sid string) error {
|
||||
}
|
||||
}
|
||||
|
||||
err := client.Delete(sid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return client.Delete(sid)
|
||||
}
|
||||
|
||||
func (rp *MemProvider) connectInit() error {
|
||||
@ -219,7 +215,6 @@ func (rp *MemProvider) connectInit() error {
|
||||
|
||||
// SessionGC Impelment method, no used.
|
||||
func (rp *MemProvider) SessionGC() {
|
||||
return
|
||||
}
|
||||
|
||||
// SessionAll return all activeSession
|
||||
|
@ -170,10 +170,7 @@ func (mp *Provider) SessionExist(sid string) bool {
|
||||
row := c.QueryRow("select session_data from "+TableName+" where session_key=?", sid)
|
||||
var sessiondata []byte
|
||||
err := row.Scan(&sessiondata)
|
||||
if err == sql.ErrNoRows {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
return !(err == sql.ErrNoRows)
|
||||
}
|
||||
|
||||
// SessionRegenerate generate new sid for mysql session
|
||||
@ -212,7 +209,6 @@ func (mp *Provider) SessionGC() {
|
||||
c := mp.connectInit()
|
||||
c.Exec("DELETE from "+TableName+" where session_expiry < ?", time.Now().Unix()-mp.maxlifetime)
|
||||
c.Close()
|
||||
return
|
||||
}
|
||||
|
||||
// SessionAll count values in mysql session
|
||||
|
@ -184,11 +184,7 @@ func (mp *Provider) SessionExist(sid string) bool {
|
||||
row := c.QueryRow("select session_data from session where session_key=$1", sid)
|
||||
var sessiondata []byte
|
||||
err := row.Scan(&sessiondata)
|
||||
|
||||
if err == sql.ErrNoRows {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
return !(err == sql.ErrNoRows)
|
||||
}
|
||||
|
||||
// SessionRegenerate generate new sid for postgresql session
|
||||
@ -228,7 +224,6 @@ func (mp *Provider) SessionGC() {
|
||||
c := mp.connectInit()
|
||||
c.Exec("DELETE from session where EXTRACT(EPOCH FROM (current_timestamp - session_expiry)) > $1", mp.maxlifetime)
|
||||
c.Close()
|
||||
return
|
||||
}
|
||||
|
||||
// SessionAll count values in postgresql session
|
||||
|
@ -128,7 +128,7 @@ func (rp *Provider) SessionInit(maxlifetime int64, savePath string) error {
|
||||
}
|
||||
if len(configs) > 1 {
|
||||
poolsize, err := strconv.Atoi(configs[1])
|
||||
if err != nil || poolsize <= 0 {
|
||||
if err != nil || poolsize < 0 {
|
||||
rp.poolsize = MaxPoolSize
|
||||
} else {
|
||||
rp.poolsize = poolsize
|
||||
@ -155,7 +155,7 @@ func (rp *Provider) SessionInit(maxlifetime int64, savePath string) error {
|
||||
return nil, err
|
||||
}
|
||||
if rp.password != "" {
|
||||
if _, err := c.Do("AUTH", rp.password); err != nil {
|
||||
if _, err = c.Do("AUTH", rp.password); err != nil {
|
||||
c.Close()
|
||||
return nil, err
|
||||
}
|
||||
@ -176,13 +176,20 @@ func (rp *Provider) SessionRead(sid string) (session.Store, error) {
|
||||
c := rp.poollist.Get()
|
||||
defer c.Close()
|
||||
|
||||
kvs, err := redis.String(c.Do("GET", sid))
|
||||
var kv map[interface{}]interface{}
|
||||
var (
|
||||
kv map[interface{}]interface{}
|
||||
kvs string
|
||||
err error
|
||||
)
|
||||
|
||||
if kvs, err = redis.String(c.Do("GET", sid)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(kvs) == 0 {
|
||||
kv = make(map[interface{}]interface{})
|
||||
} else {
|
||||
kv, err = session.DecodeGob([]byte(kvs))
|
||||
if err != nil {
|
||||
if kv, err = session.DecodeGob([]byte(kvs)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
@ -216,20 +223,7 @@ func (rp *Provider) SessionRegenerate(oldsid, sid string) (session.Store, error)
|
||||
c.Do("RENAME", oldsid, sid)
|
||||
c.Do("EXPIRE", sid, rp.maxlifetime)
|
||||
}
|
||||
|
||||
kvs, err := redis.String(c.Do("GET", sid))
|
||||
var kv map[interface{}]interface{}
|
||||
if len(kvs) == 0 {
|
||||
kv = make(map[interface{}]interface{})
|
||||
} else {
|
||||
kv, err = session.DecodeGob([]byte(kvs))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
rs := &SessionStore{p: rp.poollist, sid: sid, values: kv, maxlifetime: rp.maxlifetime}
|
||||
return rs, nil
|
||||
return rp.SessionRead(sid)
|
||||
}
|
||||
|
||||
// SessionDestroy delete redis session by id
|
||||
@ -243,7 +237,6 @@ func (rp *Provider) SessionDestroy(sid string) error {
|
||||
|
||||
// SessionGC Impelment method, no used.
|
||||
func (rp *Provider) SessionGC() {
|
||||
return
|
||||
}
|
||||
|
||||
// SessionAll return all activeSession
|
||||
|
@ -74,21 +74,16 @@ func (st *CookieSessionStore) SessionID() string {
|
||||
|
||||
// SessionRelease Write cookie session to http response cookie
|
||||
func (st *CookieSessionStore) SessionRelease(w http.ResponseWriter) {
|
||||
str, err := encodeCookie(cookiepder.block,
|
||||
cookiepder.config.SecurityKey,
|
||||
cookiepder.config.SecurityName,
|
||||
st.values)
|
||||
if err != nil {
|
||||
return
|
||||
encodedCookie, err := encodeCookie(cookiepder.block, cookiepder.config.SecurityKey, cookiepder.config.SecurityName, st.values)
|
||||
if err == nil {
|
||||
cookie := &http.Cookie{Name: cookiepder.config.CookieName,
|
||||
Value: url.QueryEscape(encodedCookie),
|
||||
Path: "/",
|
||||
HttpOnly: true,
|
||||
Secure: cookiepder.config.Secure,
|
||||
MaxAge: cookiepder.config.Maxage}
|
||||
http.SetCookie(w, cookie)
|
||||
}
|
||||
cookie := &http.Cookie{Name: cookiepder.config.CookieName,
|
||||
Value: url.QueryEscape(str),
|
||||
Path: "/",
|
||||
HttpOnly: true,
|
||||
Secure: cookiepder.config.Secure,
|
||||
MaxAge: cookiepder.config.Maxage}
|
||||
http.SetCookie(w, cookie)
|
||||
return
|
||||
}
|
||||
|
||||
type cookieConfig struct {
|
||||
@ -166,7 +161,6 @@ func (pder *CookieProvider) SessionDestroy(sid string) error {
|
||||
|
||||
// SessionGC Implement method, no used.
|
||||
func (pder *CookieProvider) SessionGC() {
|
||||
return
|
||||
}
|
||||
|
||||
// SessionAll Implement method, return 0.
|
||||
|
@ -15,8 +15,7 @@
|
||||
package session
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
@ -88,10 +87,9 @@ func (fs *FileSessionStore) SessionRelease(w http.ResponseWriter) {
|
||||
var f *os.File
|
||||
if err == nil {
|
||||
f, err = os.OpenFile(path.Join(filepder.savePath, string(fs.sid[0]), string(fs.sid[1]), fs.sid), os.O_RDWR, 0777)
|
||||
SLogger.Println(err)
|
||||
} else if os.IsNotExist(err) {
|
||||
f, err = os.Create(path.Join(filepder.savePath, string(fs.sid[0]), string(fs.sid[1]), fs.sid))
|
||||
SLogger.Println(err)
|
||||
|
||||
} else {
|
||||
return
|
||||
}
|
||||
@ -136,6 +134,9 @@ func (fp *FileProvider) SessionRead(sid string) (Store, error) {
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer f.Close()
|
||||
|
||||
os.Chtimes(path.Join(fp.savePath, string(sid[0]), string(sid[1]), sid), time.Now(), time.Now())
|
||||
var kv map[interface{}]interface{}
|
||||
b, err := ioutil.ReadAll(f)
|
||||
@ -150,7 +151,7 @@ func (fp *FileProvider) SessionRead(sid string) (Store, error) {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
f.Close()
|
||||
|
||||
ss := &FileSessionStore{sid: sid, values: kv}
|
||||
return ss, nil
|
||||
}
|
||||
@ -162,10 +163,7 @@ func (fp *FileProvider) SessionExist(sid string) bool {
|
||||
defer filepder.lock.Unlock()
|
||||
|
||||
_, err := os.Stat(path.Join(fp.savePath, string(sid[0]), string(sid[1]), sid))
|
||||
if err == nil {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// SessionDestroy Remove all files in this save path
|
||||
@ -205,49 +203,58 @@ func (fp *FileProvider) SessionRegenerate(oldsid, sid string) (Store, error) {
|
||||
filepder.lock.Lock()
|
||||
defer filepder.lock.Unlock()
|
||||
|
||||
err := os.MkdirAll(path.Join(fp.savePath, string(oldsid[0]), string(oldsid[1])), 0777)
|
||||
if err != nil {
|
||||
SLogger.Println(err.Error())
|
||||
}
|
||||
err = os.MkdirAll(path.Join(fp.savePath, string(sid[0]), string(sid[1])), 0777)
|
||||
if err != nil {
|
||||
SLogger.Println(err.Error())
|
||||
}
|
||||
_, err = os.Stat(path.Join(fp.savePath, string(sid[0]), string(sid[1]), sid))
|
||||
var newf *os.File
|
||||
oldPath := path.Join(fp.savePath, string(oldsid[0]), string(oldsid[1]))
|
||||
oldSidFile := path.Join(oldPath, oldsid)
|
||||
newPath := path.Join(fp.savePath, string(sid[0]), string(sid[1]))
|
||||
newSidFile := path.Join(newPath, sid)
|
||||
|
||||
// new sid file is exist
|
||||
_, err := os.Stat(newSidFile)
|
||||
if err == nil {
|
||||
return nil, errors.New("newsid exist")
|
||||
} else if os.IsNotExist(err) {
|
||||
newf, err = os.Create(path.Join(fp.savePath, string(sid[0]), string(sid[1]), sid))
|
||||
return nil, fmt.Errorf("newsid %s exist", newSidFile)
|
||||
}
|
||||
|
||||
_, err = os.Stat(path.Join(fp.savePath, string(oldsid[0]), string(oldsid[1]), oldsid))
|
||||
var f *os.File
|
||||
if err == nil {
|
||||
f, err = os.OpenFile(path.Join(fp.savePath, string(oldsid[0]), string(oldsid[1]), oldsid), os.O_RDWR, 0777)
|
||||
io.Copy(newf, f)
|
||||
} else if os.IsNotExist(err) {
|
||||
newf, err = os.Create(path.Join(fp.savePath, string(sid[0]), string(sid[1]), sid))
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
f.Close()
|
||||
os.Remove(path.Join(fp.savePath, string(oldsid[0]), string(oldsid[1])))
|
||||
os.Chtimes(path.Join(fp.savePath, string(sid[0]), string(sid[1]), sid), time.Now(), time.Now())
|
||||
var kv map[interface{}]interface{}
|
||||
b, err := ioutil.ReadAll(newf)
|
||||
err = os.MkdirAll(newPath, 0777)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
SLogger.Println(err.Error())
|
||||
}
|
||||
if len(b) == 0 {
|
||||
kv = make(map[interface{}]interface{})
|
||||
} else {
|
||||
kv, err = DecodeGob(b)
|
||||
|
||||
// if old sid file exist
|
||||
// 1.read and parse file content
|
||||
// 2.write content to new sid file
|
||||
// 3.remove old sid file, change new sid file atime and ctime
|
||||
// 4.return FileSessionStore
|
||||
_, err = os.Stat(oldSidFile)
|
||||
if err == nil {
|
||||
b, err := ioutil.ReadFile(oldSidFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var kv map[interface{}]interface{}
|
||||
if len(b) == 0 {
|
||||
kv = make(map[interface{}]interface{})
|
||||
} else {
|
||||
kv, err = DecodeGob(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
ioutil.WriteFile(newSidFile, b, 0777)
|
||||
os.Remove(oldSidFile)
|
||||
os.Chtimes(newSidFile, time.Now(), time.Now())
|
||||
ss := &FileSessionStore{sid: sid, values: kv}
|
||||
return ss, nil
|
||||
}
|
||||
ss := &FileSessionStore{sid: sid, values: kv}
|
||||
|
||||
// if old sid file not exist, just create new sid file and return
|
||||
newf, err := os.Create(newSidFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
newf.Close()
|
||||
ss := &FileSessionStore{sid: sid, values: make(map[interface{}]interface{})}
|
||||
return ss, nil
|
||||
}
|
||||
|
||||
|
@ -115,7 +115,7 @@ func TestParseConfig(t *testing.T) {
|
||||
if cf2.Gclifetime != 3600 {
|
||||
t.Fatal("parseconfig get gclifetime error")
|
||||
}
|
||||
if cf2.EnableSetCookie != false {
|
||||
if cf2.EnableSetCookie {
|
||||
t.Fatal("parseconfig get enableSetCookie error")
|
||||
}
|
||||
cconfig := new(cookieConfig)
|
||||
|
@ -86,6 +86,7 @@ type ManagerConfig struct {
|
||||
EnableSetCookie bool `json:"enableSetCookie,omitempty"`
|
||||
Gclifetime int64 `json:"gclifetime"`
|
||||
Maxlifetime int64 `json:"maxLifetime"`
|
||||
DisableHTTPOnly bool `json:"disableHTTPOnly"`
|
||||
Secure bool `json:"secure"`
|
||||
CookieLifeTime int `json:"cookieLifeTime"`
|
||||
ProviderConfig string `json:"providerConfig"`
|
||||
@ -206,13 +207,13 @@ func (manager *Manager) SessionStart(w http.ResponseWriter, r *http.Request) (se
|
||||
|
||||
session, err = manager.provider.SessionRead(sid)
|
||||
if err != nil {
|
||||
return nil, errs
|
||||
return nil, err
|
||||
}
|
||||
cookie := &http.Cookie{
|
||||
Name: manager.config.CookieName,
|
||||
Value: url.QueryEscape(sid),
|
||||
Path: "/",
|
||||
HttpOnly: true,
|
||||
HttpOnly: !manager.config.DisableHTTPOnly,
|
||||
Secure: manager.isSecure(r),
|
||||
Domain: manager.config.Domain,
|
||||
}
|
||||
@ -251,7 +252,7 @@ func (manager *Manager) SessionDestroy(w http.ResponseWriter, r *http.Request) {
|
||||
expiration := time.Now()
|
||||
cookie = &http.Cookie{Name: manager.config.CookieName,
|
||||
Path: "/",
|
||||
HttpOnly: true,
|
||||
HttpOnly: !manager.config.DisableHTTPOnly,
|
||||
Expires: expiration,
|
||||
MaxAge: -1}
|
||||
|
||||
@ -285,7 +286,7 @@ func (manager *Manager) SessionRegenerateID(w http.ResponseWriter, r *http.Reque
|
||||
cookie = &http.Cookie{Name: manager.config.CookieName,
|
||||
Value: url.QueryEscape(sid),
|
||||
Path: "/",
|
||||
HttpOnly: true,
|
||||
HttpOnly: !manager.config.DisableHTTPOnly,
|
||||
Secure: manager.isSecure(r),
|
||||
Domain: manager.config.Domain,
|
||||
}
|
||||
|
@ -26,26 +26,19 @@ func (p *SsdbProvider) connectInit() error {
|
||||
return errors.New("SessionInit First")
|
||||
}
|
||||
p.client, err = ssdb.Connect(p.host, p.port)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return err
|
||||
}
|
||||
|
||||
func (p *SsdbProvider) SessionInit(maxLifetime int64, savePath string) error {
|
||||
var e error = nil
|
||||
p.maxLifetime = maxLifetime
|
||||
address := strings.Split(savePath, ":")
|
||||
p.host = address[0]
|
||||
p.port, e = strconv.Atoi(address[1])
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
err := p.connectInit()
|
||||
if err != nil {
|
||||
|
||||
var err error
|
||||
if p.port, err = strconv.Atoi(address[1]); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return p.connectInit()
|
||||
}
|
||||
|
||||
func (p *SsdbProvider) SessionRead(sid string) (session.Store, error) {
|
||||
@ -85,8 +78,8 @@ func (p *SsdbProvider) SessionExist(sid string) bool {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
|
||||
}
|
||||
|
||||
func (p *SsdbProvider) SessionRegenerate(oldsid, sid string) (session.Store, error) {
|
||||
//conn.Do("setx", key, v, ttl)
|
||||
if p.client == nil {
|
||||
@ -126,14 +119,10 @@ func (p *SsdbProvider) SessionDestroy(sid string) error {
|
||||
}
|
||||
}
|
||||
_, err := p.client.Del(sid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return err
|
||||
}
|
||||
|
||||
func (p *SsdbProvider) SessionGC() {
|
||||
return
|
||||
}
|
||||
|
||||
func (p *SsdbProvider) SessionAll() int {
|
||||
|
@ -90,8 +90,6 @@ func serverStaticRouter(ctx *context.Context) {
|
||||
}
|
||||
|
||||
http.ServeContent(ctx.ResponseWriter, ctx.Request, filePath, sch.modTime, sch)
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
type serveContentHolder struct {
|
||||
@ -109,14 +107,14 @@ var (
|
||||
func openFile(filePath string, fi os.FileInfo, acceptEncoding string) (bool, string, *serveContentHolder, error) {
|
||||
mapKey := acceptEncoding + ":" + filePath
|
||||
mapLock.RLock()
|
||||
mapFile, _ := staticFileMap[mapKey]
|
||||
mapFile := staticFileMap[mapKey]
|
||||
mapLock.RUnlock()
|
||||
if isOk(mapFile, fi) {
|
||||
return mapFile.encoding != "", mapFile.encoding, mapFile, nil
|
||||
}
|
||||
mapLock.Lock()
|
||||
defer mapLock.Unlock()
|
||||
if mapFile, _ = staticFileMap[mapKey]; !isOk(mapFile, fi) {
|
||||
if mapFile = staticFileMap[mapKey]; !isOk(mapFile, fi) {
|
||||
file, err := os.Open(filePath)
|
||||
if err != nil {
|
||||
return false, "", nil, err
|
||||
@ -196,9 +194,11 @@ func lookupFile(ctx *context.Context) (bool, string, os.FileInfo, error) {
|
||||
if !fi.IsDir() {
|
||||
return false, fp, fi, err
|
||||
}
|
||||
ifp := filepath.Join(fp, "index.html")
|
||||
if ifi, _ := os.Stat(ifp); ifi != nil && ifi.Mode().IsRegular() {
|
||||
return false, ifp, ifi, err
|
||||
if requestURL := ctx.Input.URL(); requestURL[len(requestURL)-1] == '/' {
|
||||
ifp := filepath.Join(fp, "index.html")
|
||||
if ifi, _ := os.Stat(ifp); ifi != nil && ifi.Mode().IsRegular() {
|
||||
return false, ifp, ifi, err
|
||||
}
|
||||
}
|
||||
return !BConfig.WebConfig.DirectoryIndex, fp, fi, err
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user