mirror of
https://github.com/astaxie/beego.git
synced 2025-07-11 19:21:00 +00:00
Compare commits
285 Commits
Author | SHA1 | Date | |
---|---|---|---|
bf5c5626ab | |||
0fbbc67c3d | |||
3e1916ec3c | |||
b068a676dd | |||
ed73bdcfab | |||
ae94b705ea | |||
08fb921053 | |||
e67e57f8fb | |||
b30969704a | |||
646acc423e | |||
c3a81a23f9 | |||
103dd22151 | |||
ec6cb43711 | |||
84cb9a5986 | |||
9b8bc2aef7 | |||
9710d9e961 | |||
58bb49a78c | |||
df37739c7d | |||
a5dd5d161d | |||
6827107177 | |||
a30a89e57e | |||
d352e4abcb | |||
a948d4c1e1 | |||
b7eb3963f5 | |||
f7afb3cb75 | |||
348bf51a42 | |||
dfea2cc5f3 | |||
532eab8e1d | |||
3872382a4b | |||
4018693fbd | |||
3b829504f6 | |||
80fa51468c | |||
3504d2a4da | |||
229d8b9530 | |||
9536d460d0 | |||
e211e4839c | |||
3332dbe595 | |||
b9c8c08c03 | |||
663f22d849 | |||
fbaa4d1233 | |||
7dc8991140 | |||
0ce70b8c99 | |||
b169ea4b63 | |||
72ec4df679 | |||
b91263a254 | |||
e91afb1938 | |||
74eb613919 | |||
32d4310861 | |||
c56704f3fd | |||
c5118e9535 | |||
9b57566963 | |||
8d59e7afd1 | |||
fd733f76f0 | |||
37d4bb3df5 | |||
5697c6d7cc | |||
51a6162363 | |||
5a12b3d020 | |||
bebd2c469d | |||
d15e66a4ff | |||
c04d43695c | |||
d813334a24 | |||
9fef2f2eb4 | |||
0c746f4547 | |||
f5c8b1c6ac | |||
4921014c64 | |||
520753415f | |||
3162da131d | |||
a7354d2d08 | |||
07a9a2d0f3 | |||
c8c25549e7 | |||
6641a436a2 | |||
1dd50fb65f | |||
4bc4f77c29 | |||
ef36ecd376 | |||
c6cef853c7 | |||
33ad8d5db4 | |||
33e6d57754 | |||
afa57ca1f2 | |||
510dd02a06 | |||
166e88c103 | |||
51b6adeb24 | |||
51c19c374a | |||
b23452dc3f | |||
f61038e6bd | |||
b9117e2ff1 | |||
5a7a3da909 | |||
3f4502990a | |||
e14113aa0e | |||
d96289a81b | |||
4fc95b0d69 | |||
aa3d6c5363 | |||
5ac0cb929c | |||
b27ab53017 | |||
1aba294405 | |||
657e55ed59 | |||
621c25396e | |||
715ba918f0 | |||
8bb0a70847 | |||
94e79eddcf | |||
749a4028b4 | |||
fc55c2b57c | |||
d58ad2ee36 | |||
cb38ab4f85 | |||
fc86f6422d | |||
d453242e48 | |||
7c2ec075a4 | |||
c903de41e4 | |||
e8c8366308 | |||
4901567bba | |||
29bcd31b27 | |||
83a563c0ab | |||
e888fee4e0 | |||
c1ba11f531 | |||
ed558a0e70 | |||
6b9c3f4824 | |||
7ec819deed | |||
4cfb3678f8 | |||
3c17e2a7e6 | |||
e72b02b7cc | |||
82586c70e9 | |||
cb86bcc9e8 | |||
234708062a | |||
6e34f43721 | |||
3249ec8ebf | |||
31b2b21dbc | |||
d0c1936922 | |||
338a23a12b | |||
932def1ed2 | |||
16b5a11484 | |||
547fbce86c | |||
5a2eea07cb | |||
2231841d74 | |||
805a674825 | |||
f2925978f1 | |||
fe3a224a23 | |||
2754edc849 | |||
79f60274a0 | |||
a87c1c5e8e | |||
2b00b7d66d | |||
3d9286f089 | |||
55e6c15073 | |||
8b504e7d51 | |||
47ef2b343e | |||
80dcdb8645 | |||
d1c3bd8416 | |||
0240e182c6 | |||
7f2e3feb3c | |||
d15dd2795c | |||
0ea34fff27 | |||
4e8f212069 | |||
eb71d0ea7f | |||
b24ddb953c | |||
12f8fbe37f | |||
5e8312bc23 | |||
88d07058a5 | |||
cab8458c1c | |||
f0b95c552b | |||
ce677202e5 | |||
720c323e20 | |||
47e351e11d | |||
248beab557 | |||
388a5610fa | |||
41498758fe | |||
655484b4df | |||
11b4bf8aaa | |||
2513bcf584 | |||
3e51823c0f | |||
e32a18203b | |||
ee1d8bc30e | |||
828cbbdf5d | |||
d54cd4fa5f | |||
7747e9ec8b | |||
9765519f38 | |||
69f0b94745 | |||
3c9b6c99b7 | |||
b5c6eb54d2 | |||
e1c90bfc09 | |||
91400f10b0 | |||
c814893d65 | |||
2325090101 | |||
40bc52b844 | |||
589f3755f0 | |||
1004678005 | |||
0ac2e47162 | |||
b6a35a8944 | |||
74dc3c7500 | |||
cb4f252a06 | |||
bceefc9075 | |||
10cd1070f4 | |||
9b01b1c63d | |||
b2e7720fcd | |||
83814a76cc | |||
d3a16dca85 | |||
7452151bee | |||
1b8f05cef1 | |||
cfb2f68dd6 | |||
e76423e6dc | |||
947980b5eb | |||
44bdf1df63 | |||
79b66ef053 | |||
a91e2e9950 | |||
ea3d0690cf | |||
1c32c011a1 | |||
64b475d7d6 | |||
aa8f7bc146 | |||
3e29078f68 | |||
a1bc94e648 | |||
4cba78afd9 | |||
f311ae9ebe | |||
cbd831042a | |||
522b3a4a70 | |||
cd67f13bf9 | |||
0f554d9b1a | |||
9b79437778 | |||
3742d1178c | |||
b9e3cbbf44 | |||
3c0c87f473 | |||
3b29a9c12a | |||
d03285a0ee | |||
e810f2e930 | |||
41aac79ac0 | |||
52f916a28a | |||
864693d2f8 | |||
08ea9b3339 | |||
19f4a6ac0b | |||
bf6bd6b292 | |||
89e01d125c | |||
3bb4ca5adc | |||
712df81c99 | |||
3d7ef599cc | |||
9aedb4d05a | |||
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 | |||
49fffe3ebe | |||
a8a2dffc59 |
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
|
18
.travis.yml
18
.travis.yml
@ -1,9 +1,9 @@
|
||||
language: go
|
||||
|
||||
go:
|
||||
- 1.6
|
||||
- 1.5.3
|
||||
- 1.4.3
|
||||
- 1.7.5
|
||||
- 1.8.5
|
||||
- 1.9.2
|
||||
services:
|
||||
- redis-server
|
||||
- mysql
|
||||
@ -11,7 +11,6 @@ services:
|
||||
- memcached
|
||||
env:
|
||||
- ORM_DRIVER=sqlite3 ORM_SOURCE=$TRAVIS_BUILD_DIR/orm_test.db
|
||||
- ORM_DRIVER=mysql ORM_SOURCE="root:@/orm_test?charset=utf8"
|
||||
- ORM_DRIVER=postgres ORM_SOURCE="user=postgres dbname=orm_test sslmode=disable"
|
||||
before_install:
|
||||
- git clone git://github.com/ideawu/ssdb.git
|
||||
@ -33,6 +32,12 @@ install:
|
||||
- go get github.com/ssdb/gossdb/ssdb
|
||||
- go get github.com/cloudflare/golz4
|
||||
- go get github.com/gogo/protobuf/proto
|
||||
- go get github.com/Knetic/govaluate
|
||||
- go get github.com/casbin/casbin
|
||||
- go get -u honnef.co/go/tools/cmd/gosimple
|
||||
- go get -u github.com/mdempsky/unconvert
|
||||
- go get -u github.com/gordonklaus/ineffassign
|
||||
- go get -u github.com/golang/lint/golint
|
||||
before_script:
|
||||
- psql --version
|
||||
- sh -c "if [ '$ORM_DRIVER' = 'postgres' ]; then psql -c 'create database orm_test;' -U postgres; fi"
|
||||
@ -47,5 +52,10 @@ 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/)
|
||||
- ineffassign .
|
||||
- find . ! \( -path './vendor' -prune \) -type f -name '*.go' -print0 | xargs -0 gofmt -l -s
|
||||
- golint ./...
|
||||
addons:
|
||||
postgresql: "9.4"
|
||||
|
31
README.md
31
README.md
@ -1,20 +1,18 @@
|
||||
## Beego
|
||||
# Beego [](https://travis-ci.org/astaxie/beego) [](http://godoc.org/github.com/astaxie/beego) [](http://golangfoundation.org) [](https://goreportcard.com/report/github.com/astaxie/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
|
||||
|
||||
@ -24,15 +22,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
|
||||
|
||||
@ -56,7 +55,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).
|
||||
|
87
admin.go
87
admin.go
@ -37,7 +37,7 @@ var beeAdminApp *adminApp
|
||||
// FilterMonitorFunc is default monitor filter when admin module is enable.
|
||||
// if this func returns, admin module records qbs for this request by condition of this function logic.
|
||||
// usage:
|
||||
// func MyFilterMonitor(method, requestPath string, t time.Duration) bool {
|
||||
// func MyFilterMonitor(method, requestPath string, t time.Duration, pattern string, statusCode int) bool {
|
||||
// if method == "POST" {
|
||||
// return false
|
||||
// }
|
||||
@ -50,7 +50,7 @@ var beeAdminApp *adminApp
|
||||
// return true
|
||||
// }
|
||||
// beego.FilterMonitorFunc = MyFilterMonitor.
|
||||
var FilterMonitorFunc func(string, string, time.Duration) bool
|
||||
var FilterMonitorFunc func(string, string, time.Duration, string, int) bool
|
||||
|
||||
func init() {
|
||||
beeAdminApp = &adminApp{
|
||||
@ -62,7 +62,7 @@ func init() {
|
||||
beeAdminApp.Route("/healthcheck", healthcheck)
|
||||
beeAdminApp.Route("/task", taskStatus)
|
||||
beeAdminApp.Route("/listconf", listConf)
|
||||
FilterMonitorFunc = func(string, string, time.Duration) bool { return true }
|
||||
FilterMonitorFunc = func(string, string, time.Duration, string, int) bool { return true }
|
||||
}
|
||||
|
||||
// AdminIndex is the default http.Handler for admin module.
|
||||
@ -105,29 +105,12 @@ func listConf(rw http.ResponseWriter, r *http.Request) {
|
||||
tmpl.Execute(rw, data)
|
||||
|
||||
case "router":
|
||||
var (
|
||||
content = map[string]interface{}{
|
||||
"Fields": []string{
|
||||
"Router Pattern",
|
||||
"Methods",
|
||||
"Controller",
|
||||
},
|
||||
}
|
||||
methods = []string{}
|
||||
methodsData = make(map[string]interface{})
|
||||
)
|
||||
for method, t := range BeeApp.Handlers.routers {
|
||||
|
||||
resultList := new([][]string)
|
||||
|
||||
printTree(resultList, t)
|
||||
|
||||
methods = append(methods, method)
|
||||
methodsData[method] = resultList
|
||||
content := PrintTree()
|
||||
content["Fields"] = []string{
|
||||
"Router Pattern",
|
||||
"Methods",
|
||||
"Controller",
|
||||
}
|
||||
|
||||
content["Data"] = methodsData
|
||||
content["Methods"] = methods
|
||||
data["Content"] = content
|
||||
data["Title"] = "Routers"
|
||||
execTpl(rw, data, routerAndFilterTpl, defaultScriptsTpl)
|
||||
@ -157,8 +140,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)
|
||||
}
|
||||
@ -200,6 +183,28 @@ func list(root string, p interface{}, m map[string]interface{}) {
|
||||
}
|
||||
}
|
||||
|
||||
// PrintTree prints all registered routers.
|
||||
func PrintTree() map[string]interface{} {
|
||||
var (
|
||||
content = map[string]interface{}{}
|
||||
methods = []string{}
|
||||
methodsData = make(map[string]interface{})
|
||||
)
|
||||
for method, t := range BeeApp.Handlers.routers {
|
||||
|
||||
resultList := new([][]string)
|
||||
|
||||
printTree(resultList, t)
|
||||
|
||||
methods = append(methods, method)
|
||||
methodsData[method] = resultList
|
||||
}
|
||||
|
||||
content["Data"] = methodsData
|
||||
content["Methods"] = methods
|
||||
return content
|
||||
}
|
||||
|
||||
func printTree(resultList *[][]string, t *Tree) {
|
||||
for _, tr := range t.fixrouters {
|
||||
printTree(resultList, tr)
|
||||
@ -208,12 +213,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 +281,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 +292,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 +334,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 +342,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)
|
||||
|
@ -67,6 +67,7 @@ func oldMap() map[string]interface{} {
|
||||
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.AccessLogsFormat"] = BConfig.Log.AccessLogsFormat
|
||||
m["BConfig.Log.FileLineNum"] = BConfig.Log.FileLineNum
|
||||
m["BConfig.Log.Outputs"] = BConfig.Log.Outputs
|
||||
return m
|
||||
|
133
app.go
133
app.go
@ -15,13 +15,17 @@
|
||||
package beego
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/fcgi"
|
||||
"os"
|
||||
"path"
|
||||
"time"
|
||||
"strings"
|
||||
|
||||
"github.com/astaxie/beego/grace"
|
||||
"github.com/astaxie/beego/logs"
|
||||
@ -51,8 +55,11 @@ func NewApp() *App {
|
||||
return app
|
||||
}
|
||||
|
||||
// MiddleWare function for http.Handler
|
||||
type MiddleWare func(http.Handler) http.Handler
|
||||
|
||||
// Run beego application.
|
||||
func (app *App) Run() {
|
||||
func (app *App) Run(mws ...MiddleWare) {
|
||||
addr := BConfig.Listen.HTTPAddr
|
||||
|
||||
if BConfig.Listen.HTTPPort != 0 {
|
||||
@ -94,6 +101,12 @@ func (app *App) Run() {
|
||||
}
|
||||
|
||||
app.Server.Handler = app.Handlers
|
||||
for i:=len(mws)-1;i>=0;i-- {
|
||||
if mws[i] == nil {
|
||||
continue
|
||||
}
|
||||
app.Server.Handler = mws[i](app.Server.Handler)
|
||||
}
|
||||
app.Server.ReadTimeout = time.Duration(BConfig.Listen.ServerTimeOut) * time.Second
|
||||
app.Server.WriteTimeout = time.Duration(BConfig.Listen.ServerTimeOut) * time.Second
|
||||
app.Server.ErrorLog = logs.GetLogger("HTTP")
|
||||
@ -102,7 +115,7 @@ func (app *App) Run() {
|
||||
if BConfig.Listen.Graceful {
|
||||
httpsAddr := BConfig.Listen.HTTPSAddr
|
||||
app.Server.Addr = httpsAddr
|
||||
if BConfig.Listen.EnableHTTPS {
|
||||
if BConfig.Listen.EnableHTTPS || BConfig.Listen.EnableMutualHTTPS {
|
||||
go func() {
|
||||
time.Sleep(20 * time.Microsecond)
|
||||
if BConfig.Listen.HTTPSPort != 0 {
|
||||
@ -112,10 +125,19 @@ func (app *App) Run() {
|
||||
server := grace.NewServer(httpsAddr, app.Handlers)
|
||||
server.Server.ReadTimeout = app.Server.ReadTimeout
|
||||
server.Server.WriteTimeout = app.Server.WriteTimeout
|
||||
if err := server.ListenAndServeTLS(BConfig.Listen.HTTPSCertFile, BConfig.Listen.HTTPSKeyFile); err != nil {
|
||||
logs.Critical("ListenAndServeTLS: ", err, fmt.Sprintf("%d", os.Getpid()))
|
||||
time.Sleep(100 * time.Microsecond)
|
||||
endRunning <- true
|
||||
if BConfig.Listen.EnableMutualHTTPS {
|
||||
|
||||
if err := server.ListenAndServeMutualTLS(BConfig.Listen.HTTPSCertFile, BConfig.Listen.HTTPSKeyFile, BConfig.Listen.TrustCaFile); err != nil {
|
||||
logs.Critical("ListenAndServeTLS: ", err, fmt.Sprintf("%d", os.Getpid()))
|
||||
time.Sleep(100 * time.Microsecond)
|
||||
endRunning <- true
|
||||
}
|
||||
} else {
|
||||
if err := server.ListenAndServeTLS(BConfig.Listen.HTTPSCertFile, BConfig.Listen.HTTPSKeyFile); err != nil {
|
||||
logs.Critical("ListenAndServeTLS: ", err, fmt.Sprintf("%d", os.Getpid()))
|
||||
time.Sleep(100 * time.Microsecond)
|
||||
endRunning <- true
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
@ -139,7 +161,7 @@ func (app *App) Run() {
|
||||
}
|
||||
|
||||
// run normal mode
|
||||
if BConfig.Listen.EnableHTTPS {
|
||||
if BConfig.Listen.EnableHTTPS || BConfig.Listen.EnableMutualHTTPS {
|
||||
go func() {
|
||||
time.Sleep(20 * time.Microsecond)
|
||||
if BConfig.Listen.HTTPSPort != 0 {
|
||||
@ -149,6 +171,19 @@ func (app *App) Run() {
|
||||
return
|
||||
}
|
||||
logs.Info("https server Running on https://%s", app.Server.Addr)
|
||||
if BConfig.Listen.EnableMutualHTTPS {
|
||||
pool := x509.NewCertPool()
|
||||
data, err := ioutil.ReadFile(BConfig.Listen.TrustCaFile)
|
||||
if err != nil {
|
||||
BeeLogger.Info("MutualHTTPS should provide TrustCaFile")
|
||||
return
|
||||
}
|
||||
pool.AppendCertsFromPEM(data)
|
||||
app.Server.TLSConfig = &tls.Config{
|
||||
ClientCAs: pool,
|
||||
ClientAuth: tls.RequireAndVerifyClientCert,
|
||||
}
|
||||
}
|
||||
if err := app.Server.ListenAndServeTLS(BConfig.Listen.HTTPSCertFile, BConfig.Listen.HTTPSKeyFile); err != nil {
|
||||
logs.Critical("ListenAndServeTLS: ", err)
|
||||
time.Sleep(100 * time.Microsecond)
|
||||
@ -207,6 +242,84 @@ func Router(rootpath string, c ControllerInterface, mappingMethods ...string) *A
|
||||
return BeeApp
|
||||
}
|
||||
|
||||
// UnregisterFixedRoute unregisters the route with the specified fixedRoute. It is particularly useful
|
||||
// in web applications that inherit most routes from a base webapp via the underscore
|
||||
// import, and aim to overwrite only certain paths.
|
||||
// The method parameter can be empty or "*" for all HTTP methods, or a particular
|
||||
// method type (e.g. "GET" or "POST") for selective removal.
|
||||
//
|
||||
// Usage (replace "GET" with "*" for all methods):
|
||||
// beego.UnregisterFixedRoute("/yourpreviouspath", "GET")
|
||||
// beego.Router("/yourpreviouspath", yourControllerAddress, "get:GetNewPage")
|
||||
func UnregisterFixedRoute(fixedRoute string, method string) *App {
|
||||
subPaths := splitPath(fixedRoute)
|
||||
if method == "" || method == "*" {
|
||||
for m := range HTTPMETHOD {
|
||||
if _, ok := BeeApp.Handlers.routers[m]; !ok {
|
||||
continue
|
||||
}
|
||||
if BeeApp.Handlers.routers[m].prefix == strings.Trim(fixedRoute, "/ ") {
|
||||
findAndRemoveSingleTree(BeeApp.Handlers.routers[m])
|
||||
continue
|
||||
}
|
||||
findAndRemoveTree(subPaths, BeeApp.Handlers.routers[m], m)
|
||||
}
|
||||
return BeeApp
|
||||
}
|
||||
// Single HTTP method
|
||||
um := strings.ToUpper(method)
|
||||
if _, ok := BeeApp.Handlers.routers[um]; ok {
|
||||
if BeeApp.Handlers.routers[um].prefix == strings.Trim(fixedRoute, "/ ") {
|
||||
findAndRemoveSingleTree(BeeApp.Handlers.routers[um])
|
||||
return BeeApp
|
||||
}
|
||||
findAndRemoveTree(subPaths, BeeApp.Handlers.routers[um], um)
|
||||
}
|
||||
return BeeApp
|
||||
}
|
||||
|
||||
func findAndRemoveTree(paths []string, entryPointTree *Tree, method string) {
|
||||
for i := range entryPointTree.fixrouters {
|
||||
if entryPointTree.fixrouters[i].prefix == paths[0] {
|
||||
if len(paths) == 1 {
|
||||
if len(entryPointTree.fixrouters[i].fixrouters) > 0 {
|
||||
// If the route had children subtrees, remove just the functional leaf,
|
||||
// to allow children to function as before
|
||||
if len(entryPointTree.fixrouters[i].leaves) > 0 {
|
||||
entryPointTree.fixrouters[i].leaves[0] = nil
|
||||
entryPointTree.fixrouters[i].leaves = entryPointTree.fixrouters[i].leaves[1:]
|
||||
}
|
||||
} else {
|
||||
// Remove the *Tree from the fixrouters slice
|
||||
entryPointTree.fixrouters[i] = nil
|
||||
|
||||
if i == len(entryPointTree.fixrouters)-1 {
|
||||
entryPointTree.fixrouters = entryPointTree.fixrouters[:i]
|
||||
} else {
|
||||
entryPointTree.fixrouters = append(entryPointTree.fixrouters[:i], entryPointTree.fixrouters[i+1:len(entryPointTree.fixrouters)]...)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
findAndRemoveTree(paths[1:], entryPointTree.fixrouters[i], method)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func findAndRemoveSingleTree(entryPointTree *Tree) {
|
||||
if entryPointTree == nil {
|
||||
return
|
||||
}
|
||||
if len(entryPointTree.fixrouters) > 0 {
|
||||
// If the route had children subtrees, remove just the functional leaf,
|
||||
// to allow children to function as before
|
||||
if len(entryPointTree.leaves) > 0 {
|
||||
entryPointTree.leaves[0] = nil
|
||||
entryPointTree.leaves = entryPointTree.leaves[1:]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Include will generate router file in the router/xxx.go from the controller's comments
|
||||
// usage:
|
||||
// beego.Include(&BankAccount{}, &OrderController{},&RefundController{},&ReceiptController{})
|
||||
@ -348,9 +461,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
|
||||
|
37
beego.go
37
beego.go
@ -23,7 +23,7 @@ import (
|
||||
|
||||
const (
|
||||
// VERSION represent beego web framework version.
|
||||
VERSION = "1.8.0"
|
||||
VERSION = "1.9.2"
|
||||
|
||||
// DEV is for develop
|
||||
DEV = "dev"
|
||||
@ -40,9 +40,9 @@ var (
|
||||
|
||||
// AddAPPStartHook is used to register the hookfunc
|
||||
// The hookfuncs will run in beego.Run()
|
||||
// such as sessionInit, middlerware start, buildtemplate, admin start
|
||||
func AddAPPStartHook(hf hookfunc) {
|
||||
hooks = append(hooks, hf)
|
||||
// such as initiating session , starting middleware , building template, starting admin control and so on.
|
||||
func AddAPPStartHook(hf ...hookfunc) {
|
||||
hooks = append(hooks, hf...)
|
||||
}
|
||||
|
||||
// Run beego application.
|
||||
@ -67,14 +67,31 @@ func Run(params ...string) {
|
||||
BeeApp.Run()
|
||||
}
|
||||
|
||||
// RunWithMiddleWares Run beego application with middlewares.
|
||||
func RunWithMiddleWares(addr string, mws ...MiddleWare) {
|
||||
initBeforeHTTPRun()
|
||||
|
||||
strs := strings.Split(addr, ":")
|
||||
if len(strs) > 0 && strs[0] != "" {
|
||||
BConfig.Listen.HTTPAddr = strs[0]
|
||||
}
|
||||
if len(strs) > 1 && strs[1] != "" {
|
||||
BConfig.Listen.HTTPPort, _ = strconv.Atoi(strs[1])
|
||||
}
|
||||
|
||||
BeeApp.Run(mws...)
|
||||
}
|
||||
|
||||
func initBeforeHTTPRun() {
|
||||
//init hooks
|
||||
AddAPPStartHook(registerMime)
|
||||
AddAPPStartHook(registerDefaultErrorHandler)
|
||||
AddAPPStartHook(registerSession)
|
||||
AddAPPStartHook(registerTemplate)
|
||||
AddAPPStartHook(registerAdmin)
|
||||
AddAPPStartHook(registerGzip)
|
||||
AddAPPStartHook(
|
||||
registerMime,
|
||||
registerDefaultErrorHandler,
|
||||
registerSession,
|
||||
registerTemplate,
|
||||
registerAdmin,
|
||||
registerGzip,
|
||||
)
|
||||
|
||||
for _, hk := range hooks {
|
||||
if err := hk(); err != nil {
|
||||
|
2
cache/cache.go
vendored
2
cache/cache.go
vendored
@ -12,7 +12,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package cache provide a Cache interface and some implemetn engine
|
||||
// Package cache provide a Cache interface and some implement engine
|
||||
// Usage:
|
||||
//
|
||||
// import(
|
||||
|
2
cache/conv.go
vendored
2
cache/conv.go
vendored
@ -28,7 +28,7 @@ func GetString(v interface{}) string {
|
||||
return string(result)
|
||||
default:
|
||||
if v != nil {
|
||||
return fmt.Sprintf("%v", result)
|
||||
return fmt.Sprint(result)
|
||||
}
|
||||
}
|
||||
return ""
|
||||
|
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")
|
||||
}
|
||||
}
|
||||
|
5
cache/memcache/memcache.go
vendored
5
cache/memcache/memcache.go
vendored
@ -146,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.
|
||||
|
31
cache/memory.go
vendored
31
cache/memory.go
vendored
@ -217,26 +217,31 @@ func (bc *MemoryCache) vaccuum() {
|
||||
if bc.items == nil {
|
||||
return
|
||||
}
|
||||
for name := range bc.items {
|
||||
bc.itemExpired(name)
|
||||
if keys := bc.expiredKeys(); len(keys) != 0 {
|
||||
bc.clearItems(keys)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// itemExpired returns true if an item is expired.
|
||||
func (bc *MemoryCache) itemExpired(name string) bool {
|
||||
// expiredKeys returns key list which are expired.
|
||||
func (bc *MemoryCache) expiredKeys() (keys []string) {
|
||||
bc.RLock()
|
||||
defer bc.RUnlock()
|
||||
for key, itm := range bc.items {
|
||||
if itm.isExpire() {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// clearItems removes all the items which key in keys.
|
||||
func (bc *MemoryCache) clearItems(keys []string) {
|
||||
bc.Lock()
|
||||
defer bc.Unlock()
|
||||
|
||||
itm, ok := bc.items[name]
|
||||
if !ok {
|
||||
return true
|
||||
for _, key := range keys {
|
||||
delete(bc.items, key)
|
||||
}
|
||||
if itm.isExpire() {
|
||||
delete(bc.items, name)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
69
cache/redis/redis.go
vendored
69
cache/redis/redis.go
vendored
@ -32,6 +32,7 @@ package redis
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
@ -59,14 +60,23 @@ func NewRedisCache() cache.Cache {
|
||||
return &Cache{key: DefaultKey}
|
||||
}
|
||||
|
||||
// actually do the redis cmds
|
||||
// actually do the redis cmds, args[0] must be the key name.
|
||||
func (rc *Cache) do(commandName string, args ...interface{}) (reply interface{}, err error) {
|
||||
if len(args) < 1 {
|
||||
return nil, errors.New("missing required arguments")
|
||||
}
|
||||
args[0] = rc.associate(args[0])
|
||||
c := rc.p.Get()
|
||||
defer c.Close()
|
||||
|
||||
return c.Do(commandName, args...)
|
||||
}
|
||||
|
||||
// associate with config key.
|
||||
func (rc *Cache) associate(originKey interface{}) string {
|
||||
return fmt.Sprintf("%s:%s", rc.key, originKey)
|
||||
}
|
||||
|
||||
// Get cache from redis.
|
||||
func (rc *Cache) Get(key string) interface{} {
|
||||
if v, err := rc.do("GET", key); err == nil {
|
||||
@ -77,57 +87,28 @@ func (rc *Cache) Get(key string) interface{} {
|
||||
|
||||
// GetMulti get cache from redis.
|
||||
func (rc *Cache) GetMulti(keys []string) []interface{} {
|
||||
size := len(keys)
|
||||
var rv []interface{}
|
||||
c := rc.p.Get()
|
||||
defer c.Close()
|
||||
var err error
|
||||
var args []interface{}
|
||||
for _, key := range keys {
|
||||
err = c.Send("GET", key)
|
||||
if err != nil {
|
||||
goto ERROR
|
||||
}
|
||||
args = append(args, rc.associate(key))
|
||||
}
|
||||
if err = c.Flush(); err != nil {
|
||||
goto ERROR
|
||||
values, err := redis.Values(c.Do("MGET", args...))
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
for i := 0; i < size; i++ {
|
||||
if v, err := c.Receive(); err == nil {
|
||||
rv = append(rv, v.([]byte))
|
||||
} else {
|
||||
rv = append(rv, err)
|
||||
}
|
||||
}
|
||||
return rv
|
||||
ERROR:
|
||||
rv = rv[0:0]
|
||||
for i := 0; i < size; i++ {
|
||||
rv = append(rv, nil)
|
||||
}
|
||||
|
||||
return rv
|
||||
return values
|
||||
}
|
||||
|
||||
// Put put cache to redis.
|
||||
func (rc *Cache) Put(key string, val interface{}, timeout time.Duration) error {
|
||||
var err error
|
||||
if _, err = rc.do("SETEX", key, int64(timeout/time.Second), val); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err = rc.do("HSET", rc.key, key, true); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err := rc.do("SETEX", key, int64(timeout/time.Second), val)
|
||||
return err
|
||||
}
|
||||
|
||||
// Delete delete cache in redis.
|
||||
func (rc *Cache) Delete(key string) error {
|
||||
var err error
|
||||
if _, err = rc.do("DEL", key); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = rc.do("HDEL", rc.key, key)
|
||||
_, err := rc.do("DEL", key)
|
||||
return err
|
||||
}
|
||||
|
||||
@ -137,11 +118,6 @@ func (rc *Cache) IsExist(key string) bool {
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if v == false {
|
||||
if _, err = rc.do("HDEL", rc.key, key); err != nil {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
@ -159,16 +135,17 @@ func (rc *Cache) Decr(key string) error {
|
||||
|
||||
// ClearAll clean all cache in redis. delete this redis collection.
|
||||
func (rc *Cache) ClearAll() error {
|
||||
cachedKeys, err := redis.Strings(rc.do("HKEYS", rc.key))
|
||||
c := rc.p.Get()
|
||||
defer c.Close()
|
||||
cachedKeys, err := redis.Strings(c.Do("KEYS", rc.key+":*"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, str := range cachedKeys {
|
||||
if _, err = rc.do("DEL", str); err != nil {
|
||||
if _, err = c.Do("DEL", str); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
_, err = rc.do("DEL", rc.key)
|
||||
return err
|
||||
}
|
||||
|
||||
|
19
cache/ssdb/ssdb.go
vendored
19
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.
|
||||
@ -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() {
|
||||
|
58
config.go
58
config.go
@ -49,22 +49,24 @@ type Config struct {
|
||||
|
||||
// Listen holds for http and https related config
|
||||
type Listen struct {
|
||||
Graceful bool // Graceful means use graceful module to start the server
|
||||
ServerTimeOut int64
|
||||
ListenTCP4 bool
|
||||
EnableHTTP bool
|
||||
HTTPAddr string
|
||||
HTTPPort int
|
||||
EnableHTTPS bool
|
||||
HTTPSAddr string
|
||||
HTTPSPort int
|
||||
HTTPSCertFile string
|
||||
HTTPSKeyFile string
|
||||
EnableAdmin bool
|
||||
AdminAddr string
|
||||
AdminPort int
|
||||
EnableFcgi bool
|
||||
EnableStdIo bool // EnableStdIo works with EnableFcgi Use FCGI via standard I/O
|
||||
Graceful bool // Graceful means use graceful module to start the server
|
||||
ServerTimeOut int64
|
||||
ListenTCP4 bool
|
||||
EnableHTTP bool
|
||||
HTTPAddr string
|
||||
HTTPPort int
|
||||
EnableHTTPS bool
|
||||
EnableMutualHTTPS bool
|
||||
HTTPSAddr string
|
||||
HTTPSPort int
|
||||
HTTPSCertFile string
|
||||
HTTPSKeyFile string
|
||||
TrustCaFile string
|
||||
EnableAdmin bool
|
||||
AdminAddr string
|
||||
AdminPort int
|
||||
EnableFcgi bool
|
||||
EnableStdIo bool // EnableStdIo works with EnableFcgi Use FCGI via standard I/O
|
||||
}
|
||||
|
||||
// WebConfig holds web related config
|
||||
@ -103,9 +105,10 @@ type SessionConfig struct {
|
||||
|
||||
// LogConfig holds Log related config
|
||||
type LogConfig struct {
|
||||
AccessLogs bool
|
||||
FileLineNum bool
|
||||
Outputs map[string]string // Store Adaptor : config
|
||||
AccessLogs bool
|
||||
AccessLogsFormat string //access log format: JSON_FORMAT, APACHE_FORMAT or empty string
|
||||
FileLineNum bool
|
||||
Outputs map[string]string // Store Adaptor : config
|
||||
}
|
||||
|
||||
var (
|
||||
@ -134,9 +137,13 @@ func init() {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
appConfigPath = filepath.Join(workPath, "conf", "app.conf")
|
||||
var filename = "app.conf"
|
||||
if os.Getenv("BEEGO_MODE") != "" {
|
||||
filename = os.Getenv("BEEGO_MODE") + ".app.conf"
|
||||
}
|
||||
appConfigPath = filepath.Join(workPath, "conf", filename)
|
||||
if !utils.FileExists(appConfigPath) {
|
||||
appConfigPath = filepath.Join(AppPath, "conf", "app.conf")
|
||||
appConfigPath = filepath.Join(AppPath, "conf", filename)
|
||||
if !utils.FileExists(appConfigPath) {
|
||||
AppConfig = &beegoAppConfig{innerConfig: config.NewFakeConfig()}
|
||||
return
|
||||
@ -239,9 +246,10 @@ func newBConfig() *Config {
|
||||
},
|
||||
},
|
||||
Log: LogConfig{
|
||||
AccessLogs: false,
|
||||
FileLineNum: true,
|
||||
Outputs: map[string]string{"console": ""},
|
||||
AccessLogs: false,
|
||||
AccessLogsFormat: "APACHE_FORMAT",
|
||||
FileLineNum: true,
|
||||
Outputs: map[string]string{"console": ""},
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -345,7 +353,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:
|
||||
|
@ -189,16 +189,16 @@ func ParseBool(val interface{}) (value bool, err error) {
|
||||
return false, nil
|
||||
}
|
||||
case int8, int32, int64:
|
||||
strV := fmt.Sprintf("%s", v)
|
||||
strV := fmt.Sprintf("%d", v)
|
||||
if strV == "1" {
|
||||
return true, nil
|
||||
} else if strV == "0" {
|
||||
return false, nil
|
||||
}
|
||||
case float64:
|
||||
if v == 1 {
|
||||
if v == 1.0 {
|
||||
return true, nil
|
||||
} else if v == 0 {
|
||||
} else if v == 0.0 {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
|
2
config/env/env.go
vendored
2
config/env/env.go
vendored
@ -12,6 +12,8 @@
|
||||
// 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 is used to parse environment.
|
||||
package env
|
||||
|
||||
import (
|
||||
|
@ -21,6 +21,7 @@ import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
@ -184,10 +185,17 @@ func (ini *IniConfig) parseData(dir string, data []byte) (*IniConfigContainer, e
|
||||
|
||||
// ParseData parse ini the data
|
||||
// When include other.conf,other.conf is either absolute directory
|
||||
// or under beego in default temporary directory(/tmp/beego).
|
||||
// or under beego in default temporary directory(/tmp/beego[-username]).
|
||||
func (ini *IniConfig) ParseData(data []byte) (Configer, error) {
|
||||
dir := filepath.Join(os.TempDir(), "beego")
|
||||
os.MkdirAll(dir, os.ModePerm)
|
||||
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.parseData(dir, data)
|
||||
}
|
||||
@ -317,7 +325,10 @@ func (c *IniConfigContainer) SaveConfigFile(filename string) (err error) {
|
||||
|
||||
// Get section or key comments. Fixed #1607
|
||||
getCommentStr := func(section, key string) string {
|
||||
comment, ok := "", false
|
||||
var (
|
||||
comment string
|
||||
ok bool
|
||||
)
|
||||
if len(key) == 0 {
|
||||
comment, ok = c.sectionComment[section]
|
||||
} else {
|
||||
@ -397,11 +408,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.
|
||||
@ -416,7 +424,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)
|
||||
}
|
||||
}
|
||||
|
@ -39,6 +39,7 @@ var (
|
||||
getMethodOnly bool
|
||||
)
|
||||
|
||||
// InitGzip init the gzipcompress
|
||||
func InitGzip(minLength, compressLevel int, methods []string) {
|
||||
if minLength >= 0 {
|
||||
gzipMinLength = minLength
|
||||
|
@ -171,6 +171,22 @@ func (ctx *Context) CheckXSRFCookie() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// RenderMethodResult renders the return value of a controller method to the output
|
||||
func (ctx *Context) RenderMethodResult(result interface{}) {
|
||||
if result != nil {
|
||||
renderer, ok := result.(Renderer)
|
||||
if !ok {
|
||||
err, ok := result.(error)
|
||||
if ok {
|
||||
renderer = errorRenderer(err)
|
||||
} else {
|
||||
renderer = jsonRenderer(result)
|
||||
}
|
||||
}
|
||||
renderer.Render(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
//Response is a wrapper for the http.ResponseWriter
|
||||
//started set to true if response was written to then don't execute other handler
|
||||
type Response struct {
|
||||
|
@ -16,9 +16,12 @@ package context
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"errors"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"regexp"
|
||||
@ -113,9 +116,8 @@ func (input *BeegoInput) Domain() string {
|
||||
// if no host info in request, return localhost.
|
||||
func (input *BeegoInput) Host() string {
|
||||
if input.Context.Request.Host != "" {
|
||||
hostParts := strings.Split(input.Context.Request.Host, ":")
|
||||
if len(hostParts) > 0 {
|
||||
return hostParts[0]
|
||||
if hostPart, _, err := net.SplitHostPort(input.Context.Request.Host); err == nil {
|
||||
return hostPart
|
||||
}
|
||||
return input.Context.Request.Host
|
||||
}
|
||||
@ -204,20 +206,20 @@ func (input *BeegoInput) AcceptsJSON() bool {
|
||||
|
||||
// IP returns request client ip.
|
||||
// if in proxy, return first proxy id.
|
||||
// if error, return 127.0.0.1.
|
||||
// if error, return RemoteAddr.
|
||||
func (input *BeegoInput) IP() string {
|
||||
ips := input.Proxy()
|
||||
if len(ips) > 0 && ips[0] != "" {
|
||||
rip := strings.Split(ips[0], ":")
|
||||
return rip[0]
|
||||
}
|
||||
ip := strings.Split(input.Context.Request.RemoteAddr, ":")
|
||||
if len(ip) > 0 {
|
||||
if ip[0] != "[" {
|
||||
return ip[0]
|
||||
rip, _, err := net.SplitHostPort(ips[0])
|
||||
if err != nil {
|
||||
rip = ips[0]
|
||||
}
|
||||
return rip
|
||||
}
|
||||
return "127.0.0.1"
|
||||
if ip, _, err := net.SplitHostPort(input.Context.Request.RemoteAddr); err == nil {
|
||||
return ip
|
||||
}
|
||||
return input.Context.Request.RemoteAddr
|
||||
}
|
||||
|
||||
// Proxy returns proxy client ips slice.
|
||||
@ -251,9 +253,8 @@ func (input *BeegoInput) SubDomains() string {
|
||||
// Port returns request client port.
|
||||
// when error or empty, return 80.
|
||||
func (input *BeegoInput) Port() int {
|
||||
parts := strings.Split(input.Context.Request.Host, ":")
|
||||
if len(parts) == 2 {
|
||||
port, _ := strconv.Atoi(parts[1])
|
||||
if _, portPart, err := net.SplitHostPort(input.Context.Request.Host); err == nil {
|
||||
port, _ := strconv.Atoi(portPart)
|
||||
return port
|
||||
}
|
||||
return 80
|
||||
@ -349,11 +350,22 @@ func (input *BeegoInput) CopyBody(MaxMemory int64) []byte {
|
||||
if input.Context.Request.Body == nil {
|
||||
return []byte{}
|
||||
}
|
||||
|
||||
var requestbody []byte
|
||||
safe := &io.LimitedReader{R: input.Context.Request.Body, N: MaxMemory}
|
||||
requestbody, _ := ioutil.ReadAll(safe)
|
||||
if input.Header("Content-Encoding") == "gzip" {
|
||||
reader, err := gzip.NewReader(safe)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
requestbody, _ = ioutil.ReadAll(reader)
|
||||
} else {
|
||||
requestbody, _ = ioutil.ReadAll(safe)
|
||||
}
|
||||
|
||||
input.Context.Request.Body.Close()
|
||||
bf := bytes.NewBuffer(requestbody)
|
||||
input.Context.Request.Body = ioutil.NopCloser(bf)
|
||||
input.Context.Request.Body = http.MaxBytesReader(input.Context.ResponseWriter, ioutil.NopCloser(bf), MaxMemory)
|
||||
input.RequestBody = requestbody
|
||||
return requestbody
|
||||
}
|
||||
|
@ -73,8 +73,8 @@ func TestBind(t *testing.T) {
|
||||
{"/?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{
|
||||
Human{ID: 888, Nick: "astaxie", Ms: true, Pwd: "pass01"},
|
||||
Human{ID: 999, Nick: "ysqi", Ms: true, Pwd: "pass02"},
|
||||
{ID: 888, Nick: "astaxie", Ms: true, Pwd: "pass01"},
|
||||
{ID: 999, Nick: "ysqi", Ms: true, Pwd: "pass02"},
|
||||
}}}},
|
||||
|
||||
{
|
||||
|
@ -168,6 +168,19 @@ func sanitizeValue(v string) string {
|
||||
return cookieValueSanitizer.Replace(v)
|
||||
}
|
||||
|
||||
func jsonRenderer(value interface{}) Renderer {
|
||||
return rendererFunc(func(ctx *Context) {
|
||||
ctx.Output.JSON(value, false, false)
|
||||
})
|
||||
}
|
||||
|
||||
func errorRenderer(err error) Renderer {
|
||||
return rendererFunc(func(ctx *Context) {
|
||||
ctx.Output.SetStatus(500)
|
||||
ctx.Output.Body([]byte(err.Error()))
|
||||
})
|
||||
}
|
||||
|
||||
// JSON writes json to response body.
|
||||
// if coding is true, it converts utf-8 to \u0000 type.
|
||||
func (output *BeegoOutput) JSON(data interface{}, hasIndent bool, coding bool) error {
|
||||
@ -330,9 +343,8 @@ func (output *BeegoOutput) IsServerError() bool {
|
||||
}
|
||||
|
||||
func stringsToJSON(str string) string {
|
||||
rs := []rune(str)
|
||||
var jsons bytes.Buffer
|
||||
for _, r := range rs {
|
||||
for _, r := range str {
|
||||
rint := int(r)
|
||||
if rint < 128 {
|
||||
jsons.WriteRune(r)
|
||||
|
78
context/param/conv.go
Normal file
78
context/param/conv.go
Normal file
@ -0,0 +1,78 @@
|
||||
package param
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
beecontext "github.com/astaxie/beego/context"
|
||||
"github.com/astaxie/beego/logs"
|
||||
)
|
||||
|
||||
// ConvertParams converts http method params to values that will be passed to the method controller as arguments
|
||||
func ConvertParams(methodParams []*MethodParam, methodType reflect.Type, ctx *beecontext.Context) (result []reflect.Value) {
|
||||
result = make([]reflect.Value, 0, len(methodParams))
|
||||
for i := 0; i < len(methodParams); i++ {
|
||||
reflectValue := convertParam(methodParams[i], methodType.In(i), ctx)
|
||||
result = append(result, reflectValue)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func convertParam(param *MethodParam, paramType reflect.Type, ctx *beecontext.Context) (result reflect.Value) {
|
||||
paramValue := getParamValue(param, ctx)
|
||||
if paramValue == "" {
|
||||
if param.required {
|
||||
ctx.Abort(400, fmt.Sprintf("Missing parameter %s", param.name))
|
||||
} else {
|
||||
paramValue = param.defaultValue
|
||||
}
|
||||
}
|
||||
|
||||
reflectValue, err := parseValue(param, paramValue, paramType)
|
||||
if err != nil {
|
||||
logs.Debug(fmt.Sprintf("Error converting param %s to type %s. Value: %v, Error: %s", param.name, paramType, paramValue, err))
|
||||
ctx.Abort(400, fmt.Sprintf("Invalid parameter %s. Can not convert %v to type %s", param.name, paramValue, paramType))
|
||||
}
|
||||
|
||||
return reflectValue
|
||||
}
|
||||
|
||||
func getParamValue(param *MethodParam, ctx *beecontext.Context) string {
|
||||
switch param.in {
|
||||
case body:
|
||||
return string(ctx.Input.RequestBody)
|
||||
case header:
|
||||
return ctx.Input.Header(param.name)
|
||||
case path:
|
||||
return ctx.Input.Query(":" + param.name)
|
||||
default:
|
||||
return ctx.Input.Query(param.name)
|
||||
}
|
||||
}
|
||||
|
||||
func parseValue(param *MethodParam, paramValue string, paramType reflect.Type) (result reflect.Value, err error) {
|
||||
if paramValue == "" {
|
||||
return reflect.Zero(paramType), nil
|
||||
}
|
||||
parser := getParser(param, paramType)
|
||||
value, err := parser.parse(paramValue, paramType)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
return safeConvert(reflect.ValueOf(value), paramType)
|
||||
}
|
||||
|
||||
func safeConvert(value reflect.Value, t reflect.Type) (result reflect.Value, err error) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
var ok bool
|
||||
err, ok = r.(error)
|
||||
if !ok {
|
||||
err = fmt.Errorf("%v", r)
|
||||
}
|
||||
}
|
||||
}()
|
||||
result = value.Convert(t)
|
||||
return
|
||||
}
|
69
context/param/methodparams.go
Normal file
69
context/param/methodparams.go
Normal file
@ -0,0 +1,69 @@
|
||||
package param
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
//MethodParam keeps param information to be auto passed to controller methods
|
||||
type MethodParam struct {
|
||||
name string
|
||||
in paramType
|
||||
required bool
|
||||
defaultValue string
|
||||
}
|
||||
|
||||
type paramType byte
|
||||
|
||||
const (
|
||||
param paramType = iota
|
||||
path
|
||||
body
|
||||
header
|
||||
)
|
||||
|
||||
//New creates a new MethodParam with name and specific options
|
||||
func New(name string, opts ...MethodParamOption) *MethodParam {
|
||||
return newParam(name, nil, opts)
|
||||
}
|
||||
|
||||
func newParam(name string, parser paramParser, opts []MethodParamOption) (param *MethodParam) {
|
||||
param = &MethodParam{name: name}
|
||||
for _, option := range opts {
|
||||
option(param)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
//Make creates an array of MethodParmas or an empty array
|
||||
func Make(list ...*MethodParam) []*MethodParam {
|
||||
if len(list) > 0 {
|
||||
return list
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mp *MethodParam) String() string {
|
||||
options := []string{}
|
||||
result := "param.New(\"" + mp.name + "\""
|
||||
if mp.required {
|
||||
options = append(options, "param.IsRequired")
|
||||
}
|
||||
switch mp.in {
|
||||
case path:
|
||||
options = append(options, "param.InPath")
|
||||
case body:
|
||||
options = append(options, "param.InBody")
|
||||
case header:
|
||||
options = append(options, "param.InHeader")
|
||||
}
|
||||
if mp.defaultValue != "" {
|
||||
options = append(options, fmt.Sprintf(`param.Default("%s")`, mp.defaultValue))
|
||||
}
|
||||
if len(options) > 0 {
|
||||
result += ", "
|
||||
}
|
||||
result += strings.Join(options, ", ")
|
||||
result += ")"
|
||||
return result
|
||||
}
|
37
context/param/options.go
Normal file
37
context/param/options.go
Normal file
@ -0,0 +1,37 @@
|
||||
package param
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// MethodParamOption defines a func which apply options on a MethodParam
|
||||
type MethodParamOption func(*MethodParam)
|
||||
|
||||
// IsRequired indicates that this param is required and can not be omitted from the http request
|
||||
var IsRequired MethodParamOption = func(p *MethodParam) {
|
||||
p.required = true
|
||||
}
|
||||
|
||||
// InHeader indicates that this param is passed via an http header
|
||||
var InHeader MethodParamOption = func(p *MethodParam) {
|
||||
p.in = header
|
||||
}
|
||||
|
||||
// InPath indicates that this param is part of the URL path
|
||||
var InPath MethodParamOption = func(p *MethodParam) {
|
||||
p.in = path
|
||||
}
|
||||
|
||||
// InBody indicates that this param is passed as an http request body
|
||||
var InBody MethodParamOption = func(p *MethodParam) {
|
||||
p.in = body
|
||||
}
|
||||
|
||||
// Default provides a default value for the http param
|
||||
func Default(defaultValue interface{}) MethodParamOption {
|
||||
return func(p *MethodParam) {
|
||||
if defaultValue != nil {
|
||||
p.defaultValue = fmt.Sprint(defaultValue)
|
||||
}
|
||||
}
|
||||
}
|
149
context/param/parsers.go
Normal file
149
context/param/parsers.go
Normal file
@ -0,0 +1,149 @@
|
||||
package param
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type paramParser interface {
|
||||
parse(value string, toType reflect.Type) (interface{}, error)
|
||||
}
|
||||
|
||||
func getParser(param *MethodParam, t reflect.Type) paramParser {
|
||||
switch t.Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
|
||||
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
return intParser{}
|
||||
case reflect.Slice:
|
||||
if t.Elem().Kind() == reflect.Uint8 { //treat []byte as string
|
||||
return stringParser{}
|
||||
}
|
||||
if param.in == body {
|
||||
return jsonParser{}
|
||||
}
|
||||
elemParser := getParser(param, t.Elem())
|
||||
if elemParser == (jsonParser{}) {
|
||||
return elemParser
|
||||
}
|
||||
return sliceParser(elemParser)
|
||||
case reflect.Bool:
|
||||
return boolParser{}
|
||||
case reflect.String:
|
||||
return stringParser{}
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return floatParser{}
|
||||
case reflect.Ptr:
|
||||
elemParser := getParser(param, t.Elem())
|
||||
if elemParser == (jsonParser{}) {
|
||||
return elemParser
|
||||
}
|
||||
return ptrParser(elemParser)
|
||||
default:
|
||||
if t.PkgPath() == "time" && t.Name() == "Time" {
|
||||
return timeParser{}
|
||||
}
|
||||
return jsonParser{}
|
||||
}
|
||||
}
|
||||
|
||||
type parserFunc func(value string, toType reflect.Type) (interface{}, error)
|
||||
|
||||
func (f parserFunc) parse(value string, toType reflect.Type) (interface{}, error) {
|
||||
return f(value, toType)
|
||||
}
|
||||
|
||||
type boolParser struct {
|
||||
}
|
||||
|
||||
func (p boolParser) parse(value string, toType reflect.Type) (interface{}, error) {
|
||||
return strconv.ParseBool(value)
|
||||
}
|
||||
|
||||
type stringParser struct {
|
||||
}
|
||||
|
||||
func (p stringParser) parse(value string, toType reflect.Type) (interface{}, error) {
|
||||
return value, nil
|
||||
}
|
||||
|
||||
type intParser struct {
|
||||
}
|
||||
|
||||
func (p intParser) parse(value string, toType reflect.Type) (interface{}, error) {
|
||||
return strconv.Atoi(value)
|
||||
}
|
||||
|
||||
type floatParser struct {
|
||||
}
|
||||
|
||||
func (p floatParser) parse(value string, toType reflect.Type) (interface{}, error) {
|
||||
if toType.Kind() == reflect.Float32 {
|
||||
res, err := strconv.ParseFloat(value, 32)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return float32(res), nil
|
||||
}
|
||||
return strconv.ParseFloat(value, 64)
|
||||
}
|
||||
|
||||
type timeParser struct {
|
||||
}
|
||||
|
||||
func (p timeParser) parse(value string, toType reflect.Type) (result interface{}, err error) {
|
||||
result, err = time.Parse(time.RFC3339, value)
|
||||
if err != nil {
|
||||
result, err = time.Parse("2006-01-02", value)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type jsonParser struct {
|
||||
}
|
||||
|
||||
func (p jsonParser) parse(value string, toType reflect.Type) (interface{}, error) {
|
||||
pResult := reflect.New(toType)
|
||||
v := pResult.Interface()
|
||||
err := json.Unmarshal([]byte(value), v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return pResult.Elem().Interface(), nil
|
||||
}
|
||||
|
||||
func sliceParser(elemParser paramParser) paramParser {
|
||||
return parserFunc(func(value string, toType reflect.Type) (interface{}, error) {
|
||||
values := strings.Split(value, ",")
|
||||
result := reflect.MakeSlice(toType, 0, len(values))
|
||||
elemType := toType.Elem()
|
||||
for _, v := range values {
|
||||
parsedValue, err := elemParser.parse(v, elemType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result = reflect.Append(result, reflect.ValueOf(parsedValue))
|
||||
}
|
||||
return result.Interface(), nil
|
||||
})
|
||||
}
|
||||
|
||||
func ptrParser(elemParser paramParser) paramParser {
|
||||
return parserFunc(func(value string, toType reflect.Type) (interface{}, error) {
|
||||
parsedValue, err := elemParser.parse(value, toType.Elem())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
newValPtr := reflect.New(toType.Elem())
|
||||
newVal := reflect.Indirect(newValPtr)
|
||||
convertedVal, err := safeConvert(reflect.ValueOf(parsedValue), toType.Elem())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
newVal.Set(convertedVal)
|
||||
return newValPtr.Interface(), nil
|
||||
})
|
||||
}
|
84
context/param/parsers_test.go
Normal file
84
context/param/parsers_test.go
Normal file
@ -0,0 +1,84 @@
|
||||
package param
|
||||
|
||||
import "testing"
|
||||
import "reflect"
|
||||
import "time"
|
||||
|
||||
type testDefinition struct {
|
||||
strValue string
|
||||
expectedValue interface{}
|
||||
expectedParser paramParser
|
||||
}
|
||||
|
||||
func Test_Parsers(t *testing.T) {
|
||||
|
||||
//ints
|
||||
checkParser(testDefinition{"1", 1, intParser{}}, t)
|
||||
checkParser(testDefinition{"-1", int64(-1), intParser{}}, t)
|
||||
checkParser(testDefinition{"1", uint64(1), intParser{}}, t)
|
||||
|
||||
//floats
|
||||
checkParser(testDefinition{"1.0", float32(1.0), floatParser{}}, t)
|
||||
checkParser(testDefinition{"-1.0", float64(-1.0), floatParser{}}, t)
|
||||
|
||||
//strings
|
||||
checkParser(testDefinition{"AB", "AB", stringParser{}}, t)
|
||||
checkParser(testDefinition{"AB", []byte{65, 66}, stringParser{}}, t)
|
||||
|
||||
//bools
|
||||
checkParser(testDefinition{"true", true, boolParser{}}, t)
|
||||
checkParser(testDefinition{"0", false, boolParser{}}, t)
|
||||
|
||||
//timeParser
|
||||
checkParser(testDefinition{"2017-05-30T13:54:53Z", time.Date(2017, 5, 30, 13, 54, 53, 0, time.UTC), timeParser{}}, t)
|
||||
checkParser(testDefinition{"2017-05-30", time.Date(2017, 5, 30, 0, 0, 0, 0, time.UTC), timeParser{}}, t)
|
||||
|
||||
//json
|
||||
checkParser(testDefinition{`{"X": 5, "Y":"Z"}`, struct {
|
||||
X int
|
||||
Y string
|
||||
}{5, "Z"}, jsonParser{}}, t)
|
||||
|
||||
//slice in query is parsed as comma delimited
|
||||
checkParser(testDefinition{`1,2`, []int{1, 2}, sliceParser(intParser{})}, t)
|
||||
|
||||
//slice in body is parsed as json
|
||||
checkParser(testDefinition{`["a","b"]`, []string{"a", "b"}, jsonParser{}}, t, MethodParam{in: body})
|
||||
|
||||
//pointers
|
||||
var someInt = 1
|
||||
checkParser(testDefinition{`1`, &someInt, ptrParser(intParser{})}, t)
|
||||
|
||||
var someStruct = struct{ X int }{5}
|
||||
checkParser(testDefinition{`{"X": 5}`, &someStruct, jsonParser{}}, t)
|
||||
|
||||
}
|
||||
|
||||
func checkParser(def testDefinition, t *testing.T, methodParam ...MethodParam) {
|
||||
toType := reflect.TypeOf(def.expectedValue)
|
||||
var mp MethodParam
|
||||
if len(methodParam) == 0 {
|
||||
mp = MethodParam{}
|
||||
} else {
|
||||
mp = methodParam[0]
|
||||
}
|
||||
parser := getParser(&mp, toType)
|
||||
|
||||
if reflect.TypeOf(parser) != reflect.TypeOf(def.expectedParser) {
|
||||
t.Errorf("Invalid parser for value %v. Expected: %v, actual: %v", def.strValue, reflect.TypeOf(def.expectedParser).Name(), reflect.TypeOf(parser).Name())
|
||||
return
|
||||
}
|
||||
result, err := parser.parse(def.strValue, toType)
|
||||
if err != nil {
|
||||
t.Errorf("Parsing error for value %v. Expected result: %v, error: %v", def.strValue, def.expectedValue, err)
|
||||
return
|
||||
}
|
||||
convResult, err := safeConvert(reflect.ValueOf(result), toType)
|
||||
if err != nil {
|
||||
t.Errorf("Conversion error for %v. from value: %v, toType: %v, error: %v", def.strValue, result, toType, err)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(convResult.Interface(), def.expectedValue) {
|
||||
t.Errorf("Parsing error for value %v. Expected result: %v, actual: %v", def.strValue, def.expectedValue, result)
|
||||
}
|
||||
}
|
12
context/renderer.go
Normal file
12
context/renderer.go
Normal file
@ -0,0 +1,12 @@
|
||||
package context
|
||||
|
||||
// Renderer defines an http response renderer
|
||||
type Renderer interface {
|
||||
Render(ctx *Context)
|
||||
}
|
||||
|
||||
type rendererFunc func(ctx *Context)
|
||||
|
||||
func (f rendererFunc) Render(ctx *Context) {
|
||||
f(ctx)
|
||||
}
|
27
context/response.go
Normal file
27
context/response.go
Normal file
@ -0,0 +1,27 @@
|
||||
package context
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"net/http"
|
||||
)
|
||||
|
||||
const (
|
||||
//BadRequest indicates http error 400
|
||||
BadRequest StatusCode = http.StatusBadRequest
|
||||
|
||||
//NotFound indicates http error 404
|
||||
NotFound StatusCode = http.StatusNotFound
|
||||
)
|
||||
|
||||
// StatusCode sets the http response status code
|
||||
type StatusCode int
|
||||
|
||||
func (s StatusCode) Error() string {
|
||||
return strconv.Itoa(int(s))
|
||||
}
|
||||
|
||||
// Render sets the http status code
|
||||
func (s StatusCode) Render(ctx *Context) {
|
||||
ctx.Output.SetStatus(int(s))
|
||||
}
|
@ -28,6 +28,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/astaxie/beego/context"
|
||||
"github.com/astaxie/beego/context/param"
|
||||
"github.com/astaxie/beego/session"
|
||||
)
|
||||
|
||||
@ -51,8 +52,16 @@ type ControllerComments struct {
|
||||
Router string
|
||||
AllowHTTPMethods []string
|
||||
Params []map[string]string
|
||||
MethodParams []*param.MethodParam
|
||||
}
|
||||
|
||||
// ControllerCommentsSlice implements the sort interface
|
||||
type ControllerCommentsSlice []ControllerComments
|
||||
|
||||
func (p ControllerCommentsSlice) Len() int { return len(p) }
|
||||
func (p ControllerCommentsSlice) Less(i, j int) bool { return p[i].Router < p[j].Router }
|
||||
func (p ControllerCommentsSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
|
||||
|
||||
// Controller defines some basic http request handler operations, such as
|
||||
// http context, template and view, session and xsrf.
|
||||
type Controller struct {
|
||||
@ -223,7 +232,7 @@ func (c *Controller) RenderBytes() ([]byte, error) {
|
||||
}
|
||||
|
||||
buf.Reset()
|
||||
ExecuteViewPathTemplate(&buf, c.Layout, c.viewPath() ,c.Data)
|
||||
ExecuteViewPathTemplate(&buf, c.Layout, c.viewPath(), c.Data)
|
||||
}
|
||||
return buf.Bytes(), err
|
||||
}
|
||||
@ -249,7 +258,7 @@ func (c *Controller) renderTemplate() (bytes.Buffer, error) {
|
||||
}
|
||||
}
|
||||
}
|
||||
BuildTemplate(c.viewPath() , buildFiles...)
|
||||
BuildTemplate(c.viewPath(), buildFiles...)
|
||||
}
|
||||
return buf, ExecuteViewPathTemplate(&buf, c.TplName, c.viewPath(), c.Data)
|
||||
}
|
||||
@ -314,7 +323,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)
|
||||
|
@ -172,10 +172,10 @@ func TestAdditionalViewPaths(t *testing.T) {
|
||||
t.Fatal("TestAdditionalViewPaths expected error")
|
||||
}
|
||||
}()
|
||||
ctrl.RenderString();
|
||||
ctrl.RenderString()
|
||||
}()
|
||||
|
||||
ctrl.TplName = "file2.tpl"
|
||||
ctrl.ViewPath = dir2
|
||||
ctrl.RenderString();
|
||||
ctrl.RenderString()
|
||||
}
|
||||
|
24
error.go
24
error.go
@ -252,6 +252,30 @@ func forbidden(rw http.ResponseWriter, r *http.Request) {
|
||||
)
|
||||
}
|
||||
|
||||
// show 422 missing xsrf token
|
||||
func missingxsrf(rw http.ResponseWriter, r *http.Request) {
|
||||
responseError(rw, r,
|
||||
422,
|
||||
"<br>The page you have requested is forbidden."+
|
||||
"<br>Perhaps you are here because:"+
|
||||
"<br><br><ul>"+
|
||||
"<br>'_xsrf' argument missing from POST"+
|
||||
"</ul>",
|
||||
)
|
||||
}
|
||||
|
||||
// show 417 invalid xsrf token
|
||||
func invalidxsrf(rw http.ResponseWriter, r *http.Request) {
|
||||
responseError(rw, r,
|
||||
417,
|
||||
"<br>The page you have requested is forbidden."+
|
||||
"<br>Perhaps you are here because:"+
|
||||
"<br><br><ul>"+
|
||||
"<br>expected XSRF not found"+
|
||||
"</ul>",
|
||||
)
|
||||
}
|
||||
|
||||
// show 404 not found error.
|
||||
func notFound(rw http.ResponseWriter, r *http.Request) {
|
||||
responseError(rw, r,
|
||||
|
@ -52,7 +52,7 @@ func TestErrorCode_01(t *testing.T) {
|
||||
if w.Code != code {
|
||||
t.Fail()
|
||||
}
|
||||
if !strings.Contains(string(w.Body.Bytes()), http.StatusText(code)) {
|
||||
if !strings.Contains(w.Body.String(), http.StatusText(code)) {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
@ -82,7 +82,7 @@ func TestErrorCode_03(t *testing.T) {
|
||||
if w.Code != 200 {
|
||||
t.Fail()
|
||||
}
|
||||
if string(w.Body.Bytes()) != parseCodeError {
|
||||
if w.Body.String() != parseCodeError {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
@ -3,14 +3,17 @@ package grace
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type graceConn struct {
|
||||
net.Conn
|
||||
server *Server
|
||||
m sync.Mutex
|
||||
closed bool
|
||||
}
|
||||
|
||||
func (c graceConn) Close() (err error) {
|
||||
func (c *graceConn) Close() (err error) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
switch x := r.(type) {
|
||||
@ -23,6 +26,14 @@ func (c graceConn) Close() (err error) {
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
c.m.Lock()
|
||||
if c.closed {
|
||||
c.m.Unlock()
|
||||
return
|
||||
}
|
||||
c.server.wg.Done()
|
||||
c.closed = true
|
||||
c.m.Unlock()
|
||||
return c.Conn.Close()
|
||||
}
|
||||
|
@ -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()
|
||||
}()
|
||||
@ -37,7 +37,7 @@ func (gl *graceListener) Accept() (c net.Conn, err error) {
|
||||
tc.SetKeepAlive(true)
|
||||
tc.SetKeepAlivePeriod(3 * time.Minute)
|
||||
|
||||
c = graceConn{
|
||||
c = &graceConn{
|
||||
Conn: tc,
|
||||
server: gl.server,
|
||||
}
|
||||
|
@ -2,7 +2,9 @@ package grace
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
@ -65,7 +67,7 @@ func (srv *Server) ListenAndServe() (err error) {
|
||||
log.Println(err)
|
||||
return err
|
||||
}
|
||||
err = process.Kill()
|
||||
err = process.Signal(syscall.SIGTERM)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -114,6 +116,62 @@ func (srv *Server) ListenAndServeTLS(certFile, keyFile string) (err error) {
|
||||
srv.tlsInnerListener = newGraceListener(l, srv)
|
||||
srv.GraceListener = tls.NewListener(srv.tlsInnerListener, srv.TLSConfig)
|
||||
|
||||
if srv.isChild {
|
||||
process, err := os.FindProcess(os.Getppid())
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return err
|
||||
}
|
||||
err = process.Signal(syscall.SIGTERM)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
log.Println(os.Getpid(), srv.Addr)
|
||||
return srv.Serve()
|
||||
}
|
||||
|
||||
// ListenAndServeMutualTLS listens on the TCP network address srv.Addr and then calls
|
||||
// Serve to handle requests on incoming mutual TLS connections.
|
||||
func (srv *Server) ListenAndServeMutualTLS(certFile, keyFile, trustFile string) (err error) {
|
||||
addr := srv.Addr
|
||||
if addr == "" {
|
||||
addr = ":https"
|
||||
}
|
||||
|
||||
if srv.TLSConfig == nil {
|
||||
srv.TLSConfig = &tls.Config{}
|
||||
}
|
||||
if srv.TLSConfig.NextProtos == nil {
|
||||
srv.TLSConfig.NextProtos = []string{"http/1.1"}
|
||||
}
|
||||
|
||||
srv.TLSConfig.Certificates = make([]tls.Certificate, 1)
|
||||
srv.TLSConfig.Certificates[0], err = tls.LoadX509KeyPair(certFile, keyFile)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
srv.TLSConfig.ClientAuth = tls.RequireAndVerifyClientCert
|
||||
pool := x509.NewCertPool()
|
||||
data, err := ioutil.ReadFile(trustFile)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return err
|
||||
}
|
||||
pool.AppendCertsFromPEM(data)
|
||||
srv.TLSConfig.ClientCAs = pool
|
||||
log.Println("Mutual HTTPS")
|
||||
go srv.handleSignals()
|
||||
|
||||
l, err := srv.getListener(addr)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return err
|
||||
}
|
||||
|
||||
srv.tlsInnerListener = newGraceListener(l, srv)
|
||||
srv.GraceListener = tls.NewListener(srv.tlsInnerListener, srv.TLSConfig)
|
||||
|
||||
if srv.isChild {
|
||||
process, err := os.FindProcess(os.Getppid())
|
||||
if err != nil {
|
||||
@ -196,7 +254,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
|
||||
@ -292,7 +349,7 @@ func (srv *Server) fork() (err error) {
|
||||
// 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.")
|
||||
err = fmt.Errorf("Invalid ppFlag argument. Must be either grace.PreSignal or grace.PostSignal")
|
||||
return
|
||||
}
|
||||
for _, s := range hookableSignals {
|
||||
@ -301,6 +358,6 @@ func (srv *Server) RegisterSignalHook(ppFlag int, sig os.Signal, f func()) (err
|
||||
return
|
||||
}
|
||||
}
|
||||
err = fmt.Errorf("Signal '%v' is not supported.", sig)
|
||||
err = fmt.Errorf("Signal '%v' is not supported", sig)
|
||||
return
|
||||
}
|
||||
|
8
hooks.go
8
hooks.go
@ -32,6 +32,8 @@ func registerDefaultErrorHandler() error {
|
||||
"502": badGateway,
|
||||
"503": serviceUnavailable,
|
||||
"504": gatewayTimeout,
|
||||
"417": invalidxsrf,
|
||||
"422": missingxsrf,
|
||||
}
|
||||
for e, h := range m {
|
||||
if _, ok := ErrorMaps[e]; !ok {
|
||||
@ -55,9 +57,9 @@ func registerSession() error {
|
||||
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.SessionEnableSidInHTTPHeader
|
||||
conf.SessionNameInHttpHeader = BConfig.WebConfig.Session.SessionNameInHTTPHeader
|
||||
conf.EnableSidInUrlQuery = BConfig.WebConfig.Session.SessionEnableSidInURLQuery
|
||||
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
|
||||
|
@ -32,7 +32,7 @@ The default timeout is `60` seconds, function prototype:
|
||||
|
||||
SetTimeout(connectTimeout, readWriteTimeout time.Duration)
|
||||
|
||||
Exmaple:
|
||||
Example:
|
||||
|
||||
// GET
|
||||
httplib.Get("http://beego.me/").SetTimeout(100 * time.Second, 30 * time.Second)
|
||||
|
@ -317,7 +317,19 @@ func (b *BeegoHTTPRequest) Body(data interface{}) *BeegoHTTPRequest {
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// XMLBody adds request raw body encoding by XML.
|
||||
func (b *BeegoHTTPRequest) XMLBody(obj interface{}) (*BeegoHTTPRequest, error) {
|
||||
if b.req.Body == nil && obj != nil {
|
||||
byts, err := xml.Marshal(obj)
|
||||
if err != nil {
|
||||
return b, err
|
||||
}
|
||||
b.req.Body = ioutil.NopCloser(bytes.NewReader(byts))
|
||||
b.req.ContentLength = int64(len(byts))
|
||||
b.req.Header.Set("Content-Type", "application/xml")
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
// JSONBody adds request raw body encoding by JSON.
|
||||
func (b *BeegoHTTPRequest) JSONBody(obj interface{}) (*BeegoHTTPRequest, error) {
|
||||
if b.req.Body == nil && obj != nil {
|
||||
@ -335,7 +347,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
|
||||
@ -344,7 +356,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()
|
||||
@ -520,9 +532,9 @@ func (b *BeegoHTTPRequest) Bytes() ([]byte, error) {
|
||||
return nil, err
|
||||
}
|
||||
b.body, err = ioutil.ReadAll(reader)
|
||||
} else {
|
||||
b.body, err = ioutil.ReadAll(resp.Body)
|
||||
return b.body, err
|
||||
}
|
||||
b.body, err = ioutil.ReadAll(resp.Body)
|
||||
return b.body, err
|
||||
}
|
||||
|
||||
|
@ -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()
|
||||
|
86
logs/accesslog.go
Normal file
86
logs/accesslog.go
Normal file
@ -0,0 +1,86 @@
|
||||
// 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 logs
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"time"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
const (
|
||||
apacheFormatPattern = "%s - - [%s] \"%s %d %d\" %f %s %s\n"
|
||||
apacheFormat = "APACHE_FORMAT"
|
||||
jsonFormat = "JSON_FORMAT"
|
||||
)
|
||||
|
||||
// AccessLogRecord struct for holding access log data.
|
||||
type AccessLogRecord struct {
|
||||
RemoteAddr string `json:"remote_addr"`
|
||||
RequestTime time.Time `json:"request_time"`
|
||||
RequestMethod string `json:"request_method"`
|
||||
Request string `json:"request"`
|
||||
ServerProtocol string `json:"server_protocol"`
|
||||
Host string `json:"host"`
|
||||
Status int `json:"status"`
|
||||
BodyBytesSent int64 `json:"body_bytes_sent"`
|
||||
ElapsedTime time.Duration `json:"elapsed_time"`
|
||||
HTTPReferrer string `json:"http_referrer"`
|
||||
HTTPUserAgent string `json:"http_user_agent"`
|
||||
RemoteUser string `json:"remote_user"`
|
||||
}
|
||||
|
||||
func (r *AccessLogRecord) json() ([]byte, error) {
|
||||
buffer := &bytes.Buffer{}
|
||||
encoder := json.NewEncoder(buffer)
|
||||
disableEscapeHTML(encoder)
|
||||
|
||||
err := encoder.Encode(r)
|
||||
return buffer.Bytes(), err
|
||||
}
|
||||
|
||||
func disableEscapeHTML(i interface{}) {
|
||||
e, ok := i.(interface {
|
||||
SetEscapeHTML(bool)
|
||||
});
|
||||
if ok {
|
||||
e.SetEscapeHTML(false)
|
||||
}
|
||||
}
|
||||
|
||||
// AccessLog - Format and print access log.
|
||||
func AccessLog(r *AccessLogRecord, format string) {
|
||||
var msg string
|
||||
|
||||
switch format {
|
||||
|
||||
case apacheFormat:
|
||||
timeFormatted := r.RequestTime.Format("02/Jan/2006 03:04:05")
|
||||
msg = fmt.Sprintf(apacheFormatPattern, r.RemoteAddr, timeFormatted, r.Request, r.Status, r.BodyBytesSent,
|
||||
r.ElapsedTime.Seconds(), r.HTTPReferrer, r.HTTPUserAgent)
|
||||
case jsonFormat:
|
||||
fallthrough
|
||||
default:
|
||||
jsonData, err := r.json()
|
||||
if err != nil {
|
||||
msg = fmt.Sprintf(`{"Error": "%s"}`, err)
|
||||
} else {
|
||||
msg = string(jsonData)
|
||||
}
|
||||
}
|
||||
|
||||
beeLogger.Debug(msg)
|
||||
}
|
@ -2,19 +2,23 @@ package alils
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/astaxie/beego/logs"
|
||||
"github.com/gogo/protobuf/proto"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/astaxie/beego/logs"
|
||||
"github.com/gogo/protobuf/proto"
|
||||
)
|
||||
|
||||
const (
|
||||
CacheSize int = 64
|
||||
// CacheSize set the flush size
|
||||
CacheSize int = 64
|
||||
// Delimiter define the topic delimiter
|
||||
Delimiter string = "##"
|
||||
)
|
||||
|
||||
type AliLSConfig struct {
|
||||
// Config is the Config for Ali Log
|
||||
type Config struct {
|
||||
Project string `json:"project"`
|
||||
Endpoint string `json:"endpoint"`
|
||||
KeyID string `json:"key_id"`
|
||||
@ -34,18 +38,17 @@ type aliLSWriter struct {
|
||||
withMap bool
|
||||
groupMap map[string]*LogGroup
|
||||
lock *sync.Mutex
|
||||
AliLSConfig
|
||||
Config
|
||||
}
|
||||
|
||||
// 创建提供Logger接口的日志服务
|
||||
// NewAliLS create a new Logger
|
||||
func NewAliLS() logs.Logger {
|
||||
alils := new(aliLSWriter)
|
||||
alils.Level = logs.LevelTrace
|
||||
return alils
|
||||
}
|
||||
|
||||
// 读取配置
|
||||
// 初始化必要的数据结构
|
||||
// Init parse config and init struct
|
||||
func (c *aliLSWriter) Init(jsonConfig string) (err error) {
|
||||
|
||||
json.Unmarshal([]byte(jsonConfig), c)
|
||||
@ -54,28 +57,26 @@ func (c *aliLSWriter) Init(jsonConfig string) (err error) {
|
||||
c.FlushWhen = CacheSize
|
||||
}
|
||||
|
||||
// 初始化Project
|
||||
prj := &LogProject{
|
||||
Name: c.Project,
|
||||
Endpoint: c.Endpoint,
|
||||
AccessKeyId: c.KeyID,
|
||||
AccessKeyID: c.KeyID,
|
||||
AccessKeySecret: c.KeySecret,
|
||||
}
|
||||
|
||||
// 获取logstore
|
||||
c.store, err = prj.GetLogStore(c.LogStore)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 创建默认Log Group
|
||||
// Create default Log Group
|
||||
c.group = append(c.group, &LogGroup{
|
||||
Topic: proto.String(""),
|
||||
Source: proto.String(c.Source),
|
||||
Logs: make([]*Log, 0, c.FlushWhen),
|
||||
})
|
||||
|
||||
// 创建其它Log Group
|
||||
// Create other Log Group
|
||||
c.groupMap = make(map[string]*LogGroup)
|
||||
for _, topic := range c.Topics {
|
||||
|
||||
@ -113,7 +114,7 @@ func (c *aliLSWriter) WriteMsg(when time.Time, msg string, level int) (err error
|
||||
var lg *LogGroup
|
||||
if c.withMap {
|
||||
|
||||
// 解析出Topic,并匹配LogGroup
|
||||
// Topic,LogGroup
|
||||
strs := strings.SplitN(msg, Delimiter, 2)
|
||||
if len(strs) == 2 {
|
||||
pos := strings.LastIndex(strs[0], " ")
|
||||
@ -122,27 +123,24 @@ func (c *aliLSWriter) WriteMsg(when time.Time, msg string, level int) (err error
|
||||
lg = c.groupMap[topic]
|
||||
}
|
||||
|
||||
// 默认发到空Topic
|
||||
// send to empty Topic
|
||||
if lg == nil {
|
||||
topic = ""
|
||||
content = msg
|
||||
lg = c.group[0]
|
||||
}
|
||||
} else {
|
||||
topic = ""
|
||||
content = msg
|
||||
lg = c.group[0]
|
||||
}
|
||||
|
||||
// 生成日志
|
||||
c1 := &Log_Content{
|
||||
c1 := &LogContent{
|
||||
Key: proto.String("msg"),
|
||||
Value: proto.String(content),
|
||||
}
|
||||
|
||||
l := &Log{
|
||||
Time: proto.Uint32(uint32(when.Unix())), // 填写日志时间
|
||||
Contents: []*Log_Content{
|
||||
Time: proto.Uint32(uint32(when.Unix())),
|
||||
Contents: []*LogContent{
|
||||
c1,
|
||||
},
|
||||
}
|
||||
@ -151,7 +149,6 @@ func (c *aliLSWriter) WriteMsg(when time.Time, msg string, level int) (err error
|
||||
lg.Logs = append(lg.Logs, l)
|
||||
c.lock.Unlock()
|
||||
|
||||
// 满足条件则Flush
|
||||
if len(lg.Logs) >= c.FlushWhen {
|
||||
c.flush(lg)
|
||||
}
|
||||
@ -162,7 +159,7 @@ func (c *aliLSWriter) WriteMsg(when time.Time, msg string, level int) (err error
|
||||
// Flush implementing method. empty.
|
||||
func (c *aliLSWriter) Flush() {
|
||||
|
||||
// flush所有group
|
||||
// flush all group
|
||||
for _, lg := range c.group {
|
||||
c.flush(lg)
|
||||
}
|
||||
@ -176,9 +173,6 @@ 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
|
||||
|
@ -1,30 +1,43 @@
|
||||
package alils
|
||||
|
||||
import "github.com/gogo/protobuf/proto"
|
||||
import "fmt"
|
||||
import "math"
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
|
||||
// discarding unused import gogoproto "."
|
||||
|
||||
import github_com_gogo_protobuf_proto "github.com/gogo/protobuf/proto"
|
||||
|
||||
import "io"
|
||||
"github.com/gogo/protobuf/proto"
|
||||
github_com_gogo_protobuf_proto "github.com/gogo/protobuf/proto"
|
||||
)
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
var _ = fmt.Errorf
|
||||
var _ = math.Inf
|
||||
|
||||
var (
|
||||
// ErrInvalidLengthLog invalid proto
|
||||
ErrInvalidLengthLog = fmt.Errorf("proto: negative length found during unmarshaling")
|
||||
// ErrIntOverflowLog overflow
|
||||
ErrIntOverflowLog = fmt.Errorf("proto: integer overflow")
|
||||
)
|
||||
|
||||
// Log define the proto Log
|
||||
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:"-"`
|
||||
Time *uint32 `protobuf:"varint,1,req,name=Time" json:"Time,omitempty"`
|
||||
Contents []*LogContent `protobuf:"bytes,2,rep,name=Contents" json:"Contents,omitempty"`
|
||||
XXXUnrecognized []byte `json:"-"`
|
||||
}
|
||||
|
||||
func (m *Log) Reset() { *m = Log{} }
|
||||
func (m *Log) String() string { return proto.CompactTextString(m) }
|
||||
func (*Log) ProtoMessage() {}
|
||||
// Reset the Log
|
||||
func (m *Log) Reset() { *m = Log{} }
|
||||
|
||||
// String return the Compact Log
|
||||
func (m *Log) String() string { return proto.CompactTextString(m) }
|
||||
|
||||
// ProtoMessage not implemented
|
||||
func (*Log) ProtoMessage() {}
|
||||
|
||||
// GetTime return the Log's Time
|
||||
func (m *Log) GetTime() uint32 {
|
||||
if m != nil && m.Time != nil {
|
||||
return *m.Time
|
||||
@ -32,49 +45,65 @@ func (m *Log) GetTime() uint32 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (m *Log) GetContents() []*Log_Content {
|
||||
// GetContents return the Log's Contents
|
||||
func (m *Log) GetContents() []*LogContent {
|
||||
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:"-"`
|
||||
// LogContent define the Log content struct
|
||||
type LogContent struct {
|
||||
Key *string `protobuf:"bytes,1,req,name=Key" json:"Key,omitempty"`
|
||||
Value *string `protobuf:"bytes,2,req,name=Value" json:"Value,omitempty"`
|
||||
XXXUnrecognized []byte `json:"-"`
|
||||
}
|
||||
|
||||
func (m *Log_Content) Reset() { *m = Log_Content{} }
|
||||
func (m *Log_Content) String() string { return proto.CompactTextString(m) }
|
||||
func (*Log_Content) ProtoMessage() {}
|
||||
// Reset LogContent
|
||||
func (m *LogContent) Reset() { *m = LogContent{} }
|
||||
|
||||
func (m *Log_Content) GetKey() string {
|
||||
// String return the compact text
|
||||
func (m *LogContent) String() string { return proto.CompactTextString(m) }
|
||||
|
||||
// ProtoMessage not implemented
|
||||
func (*LogContent) ProtoMessage() {}
|
||||
|
||||
// GetKey return the Key
|
||||
func (m *LogContent) GetKey() string {
|
||||
if m != nil && m.Key != nil {
|
||||
return *m.Key
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *Log_Content) GetValue() string {
|
||||
// GetValue return the Value
|
||||
func (m *LogContent) GetValue() string {
|
||||
if m != nil && m.Value != nil {
|
||||
return *m.Value
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// LogGroup define the logs struct
|
||||
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:"-"`
|
||||
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"`
|
||||
XXXUnrecognized []byte `json:"-"`
|
||||
}
|
||||
|
||||
func (m *LogGroup) Reset() { *m = LogGroup{} }
|
||||
func (m *LogGroup) String() string { return proto.CompactTextString(m) }
|
||||
func (*LogGroup) ProtoMessage() {}
|
||||
// Reset LogGroup
|
||||
func (m *LogGroup) Reset() { *m = LogGroup{} }
|
||||
|
||||
// String return the compact text
|
||||
func (m *LogGroup) String() string { return proto.CompactTextString(m) }
|
||||
|
||||
// ProtoMessage not implemented
|
||||
func (*LogGroup) ProtoMessage() {}
|
||||
|
||||
// GetLogs return the loggroup logs
|
||||
func (m *LogGroup) GetLogs() []*Log {
|
||||
if m != nil {
|
||||
return m.Logs
|
||||
@ -82,6 +111,7 @@ func (m *LogGroup) GetLogs() []*Log {
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetReserved return Reserved
|
||||
func (m *LogGroup) GetReserved() string {
|
||||
if m != nil && m.Reserved != nil {
|
||||
return *m.Reserved
|
||||
@ -89,6 +119,7 @@ func (m *LogGroup) GetReserved() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// GetTopic return Topic
|
||||
func (m *LogGroup) GetTopic() string {
|
||||
if m != nil && m.Topic != nil {
|
||||
return *m.Topic
|
||||
@ -96,6 +127,7 @@ func (m *LogGroup) GetTopic() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// GetSource return Source
|
||||
func (m *LogGroup) GetSource() string {
|
||||
if m != nil && m.Source != nil {
|
||||
return *m.Source
|
||||
@ -103,15 +135,22 @@ func (m *LogGroup) GetSource() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// LogGroupList define the LogGroups
|
||||
type LogGroupList struct {
|
||||
LogGroups []*LogGroup `protobuf:"bytes,1,rep,name=logGroups" json:"logGroups,omitempty"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
LogGroups []*LogGroup `protobuf:"bytes,1,rep,name=logGroups" json:"logGroups,omitempty"`
|
||||
XXXUnrecognized []byte `json:"-"`
|
||||
}
|
||||
|
||||
func (m *LogGroupList) Reset() { *m = LogGroupList{} }
|
||||
func (m *LogGroupList) String() string { return proto.CompactTextString(m) }
|
||||
func (*LogGroupList) ProtoMessage() {}
|
||||
// Reset LogGroupList
|
||||
func (m *LogGroupList) Reset() { *m = LogGroupList{} }
|
||||
|
||||
// String return compact text
|
||||
func (m *LogGroupList) String() string { return proto.CompactTextString(m) }
|
||||
|
||||
// ProtoMessage not implemented
|
||||
func (*LogGroupList) ProtoMessage() {}
|
||||
|
||||
// GetLogGroups return the LogGroups
|
||||
func (m *LogGroupList) GetLogGroups() []*LogGroup {
|
||||
if m != nil {
|
||||
return m.LogGroups
|
||||
@ -119,6 +158,7 @@ func (m *LogGroupList) GetLogGroups() []*LogGroup {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Marshal the logs to byte slice
|
||||
func (m *Log) Marshal() (data []byte, err error) {
|
||||
size := m.Size()
|
||||
data = make([]byte, size)
|
||||
@ -129,6 +169,7 @@ func (m *Log) Marshal() (data []byte, err error) {
|
||||
return data[:n], nil
|
||||
}
|
||||
|
||||
// MarshalTo data
|
||||
func (m *Log) MarshalTo(data []byte) (int, error) {
|
||||
var i int
|
||||
_ = i
|
||||
@ -136,11 +177,10 @@ func (m *Log) MarshalTo(data []byte) (int, error) {
|
||||
_ = 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))
|
||||
}
|
||||
data[i] = 0x8
|
||||
i++
|
||||
i = encodeVarintLog(data, i, uint64(*m.Time))
|
||||
if len(m.Contents) > 0 {
|
||||
for _, msg := range m.Contents {
|
||||
data[i] = 0x12
|
||||
@ -153,13 +193,14 @@ func (m *Log) MarshalTo(data []byte) (int, error) {
|
||||
i += n
|
||||
}
|
||||
}
|
||||
if m.XXX_unrecognized != nil {
|
||||
i += copy(data[i:], m.XXX_unrecognized)
|
||||
if m.XXXUnrecognized != nil {
|
||||
i += copy(data[i:], m.XXXUnrecognized)
|
||||
}
|
||||
return i, nil
|
||||
}
|
||||
|
||||
func (m *Log_Content) Marshal() (data []byte, err error) {
|
||||
// Marshal LogContent
|
||||
func (m *LogContent) Marshal() (data []byte, err error) {
|
||||
size := m.Size()
|
||||
data = make([]byte, size)
|
||||
n, err := m.MarshalTo(data)
|
||||
@ -169,33 +210,34 @@ func (m *Log_Content) Marshal() (data []byte, err error) {
|
||||
return data[:n], nil
|
||||
}
|
||||
|
||||
func (m *Log_Content) MarshalTo(data []byte) (int, error) {
|
||||
// MarshalTo logcontent to data
|
||||
func (m *LogContent) 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)
|
||||
}
|
||||
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)
|
||||
data[i] = 0x12
|
||||
i++
|
||||
i = encodeVarintLog(data, i, uint64(len(*m.Value)))
|
||||
i += copy(data[i:], *m.Value)
|
||||
if m.XXXUnrecognized != nil {
|
||||
i += copy(data[i:], m.XXXUnrecognized)
|
||||
}
|
||||
return i, nil
|
||||
}
|
||||
|
||||
// Marshal LogGroup
|
||||
func (m *LogGroup) Marshal() (data []byte, err error) {
|
||||
size := m.Size()
|
||||
data = make([]byte, size)
|
||||
@ -206,6 +248,7 @@ func (m *LogGroup) Marshal() (data []byte, err error) {
|
||||
return data[:n], nil
|
||||
}
|
||||
|
||||
// MarshalTo LogGroup to data
|
||||
func (m *LogGroup) MarshalTo(data []byte) (int, error) {
|
||||
var i int
|
||||
_ = i
|
||||
@ -241,12 +284,13 @@ func (m *LogGroup) MarshalTo(data []byte) (int, error) {
|
||||
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)
|
||||
if m.XXXUnrecognized != nil {
|
||||
i += copy(data[i:], m.XXXUnrecognized)
|
||||
}
|
||||
return i, nil
|
||||
}
|
||||
|
||||
// Marshal LogGroupList
|
||||
func (m *LogGroupList) Marshal() (data []byte, err error) {
|
||||
size := m.Size()
|
||||
data = make([]byte, size)
|
||||
@ -257,6 +301,7 @@ func (m *LogGroupList) Marshal() (data []byte, err error) {
|
||||
return data[:n], nil
|
||||
}
|
||||
|
||||
// MarshalTo LogGroupList to data
|
||||
func (m *LogGroupList) MarshalTo(data []byte) (int, error) {
|
||||
var i int
|
||||
_ = i
|
||||
@ -274,8 +319,8 @@ func (m *LogGroupList) MarshalTo(data []byte) (int, error) {
|
||||
i += n
|
||||
}
|
||||
}
|
||||
if m.XXX_unrecognized != nil {
|
||||
i += copy(data[i:], m.XXX_unrecognized)
|
||||
if m.XXXUnrecognized != nil {
|
||||
i += copy(data[i:], m.XXXUnrecognized)
|
||||
}
|
||||
return i, nil
|
||||
}
|
||||
@ -307,6 +352,8 @@ func encodeVarintLog(data []byte, offset int, v uint64) int {
|
||||
data[offset] = uint8(v)
|
||||
return offset + 1
|
||||
}
|
||||
|
||||
// Size return the log's size
|
||||
func (m *Log) Size() (n int) {
|
||||
var l int
|
||||
_ = l
|
||||
@ -319,13 +366,14 @@ func (m *Log) Size() (n int) {
|
||||
n += 1 + l + sovLog(uint64(l))
|
||||
}
|
||||
}
|
||||
if m.XXX_unrecognized != nil {
|
||||
n += len(m.XXX_unrecognized)
|
||||
if m.XXXUnrecognized != nil {
|
||||
n += len(m.XXXUnrecognized)
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func (m *Log_Content) Size() (n int) {
|
||||
// Size return LogContent size based on Key and Value
|
||||
func (m *LogContent) Size() (n int) {
|
||||
var l int
|
||||
_ = l
|
||||
if m.Key != nil {
|
||||
@ -336,12 +384,13 @@ func (m *Log_Content) Size() (n int) {
|
||||
l = len(*m.Value)
|
||||
n += 1 + l + sovLog(uint64(l))
|
||||
}
|
||||
if m.XXX_unrecognized != nil {
|
||||
n += len(m.XXX_unrecognized)
|
||||
if m.XXXUnrecognized != nil {
|
||||
n += len(m.XXXUnrecognized)
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// Size return LogGroup size based on Logs
|
||||
func (m *LogGroup) Size() (n int) {
|
||||
var l int
|
||||
_ = l
|
||||
@ -363,12 +412,13 @@ func (m *LogGroup) Size() (n int) {
|
||||
l = len(*m.Source)
|
||||
n += 1 + l + sovLog(uint64(l))
|
||||
}
|
||||
if m.XXX_unrecognized != nil {
|
||||
n += len(m.XXX_unrecognized)
|
||||
if m.XXXUnrecognized != nil {
|
||||
n += len(m.XXXUnrecognized)
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// Size return LogGroupList size
|
||||
func (m *LogGroupList) Size() (n int) {
|
||||
var l int
|
||||
_ = l
|
||||
@ -378,8 +428,8 @@ func (m *LogGroupList) Size() (n int) {
|
||||
n += 1 + l + sovLog(uint64(l))
|
||||
}
|
||||
}
|
||||
if m.XXX_unrecognized != nil {
|
||||
n += len(m.XXX_unrecognized)
|
||||
if m.XXXUnrecognized != nil {
|
||||
n += len(m.XXXUnrecognized)
|
||||
}
|
||||
return n
|
||||
}
|
||||
@ -395,8 +445,10 @@ func sovLog(x uint64) (n int) {
|
||||
return n
|
||||
}
|
||||
func sozLog(x uint64) (n int) {
|
||||
return sovLog(uint64((x << 1) ^ uint64((int64(x) >> 63))))
|
||||
return sovLog((x << 1) ^ (x >> 63))
|
||||
}
|
||||
|
||||
// Unmarshal data to log
|
||||
func (m *Log) Unmarshal(data []byte) error {
|
||||
var hasFields [1]uint64
|
||||
l := len(data)
|
||||
@ -474,7 +526,7 @@ func (m *Log) Unmarshal(data []byte) error {
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.Contents = append(m.Contents, &Log_Content{})
|
||||
m.Contents = append(m.Contents, &LogContent{})
|
||||
if err := m.Contents[len(m.Contents)-1].Unmarshal(data[iNdEx:postIndex]); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -491,7 +543,7 @@ func (m *Log) Unmarshal(data []byte) error {
|
||||
if (iNdEx + skippy) > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.XXX_unrecognized = append(m.XXX_unrecognized, data[iNdEx:iNdEx+skippy]...)
|
||||
m.XXXUnrecognized = append(m.XXXUnrecognized, data[iNdEx:iNdEx+skippy]...)
|
||||
iNdEx += skippy
|
||||
}
|
||||
}
|
||||
@ -504,7 +556,9 @@ func (m *Log) Unmarshal(data []byte) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (m *Log_Content) Unmarshal(data []byte) error {
|
||||
|
||||
// Unmarshal data to LogContent
|
||||
func (m *LogContent) Unmarshal(data []byte) error {
|
||||
var hasFields [1]uint64
|
||||
l := len(data)
|
||||
iNdEx := 0
|
||||
@ -608,7 +662,7 @@ func (m *Log_Content) Unmarshal(data []byte) error {
|
||||
if (iNdEx + skippy) > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.XXX_unrecognized = append(m.XXX_unrecognized, data[iNdEx:iNdEx+skippy]...)
|
||||
m.XXXUnrecognized = append(m.XXXUnrecognized, data[iNdEx:iNdEx+skippy]...)
|
||||
iNdEx += skippy
|
||||
}
|
||||
}
|
||||
@ -624,6 +678,8 @@ func (m *Log_Content) Unmarshal(data []byte) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Unmarshal data to LogGroup
|
||||
func (m *LogGroup) Unmarshal(data []byte) error {
|
||||
l := len(data)
|
||||
iNdEx := 0
|
||||
@ -786,7 +842,7 @@ func (m *LogGroup) Unmarshal(data []byte) error {
|
||||
if (iNdEx + skippy) > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.XXX_unrecognized = append(m.XXX_unrecognized, data[iNdEx:iNdEx+skippy]...)
|
||||
m.XXXUnrecognized = append(m.XXXUnrecognized, data[iNdEx:iNdEx+skippy]...)
|
||||
iNdEx += skippy
|
||||
}
|
||||
}
|
||||
@ -796,6 +852,8 @@ func (m *LogGroup) Unmarshal(data []byte) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Unmarshal data to LogGroupList
|
||||
func (m *LogGroupList) Unmarshal(data []byte) error {
|
||||
l := len(data)
|
||||
iNdEx := 0
|
||||
@ -868,7 +926,7 @@ func (m *LogGroupList) Unmarshal(data []byte) error {
|
||||
if (iNdEx + skippy) > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.XXX_unrecognized = append(m.XXX_unrecognized, data[iNdEx:iNdEx+skippy]...)
|
||||
m.XXXUnrecognized = append(m.XXXUnrecognized, data[iNdEx:iNdEx+skippy]...)
|
||||
iNdEx += skippy
|
||||
}
|
||||
}
|
||||
@ -878,6 +936,7 @@ func (m *LogGroupList) Unmarshal(data []byte) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func skipLog(data []byte) (n int, err error) {
|
||||
l := len(data)
|
||||
iNdEx := 0
|
||||
@ -940,7 +999,7 @@ func skipLog(data []byte) (n int, err error) {
|
||||
case 3:
|
||||
for {
|
||||
var innerWire uint64
|
||||
var start int = iNdEx
|
||||
var start = iNdEx
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return 0, ErrIntOverflowLog
|
||||
@ -977,8 +1036,3 @@ func skipLog(data []byte) (n int, err error) {
|
||||
}
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
var (
|
||||
ErrInvalidLengthLog = fmt.Errorf("proto: negative length found during unmarshaling")
|
||||
ErrIntOverflowLog = fmt.Errorf("proto: integer overflow")
|
||||
)
|
||||
|
@ -1,5 +1,6 @@
|
||||
package alils
|
||||
|
||||
// InputDetail define log detail
|
||||
type InputDetail struct {
|
||||
LogType string `json:"logType"`
|
||||
LogPath string `json:"logPath"`
|
||||
@ -14,11 +15,13 @@ type InputDetail struct {
|
||||
TopicFormat string `json:"topicFormat"`
|
||||
}
|
||||
|
||||
// OutputDetail define the output detail
|
||||
type OutputDetail struct {
|
||||
Endpoint string `json:"endpoint"`
|
||||
LogStoreName string `json:"logstoreName"`
|
||||
}
|
||||
|
||||
// LogConfig define Log Config
|
||||
type LogConfig struct {
|
||||
Name string `json:"configName"`
|
||||
InputType string `json:"inputType"`
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
Package sls implements the SDK(v0.5.0) of Simple Log Service(abbr. SLS).
|
||||
Package alils 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.
|
||||
@ -20,19 +20,20 @@ type errorMessage struct {
|
||||
Message string `json:"errorMessage"`
|
||||
}
|
||||
|
||||
// LogProject Define the Ali Project detail
|
||||
type LogProject struct {
|
||||
Name string // Project name
|
||||
Endpoint string // IP or hostname of SLS endpoint
|
||||
AccessKeyId string
|
||||
AccessKeyID string
|
||||
AccessKeySecret string
|
||||
}
|
||||
|
||||
// NewLogProject creates a new SLS project.
|
||||
func NewLogProject(name, endpoint, accessKeyId, accessKeySecret string) (p *LogProject, err error) {
|
||||
func NewLogProject(name, endpoint, AccessKeyID, accessKeySecret string) (p *LogProject, err error) {
|
||||
p = &LogProject{
|
||||
Name: name,
|
||||
Endpoint: endpoint,
|
||||
AccessKeyId: accessKeyId,
|
||||
AccessKeyID: AccessKeyID,
|
||||
AccessKeySecret: accessKeySecret,
|
||||
}
|
||||
return p, nil
|
||||
|
@ -12,6 +12,7 @@ import (
|
||||
"github.com/gogo/protobuf/proto"
|
||||
)
|
||||
|
||||
// LogStore Store the logs
|
||||
type LogStore struct {
|
||||
Name string `json:"logstoreName"`
|
||||
TTL int
|
||||
@ -23,6 +24,7 @@ type LogStore struct {
|
||||
project *LogProject
|
||||
}
|
||||
|
||||
// Shard define the Log Shard
|
||||
type Shard struct {
|
||||
ShardID int `json:"shardID"`
|
||||
}
|
||||
@ -116,16 +118,16 @@ func (s *LogStore) PutLogs(lg *LogGroup) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
// GetCursor gets log cursor of one shard specified by shardId.
|
||||
// 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) {
|
||||
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)
|
||||
s.Name, shardID, from)
|
||||
|
||||
r, err := request(s.project, "GET", uri, h, nil)
|
||||
if err != nil {
|
||||
@ -163,10 +165,10 @@ func (s *LogStore) GetCursor(shardId int, from string) (cursor string, err error
|
||||
return
|
||||
}
|
||||
|
||||
// GetLogsBytes gets logs binary data from shard specified by shardId according cursor.
|
||||
// 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,
|
||||
func (s *LogStore) GetLogsBytes(shardID int, cursor string,
|
||||
logGroupMaxCount int) (out []byte, nextCursor string, err error) {
|
||||
|
||||
h := map[string]string{
|
||||
@ -176,7 +178,7 @@ func (s *LogStore) GetLogsBytes(shardId int, cursor string,
|
||||
}
|
||||
|
||||
uri := fmt.Sprintf("/logstores/%v/shards/%v?type=logs&cursor=%v&count=%v",
|
||||
s.Name, shardId, cursor, logGroupMaxCount)
|
||||
s.Name, shardID, cursor, logGroupMaxCount)
|
||||
|
||||
r, err := request(s.project, "GET", uri, h, nil)
|
||||
if err != nil {
|
||||
@ -249,13 +251,13 @@ func LogsBytesDecode(data []byte) (gl *LogGroupList, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
// GetLogs gets logs from shard specified by shardId according cursor.
|
||||
// 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,
|
||||
func (s *LogStore) GetLogs(shardID int, cursor string,
|
||||
logGroupMaxCount int) (gl *LogGroupList, nextCursor string, err error) {
|
||||
|
||||
out, nextCursor, err := s.GetLogsBytes(shardId, cursor, logGroupMaxCount)
|
||||
out, nextCursor, err := s.GetLogsBytes(shardID, cursor, logGroupMaxCount)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -8,18 +8,20 @@ import (
|
||||
"net/http/httputil"
|
||||
)
|
||||
|
||||
type MachinGroupAttribute struct {
|
||||
// MachineGroupAttribute define the Attribute
|
||||
type MachineGroupAttribute struct {
|
||||
ExternalName string `json:"externalName"`
|
||||
TopicName string `json:"groupTopic"`
|
||||
}
|
||||
|
||||
// MachineGroup define the machine Group
|
||||
type MachineGroup struct {
|
||||
Name string `json:"groupName"`
|
||||
Type string `json:"groupType"`
|
||||
MachineIdType string `json:"machineIdentifyType"`
|
||||
MachineIdList []string `json:"machineList"`
|
||||
MachineIDType string `json:"machineIdentifyType"`
|
||||
MachineIDList []string `json:"machineList"`
|
||||
|
||||
Attribute MachinGroupAttribute `json:"groupAttribute"`
|
||||
Attribute MachineGroupAttribute `json:"groupAttribute"`
|
||||
|
||||
CreateTime uint32
|
||||
LastModifyTime uint32
|
||||
@ -27,12 +29,14 @@ type MachineGroup struct {
|
||||
project *LogProject
|
||||
}
|
||||
|
||||
// Machine define the Machine
|
||||
type Machine struct {
|
||||
IP string
|
||||
UniqueId string `json:"machine-uniqueid"`
|
||||
UserdefinedId string `json:"userdefined-id"`
|
||||
UniqueID string `json:"machine-uniqueid"`
|
||||
UserdefinedID string `json:"userdefined-id"`
|
||||
}
|
||||
|
||||
// MachineList define the Machine List
|
||||
type MachineList struct {
|
||||
Total int
|
||||
Machines []*Machine
|
||||
|
@ -33,12 +33,12 @@ func request(project *LogProject, method, uri string, headers map[string]string,
|
||||
}
|
||||
|
||||
// Calc Authorization
|
||||
// Authorization = "SLS <AccessKeyId>:<Signature>"
|
||||
// Authorization = "SLS <AccessKeyID>:<Signature>"
|
||||
digest, err := signature(project, method, uri, headers)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
auth := fmt.Sprintf("SLS %v:%v", project.AccessKeyId, digest)
|
||||
auth := fmt.Sprintf("SLS %v:%v", project.AccessKeyID, digest)
|
||||
headers["Authorization"] = auth
|
||||
|
||||
// Initialize http request
|
||||
|
@ -76,7 +76,7 @@ func signature(project *LogProject, method, uri string,
|
||||
var keys sort.StringSlice
|
||||
|
||||
vals := u.Query()
|
||||
for k, _ := range vals {
|
||||
for k := range vals {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
|
||||
@ -109,4 +109,3 @@ func signature(project *LogProject, method, uri string,
|
||||
digest = base64.StdEncoding.EncodeToString(mac.Sum(nil))
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -361,7 +361,7 @@ func isParameterChar(b byte) bool {
|
||||
}
|
||||
|
||||
func (cw *ansiColorWriter) Write(p []byte) (int, error) {
|
||||
r, nw, first, last := 0, 0, 0, 0
|
||||
var r, nw, first, last int
|
||||
if cw.mode != DiscardNonColorEscSeq {
|
||||
cw.state = outsideCsiCode
|
||||
cw.resetBuffer()
|
||||
|
@ -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.
|
||||
|
52
logs/file.go
52
logs/file.go
@ -56,17 +56,20 @@ type fileLogWriter struct {
|
||||
|
||||
Perm string `json:"perm"`
|
||||
|
||||
RotatePerm string `json:"rotateperm"`
|
||||
|
||||
fileNameOnly, suffix string // like "project.log", project is fileNameOnly and .log is suffix
|
||||
}
|
||||
|
||||
// newFileWriter create a FileLogWriter returning as LoggerInterface.
|
||||
func newFileWriter() Logger {
|
||||
w := &fileLogWriter{
|
||||
Daily: true,
|
||||
MaxDays: 7,
|
||||
Rotate: true,
|
||||
Level: LevelTrace,
|
||||
Perm: "0660",
|
||||
Daily: true,
|
||||
MaxDays: 7,
|
||||
Rotate: true,
|
||||
RotatePerm: "0440",
|
||||
Level: LevelTrace,
|
||||
Perm: "0660",
|
||||
}
|
||||
return w
|
||||
}
|
||||
@ -170,7 +173,7 @@ func (w *fileLogWriter) initFd() error {
|
||||
fd := w.fileWriter
|
||||
fInfo, err := fd.Stat()
|
||||
if err != nil {
|
||||
return fmt.Errorf("get stat err: %s\n", err)
|
||||
return fmt.Errorf("get stat err: %s", err)
|
||||
}
|
||||
w.maxSizeCurSize = int(fInfo.Size())
|
||||
w.dailyOpenTime = time.Now()
|
||||
@ -179,7 +182,7 @@ func (w *fileLogWriter) initFd() error {
|
||||
if w.Daily {
|
||||
go w.dailyRotate(w.dailyOpenTime)
|
||||
}
|
||||
if fInfo.Size() > 0 {
|
||||
if fInfo.Size() > 0 && w.MaxLines > 0 {
|
||||
count, err := w.lines()
|
||||
if err != nil {
|
||||
return err
|
||||
@ -193,16 +196,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) {
|
||||
@ -239,8 +240,12 @@ func (w *fileLogWriter) doRotate(logTime time.Time) error {
|
||||
// Find the next available number
|
||||
num := 1
|
||||
fName := ""
|
||||
rotatePerm, err := strconv.ParseInt(w.RotatePerm, 8, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err := os.Lstat(w.Filename)
|
||||
_, err = os.Lstat(w.Filename)
|
||||
if err != nil {
|
||||
//even if the file is not exist or other ,we should RESTART the logger
|
||||
goto RESTART_LOGGER
|
||||
@ -261,7 +266,7 @@ func (w *fileLogWriter) doRotate(logTime time.Time) error {
|
||||
}
|
||||
// return error if the last file checked still existed
|
||||
if err == nil {
|
||||
return fmt.Errorf("Rotate: Cannot find free log number to rename %s\n", w.Filename)
|
||||
return fmt.Errorf("Rotate: Cannot find free log number to rename %s", w.Filename)
|
||||
}
|
||||
|
||||
// close fileWriter before rename
|
||||
@ -270,21 +275,24 @@ 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(440))
|
||||
// re-start logger
|
||||
if err != nil {
|
||||
goto RESTART_LOGGER
|
||||
}
|
||||
|
||||
err = os.Chmod(fName, os.FileMode(rotatePerm))
|
||||
|
||||
RESTART_LOGGER:
|
||||
|
||||
startLoggerErr := w.startLogger()
|
||||
go w.deleteOldLog()
|
||||
|
||||
if startLoggerErr != nil {
|
||||
return fmt.Errorf("Rotate StartLogger: %s\n", startLoggerErr)
|
||||
return fmt.Errorf("Rotate StartLogger: %s", startLoggerErr)
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("Rotate: %s\n", err)
|
||||
return fmt.Errorf("Rotate: %s", err)
|
||||
}
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func (w *fileLogWriter) deleteOldLog() {
|
||||
|
@ -162,14 +162,35 @@ 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,
|
||||
MaxDays: 7,
|
||||
Rotate: true,
|
||||
Level: LevelTrace,
|
||||
Perm: "0660",
|
||||
Daily: true,
|
||||
MaxDays: 7,
|
||||
Rotate: true,
|
||||
Level: LevelTrace,
|
||||
Perm: "0660",
|
||||
RotatePerm: "0440",
|
||||
}
|
||||
fw.Init(fmt.Sprintf(`{"filename":"%v","maxdays":1}`, fn1))
|
||||
fw.dailyOpenTime = time.Now().Add(-24 * time.Hour)
|
||||
@ -188,11 +209,12 @@ func testFileRotate(t *testing.T, fn1, fn2 string) {
|
||||
|
||||
func testFileDailyRotate(t *testing.T, fn1, fn2 string) {
|
||||
fw := &fileLogWriter{
|
||||
Daily: true,
|
||||
MaxDays: 7,
|
||||
Rotate: true,
|
||||
Level: LevelTrace,
|
||||
Perm: "0660",
|
||||
Daily: true,
|
||||
MaxDays: 7,
|
||||
Rotate: true,
|
||||
Level: LevelTrace,
|
||||
Perm: "0660",
|
||||
RotatePerm: "0440",
|
||||
}
|
||||
fw.Init(fmt.Sprintf(`{"filename":"%v","maxdays":1}`, fn1))
|
||||
fw.dailyOpenTime = time.Now().Add(-24 * time.Hour)
|
||||
|
@ -25,11 +25,7 @@ func newJLWriter() Logger {
|
||||
|
||||
// Init JLWriter with json config string
|
||||
func (s *JLWriter) Init(jsonconfig string) error {
|
||||
err := json.Unmarshal([]byte(jsonconfig), s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return json.Unmarshal([]byte(jsonconfig), s)
|
||||
}
|
||||
|
||||
// WriteMsg write message in smtp writer.
|
||||
@ -65,12 +61,10 @@ func (s *JLWriter) WriteMsg(when time.Time, msg string, level int) error {
|
||||
|
||||
// Flush implementing method. empty.
|
||||
func (s *JLWriter) Flush() {
|
||||
return
|
||||
}
|
||||
|
||||
// Destroy implementing method. empty.
|
||||
func (s *JLWriter) Destroy() {
|
||||
return
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
13
logs/log.go
13
logs/log.go
@ -275,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
|
||||
@ -492,9 +492,9 @@ func (bl *BeeLogger) flush() {
|
||||
}
|
||||
|
||||
// beeLogger references the used application logger.
|
||||
var beeLogger *BeeLogger = NewLogger()
|
||||
var beeLogger = NewLogger()
|
||||
|
||||
// GetLogger returns the default BeeLogger
|
||||
// GetBeeLogger returns the default BeeLogger
|
||||
func GetBeeLogger() *BeeLogger {
|
||||
return beeLogger
|
||||
}
|
||||
@ -534,6 +534,7 @@ func Reset() {
|
||||
beeLogger.Reset()
|
||||
}
|
||||
|
||||
// Async set the beelogger with Async mode and hold msglen messages
|
||||
func Async(msgLen ...int64) *BeeLogger {
|
||||
return beeLogger.Async(msgLen...)
|
||||
}
|
||||
@ -561,11 +562,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.
|
||||
|
@ -87,13 +87,15 @@ const (
|
||||
mi2 = `012345678901234567890123456789012345678901234567890123456789`
|
||||
s1 = `000000000011111111112222222222333333333344444444445555555555`
|
||||
s2 = `012345678901234567890123456789012345678901234567890123456789`
|
||||
ns1 = `0123456789`
|
||||
)
|
||||
|
||||
func formatTimeHeader(when time.Time) ([]byte, int) {
|
||||
y, mo, d := when.Date()
|
||||
h, mi, s := when.Clock()
|
||||
//len("2006/01/02 15:04:05 ")==20
|
||||
var buf [20]byte
|
||||
ns := when.Nanosecond()/1000000
|
||||
//len("2006/01/02 15:04:05.123 ")==24
|
||||
var buf [24]byte
|
||||
|
||||
buf[0] = y1[y/1000%10]
|
||||
buf[1] = y2[y/100]
|
||||
@ -114,7 +116,12 @@ func formatTimeHeader(when time.Time) ([]byte, int) {
|
||||
buf[16] = ':'
|
||||
buf[17] = s1[s]
|
||||
buf[18] = s2[s]
|
||||
buf[19] = ' '
|
||||
buf[19] = '.'
|
||||
buf[20] = ns1[ns/100]
|
||||
buf[21] = ns1[ns%100/10]
|
||||
buf[22] = ns1[ns%10]
|
||||
|
||||
buf[23] = ' '
|
||||
|
||||
return buf[0:], d
|
||||
}
|
||||
@ -139,6 +146,11 @@ var (
|
||||
reset = string([]byte{27, 91, 48, 109})
|
||||
)
|
||||
|
||||
// ColorByStatus return color by http code
|
||||
// 2xx return Green
|
||||
// 3xx return White
|
||||
// 4xx return Yellow
|
||||
// 5xx return Red
|
||||
func ColorByStatus(cond bool, code int) string {
|
||||
switch {
|
||||
case code >= 200 && code < 300:
|
||||
@ -152,6 +164,14 @@ func ColorByStatus(cond bool, code int) string {
|
||||
}
|
||||
}
|
||||
|
||||
// ColorByMethod return color by http code
|
||||
// GET return Blue
|
||||
// POST return Cyan
|
||||
// PUT return Yellow
|
||||
// DELETE return Red
|
||||
// PATCH return Green
|
||||
// HEAD return Magenta
|
||||
// OPTIONS return WHITE
|
||||
func ColorByMethod(cond bool, method string) string {
|
||||
switch method {
|
||||
case "GET":
|
||||
@ -173,10 +193,10 @@ func ColorByMethod(cond bool, method string) string {
|
||||
}
|
||||
}
|
||||
|
||||
// Guard Mutex to guarantee atomicity of W32Debug(string) function
|
||||
// Guard Mutex to guarantee atomic of W32Debug(string) function
|
||||
var mu sync.Mutex
|
||||
|
||||
// Helper method to output colored logs in Windows terminals
|
||||
// W32Debug Helper method to output colored logs in Windows terminals
|
||||
func W32Debug(msg string) {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
|
@ -31,7 +31,7 @@ func TestFormatHeader_0(t *testing.T) {
|
||||
break
|
||||
}
|
||||
h, _ := formatTimeHeader(tm)
|
||||
if tm.Format("2006/01/02 15:04:05 ") != string(h) {
|
||||
if tm.Format("2006/01/02 15:04:05.999 ") != string(h) {
|
||||
t.Log(tm)
|
||||
t.FailNow()
|
||||
}
|
||||
@ -49,7 +49,7 @@ func TestFormatHeader_1(t *testing.T) {
|
||||
break
|
||||
}
|
||||
h, _ := formatTimeHeader(tm)
|
||||
if tm.Format("2006/01/02 15:04:05 ") != string(h) {
|
||||
if tm.Format("2006/01/02 15:04:05.999 ") != string(h) {
|
||||
t.Log(tm)
|
||||
t.FailNow()
|
||||
}
|
||||
|
@ -21,11 +21,7 @@ func newSLACKWriter() Logger {
|
||||
|
||||
// Init SLACKWriter with json config string
|
||||
func (s *SLACKWriter) Init(jsonconfig string) error {
|
||||
err := json.Unmarshal([]byte(jsonconfig), s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return json.Unmarshal([]byte(jsonconfig), s)
|
||||
}
|
||||
|
||||
// WriteMsg write message in smtp writer.
|
||||
@ -53,12 +49,10 @@ func (s *SLACKWriter) WriteMsg(when time.Time, msg string, level int) error {
|
||||
|
||||
// Flush implementing method. empty.
|
||||
func (s *SLACKWriter) Flush() {
|
||||
return
|
||||
}
|
||||
|
||||
// Destroy implementing method. empty.
|
||||
func (s *SLACKWriter) Destroy() {
|
||||
return
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
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() {
|
||||
|
392
migration/ddl.go
392
migration/ddl.go
@ -14,40 +14,382 @@
|
||||
|
||||
package migration
|
||||
|
||||
// Table store the tablename and Column
|
||||
type Table struct {
|
||||
TableName string
|
||||
Columns []*Column
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/astaxie/beego"
|
||||
)
|
||||
|
||||
// Index struct defines the structure of Index Columns
|
||||
type Index struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
// Create return the create sql
|
||||
func (t *Table) Create() string {
|
||||
return ""
|
||||
// Unique struct defines a single unique key combination
|
||||
type Unique struct {
|
||||
Definition string
|
||||
Columns []*Column
|
||||
}
|
||||
|
||||
// Drop return the drop sql
|
||||
func (t *Table) Drop() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// Column define the columns name type and Default
|
||||
//Column struct defines a single column of a table
|
||||
type Column struct {
|
||||
Name string
|
||||
Type string
|
||||
Default interface{}
|
||||
Name string
|
||||
Inc string
|
||||
Null string
|
||||
Default string
|
||||
Unsign string
|
||||
DataType string
|
||||
remove bool
|
||||
Modify bool
|
||||
}
|
||||
|
||||
// Create return create sql with the provided tbname and columns
|
||||
func Create(tbname string, columns ...Column) string {
|
||||
return ""
|
||||
// Foreign struct defines a single foreign relationship
|
||||
type Foreign struct {
|
||||
ForeignTable string
|
||||
ForeignColumn string
|
||||
OnDelete string
|
||||
OnUpdate string
|
||||
Column
|
||||
}
|
||||
|
||||
// Drop return the drop sql with the provided tbname and columns
|
||||
func Drop(tbname string, columns ...Column) string {
|
||||
return ""
|
||||
// RenameColumn struct allows renaming of columns
|
||||
type RenameColumn struct {
|
||||
OldName string
|
||||
OldNull string
|
||||
OldDefault string
|
||||
OldUnsign string
|
||||
OldDataType string
|
||||
NewName string
|
||||
Column
|
||||
}
|
||||
|
||||
// TableDDL is still in think
|
||||
func TableDDL(tbname string, columns ...Column) string {
|
||||
return ""
|
||||
// CreateTable creates the table on system
|
||||
func (m *Migration) CreateTable(tablename, engine, charset string, p ...func()) {
|
||||
m.TableName = tablename
|
||||
m.Engine = engine
|
||||
m.Charset = charset
|
||||
m.ModifyType = "create"
|
||||
}
|
||||
|
||||
// AlterTable set the ModifyType to alter
|
||||
func (m *Migration) AlterTable(tablename string) {
|
||||
m.TableName = tablename
|
||||
m.ModifyType = "alter"
|
||||
}
|
||||
|
||||
// NewCol creates a new standard column and attaches it to m struct
|
||||
func (m *Migration) NewCol(name string) *Column {
|
||||
col := &Column{Name: name}
|
||||
m.AddColumns(col)
|
||||
return col
|
||||
}
|
||||
|
||||
//PriCol creates a new primary column and attaches it to m struct
|
||||
func (m *Migration) PriCol(name string) *Column {
|
||||
col := &Column{Name: name}
|
||||
m.AddColumns(col)
|
||||
m.AddPrimary(col)
|
||||
return col
|
||||
}
|
||||
|
||||
//UniCol creates / appends columns to specified unique key and attaches it to m struct
|
||||
func (m *Migration) UniCol(uni, name string) *Column {
|
||||
col := &Column{Name: name}
|
||||
m.AddColumns(col)
|
||||
|
||||
uniqueOriginal := &Unique{}
|
||||
|
||||
for _, unique := range m.Uniques {
|
||||
if unique.Definition == uni {
|
||||
unique.AddColumnsToUnique(col)
|
||||
uniqueOriginal = unique
|
||||
}
|
||||
}
|
||||
if uniqueOriginal.Definition == "" {
|
||||
unique := &Unique{Definition: uni}
|
||||
unique.AddColumnsToUnique(col)
|
||||
m.AddUnique(unique)
|
||||
}
|
||||
|
||||
return col
|
||||
}
|
||||
|
||||
//ForeignCol creates a new foreign column and returns the instance of column
|
||||
func (m *Migration) ForeignCol(colname, foreigncol, foreigntable string) (foreign *Foreign) {
|
||||
|
||||
foreign = &Foreign{ForeignColumn: foreigncol, ForeignTable: foreigntable}
|
||||
foreign.Name = colname
|
||||
m.AddForeign(foreign)
|
||||
return foreign
|
||||
}
|
||||
|
||||
//SetOnDelete sets the on delete of foreign
|
||||
func (foreign *Foreign) SetOnDelete(del string) *Foreign {
|
||||
foreign.OnDelete = "ON DELETE" + del
|
||||
return foreign
|
||||
}
|
||||
|
||||
//SetOnUpdate sets the on update of foreign
|
||||
func (foreign *Foreign) SetOnUpdate(update string) *Foreign {
|
||||
foreign.OnUpdate = "ON UPDATE" + update
|
||||
return foreign
|
||||
}
|
||||
|
||||
//Remove marks the columns to be removed.
|
||||
//it allows reverse m to create the column.
|
||||
func (c *Column) Remove() {
|
||||
c.remove = true
|
||||
}
|
||||
|
||||
//SetAuto enables auto_increment of column (can be used once)
|
||||
func (c *Column) SetAuto(inc bool) *Column {
|
||||
if inc {
|
||||
c.Inc = "auto_increment"
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
//SetNullable sets the column to be null
|
||||
func (c *Column) SetNullable(null bool) *Column {
|
||||
if null {
|
||||
c.Null = ""
|
||||
|
||||
} else {
|
||||
c.Null = "NOT NULL"
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
//SetDefault sets the default value, prepend with "DEFAULT "
|
||||
func (c *Column) SetDefault(def string) *Column {
|
||||
c.Default = "DEFAULT " + def
|
||||
return c
|
||||
}
|
||||
|
||||
//SetUnsigned sets the column to be unsigned int
|
||||
func (c *Column) SetUnsigned(unsign bool) *Column {
|
||||
if unsign {
|
||||
c.Unsign = "UNSIGNED"
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
//SetDataType sets the dataType of the column
|
||||
func (c *Column) SetDataType(dataType string) *Column {
|
||||
c.DataType = dataType
|
||||
return c
|
||||
}
|
||||
|
||||
//SetOldNullable allows reverting to previous nullable on reverse ms
|
||||
func (c *RenameColumn) SetOldNullable(null bool) *RenameColumn {
|
||||
if null {
|
||||
c.OldNull = ""
|
||||
|
||||
} else {
|
||||
c.OldNull = "NOT NULL"
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
//SetOldDefault allows reverting to previous default on reverse ms
|
||||
func (c *RenameColumn) SetOldDefault(def string) *RenameColumn {
|
||||
c.OldDefault = def
|
||||
return c
|
||||
}
|
||||
|
||||
//SetOldUnsigned allows reverting to previous unsgined on reverse ms
|
||||
func (c *RenameColumn) SetOldUnsigned(unsign bool) *RenameColumn {
|
||||
if unsign {
|
||||
c.OldUnsign = "UNSIGNED"
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
//SetOldDataType allows reverting to previous datatype on reverse ms
|
||||
func (c *RenameColumn) SetOldDataType(dataType string) *RenameColumn {
|
||||
c.OldDataType = dataType
|
||||
return c
|
||||
}
|
||||
|
||||
//SetPrimary adds the columns to the primary key (can only be used any number of times in only one m)
|
||||
func (c *Column) SetPrimary(m *Migration) *Column {
|
||||
m.Primary = append(m.Primary, c)
|
||||
return c
|
||||
}
|
||||
|
||||
//AddColumnsToUnique adds the columns to Unique Struct
|
||||
func (unique *Unique) AddColumnsToUnique(columns ...*Column) *Unique {
|
||||
|
||||
unique.Columns = append(unique.Columns, columns...)
|
||||
|
||||
return unique
|
||||
}
|
||||
|
||||
//AddColumns adds columns to m struct
|
||||
func (m *Migration) AddColumns(columns ...*Column) *Migration {
|
||||
|
||||
m.Columns = append(m.Columns, columns...)
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
//AddPrimary adds the column to primary in m struct
|
||||
func (m *Migration) AddPrimary(primary *Column) *Migration {
|
||||
m.Primary = append(m.Primary, primary)
|
||||
return m
|
||||
}
|
||||
|
||||
//AddUnique adds the column to unique in m struct
|
||||
func (m *Migration) AddUnique(unique *Unique) *Migration {
|
||||
m.Uniques = append(m.Uniques, unique)
|
||||
return m
|
||||
}
|
||||
|
||||
//AddForeign adds the column to foreign in m struct
|
||||
func (m *Migration) AddForeign(foreign *Foreign) *Migration {
|
||||
m.Foreigns = append(m.Foreigns, foreign)
|
||||
return m
|
||||
}
|
||||
|
||||
//AddIndex adds the column to index in m struct
|
||||
func (m *Migration) AddIndex(index *Index) *Migration {
|
||||
m.Indexes = append(m.Indexes, index)
|
||||
return m
|
||||
}
|
||||
|
||||
//RenameColumn allows renaming of columns
|
||||
func (m *Migration) RenameColumn(from, to string) *RenameColumn {
|
||||
rename := &RenameColumn{OldName: from, NewName: to}
|
||||
m.Renames = append(m.Renames, rename)
|
||||
return rename
|
||||
}
|
||||
|
||||
//GetSQL returns the generated sql depending on ModifyType
|
||||
func (m *Migration) GetSQL() (sql string) {
|
||||
sql = ""
|
||||
switch m.ModifyType {
|
||||
case "create":
|
||||
{
|
||||
sql += fmt.Sprintf("CREATE TABLE `%s` (", m.TableName)
|
||||
for index, column := range m.Columns {
|
||||
sql += fmt.Sprintf("\n `%s` %s %s %s %s %s", column.Name, column.DataType, column.Unsign, column.Null, column.Inc, column.Default)
|
||||
if len(m.Columns) > index+1 {
|
||||
sql += ","
|
||||
}
|
||||
}
|
||||
|
||||
if len(m.Primary) > 0 {
|
||||
sql += fmt.Sprintf(",\n PRIMARY KEY( ")
|
||||
}
|
||||
for index, column := range m.Primary {
|
||||
sql += fmt.Sprintf(" `%s`", column.Name)
|
||||
if len(m.Primary) > index+1 {
|
||||
sql += ","
|
||||
}
|
||||
|
||||
}
|
||||
if len(m.Primary) > 0 {
|
||||
sql += fmt.Sprintf(")")
|
||||
}
|
||||
|
||||
for _, unique := range m.Uniques {
|
||||
sql += fmt.Sprintf(",\n UNIQUE KEY `%s`( ", unique.Definition)
|
||||
for index, column := range unique.Columns {
|
||||
sql += fmt.Sprintf(" `%s`", column.Name)
|
||||
if len(unique.Columns) > index+1 {
|
||||
sql += ","
|
||||
}
|
||||
}
|
||||
sql += fmt.Sprintf(")")
|
||||
}
|
||||
for _, foreign := range m.Foreigns {
|
||||
sql += fmt.Sprintf(",\n `%s` %s %s %s %s %s", foreign.Name, foreign.DataType, foreign.Unsign, foreign.Null, foreign.Inc, foreign.Default)
|
||||
sql += fmt.Sprintf(",\n KEY `%s_%s_foreign`(`%s`),", m.TableName, foreign.Column.Name, foreign.Column.Name)
|
||||
sql += fmt.Sprintf("\n CONSTRAINT `%s_%s_foreign` FOREIGN KEY (`%s`) REFERENCES `%s` (`%s`) %s %s", m.TableName, foreign.Column.Name, foreign.Column.Name, foreign.ForeignTable, foreign.ForeignColumn, foreign.OnDelete, foreign.OnUpdate)
|
||||
|
||||
}
|
||||
sql += fmt.Sprintf(")ENGINE=%s DEFAULT CHARSET=%s;", m.Engine, m.Charset)
|
||||
break
|
||||
}
|
||||
case "alter":
|
||||
{
|
||||
sql += fmt.Sprintf("ALTER TABLE `%s` ", m.TableName)
|
||||
for index, column := range m.Columns {
|
||||
if !column.remove {
|
||||
beego.BeeLogger.Info("col")
|
||||
sql += fmt.Sprintf("\n ADD `%s` %s %s %s %s %s", column.Name, column.DataType, column.Unsign, column.Null, column.Inc, column.Default)
|
||||
} else {
|
||||
sql += fmt.Sprintf("\n DROP COLUMN `%s`", column.Name)
|
||||
}
|
||||
|
||||
if len(m.Columns) > index {
|
||||
sql += ","
|
||||
}
|
||||
}
|
||||
for index, column := range m.Renames {
|
||||
sql += fmt.Sprintf("CHANGE COLUMN `%s` `%s` %s %s %s %s %s", column.OldName, column.NewName, column.DataType, column.Unsign, column.Null, column.Inc, column.Default)
|
||||
if len(m.Renames) > index+1 {
|
||||
sql += ","
|
||||
}
|
||||
}
|
||||
|
||||
for index, foreign := range m.Foreigns {
|
||||
sql += fmt.Sprintf("ADD `%s` %s %s %s %s %s", foreign.Name, foreign.DataType, foreign.Unsign, foreign.Null, foreign.Inc, foreign.Default)
|
||||
sql += fmt.Sprintf(",\n ADD KEY `%s_%s_foreign`(`%s`)", m.TableName, foreign.Column.Name, foreign.Column.Name)
|
||||
sql += fmt.Sprintf(",\n ADD CONSTRAINT `%s_%s_foreign` FOREIGN KEY (`%s`) REFERENCES `%s` (`%s`) %s %s", m.TableName, foreign.Column.Name, foreign.Column.Name, foreign.ForeignTable, foreign.ForeignColumn, foreign.OnDelete, foreign.OnUpdate)
|
||||
if len(m.Foreigns) > index+1 {
|
||||
sql += ","
|
||||
}
|
||||
}
|
||||
sql += ";"
|
||||
|
||||
break
|
||||
}
|
||||
case "reverse":
|
||||
{
|
||||
|
||||
sql += fmt.Sprintf("ALTER TABLE `%s`", m.TableName)
|
||||
for index, column := range m.Columns {
|
||||
if column.remove {
|
||||
sql += fmt.Sprintf("\n ADD `%s` %s %s %s %s %s", column.Name, column.DataType, column.Unsign, column.Null, column.Inc, column.Default)
|
||||
} else {
|
||||
sql += fmt.Sprintf("\n DROP COLUMN `%s`", column.Name)
|
||||
}
|
||||
if len(m.Columns) > index {
|
||||
sql += ","
|
||||
}
|
||||
}
|
||||
|
||||
if len(m.Primary) > 0 {
|
||||
sql += fmt.Sprintf("\n DROP PRIMARY KEY,")
|
||||
}
|
||||
|
||||
for index, unique := range m.Uniques {
|
||||
sql += fmt.Sprintf("\n DROP KEY `%s`", unique.Definition)
|
||||
if len(m.Uniques) > index {
|
||||
sql += ","
|
||||
}
|
||||
|
||||
}
|
||||
for index, column := range m.Renames {
|
||||
sql += fmt.Sprintf("\n CHANGE COLUMN `%s` `%s` %s %s %s %s", column.NewName, column.OldName, column.OldDataType, column.OldUnsign, column.OldNull, column.OldDefault)
|
||||
if len(m.Renames) > index {
|
||||
sql += ","
|
||||
}
|
||||
}
|
||||
|
||||
for _, foreign := range m.Foreigns {
|
||||
sql += fmt.Sprintf("\n DROP KEY `%s_%s_foreign`", m.TableName, foreign.Column.Name)
|
||||
sql += fmt.Sprintf(",\n DROP FOREIGN KEY `%s_%s_foreign`", m.TableName, foreign.Column.Name)
|
||||
sql += fmt.Sprintf(",\n DROP COLUMN `%s`", foreign.Name)
|
||||
}
|
||||
sql += ";"
|
||||
}
|
||||
case "delete":
|
||||
{
|
||||
sql += fmt.Sprintf("DROP TABLE IF EXISTS `%s`;", m.TableName)
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
32
migration/doc.go
Normal file
32
migration/doc.go
Normal file
@ -0,0 +1,32 @@
|
||||
// Package migration enables you to generate migrations back and forth. It generates both migrations.
|
||||
//
|
||||
// //Creates a table
|
||||
// m.CreateTable("tablename","InnoDB","utf8");
|
||||
//
|
||||
// //Alter a table
|
||||
// m.AlterTable("tablename")
|
||||
//
|
||||
// Standard Column Methods
|
||||
// * SetDataType
|
||||
// * SetNullable
|
||||
// * SetDefault
|
||||
// * SetUnsigned (use only on integer types unless produces error)
|
||||
//
|
||||
// //Sets a primary column, multiple calls allowed, standard column methods available
|
||||
// m.PriCol("id").SetAuto(true).SetNullable(false).SetDataType("INT(10)").SetUnsigned(true)
|
||||
//
|
||||
// //UniCol Can be used multiple times, allows standard Column methods. Use same "index" string to add to same index
|
||||
// m.UniCol("index","column")
|
||||
//
|
||||
// //Standard Column Initialisation, can call .Remove() after NewCol("") on alter to remove
|
||||
// m.NewCol("name").SetDataType("VARCHAR(255) COLLATE utf8_unicode_ci").SetNullable(false)
|
||||
// m.NewCol("value").SetDataType("DOUBLE(8,2)").SetNullable(false)
|
||||
//
|
||||
// //Rename Columns , only use with Alter table, doesn't works with Create, prefix standard column methods with "Old" to
|
||||
// //create a true reversible migration eg: SetOldDataType("DOUBLE(12,3)")
|
||||
// m.RenameColumn("from","to")...
|
||||
//
|
||||
// //Foreign Columns, single columns are only supported, SetOnDelete & SetOnUpdate are available, call appropriately.
|
||||
// //Supports standard column methods, automatic reverse.
|
||||
// m.ForeignCol("local_col","foreign_col","foreign_table")
|
||||
package migration
|
@ -52,6 +52,26 @@ type Migrationer interface {
|
||||
GetCreated() int64
|
||||
}
|
||||
|
||||
//Migration defines the migrations by either SQL or DDL
|
||||
type Migration struct {
|
||||
sqls []string
|
||||
Created string
|
||||
TableName string
|
||||
Engine string
|
||||
Charset string
|
||||
ModifyType string
|
||||
Columns []*Column
|
||||
Indexes []*Index
|
||||
Primary []*Column
|
||||
Uniques []*Unique
|
||||
Foreigns []*Foreign
|
||||
Renames []*RenameColumn
|
||||
RemoveColumns []*Column
|
||||
RemoveIndexes []*Index
|
||||
RemoveUniques []*Unique
|
||||
RemoveForeigns []*Foreign
|
||||
}
|
||||
|
||||
var (
|
||||
migrationMap map[string]Migrationer
|
||||
)
|
||||
@ -60,20 +80,34 @@ func init() {
|
||||
migrationMap = make(map[string]Migrationer)
|
||||
}
|
||||
|
||||
// Migration the basic type which will implement the basic type
|
||||
type Migration struct {
|
||||
sqls []string
|
||||
Created string
|
||||
}
|
||||
|
||||
// Up implement in the Inheritance struct for upgrade
|
||||
func (m *Migration) Up() {
|
||||
|
||||
switch m.ModifyType {
|
||||
case "reverse":
|
||||
m.ModifyType = "alter"
|
||||
case "delete":
|
||||
m.ModifyType = "create"
|
||||
}
|
||||
m.sqls = append(m.sqls, m.GetSQL())
|
||||
}
|
||||
|
||||
// Down implement in the Inheritance struct for down
|
||||
func (m *Migration) Down() {
|
||||
|
||||
switch m.ModifyType {
|
||||
case "alter":
|
||||
m.ModifyType = "reverse"
|
||||
case "create":
|
||||
m.ModifyType = "delete"
|
||||
}
|
||||
m.sqls = append(m.sqls, m.GetSQL())
|
||||
}
|
||||
|
||||
//Migrate adds the SQL to the execution list
|
||||
func (m *Migration) Migrate(migrationType string) {
|
||||
m.ModifyType = migrationType
|
||||
m.sqls = append(m.sqls, m.GetSQL())
|
||||
}
|
||||
|
||||
// SQL add sql want to execute
|
||||
@ -138,7 +172,7 @@ func Register(name string, m Migrationer) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Upgrade upgrate the migration from lasttime
|
||||
// Upgrade upgrade the migration from lasttime
|
||||
func Upgrade(lasttime int64) error {
|
||||
sm := sortMap(migrationMap)
|
||||
i := 0
|
||||
|
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)
|
||||
|
@ -61,6 +61,9 @@ func init() {
|
||||
|
||||
// set default database
|
||||
orm.RegisterDataBase("default", "mysql", "root:root@/my_db?charset=utf8", 30)
|
||||
|
||||
// create table
|
||||
orm.RunSyncdb("default", false, true)
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -51,12 +51,14 @@ checkColumn:
|
||||
switch fieldType {
|
||||
case TypeBooleanField:
|
||||
col = T["bool"]
|
||||
case TypeCharField:
|
||||
case TypeVarCharField:
|
||||
if al.Driver == DRPostgres && fi.toText {
|
||||
col = T["string-text"]
|
||||
} else {
|
||||
col = fmt.Sprintf(T["string"], fieldSize)
|
||||
}
|
||||
case TypeCharField:
|
||||
col = fmt.Sprintf(T["string-char"], fieldSize)
|
||||
case TypeTextField:
|
||||
col = T["string-text"]
|
||||
case TypeTimeField:
|
||||
@ -89,20 +91,20 @@ 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)
|
||||
}
|
||||
case TypeJSONField:
|
||||
if al.Driver != DRPostgres {
|
||||
fieldType = TypeCharField
|
||||
fieldType = TypeVarCharField
|
||||
goto checkColumn
|
||||
}
|
||||
col = T["json"]
|
||||
case TypeJsonbField:
|
||||
if al.Driver != DRPostgres {
|
||||
fieldType = TypeCharField
|
||||
fieldType = TypeVarCharField
|
||||
goto checkColumn
|
||||
}
|
||||
col = T["jsonb"]
|
||||
@ -120,7 +122,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 +174,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 +194,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)
|
||||
}
|
||||
|
||||
|
51
orm/db.go
51
orm/db.go
@ -48,7 +48,7 @@ var (
|
||||
"lte": true,
|
||||
"eq": true,
|
||||
"nq": true,
|
||||
"ne": true,
|
||||
"ne": true,
|
||||
"startswith": true,
|
||||
"endswith": true,
|
||||
"istartswith": true,
|
||||
@ -87,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)
|
||||
@ -142,7 +142,7 @@ func (d *dbBase) collectFieldValue(mi *modelInfo, fi *fieldInfo, ind reflect.Val
|
||||
} else {
|
||||
value = field.Bool()
|
||||
}
|
||||
case TypeCharField, TypeTextField, TypeJSONField, TypeJsonbField:
|
||||
case TypeVarCharField, TypeCharField, TypeTextField, TypeJSONField, TypeJsonbField:
|
||||
if ns, ok := field.Interface().(sql.NullString); ok {
|
||||
value = nil
|
||||
if ns.Valid {
|
||||
@ -224,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)
|
||||
}
|
||||
}
|
||||
@ -271,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, "?")
|
||||
}
|
||||
@ -326,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}
|
||||
@ -507,10 +507,9 @@ func (d *dbBase) InsertOrUpdate(q dbQuerier, mi *modelInfo, ind reflect.Value, a
|
||||
case DRPostgres:
|
||||
if len(args) == 0 {
|
||||
return 0, fmt.Errorf("`%s` use InsertOrUpdate must have a conflict column", a.DriverName)
|
||||
} else {
|
||||
args0 = strings.ToLower(args[0])
|
||||
iouStr = fmt.Sprintf("ON CONFLICT (%s) DO UPDATE SET", args0)
|
||||
}
|
||||
args0 = strings.ToLower(args[0])
|
||||
iouStr = fmt.Sprintf("ON CONFLICT (%s) DO UPDATE SET", args0)
|
||||
default:
|
||||
return 0, fmt.Errorf("`%s` nonsupport InsertOrUpdate in beego", a.DriverName)
|
||||
}
|
||||
@ -592,7 +591,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
|
||||
@ -601,7 +600,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
|
||||
}
|
||||
|
||||
@ -654,7 +653,7 @@ func (d *dbBase) Delete(q dbQuerier, mi *modelInfo, ind reflect.Value, tz *time.
|
||||
} else {
|
||||
// default use pk value as where condtion.
|
||||
pkColumn, pkValue, ok := getExistPk(mi, ind)
|
||||
if ok == false {
|
||||
if !ok {
|
||||
return 0, ErrMissPK
|
||||
}
|
||||
whereCols = []string{pkColumn}
|
||||
@ -699,7 +698,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)
|
||||
@ -834,7 +833,11 @@ func (d *dbBase) DeleteBatch(q dbQuerier, qs *querySet, mi *modelInfo, cond *Con
|
||||
if err := rs.Scan(&ref); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
args = append(args, reflect.ValueOf(ref).Interface())
|
||||
pkValue, err := d.convertValueFromDB(mi.fields.pk, reflect.ValueOf(ref).Interface(), tz)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
args = append(args, pkValue)
|
||||
cnt++
|
||||
}
|
||||
|
||||
@ -929,7 +932,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)
|
||||
}
|
||||
}
|
||||
@ -987,7 +990,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
|
||||
}
|
||||
@ -1067,7 +1070,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 {
|
||||
@ -1110,7 +1113,7 @@ func (d *dbBase) Count(q dbQuerier, qs *querySet, mi *modelInfo, cond *Condition
|
||||
|
||||
// generate sql with replacing operator string placeholders and replaced values.
|
||||
func (d *dbBase) GenerateOperatorSQL(mi *modelInfo, fi *fieldInfo, operator string, args []interface{}, tz *time.Location) (string, []interface{}) {
|
||||
sql := ""
|
||||
var sql string
|
||||
params := getFlatParams(fi, args, tz)
|
||||
|
||||
if len(params) == 0 {
|
||||
@ -1237,7 +1240,7 @@ setValue:
|
||||
}
|
||||
value = b
|
||||
}
|
||||
case fieldType == TypeCharField || fieldType == TypeTextField || fieldType == TypeJSONField || fieldType == TypeJsonbField:
|
||||
case fieldType == TypeVarCharField || fieldType == TypeCharField || fieldType == TypeTextField || fieldType == TypeJSONField || fieldType == TypeJsonbField:
|
||||
if str == nil {
|
||||
value = ToStr(val)
|
||||
} else {
|
||||
@ -1357,7 +1360,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 {
|
||||
@ -1383,7 +1386,7 @@ setValue:
|
||||
field.SetBool(value.(bool))
|
||||
}
|
||||
}
|
||||
case fieldType == TypeCharField || fieldType == TypeTextField || fieldType == TypeJSONField || fieldType == TypeJsonbField:
|
||||
case fieldType == TypeVarCharField || fieldType == TypeCharField || fieldType == TypeTextField || fieldType == TypeJSONField || fieldType == TypeJsonbField:
|
||||
if isNative {
|
||||
if ns, ok := field.Interface().(sql.NullString); ok {
|
||||
if value == nil {
|
||||
@ -1533,7 +1536,7 @@ setValue:
|
||||
}
|
||||
}
|
||||
|
||||
if isNative == false {
|
||||
if !isNative {
|
||||
fd := field.Addr().Interface().(Fielder)
|
||||
err := fd.SetRaw(value)
|
||||
if err != nil {
|
||||
@ -1594,7 +1597,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))
|
||||
@ -1733,7 +1736,7 @@ func (d *dbBase) TableQuote() string {
|
||||
return "`"
|
||||
}
|
||||
|
||||
// replace value placeholer in parametered sql string.
|
||||
// replace value placeholder in parametered sql string.
|
||||
func (d *dbBase) ReplaceMarks(query *string) {
|
||||
// default use `?` as mark, do nothing
|
||||
}
|
||||
|
@ -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(),
|
||||
@ -117,7 +119,7 @@ type alias struct {
|
||||
func detectTZ(al *alias) {
|
||||
// orm timezone system match database
|
||||
// default use Local
|
||||
al.TZ = time.Local
|
||||
al.TZ = DefaultTimeLoc
|
||||
|
||||
if al.DriverName == "sphinx" {
|
||||
return
|
||||
@ -134,7 +136,9 @@ func detectTZ(al *alias) {
|
||||
}
|
||||
t, err := time.Parse("-07:00:00", tz)
|
||||
if err == nil {
|
||||
al.TZ = t.Location()
|
||||
if t.Location().String() != "" {
|
||||
al.TZ = t.Location()
|
||||
}
|
||||
} else {
|
||||
DebugLog.Printf("Detect DB timezone: %s %s\n", tz, err.Error())
|
||||
}
|
||||
@ -186,7 +190,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,11 +248,11 @@ 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 {
|
||||
return fmt.Errorf("driverName `%s` db driver already registered and is other type\n", driverName)
|
||||
return fmt.Errorf("driverName `%s` db driver already registered and is other type", driverName)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@ -259,7 +263,7 @@ func SetDataBaseTZ(aliasName string, tz *time.Location) error {
|
||||
if al, ok := dataBaseCache.get(aliasName); ok {
|
||||
al.TZ = tz
|
||||
} else {
|
||||
return fmt.Errorf("DataBase alias name `%s` not registered\n", aliasName)
|
||||
return fmt.Errorf("DataBase alias name `%s` not registered", aliasName)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@ -294,5 +298,5 @@ func GetDB(aliasNames ...string) (*sql.DB, error) {
|
||||
if ok {
|
||||
return al.DB, nil
|
||||
}
|
||||
return nil, fmt.Errorf("DataBase of alias name `%s` not found\n", name)
|
||||
return nil, fmt.Errorf("DataBase of alias name `%s` not found", name)
|
||||
}
|
||||
|
@ -46,6 +46,7 @@ var mysqlTypes = map[string]string{
|
||||
"pk": "NOT NULL PRIMARY KEY",
|
||||
"bool": "bool",
|
||||
"string": "varchar(%d)",
|
||||
"string-char": "char(%d)",
|
||||
"string-text": "longtext",
|
||||
"time.Time-date": "date",
|
||||
"time.Time": "datetime",
|
||||
@ -103,8 +104,7 @@ func (d *dbBaseMysql) IndexExists(db dbQuerier, table string, name string) bool
|
||||
// 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 := ""
|
||||
var iouStr string
|
||||
argsMap := map[string]string{}
|
||||
|
||||
iouStr = "ON DUPLICATE KEY UPDATE"
|
||||
|
@ -34,6 +34,7 @@ var oracleTypes = map[string]string{
|
||||
"pk": "NOT NULL PRIMARY KEY",
|
||||
"bool": "bool",
|
||||
"string": "VARCHAR2(%d)",
|
||||
"string-char": "CHAR(%d)",
|
||||
"string-text": "VARCHAR2(%d)",
|
||||
"time.Time-date": "DATE",
|
||||
"time.Time": "TIMESTAMP",
|
||||
@ -94,3 +95,43 @@ func (d *dbBaseOracle) IndexExists(db dbQuerier, table string, name string) bool
|
||||
row.Scan(&cnt)
|
||||
return cnt > 0
|
||||
}
|
||||
|
||||
// execute insert sql with given struct and given values.
|
||||
// insert the given values, not the field values in struct.
|
||||
func (d *dbBaseOracle) InsertValue(q dbQuerier, mi *modelInfo, isMulti bool, names []string, values []interface{}) (int64, error) {
|
||||
Q := d.ins.TableQuote()
|
||||
|
||||
marks := make([]string, len(names))
|
||||
for i := range marks {
|
||||
marks[i] = ":" + names[i]
|
||||
}
|
||||
|
||||
sep := fmt.Sprintf("%s, %s", Q, Q)
|
||||
qmarks := strings.Join(marks, ", ")
|
||||
columns := strings.Join(names, sep)
|
||||
|
||||
multi := len(values) / len(names)
|
||||
|
||||
if isMulti {
|
||||
qmarks = strings.Repeat(qmarks+"), (", multi-1) + qmarks
|
||||
}
|
||||
|
||||
query := fmt.Sprintf("INSERT INTO %s%s%s (%s%s%s) VALUES (%s)", Q, mi.table, Q, Q, columns, Q, qmarks)
|
||||
|
||||
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
|
||||
}
|
||||
|
@ -43,6 +43,7 @@ var postgresTypes = map[string]string{
|
||||
"pk": "NOT NULL PRIMARY KEY",
|
||||
"bool": "bool",
|
||||
"string": "varchar(%d)",
|
||||
"string-char": "char(%d)",
|
||||
"string-text": "text",
|
||||
"time.Time-date": "date",
|
||||
"time.Time": "timestamp with time zone",
|
||||
|
@ -43,6 +43,7 @@ var sqliteTypes = map[string]string{
|
||||
"pk": "NOT NULL PRIMARY KEY",
|
||||
"bool": "bool",
|
||||
"string": "varchar(%d)",
|
||||
"string-char": "character(%d)",
|
||||
"string-text": "text",
|
||||
"time.Time-date": "date",
|
||||
"time.Time": "datetime",
|
||||
@ -134,7 +135,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)))
|
||||
}
|
||||
|
||||
|
@ -52,7 +52,7 @@ func (mc *_modelCache) all() map[string]*modelInfo {
|
||||
return m
|
||||
}
|
||||
|
||||
// get orderd model info
|
||||
// get ordered model info
|
||||
func (mc *_modelCache) allOrdered() []*modelInfo {
|
||||
m := make([]*modelInfo, 0, len(mc.orders))
|
||||
for _, table := range mc.orders {
|
||||
|
@ -75,7 +75,7 @@ func registerModel(PrefixOrSuffix string, model interface{}, isPrefix bool) {
|
||||
}
|
||||
|
||||
if mi.fields.pk == nil {
|
||||
fmt.Printf("<orm.RegisterModel> `%s` need a primary key field, default use 'id' if not set\n", name)
|
||||
fmt.Printf("<orm.RegisterModel> `%s` needs a primary key field, default is to use 'id' if not set\n", name)
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
@ -89,7 +89,7 @@ func registerModel(PrefixOrSuffix string, model interface{}, isPrefix bool) {
|
||||
modelCache.set(table, mi)
|
||||
}
|
||||
|
||||
// boostrap models
|
||||
// bootstrap models
|
||||
func bootStrap() {
|
||||
if modelCache.done {
|
||||
return
|
||||
@ -128,7 +128,7 @@ func bootStrap() {
|
||||
if i := strings.LastIndex(fi.relThrough, "."); i != -1 && len(fi.relThrough) > (i+1) {
|
||||
pn := fi.relThrough[:i]
|
||||
rmi, ok := modelCache.getByFullName(fi.relThrough)
|
||||
if ok == false || pn != rmi.pkg {
|
||||
if !ok || pn != rmi.pkg {
|
||||
err = fmt.Errorf("field `%s` wrong rel_through value `%s` cannot find table", fi.fullName, fi.relThrough)
|
||||
goto end
|
||||
}
|
||||
@ -171,7 +171,7 @@ func bootStrap() {
|
||||
break
|
||||
}
|
||||
}
|
||||
if inModel == false {
|
||||
if !inModel {
|
||||
rmi := fi.relModelInfo
|
||||
ffi := new(fieldInfo)
|
||||
ffi.name = mi.name
|
||||
@ -185,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)
|
||||
@ -195,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))
|
||||
}
|
||||
}
|
||||
@ -248,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
|
||||
}
|
||||
@ -267,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 ||
|
||||
@ -287,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
|
||||
}
|
||||
@ -332,7 +332,7 @@ func RegisterModelWithSuffix(suffix string, models ...interface{}) {
|
||||
}
|
||||
}
|
||||
|
||||
// BootStrap bootrap models.
|
||||
// BootStrap bootstrap models.
|
||||
// make all model parsed and can not add more models
|
||||
func BootStrap() {
|
||||
if modelCache.done {
|
||||
|
@ -23,6 +23,7 @@ import (
|
||||
// Define the Type enum
|
||||
const (
|
||||
TypeBooleanField = 1 << iota
|
||||
TypeVarCharField
|
||||
TypeCharField
|
||||
TypeTextField
|
||||
TypeTimeField
|
||||
@ -49,9 +50,9 @@ const (
|
||||
|
||||
// Define some logic enum
|
||||
const (
|
||||
IsIntegerField = ^-TypePositiveBigIntegerField >> 5 << 6
|
||||
IsPositiveIntegerField = ^-TypePositiveBigIntegerField >> 9 << 10
|
||||
IsRelField = ^-RelReverseMany >> 17 << 18
|
||||
IsIntegerField = ^-TypePositiveBigIntegerField >> 6 << 7
|
||||
IsPositiveIntegerField = ^-TypePositiveBigIntegerField >> 10 << 11
|
||||
IsRelField = ^-RelReverseMany >> 18 << 19
|
||||
IsFieldType = ^-RelReverseMany<<1 + 1
|
||||
)
|
||||
|
||||
@ -126,7 +127,7 @@ func (e *CharField) String() string {
|
||||
|
||||
// FieldType return the enum type
|
||||
func (e *CharField) FieldType() int {
|
||||
return TypeCharField
|
||||
return TypeVarCharField
|
||||
}
|
||||
|
||||
// SetRaw set the interface to string
|
||||
@ -232,7 +233,7 @@ func (e *DateField) Set(d time.Time) {
|
||||
*e = DateField(d)
|
||||
}
|
||||
|
||||
// String convert datatime to string
|
||||
// String convert datetime to string
|
||||
func (e *DateField) String() string {
|
||||
return e.Value().String()
|
||||
}
|
||||
@ -272,12 +273,12 @@ var _ Fielder = new(DateField)
|
||||
// Takes the same extra arguments as DateField.
|
||||
type DateTimeField time.Time
|
||||
|
||||
// Value return the datatime value
|
||||
// Value return the datetime value
|
||||
func (e DateTimeField) Value() time.Time {
|
||||
return time.Time(e)
|
||||
}
|
||||
|
||||
// Set set the time.Time to datatime
|
||||
// Set set the time.Time to datetime
|
||||
func (e *DateTimeField) Set(d time.Time) {
|
||||
*e = DateTimeField(d)
|
||||
}
|
||||
@ -309,12 +310,12 @@ func (e *DateTimeField) SetRaw(value interface{}) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// RawValue return the datatime value
|
||||
// RawValue return the datetime value
|
||||
func (e *DateTimeField) RawValue() interface{} {
|
||||
return e.Value()
|
||||
}
|
||||
|
||||
// verify datatime implement fielder
|
||||
// verify datetime implement fielder
|
||||
var _ Fielder = new(DateTimeField)
|
||||
|
||||
// FloatField A floating-point number represented in go by a float32 value.
|
||||
|
@ -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)
|
||||
@ -244,8 +244,10 @@ checkType:
|
||||
if err != nil {
|
||||
goto end
|
||||
}
|
||||
if fieldType == TypeCharField {
|
||||
if fieldType == TypeVarCharField {
|
||||
switch tags["type"] {
|
||||
case "char":
|
||||
fieldType = TypeCharField
|
||||
case "text":
|
||||
fieldType = TypeTextField
|
||||
case "json":
|
||||
@ -334,12 +336,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
|
||||
}
|
||||
@ -357,7 +359,7 @@ checkType:
|
||||
|
||||
switch fieldType {
|
||||
case TypeBooleanField:
|
||||
case TypeCharField, TypeJSONField, TypeJsonbField:
|
||||
case TypeVarCharField, TypeCharField, TypeJSONField, TypeJsonbField:
|
||||
if size != "" {
|
||||
v, e := StrTo(size).Int32()
|
||||
if e != nil {
|
||||
|
@ -78,7 +78,7 @@ func addModelFields(mi *modelInfo, ind reflect.Value, mName string, index []int)
|
||||
fi.fieldIndex = append(index, i)
|
||||
fi.mi = mi
|
||||
fi.inModel = true
|
||||
if mi.fields.Add(fi) == false {
|
||||
if !mi.fields.Add(fi) {
|
||||
err = fmt.Errorf("duplicate column name: %s", fi.column)
|
||||
break
|
||||
}
|
||||
|
@ -49,7 +49,7 @@ func (e *SliceStringField) String() string {
|
||||
}
|
||||
|
||||
func (e *SliceStringField) FieldType() int {
|
||||
return TypeCharField
|
||||
return TypeVarCharField
|
||||
}
|
||||
|
||||
func (e *SliceStringField) SetRaw(value interface{}) error {
|
||||
|
@ -149,7 +149,7 @@ func getFieldType(val reflect.Value) (ft int, err error) {
|
||||
case reflect.TypeOf(new(bool)):
|
||||
ft = TypeBooleanField
|
||||
case reflect.TypeOf(new(string)):
|
||||
ft = TypeCharField
|
||||
ft = TypeVarCharField
|
||||
case reflect.TypeOf(new(time.Time)):
|
||||
ft = TypeDateTimeField
|
||||
default:
|
||||
@ -176,7 +176,7 @@ func getFieldType(val reflect.Value) (ft int, err error) {
|
||||
case reflect.Bool:
|
||||
ft = TypeBooleanField
|
||||
case reflect.String:
|
||||
ft = TypeCharField
|
||||
ft = TypeVarCharField
|
||||
default:
|
||||
if elm.Interface() == nil {
|
||||
panic(fmt.Errorf("%s is nil pointer, may be miss setting tag", val))
|
||||
@ -189,7 +189,7 @@ func getFieldType(val reflect.Value) (ft int, err error) {
|
||||
case sql.NullBool:
|
||||
ft = TypeBooleanField
|
||||
case sql.NullString:
|
||||
ft = TypeCharField
|
||||
ft = TypeVarCharField
|
||||
case time.Time:
|
||||
ft = TypeDateTimeField
|
||||
}
|
||||
|
30
orm/orm.go
30
orm/orm.go
@ -107,7 +107,7 @@ func (o *orm) getMiInd(md interface{}, needPtr bool) (mi *modelInfo, ind reflect
|
||||
if mi, ok := modelCache.getByFullName(name); ok {
|
||||
return mi, ind
|
||||
}
|
||||
panic(fmt.Errorf("<Ormer> table: `%s` not found, maybe not RegisterModel", name))
|
||||
panic(fmt.Errorf("<Ormer> table: `%s` not found, make sure it was registered with `RegisterModel()`", name))
|
||||
}
|
||||
|
||||
// get field info from model info by given field name
|
||||
@ -122,21 +122,13 @@ 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, false)
|
||||
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)
|
||||
err := o.alias.DbBaser.Read(o.db, mi, ind, o.alias.TZ, cols, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
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
|
||||
@ -238,15 +230,11 @@ 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
|
||||
// cols shows the delete conditions values read from. deafult is pk
|
||||
// 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, cols)
|
||||
@ -361,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)
|
||||
}
|
||||
|
||||
@ -432,7 +420,7 @@ func (o *orm) getRelQs(md interface{}, mi *modelInfo, fi *fieldInfo) *querySet {
|
||||
// table name can be string or struct.
|
||||
// e.g. QueryTable("user"), QueryTable(&user{}) or QueryTable((*User)(nil)),
|
||||
func (o *orm) QueryTable(ptrStructOrTableName interface{}) (qs QuerySeter) {
|
||||
name := ""
|
||||
var name string
|
||||
if table, ok := ptrStructOrTableName.(string); ok {
|
||||
name = snakeString(table)
|
||||
if mi, ok := modelCache.get(name); ok {
|
||||
@ -489,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()
|
||||
@ -504,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()
|
||||
|
@ -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
|
||||
|
@ -493,19 +493,33 @@ func (o *rawSet) QueryRows(containers ...interface{}) (int64, error) {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for i := 0; i < ind.NumField(); i++ {
|
||||
f := ind.Field(i)
|
||||
fe := ind.Type().Field(i)
|
||||
_, tags := parseStructTag(fe.Tag.Get(defaultStructTagName))
|
||||
var col string
|
||||
if col = tags["column"]; col == "" {
|
||||
col = snakeString(fe.Name)
|
||||
}
|
||||
if v, ok := columnsMp[col]; ok {
|
||||
value := reflect.ValueOf(v).Elem().Interface()
|
||||
o.setFieldValue(f, value)
|
||||
// define recursive function
|
||||
var recursiveSetField func(rv reflect.Value)
|
||||
recursiveSetField = func(rv reflect.Value) {
|
||||
for i := 0; i < rv.NumField(); i++ {
|
||||
f := rv.Field(i)
|
||||
fe := rv.Type().Field(i)
|
||||
|
||||
// check if the field is a Struct
|
||||
// recursive the Struct type
|
||||
if fe.Type.Kind() == reflect.Struct {
|
||||
recursiveSetField(f)
|
||||
}
|
||||
|
||||
_, tags := parseStructTag(fe.Tag.Get(defaultStructTagName))
|
||||
var col string
|
||||
if col = tags["column"]; col == "" {
|
||||
col = snakeString(fe.Name)
|
||||
}
|
||||
if v, ok := columnsMp[col]; ok {
|
||||
value := reflect.ValueOf(v).Elem().Interface()
|
||||
o.setFieldValue(f, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// init call the recursive function
|
||||
recursiveSetField(ind)
|
||||
}
|
||||
|
||||
if eTyps[0].Kind() == reflect.Ptr {
|
||||
@ -671,7 +685,7 @@ func (o *rawSet) queryRowsTo(container interface{}, keyCol, valueCol string) (in
|
||||
ind *reflect.Value
|
||||
)
|
||||
|
||||
typ := 0
|
||||
var typ int
|
||||
switch container.(type) {
|
||||
case *Params:
|
||||
typ = 1
|
||||
|
@ -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
|
||||
@ -135,7 +135,7 @@ func getCaller(skip int) string {
|
||||
if i := strings.LastIndex(funName, "."); i > -1 {
|
||||
funName = funName[i+1:]
|
||||
}
|
||||
return fmt.Sprintf("%s:%d: \n%s", fn, line, strings.Join(codes, "\n"))
|
||||
return fmt.Sprintf("%s:%s:%d: \n%s", fn, funName, line, strings.Join(codes, "\n"))
|
||||
}
|
||||
|
||||
func throwFail(t *testing.T, err error, args ...interface{}) {
|
||||
@ -1014,6 +1014,8 @@ func TestAll(t *testing.T) {
|
||||
var users3 []*User
|
||||
qs = dORM.QueryTable("user")
|
||||
num, err = qs.Filter("user_name", "nothing").All(&users3)
|
||||
throwFailNow(t, err)
|
||||
throwFailNow(t, AssertIs(num, 0))
|
||||
throwFailNow(t, AssertIs(users3 == nil, false))
|
||||
}
|
||||
|
||||
@ -1138,6 +1140,7 @@ func TestRelatedSel(t *testing.T) {
|
||||
}
|
||||
|
||||
err = qs.Filter("user_name", "nobody").RelatedSel("profile").One(&user)
|
||||
throwFail(t, err)
|
||||
throwFail(t, AssertIs(num, 1))
|
||||
throwFail(t, AssertIs(user.Profile, nil))
|
||||
|
||||
@ -1246,20 +1249,24 @@ func TestLoadRelated(t *testing.T) {
|
||||
|
||||
num, err = dORM.LoadRelated(&user, "Posts", true)
|
||||
throwFailNow(t, err)
|
||||
throwFailNow(t, AssertIs(num, 2))
|
||||
throwFailNow(t, AssertIs(len(user.Posts), 2))
|
||||
throwFailNow(t, AssertIs(user.Posts[0].User.UserName, "astaxie"))
|
||||
|
||||
num, err = dORM.LoadRelated(&user, "Posts", true, 1)
|
||||
throwFailNow(t, err)
|
||||
throwFailNow(t, AssertIs(num, 1))
|
||||
throwFailNow(t, AssertIs(len(user.Posts), 1))
|
||||
|
||||
num, err = dORM.LoadRelated(&user, "Posts", true, 0, 0, "-Id")
|
||||
throwFailNow(t, err)
|
||||
throwFailNow(t, AssertIs(num, 2))
|
||||
throwFailNow(t, AssertIs(len(user.Posts), 2))
|
||||
throwFailNow(t, AssertIs(user.Posts[0].Title, "Formatting"))
|
||||
|
||||
num, err = dORM.LoadRelated(&user, "Posts", true, 1, 1, "Id")
|
||||
throwFailNow(t, err)
|
||||
throwFailNow(t, AssertIs(num, 1))
|
||||
throwFailNow(t, AssertIs(len(user.Posts), 1))
|
||||
throwFailNow(t, AssertIs(user.Posts[0].Title, "Formatting"))
|
||||
|
||||
@ -1654,6 +1661,13 @@ func TestRawQueryRow(t *testing.T) {
|
||||
throwFail(t, AssertIs(pid, nil))
|
||||
}
|
||||
|
||||
// user_profile table
|
||||
type userProfile struct {
|
||||
User
|
||||
Age int
|
||||
Money float64
|
||||
}
|
||||
|
||||
func TestQueryRows(t *testing.T) {
|
||||
Q := dDbBaser.TableQuote()
|
||||
|
||||
@ -1724,6 +1738,19 @@ func TestQueryRows(t *testing.T) {
|
||||
throwFailNow(t, AssertIs(usernames[1], "astaxie"))
|
||||
throwFailNow(t, AssertIs(ids[2], 4))
|
||||
throwFailNow(t, AssertIs(usernames[2], "nobody"))
|
||||
|
||||
//test query rows by nested struct
|
||||
var l []userProfile
|
||||
query = fmt.Sprintf("SELECT * FROM %suser_profile%s LEFT JOIN %suser%s ON %suser_profile%s.%sid%s = %suser%s.%sid%s", Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, Q)
|
||||
num, err = dORM.Raw(query).QueryRows(&l)
|
||||
throwFailNow(t, err)
|
||||
throwFailNow(t, AssertIs(num, 2))
|
||||
throwFailNow(t, AssertIs(len(l), 2))
|
||||
throwFailNow(t, AssertIs(l[0].UserName, "slene"))
|
||||
throwFailNow(t, AssertIs(l[0].Age, 28))
|
||||
throwFailNow(t, AssertIs(l[1].UserName, "astaxie"))
|
||||
throwFailNow(t, AssertIs(l[1].Age, 30))
|
||||
|
||||
}
|
||||
|
||||
func TestRawValues(t *testing.T) {
|
||||
@ -1976,6 +2003,7 @@ func TestReadOrCreate(t *testing.T) {
|
||||
created, pk, err := dORM.ReadOrCreate(u, "UserName")
|
||||
throwFail(t, err)
|
||||
throwFail(t, AssertIs(created, true))
|
||||
throwFail(t, AssertIs(u.ID, pk))
|
||||
throwFail(t, AssertIs(u.UserName, "Kyle"))
|
||||
throwFail(t, AssertIs(u.Email, "kylemcc@gmail.com"))
|
||||
throwFail(t, AssertIs(u.Password, "other_pass"))
|
||||
@ -2130,13 +2158,13 @@ func TestUintPk(t *testing.T) {
|
||||
Name: name,
|
||||
}
|
||||
|
||||
created, pk, err := dORM.ReadOrCreate(u, "ID")
|
||||
created, _, err := dORM.ReadOrCreate(u, "ID")
|
||||
throwFail(t, err)
|
||||
throwFail(t, AssertIs(created, true))
|
||||
throwFail(t, AssertIs(u.Name, name))
|
||||
|
||||
nu := &UintPk{ID: 8}
|
||||
created, pk, err = dORM.ReadOrCreate(nu, "ID")
|
||||
created, pk, err := dORM.ReadOrCreate(nu, "ID")
|
||||
throwFail(t, err)
|
||||
throwFail(t, AssertIs(created, false))
|
||||
throwFail(t, AssertIs(nu.ID, u.ID))
|
||||
|
10
orm/utils.go
10
orm/utils.go
@ -92,11 +92,11 @@ func (f StrTo) Int64() (int64, error) {
|
||||
i := new(big.Int)
|
||||
ni, ok := i.SetString(f.String(), 10) // octal
|
||||
if !ok {
|
||||
return int64(v), err
|
||||
return v, err
|
||||
}
|
||||
return ni.Int64(), nil
|
||||
}
|
||||
return int64(v), err
|
||||
return v, err
|
||||
}
|
||||
|
||||
// Uint string to uint
|
||||
@ -130,11 +130,11 @@ func (f StrTo) Uint64() (uint64, error) {
|
||||
i := new(big.Int)
|
||||
ni, ok := i.SetString(f.String(), 10)
|
||||
if !ok {
|
||||
return uint64(v), err
|
||||
return v, err
|
||||
}
|
||||
return ni.Uint64(), nil
|
||||
}
|
||||
return uint64(v), err
|
||||
return v, err
|
||||
}
|
||||
|
||||
// String string to string
|
||||
@ -225,7 +225,7 @@ func camelString(s string) string {
|
||||
if d == '_' {
|
||||
flag = true
|
||||
continue
|
||||
} else if flag == true {
|
||||
} else if flag {
|
||||
if d >= 'a' && d <= 'z' {
|
||||
d = d - 32
|
||||
}
|
||||
|
212
parser.go
212
parser.go
@ -24,9 +24,13 @@ import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"github.com/astaxie/beego/context/param"
|
||||
"github.com/astaxie/beego/logs"
|
||||
"github.com/astaxie/beego/utils"
|
||||
)
|
||||
@ -35,6 +39,7 @@ var globalRouterTemplate = `package routers
|
||||
|
||||
import (
|
||||
"github.com/astaxie/beego"
|
||||
"github.com/astaxie/beego/context/param"
|
||||
)
|
||||
|
||||
func init() {
|
||||
@ -81,7 +86,7 @@ func parserPkg(pkgRealpath, pkgpath string) error {
|
||||
if specDecl.Recv != nil {
|
||||
exp, ok := specDecl.Recv.List[0].Type.(*ast.StarExpr) // Check that the type is correct first beforing throwing to parser
|
||||
if ok {
|
||||
parserComments(specDecl.Doc, specDecl.Name.String(), fmt.Sprint(exp.X), pkgpath)
|
||||
parserComments(specDecl, fmt.Sprint(exp.X), pkgpath)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -93,44 +98,170 @@ func parserPkg(pkgRealpath, pkgpath string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func parserComments(comments *ast.CommentGroup, funcName, controllerName, pkgpath string) error {
|
||||
if comments != nil && comments.List != nil {
|
||||
for _, c := range comments.List {
|
||||
t := strings.TrimSpace(strings.TrimLeft(c.Text, "//"))
|
||||
if strings.HasPrefix(t, "@router") {
|
||||
elements := strings.TrimLeft(t, "@router ")
|
||||
e1 := strings.SplitN(elements, " ", 2)
|
||||
if len(e1) < 1 {
|
||||
return errors.New("you should has router information")
|
||||
}
|
||||
key := pkgpath + ":" + controllerName
|
||||
cc := ControllerComments{}
|
||||
cc.Method = funcName
|
||||
cc.Router = e1[0]
|
||||
if len(e1) == 2 && e1[1] != "" {
|
||||
e1 = strings.SplitN(e1[1], " ", 2)
|
||||
if len(e1) >= 1 {
|
||||
cc.AllowHTTPMethods = strings.Split(strings.Trim(e1[0], "[]"), ",")
|
||||
} else {
|
||||
cc.AllowHTTPMethods = append(cc.AllowHTTPMethods, "get")
|
||||
}
|
||||
} else {
|
||||
cc.AllowHTTPMethods = append(cc.AllowHTTPMethods, "get")
|
||||
}
|
||||
if len(e1) == 2 && e1[1] != "" {
|
||||
keyval := strings.Split(strings.Trim(e1[1], "[]"), " ")
|
||||
for _, kv := range keyval {
|
||||
kk := strings.Split(kv, ":")
|
||||
cc.Params = append(cc.Params, map[string]string{strings.Join(kk[:len(kk)-1], ":"): kk[len(kk)-1]})
|
||||
}
|
||||
}
|
||||
genInfoList[key] = append(genInfoList[key], cc)
|
||||
}
|
||||
type parsedComment struct {
|
||||
routerPath string
|
||||
methods []string
|
||||
params map[string]parsedParam
|
||||
}
|
||||
|
||||
type parsedParam struct {
|
||||
name string
|
||||
datatype string
|
||||
location string
|
||||
defValue string
|
||||
required bool
|
||||
}
|
||||
|
||||
func parserComments(f *ast.FuncDecl, controllerName, pkgpath string) error {
|
||||
if f.Doc != nil {
|
||||
parsedComment, err := parseComment(f.Doc.List)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if parsedComment.routerPath != "" {
|
||||
key := pkgpath + ":" + controllerName
|
||||
cc := ControllerComments{}
|
||||
cc.Method = f.Name.String()
|
||||
cc.Router = parsedComment.routerPath
|
||||
cc.AllowHTTPMethods = parsedComment.methods
|
||||
cc.MethodParams = buildMethodParams(f.Type.Params.List, parsedComment)
|
||||
genInfoList[key] = append(genInfoList[key], cc)
|
||||
}
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func buildMethodParams(funcParams []*ast.Field, pc *parsedComment) []*param.MethodParam {
|
||||
result := make([]*param.MethodParam, 0, len(funcParams))
|
||||
for _, fparam := range funcParams {
|
||||
for _, pName := range fparam.Names {
|
||||
methodParam := buildMethodParam(fparam, pName.Name, pc)
|
||||
result = append(result, methodParam)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func buildMethodParam(fparam *ast.Field, name string, pc *parsedComment) *param.MethodParam {
|
||||
options := []param.MethodParamOption{}
|
||||
if cparam, ok := pc.params[name]; ok {
|
||||
//Build param from comment info
|
||||
name = cparam.name
|
||||
if cparam.required {
|
||||
options = append(options, param.IsRequired)
|
||||
}
|
||||
switch cparam.location {
|
||||
case "body":
|
||||
options = append(options, param.InBody)
|
||||
case "header":
|
||||
options = append(options, param.InHeader)
|
||||
case "path":
|
||||
options = append(options, param.InPath)
|
||||
}
|
||||
if cparam.defValue != "" {
|
||||
options = append(options, param.Default(cparam.defValue))
|
||||
}
|
||||
} else {
|
||||
if paramInPath(name, pc.routerPath) {
|
||||
options = append(options, param.InPath)
|
||||
}
|
||||
}
|
||||
return param.New(name, options...)
|
||||
}
|
||||
|
||||
func paramInPath(name, route string) bool {
|
||||
return strings.HasSuffix(route, ":"+name) ||
|
||||
strings.Contains(route, ":"+name+"/")
|
||||
}
|
||||
|
||||
var routeRegex = regexp.MustCompile(`@router\s+(\S+)(?:\s+\[(\S+)\])?`)
|
||||
|
||||
func parseComment(lines []*ast.Comment) (pc *parsedComment, err error) {
|
||||
pc = &parsedComment{}
|
||||
for _, c := range lines {
|
||||
t := strings.TrimSpace(strings.TrimLeft(c.Text, "//"))
|
||||
if strings.HasPrefix(t, "@router") {
|
||||
matches := routeRegex.FindStringSubmatch(t)
|
||||
if len(matches) == 3 {
|
||||
pc.routerPath = matches[1]
|
||||
methods := matches[2]
|
||||
if methods == "" {
|
||||
pc.methods = []string{"get"}
|
||||
//pc.hasGet = true
|
||||
} else {
|
||||
pc.methods = strings.Split(methods, ",")
|
||||
//pc.hasGet = strings.Contains(methods, "get")
|
||||
}
|
||||
} else {
|
||||
return nil, errors.New("Router information is missing")
|
||||
}
|
||||
} else if strings.HasPrefix(t, "@Param") {
|
||||
pv := getparams(strings.TrimSpace(strings.TrimLeft(t, "@Param")))
|
||||
if len(pv) < 4 {
|
||||
logs.Error("Invalid @Param format. Needs at least 4 parameters")
|
||||
}
|
||||
p := parsedParam{}
|
||||
names := strings.SplitN(pv[0], "=>", 2)
|
||||
p.name = names[0]
|
||||
funcParamName := p.name
|
||||
if len(names) > 1 {
|
||||
funcParamName = names[1]
|
||||
}
|
||||
p.location = pv[1]
|
||||
p.datatype = pv[2]
|
||||
switch len(pv) {
|
||||
case 5:
|
||||
p.required, _ = strconv.ParseBool(pv[3])
|
||||
case 6:
|
||||
p.defValue = pv[3]
|
||||
p.required, _ = strconv.ParseBool(pv[4])
|
||||
}
|
||||
if pc.params == nil {
|
||||
pc.params = map[string]parsedParam{}
|
||||
}
|
||||
pc.params[funcParamName] = p
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// direct copy from bee\g_docs.go
|
||||
// analysis params return []string
|
||||
// @Param query form string true "The email for login"
|
||||
// [query form string true "The email for login"]
|
||||
func getparams(str string) []string {
|
||||
var s []rune
|
||||
var j int
|
||||
var start bool
|
||||
var r []string
|
||||
var quoted int8
|
||||
for _, c := range str {
|
||||
if unicode.IsSpace(c) && quoted == 0 {
|
||||
if !start {
|
||||
continue
|
||||
} else {
|
||||
start = false
|
||||
j++
|
||||
r = append(r, string(s))
|
||||
s = make([]rune, 0)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
start = true
|
||||
if c == '"' {
|
||||
quoted ^= 1
|
||||
continue
|
||||
}
|
||||
s = append(s, c)
|
||||
}
|
||||
if len(s) > 0 {
|
||||
r = append(r, string(s))
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func genRouterCode(pkgRealpath string) {
|
||||
os.Mkdir(getRouterDir(pkgRealpath), 0755)
|
||||
logs.Info("generate router from comments")
|
||||
@ -144,6 +275,7 @@ func genRouterCode(pkgRealpath string) {
|
||||
sort.Strings(sortKey)
|
||||
for _, k := range sortKey {
|
||||
cList := genInfoList[k]
|
||||
sort.Sort(ControllerCommentsSlice(cList))
|
||||
for _, c := range cList {
|
||||
allmethod := "nil"
|
||||
if len(c.AllowHTTPMethods) > 0 {
|
||||
@ -163,12 +295,24 @@ func genRouterCode(pkgRealpath string) {
|
||||
}
|
||||
params = strings.TrimRight(params, ",") + "}"
|
||||
}
|
||||
methodParams := "param.Make("
|
||||
if len(c.MethodParams) > 0 {
|
||||
lines := make([]string, 0, len(c.MethodParams))
|
||||
for _, m := range c.MethodParams {
|
||||
lines = append(lines, fmt.Sprint(m))
|
||||
}
|
||||
methodParams += "\n " +
|
||||
strings.Join(lines, ",\n ") +
|
||||
",\n "
|
||||
}
|
||||
methodParams += ")"
|
||||
globalinfo = globalinfo + `
|
||||
beego.GlobalControllerRouter["` + k + `"] = append(beego.GlobalControllerRouter["` + k + `"],
|
||||
beego.ControllerComments{
|
||||
Method: "` + strings.TrimSpace(c.Method) + `",
|
||||
` + "Router: `" + c.Router + "`" + `,
|
||||
AllowHTTPMethods: ` + allmethod + `,
|
||||
MethodParams: ` + methodParams + `,
|
||||
Params: ` + params + `})
|
||||
`
|
||||
}
|
||||
|
86
plugins/authz/authz.go
Normal file
86
plugins/authz/authz.go
Normal file
@ -0,0 +1,86 @@
|
||||
// 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 authz provides handlers to enable ACL, RBAC, ABAC authorization support.
|
||||
// Simple Usage:
|
||||
// import(
|
||||
// "github.com/astaxie/beego"
|
||||
// "github.com/astaxie/beego/plugins/authz"
|
||||
// "github.com/casbin/casbin"
|
||||
// )
|
||||
//
|
||||
// func main(){
|
||||
// // mediate the access for every request
|
||||
// beego.InsertFilter("*", beego.BeforeRouter, authz.NewAuthorizer(casbin.NewEnforcer("authz_model.conf", "authz_policy.csv")))
|
||||
// beego.Run()
|
||||
// }
|
||||
//
|
||||
//
|
||||
// Advanced Usage:
|
||||
//
|
||||
// func main(){
|
||||
// e := casbin.NewEnforcer("authz_model.conf", "")
|
||||
// e.AddRoleForUser("alice", "admin")
|
||||
// e.AddPolicy(...)
|
||||
//
|
||||
// beego.InsertFilter("*", beego.BeforeRouter, authz.NewAuthorizer(e))
|
||||
// beego.Run()
|
||||
// }
|
||||
package authz
|
||||
|
||||
import (
|
||||
"github.com/astaxie/beego"
|
||||
"github.com/astaxie/beego/context"
|
||||
"github.com/casbin/casbin"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// NewAuthorizer returns the authorizer.
|
||||
// Use a casbin enforcer as input
|
||||
func NewAuthorizer(e *casbin.Enforcer) beego.FilterFunc {
|
||||
return func(ctx *context.Context) {
|
||||
a := &BasicAuthorizer{enforcer: e}
|
||||
|
||||
if !a.CheckPermission(ctx.Request) {
|
||||
a.RequirePermission(ctx.ResponseWriter)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// BasicAuthorizer stores the casbin handler
|
||||
type BasicAuthorizer struct {
|
||||
enforcer *casbin.Enforcer
|
||||
}
|
||||
|
||||
// GetUserName gets the user name from the request.
|
||||
// Currently, only HTTP basic authentication is supported
|
||||
func (a *BasicAuthorizer) GetUserName(r *http.Request) string {
|
||||
username, _, _ := r.BasicAuth()
|
||||
return username
|
||||
}
|
||||
|
||||
// CheckPermission checks the user/method/path combination from the request.
|
||||
// Returns true (permission granted) or false (permission forbidden)
|
||||
func (a *BasicAuthorizer) CheckPermission(r *http.Request) bool {
|
||||
user := a.GetUserName(r)
|
||||
method := r.Method
|
||||
path := r.URL.Path
|
||||
return a.enforcer.Enforce(user, path, method)
|
||||
}
|
||||
|
||||
// RequirePermission returns the 403 Forbidden to the client
|
||||
func (a *BasicAuthorizer) RequirePermission(w http.ResponseWriter) {
|
||||
w.WriteHeader(403)
|
||||
w.Write([]byte("403 Forbidden\n"))
|
||||
}
|
14
plugins/authz/authz_model.conf
Normal file
14
plugins/authz/authz_model.conf
Normal file
@ -0,0 +1,14 @@
|
||||
[request_definition]
|
||||
r = sub, obj, act
|
||||
|
||||
[policy_definition]
|
||||
p = sub, obj, act
|
||||
|
||||
[role_definition]
|
||||
g = _, _
|
||||
|
||||
[policy_effect]
|
||||
e = some(where (p.eft == allow))
|
||||
|
||||
[matchers]
|
||||
m = g(r.sub, p.sub) && keyMatch(r.obj, p.obj) && (r.act == p.act || p.act == "*")
|
7
plugins/authz/authz_policy.csv
Normal file
7
plugins/authz/authz_policy.csv
Normal file
@ -0,0 +1,7 @@
|
||||
p, alice, /dataset1/*, GET
|
||||
p, alice, /dataset1/resource1, POST
|
||||
p, bob, /dataset2/resource1, *
|
||||
p, bob, /dataset2/resource2, GET
|
||||
p, bob, /dataset2/folder1/*, POST
|
||||
p, dataset1_admin, /dataset1/*, *
|
||||
g, cathy, dataset1_admin
|
|
107
plugins/authz/authz_test.go
Normal file
107
plugins/authz/authz_test.go
Normal file
@ -0,0 +1,107 @@
|
||||
// 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 authz
|
||||
|
||||
import (
|
||||
"github.com/astaxie/beego"
|
||||
"github.com/astaxie/beego/context"
|
||||
"github.com/astaxie/beego/plugins/auth"
|
||||
"github.com/casbin/casbin"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func testRequest(t *testing.T, handler *beego.ControllerRegister, user string, path string, method string, code int) {
|
||||
r, _ := http.NewRequest(method, path, nil)
|
||||
r.SetBasicAuth(user, "123")
|
||||
w := httptest.NewRecorder()
|
||||
handler.ServeHTTP(w, r)
|
||||
|
||||
if w.Code != code {
|
||||
t.Errorf("%s, %s, %s: %d, supposed to be %d", user, path, method, w.Code, code)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBasic(t *testing.T) {
|
||||
handler := beego.NewControllerRegister()
|
||||
|
||||
handler.InsertFilter("*", beego.BeforeRouter, auth.Basic("alice", "123"))
|
||||
handler.InsertFilter("*", beego.BeforeRouter, NewAuthorizer(casbin.NewEnforcer("authz_model.conf", "authz_policy.csv")))
|
||||
|
||||
handler.Any("*", func(ctx *context.Context) {
|
||||
ctx.Output.SetStatus(200)
|
||||
})
|
||||
|
||||
testRequest(t, handler, "alice", "/dataset1/resource1", "GET", 200)
|
||||
testRequest(t, handler, "alice", "/dataset1/resource1", "POST", 200)
|
||||
testRequest(t, handler, "alice", "/dataset1/resource2", "GET", 200)
|
||||
testRequest(t, handler, "alice", "/dataset1/resource2", "POST", 403)
|
||||
}
|
||||
|
||||
func TestPathWildcard(t *testing.T) {
|
||||
handler := beego.NewControllerRegister()
|
||||
|
||||
handler.InsertFilter("*", beego.BeforeRouter, auth.Basic("bob", "123"))
|
||||
handler.InsertFilter("*", beego.BeforeRouter, NewAuthorizer(casbin.NewEnforcer("authz_model.conf", "authz_policy.csv")))
|
||||
|
||||
handler.Any("*", func(ctx *context.Context) {
|
||||
ctx.Output.SetStatus(200)
|
||||
})
|
||||
|
||||
testRequest(t, handler, "bob", "/dataset2/resource1", "GET", 200)
|
||||
testRequest(t, handler, "bob", "/dataset2/resource1", "POST", 200)
|
||||
testRequest(t, handler, "bob", "/dataset2/resource1", "DELETE", 200)
|
||||
testRequest(t, handler, "bob", "/dataset2/resource2", "GET", 200)
|
||||
testRequest(t, handler, "bob", "/dataset2/resource2", "POST", 403)
|
||||
testRequest(t, handler, "bob", "/dataset2/resource2", "DELETE", 403)
|
||||
|
||||
testRequest(t, handler, "bob", "/dataset2/folder1/item1", "GET", 403)
|
||||
testRequest(t, handler, "bob", "/dataset2/folder1/item1", "POST", 200)
|
||||
testRequest(t, handler, "bob", "/dataset2/folder1/item1", "DELETE", 403)
|
||||
testRequest(t, handler, "bob", "/dataset2/folder1/item2", "GET", 403)
|
||||
testRequest(t, handler, "bob", "/dataset2/folder1/item2", "POST", 200)
|
||||
testRequest(t, handler, "bob", "/dataset2/folder1/item2", "DELETE", 403)
|
||||
}
|
||||
|
||||
func TestRBAC(t *testing.T) {
|
||||
handler := beego.NewControllerRegister()
|
||||
|
||||
handler.InsertFilter("*", beego.BeforeRouter, auth.Basic("cathy", "123"))
|
||||
e := casbin.NewEnforcer("authz_model.conf", "authz_policy.csv")
|
||||
handler.InsertFilter("*", beego.BeforeRouter, NewAuthorizer(e))
|
||||
|
||||
handler.Any("*", func(ctx *context.Context) {
|
||||
ctx.Output.SetStatus(200)
|
||||
})
|
||||
|
||||
// cathy can access all /dataset1/* resources via all methods because it has the dataset1_admin role.
|
||||
testRequest(t, handler, "cathy", "/dataset1/item", "GET", 200)
|
||||
testRequest(t, handler, "cathy", "/dataset1/item", "POST", 200)
|
||||
testRequest(t, handler, "cathy", "/dataset1/item", "DELETE", 200)
|
||||
testRequest(t, handler, "cathy", "/dataset2/item", "GET", 403)
|
||||
testRequest(t, handler, "cathy", "/dataset2/item", "POST", 403)
|
||||
testRequest(t, handler, "cathy", "/dataset2/item", "DELETE", 403)
|
||||
|
||||
// delete all roles on user cathy, so cathy cannot access any resources now.
|
||||
e.DeleteRolesForUser("cathy")
|
||||
|
||||
testRequest(t, handler, "cathy", "/dataset1/item", "GET", 403)
|
||||
testRequest(t, handler, "cathy", "/dataset1/item", "POST", 403)
|
||||
testRequest(t, handler, "cathy", "/dataset1/item", "DELETE", 403)
|
||||
testRequest(t, handler, "cathy", "/dataset2/item", "GET", 403)
|
||||
testRequest(t, handler, "cathy", "/dataset2/item", "POST", 403)
|
||||
testRequest(t, handler, "cathy", "/dataset2/item", "DELETE", 403)
|
||||
}
|
@ -23,7 +23,7 @@ import (
|
||||
// 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
|
||||
// FindPolicy Find Router info for URL
|
||||
func (p *ControllerRegister) FindPolicy(cont *context.Context) []PolicyFunc {
|
||||
var urlPath = cont.Input.URL()
|
||||
if !BConfig.RouterCaseSensitive {
|
||||
@ -71,7 +71,7 @@ func (p *ControllerRegister) addToPolicy(method, pattern string, r ...PolicyFunc
|
||||
}
|
||||
}
|
||||
|
||||
// Register new policy in beego
|
||||
// Policy Register new policy in beego
|
||||
func Policy(pattern, method string, policy ...PolicyFunc) {
|
||||
BeeApp.Handlers.addToPolicy(method, pattern, policy...)
|
||||
}
|
||||
|
258
router.go
258
router.go
@ -17,7 +17,6 @@ package beego
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
@ -28,6 +27,7 @@ import (
|
||||
"time"
|
||||
|
||||
beecontext "github.com/astaxie/beego/context"
|
||||
"github.com/astaxie/beego/context/param"
|
||||
"github.com/astaxie/beego/logs"
|
||||
"github.com/astaxie/beego/toolbox"
|
||||
"github.com/astaxie/beego/utils"
|
||||
@ -50,23 +50,23 @@ 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",
|
||||
"MKCOL": "MKCOL",
|
||||
"COPY": "COPY",
|
||||
"MOVE": "MOVE",
|
||||
"PROPFIND": "PROPFIND",
|
||||
"PROPPATCH": "PROPPATCH",
|
||||
"LOCK": "LOCK",
|
||||
"UNLOCK": "UNLOCK",
|
||||
HTTPMETHOD = map[string]bool{
|
||||
"GET": true,
|
||||
"POST": true,
|
||||
"PUT": true,
|
||||
"DELETE": true,
|
||||
"PATCH": true,
|
||||
"OPTIONS": true,
|
||||
"HEAD": true,
|
||||
"TRACE": true,
|
||||
"CONNECT": true,
|
||||
"MKCOL": true,
|
||||
"COPY": true,
|
||||
"MOVE": true,
|
||||
"PROPFIND": true,
|
||||
"PROPPATCH": true,
|
||||
"LOCK": true,
|
||||
"UNLOCK": true,
|
||||
}
|
||||
// these beego.Controller's methods shouldn't reflect to AutoRouter
|
||||
exceptMethod = []string{"Init", "Prepare", "Finish", "Render", "RenderString",
|
||||
@ -109,13 +109,16 @@ 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
|
||||
handler http.Handler
|
||||
runFunction FilterFunc
|
||||
routerType int
|
||||
initialize func() ControllerInterface
|
||||
methodParams []*param.MethodParam
|
||||
}
|
||||
|
||||
// ControllerRegister containers registered router rules, controller handlers and filters.
|
||||
@ -151,6 +154,10 @@ func NewControllerRegister() *ControllerRegister {
|
||||
// Add("/api",&RestController{},"get,post:ApiFunc"
|
||||
// Add("/simple",&SimpleController{},"get:GetFunc;post:PostFunc")
|
||||
func (p *ControllerRegister) Add(pattern string, c ControllerInterface, mappingMethods ...string) {
|
||||
p.addWithMethodParams(pattern, c, nil, mappingMethods...)
|
||||
}
|
||||
|
||||
func (p *ControllerRegister) addWithMethodParams(pattern string, c ControllerInterface, methodParams []*param.MethodParam, mappingMethods ...string) {
|
||||
reflectVal := reflect.ValueOf(c)
|
||||
t := reflect.Indirect(reflectVal).Type()
|
||||
methods := make(map[string]string)
|
||||
@ -163,7 +170,7 @@ func (p *ControllerRegister) Add(pattern string, c ControllerInterface, mappingM
|
||||
}
|
||||
comma := strings.Split(colon[0], ",")
|
||||
for _, m := range comma {
|
||||
if _, ok := HTTPMETHOD[strings.ToUpper(m)]; m == "*" || ok {
|
||||
if m == "*" || HTTPMETHOD[strings.ToUpper(m)] {
|
||||
if val := reflectVal.MethodByName(colon[1]); val.IsValid() {
|
||||
methods[strings.ToUpper(m)] = colon[1]
|
||||
} else {
|
||||
@ -176,19 +183,41 @@ func (p *ControllerRegister) Add(pattern string, c ControllerInterface, mappingM
|
||||
}
|
||||
}
|
||||
|
||||
route := &controllerInfo{}
|
||||
route := &ControllerInfo{}
|
||||
route.pattern = pattern
|
||||
route.methods = methods
|
||||
route.routerType = routerTypeBeego
|
||||
route.controllerType = t
|
||||
route.initialize = func() ControllerInterface {
|
||||
vc := reflect.New(route.controllerType)
|
||||
execController, ok := vc.Interface().(ControllerInterface)
|
||||
if !ok {
|
||||
panic("controller is not ControllerInterface")
|
||||
}
|
||||
|
||||
elemVal := reflect.ValueOf(c).Elem()
|
||||
elemType := reflect.TypeOf(c).Elem()
|
||||
execElem := reflect.ValueOf(execController).Elem()
|
||||
|
||||
numOfFields := elemVal.NumField()
|
||||
for i := 0; i < numOfFields; i++ {
|
||||
fieldVal := elemVal.Field(i)
|
||||
fieldType := elemType.Field(i)
|
||||
execElem.FieldByName(fieldType.Name).Set(fieldVal)
|
||||
}
|
||||
|
||||
return execController
|
||||
}
|
||||
|
||||
route.methodParams = methodParams
|
||||
if len(methods) == 0 {
|
||||
for _, m := range HTTPMETHOD {
|
||||
for m := range HTTPMETHOD {
|
||||
p.addToRouter(m, pattern, route)
|
||||
}
|
||||
} else {
|
||||
for k := range methods {
|
||||
if k == "*" {
|
||||
for _, m := range HTTPMETHOD {
|
||||
for m := range HTTPMETHOD {
|
||||
p.addToRouter(m, pattern, route)
|
||||
}
|
||||
} else {
|
||||
@ -198,7 +227,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)
|
||||
}
|
||||
@ -219,13 +248,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) {
|
||||
@ -247,7 +274,7 @@ func (p *ControllerRegister) Include(cList ...ControllerInterface) {
|
||||
key := t.PkgPath() + ":" + t.Name()
|
||||
if comm, ok := GlobalControllerRouter[key]; ok {
|
||||
for _, a := range comm {
|
||||
p.Add(a.Router, c, strings.Join(a.AllowHTTPMethods, ",")+":"+a.Method)
|
||||
p.addWithMethodParams(a.Router, c, a.MethodParams, strings.Join(a.AllowHTTPMethods, ",")+":"+a.Method)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -332,16 +359,16 @@ func (p *ControllerRegister) Any(pattern string, f FilterFunc) {
|
||||
// })
|
||||
func (p *ControllerRegister) AddMethod(method, pattern string, f FilterFunc) {
|
||||
method = strings.ToUpper(method)
|
||||
if _, ok := HTTPMETHOD[method]; method != "*" && !ok {
|
||||
if method != "*" && !HTTPMETHOD[method] {
|
||||
panic("not support http method: " + method)
|
||||
}
|
||||
route := &controllerInfo{}
|
||||
route := &ControllerInfo{}
|
||||
route.pattern = pattern
|
||||
route.routerType = routerTypeRESTFul
|
||||
route.runFunction = f
|
||||
methods := make(map[string]string)
|
||||
if method == "*" {
|
||||
for _, val := range HTTPMETHOD {
|
||||
for val := range HTTPMETHOD {
|
||||
methods[val] = val
|
||||
}
|
||||
} else {
|
||||
@ -350,7 +377,7 @@ func (p *ControllerRegister) AddMethod(method, pattern string, f FilterFunc) {
|
||||
route.methods = methods
|
||||
for k := range methods {
|
||||
if k == "*" {
|
||||
for _, m := range HTTPMETHOD {
|
||||
for m := range HTTPMETHOD {
|
||||
p.addToRouter(m, pattern, route)
|
||||
}
|
||||
} else {
|
||||
@ -361,7 +388,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
|
||||
@ -370,7 +397,7 @@ func (p *ControllerRegister) Handler(pattern string, h http.Handler, options ...
|
||||
pattern = path.Join(pattern, "?:all(.*)")
|
||||
}
|
||||
}
|
||||
for _, m := range HTTPMETHOD {
|
||||
for m := range HTTPMETHOD {
|
||||
p.addToRouter(m, pattern, route)
|
||||
}
|
||||
}
|
||||
@ -396,7 +423,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
|
||||
@ -405,7 +432,7 @@ func (p *ControllerRegister) AddAutoPrefix(prefix string, c ControllerInterface)
|
||||
patternFix := path.Join(prefix, strings.ToLower(controllerName), strings.ToLower(rt.Method(i).Name))
|
||||
patternFixInit := path.Join(prefix, controllerName, rt.Method(i).Name)
|
||||
route.pattern = pattern
|
||||
for _, m := range HTTPMETHOD {
|
||||
for m := range HTTPMETHOD {
|
||||
p.addToRouter(m, pattern, route)
|
||||
p.addToRouter(m, patternInit, route)
|
||||
p.addToRouter(m, patternFix, route)
|
||||
@ -502,11 +529,11 @@ 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
|
||||
if _, ok := HTTPMETHOD[strings.ToUpper(methodName)]; ok {
|
||||
if HTTPMETHOD[strings.ToUpper(methodName)] {
|
||||
if len(c.methods) == 0 {
|
||||
find = true
|
||||
} else if m, ok := c.methods[strings.ToUpper(methodName)]; ok && m == strings.ToUpper(methodName) {
|
||||
@ -626,11 +653,12 @@ func (p *ControllerRegister) execFilter(context *beecontext.Context, urlPath str
|
||||
func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
|
||||
startTime := time.Now()
|
||||
var (
|
||||
runRouter reflect.Type
|
||||
findRouter bool
|
||||
runMethod string
|
||||
routerInfo *controllerInfo
|
||||
isRunnable bool
|
||||
runRouter reflect.Type
|
||||
findRouter bool
|
||||
runMethod string
|
||||
methodParams []*param.MethodParam
|
||||
routerInfo *ControllerInfo
|
||||
isRunnable bool
|
||||
)
|
||||
context := p.pool.Get().(*beecontext.Context)
|
||||
context.Reset(rw, r)
|
||||
@ -653,7 +681,7 @@ func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request)
|
||||
}
|
||||
|
||||
// filter wrong http method
|
||||
if _, ok := HTTPMETHOD[r.Method]; !ok {
|
||||
if !HTTPMETHOD[r.Method] {
|
||||
http.Error(rw, "Method Not Allowed", 405)
|
||||
goto Admin
|
||||
}
|
||||
@ -670,7 +698,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)
|
||||
}
|
||||
@ -698,7 +726,6 @@ func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request)
|
||||
// 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 {
|
||||
@ -742,12 +769,13 @@ func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request)
|
||||
routerInfo.handler.ServeHTTP(rw, r)
|
||||
} else {
|
||||
runRouter = routerInfo.controllerType
|
||||
methodParams = routerInfo.methodParams
|
||||
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
|
||||
@ -762,14 +790,20 @@ func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request)
|
||||
// also defined runRouter & runMethod from filter
|
||||
if !isRunnable {
|
||||
//Invoke the request handler
|
||||
vc := reflect.New(runRouter)
|
||||
execController, ok := vc.Interface().(ControllerInterface)
|
||||
if !ok {
|
||||
panic("controller is not ControllerInterface")
|
||||
var execController ControllerInterface
|
||||
if routerInfo.initialize != nil {
|
||||
execController = routerInfo.initialize()
|
||||
} else {
|
||||
vc := reflect.New(runRouter)
|
||||
var ok bool
|
||||
execController, ok = vc.Interface().(ControllerInterface)
|
||||
if !ok {
|
||||
panic("controller is not ControllerInterface")
|
||||
}
|
||||
}
|
||||
|
||||
//call the controller init function
|
||||
execController.Init(context, runRouter.Name(), runMethod, vc.Interface())
|
||||
execController.Init(context, runRouter.Name(), runMethod, execController)
|
||||
|
||||
//call prepare function
|
||||
execController.Prepare()
|
||||
@ -777,8 +811,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()
|
||||
}
|
||||
}
|
||||
@ -788,25 +822,31 @@ 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) {
|
||||
var in []reflect.Value
|
||||
vc := reflect.ValueOf(execController)
|
||||
method := vc.MethodByName(runMethod)
|
||||
method.Call(in)
|
||||
in := param.ConvertParams(methodParams, method.Type(), context)
|
||||
out := method.Call(in)
|
||||
|
||||
//For backward compatibility we only handle response if we had incoming methodParams
|
||||
if methodParams != nil {
|
||||
p.handleParamResponse(context, execController, out)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -835,9 +875,20 @@ func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request)
|
||||
|
||||
Admin:
|
||||
//admin module record QPS
|
||||
|
||||
statusCode := context.ResponseWriter.Status
|
||||
if statusCode == 0 {
|
||||
statusCode = 200
|
||||
}
|
||||
|
||||
if BConfig.Listen.EnableAdmin {
|
||||
timeDur := time.Since(startTime)
|
||||
if FilterMonitorFunc(r.Method, r.URL.Path, timeDur) {
|
||||
pattern := ""
|
||||
if routerInfo != nil {
|
||||
pattern = routerInfo.pattern
|
||||
}
|
||||
|
||||
if FilterMonitorFunc(r.Method, r.URL.Path, timeDur, pattern, statusCode) {
|
||||
if runRouter != nil {
|
||||
go toolbox.StatisticsMap.AddStatistics(r.Method, r.URL.Path, runRouter.Name(), timeDur)
|
||||
} else {
|
||||
@ -850,44 +901,69 @@ Admin:
|
||||
timeDur := time.Since(startTime)
|
||||
var devInfo string
|
||||
|
||||
statusCode := context.ResponseWriter.Status
|
||||
if statusCode == 0 {
|
||||
statusCode = 200
|
||||
}
|
||||
|
||||
iswin := (runtime.GOOS == "windows")
|
||||
statusColor := logs.ColorByStatus(iswin, statusCode)
|
||||
methodColor := logs.ColorByMethod(iswin, r.Method)
|
||||
resetColor := logs.ColorByMethod(iswin, "")
|
||||
|
||||
if findRouter {
|
||||
if routerInfo != nil {
|
||||
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)
|
||||
if BConfig.Log.AccessLogsFormat != "" {
|
||||
record := &logs.AccessLogRecord{
|
||||
RemoteAddr: context.Input.IP(),
|
||||
RequestTime: startTime,
|
||||
RequestMethod: r.Method,
|
||||
Request: fmt.Sprintf("%s %s %s", r.Method, r.RequestURI, r.Proto),
|
||||
ServerProtocol: r.Proto,
|
||||
Host: r.Host,
|
||||
Status: statusCode,
|
||||
ElapsedTime: timeDur,
|
||||
HTTPReferrer: r.Header.Get("Referer"),
|
||||
HTTPUserAgent: r.Header.Get("User-Agent"),
|
||||
RemoteUser: r.Header.Get("Remote-User"),
|
||||
BodyBytesSent: 0, //@todo this one is missing!
|
||||
}
|
||||
logs.AccessLog(record, BConfig.Log.AccessLogsFormat)
|
||||
} else {
|
||||
if findRouter {
|
||||
if routerInfo != nil {
|
||||
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("|%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("|%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)
|
||||
timeDur.String(), "nomatch", methodColor, r.Method, resetColor, r.URL.Path)
|
||||
}
|
||||
if iswin {
|
||||
logs.W32Debug(devInfo)
|
||||
} else {
|
||||
logs.Debug(devInfo)
|
||||
}
|
||||
} else {
|
||||
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)
|
||||
} else {
|
||||
logs.Debug(devInfo)
|
||||
}
|
||||
}
|
||||
|
||||
// Call WriteHeader if status code has been set changed
|
||||
if context.Output.Status != 0 {
|
||||
context.ResponseWriter.WriteHeader(context.Output.Status)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *ControllerRegister) handleParamResponse(context *beecontext.Context, execController ControllerInterface, results []reflect.Value) {
|
||||
//looping in reverse order for the case when both error and value are returned and error sets the response status code
|
||||
for i := len(results) - 1; i >= 0; i-- {
|
||||
result := results[i]
|
||||
if result.Kind() != reflect.Interface || !result.IsNil() {
|
||||
resultValue := result.Interface()
|
||||
context.RenderMethodResult(resultValue)
|
||||
}
|
||||
}
|
||||
if !context.ResponseWriter.Started && context.Output.Status == 0 {
|
||||
context.Output.SetStatus(200)
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
@ -895,7 +971,7 @@ 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
|
||||
}
|
||||
}
|
||||
|
@ -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,11 +155,16 @@ 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{}
|
||||
if doc == nil {
|
||||
err = cp.b.Get(sid, &doc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if doc == nil {
|
||||
kv = make(map[interface{}]interface{})
|
||||
} else {
|
||||
kv, err = session.DecodeGob(doc)
|
||||
@ -230,7 +235,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,33 @@ 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{}
|
||||
err error
|
||||
)
|
||||
|
||||
kvs, _ := c.Get([]byte(sid))
|
||||
|
||||
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 +133,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 +150,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 +161,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
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user