作者 唐旭辉

认证校验修改

正在显示 65 个修改的文件 包含 2614 行增加332 行删除
@@ -3,6 +3,7 @@ package common @@ -3,6 +3,7 @@ package common
3 import ( 3 import (
4 "oppmg/common/config" 4 "oppmg/common/config"
5 "oppmg/common/log" 5 "oppmg/common/log"
  6 + "oppmg/common/redis"
6 7
7 "github.com/astaxie/beego" 8 "github.com/astaxie/beego"
8 ) 9 )
@@ -26,4 +27,5 @@ func ResetCommonConfig() { @@ -26,4 +27,5 @@ func ResetCommonConfig() {
26 logCallfunc = true 27 logCallfunc = true
27 } 28 }
28 log.ResetLog(setlog, logCallfunc) 29 log.ResetLog(setlog, logCallfunc)
  30 + redis.SetRedis(mconfig.RedisAddPort, mconfig.RedisAuth, mconfig.RedisDB)
29 } 31 }
@@ -12,7 +12,7 @@ type MyConfig struct { @@ -12,7 +12,7 @@ type MyConfig struct {
12 SqlConn string //数据库连接 12 SqlConn string //数据库连接
13 RedisAddPort string // 13 RedisAddPort string //
14 RedisAuth string 14 RedisAuth string
15 - RedisDB string 15 + RedisDB int
16 LogOutput string 16 LogOutput string
17 LogFilename string 17 LogFilename string
18 LogLevel string 18 LogLevel string
@@ -35,7 +35,7 @@ func RestMyConfig() *MyConfig { @@ -35,7 +35,7 @@ func RestMyConfig() *MyConfig {
35 SqlConn: sqlconn, 35 SqlConn: sqlconn,
36 RedisAddPort: beego.AppConfig.String("redis_add_port"), 36 RedisAddPort: beego.AppConfig.String("redis_add_port"),
37 RedisAuth: beego.AppConfig.DefaultString("redis_auth", ""), 37 RedisAuth: beego.AppConfig.DefaultString("redis_auth", ""),
38 - RedisDB: beego.AppConfig.DefaultString("redis_db", "0"), 38 + RedisDB: beego.AppConfig.DefaultInt("redis_db", 0),
39 LogOutput: beego.AppConfig.DefaultString("log_output", "console"), 39 LogOutput: beego.AppConfig.DefaultString("log_output", "console"),
40 LogFilename: beego.AppConfig.DefaultString("log_filename", "./log/ability.log"), 40 LogFilename: beego.AppConfig.DefaultString("log_filename", "./log/ability.log"),
41 LogLevel: beego.AppConfig.DefaultString("log_Level", "debug"), 41 LogLevel: beego.AppConfig.DefaultString("log_Level", "debug"),
@@ -2,12 +2,9 @@ package controllers @@ -2,12 +2,9 @@ package controllers
2 2
3 import ( 3 import (
4 "encoding/json" 4 "encoding/json"
5 - "fmt"  
6 "oppmg/common/log" 5 "oppmg/common/log"
7 "oppmg/protocol" 6 "oppmg/protocol"
8 - "oppmg/services/auth"  
9 -  
10 - "github.com/astaxie/beego/validation" 7 + serveauth "oppmg/services/auth"
11 ) 8 )
12 9
13 type AuthController struct { 10 type AuthController struct {
@@ -16,55 +13,53 @@ type AuthController struct { @@ -16,55 +13,53 @@ type AuthController struct {
16 13
17 //URLMapping 实现ControllerInterface中的URLMapping 14 //URLMapping 实现ControllerInterface中的URLMapping
18 func (c *AuthController) URLMapping() { 15 func (c *AuthController) URLMapping() {
19 - c.Mapping("AccessToken", c.AccessToken)  
20 16
21 } 17 }
22 18
23 -// AccessToken ....  
24 -// @router /AccessToken [post]  
25 -func (c *AuthController) AccessToken() { 19 +// RefreshToken ....
  20 +// @router /refresh_token [get]
  21 +func (c *AuthController) RefreshToken() {
26 log.Debug("运行cotrollers") 22 log.Debug("运行cotrollers")
27 var msg *protocol.ResponseMessage 23 var msg *protocol.ResponseMessage
28 defer func() { 24 defer func() {
29 c.ResposeJson(msg) 25 c.ResposeJson(msg)
30 }() 26 }()
31 - var param protocol.RequestCheckSmsCode  
32 - if err := json.Unmarshal(c.Ctx.Input.RequestBody, &param); err != nil {  
33 - log.Error("json 解析失败", err)  
34 - msg = protocol.BadRequestParam("00001")  
35 - return  
36 - }  
37 - valid := validation.Validation{}  
38 - ok, err := valid.Valid(&param)  
39 - if err != nil {  
40 - //TODO  
41 - log.Error("系统错误", err)  
42 - }  
43 - if !ok {  
44 - //TODO  
45 - log.Error("参数错误")  
46 - }  
47 - data, commErr := auth.GetAccessToken(param)  
48 - msg = protocol.NewReturnResponse(data, commErr) 27 +
  28 + // data, commErr := auth.GetAccessToken(param)
  29 + // msg = protocol.NewReturnResponse(data, commErr)
49 return 30 return
50 } 31 }
51 32
52 -// Demo 登录  
53 -// @router /demo [post]  
54 -func (c *AuthController) Demo() { 33 +// Login 登录
  34 +// @router /login [post]
  35 +func (c *AuthController) Login() {
55 var msg *protocol.ResponseMessage 36 var msg *protocol.ResponseMessage
56 defer func() { 37 defer func() {
57 c.ResposeJson(msg) 38 c.ResposeJson(msg)
58 }() 39 }()
59 - type Parameter struct {  
60 - }  
61 - var param Parameter 40 +
  41 + var param protocol.RequestLogin
62 if err := json.Unmarshal(c.Ctx.Input.RequestBody, &param); err != nil { 42 if err := json.Unmarshal(c.Ctx.Input.RequestBody, &param); err != nil {
63 - fmt.Println("json 解析失败", err)  
64 - // msg.Code = "1" 43 + log.Error("json 解析失败", err)
  44 + msg = protocol.BadRequestParam("1")
65 return 45 return
66 } 46 }
67 - /**/  
68 - 47 + if len(param.Account) == 0 || len(param.Password) == 0 {
  48 + msg = protocol.BadRequestParam("10021")
  49 + return
  50 + }
  51 + logintoken, err := serveauth.LoginAuthByPassword(param.Account, param.Password)
  52 + if err != nil {
  53 + msg = protocol.NewReturnResponse(nil, err)
  54 + return
  55 + }
  56 + err = serveauth.ResetLoginToken(logintoken)
  57 + msg = protocol.NewReturnResponse(logintoken, err)
69 return 58 return
70 } 59 }
  60 +
  61 +//ChangeCompany 切换公司
  62 +//@Router /change_company [post]
  63 +func (c *AuthController) ChangeCompany() {
  64 +
  65 +}
@@ -3,7 +3,7 @@ module oppmg @@ -3,7 +3,7 @@ module oppmg
3 go 1.13 3 go 1.13
4 4
5 require ( 5 require (
6 - github.com/astaxie/beego v1.10.0 6 + github.com/astaxie/beego v1.11.1
7 github.com/dgrijalva/jwt-go v3.2.0+incompatible 7 github.com/dgrijalva/jwt-go v3.2.0+incompatible
8 github.com/go-redis/redis v6.15.6+incompatible 8 github.com/go-redis/redis v6.15.6+incompatible
9 github.com/go-sql-driver/mysql v1.4.1 9 github.com/go-sql-driver/mysql v1.4.1
@@ -2,10 +2,13 @@ github.com/Knetic/govaluate v3.0.0+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8L @@ -2,10 +2,13 @@ github.com/Knetic/govaluate v3.0.0+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8L
2 github.com/OwnLocal/goes v1.0.0/go.mod h1:8rIFjBGTue3lCU0wplczcUgt9Gxgrkkrw7etMIcn8TM= 2 github.com/OwnLocal/goes v1.0.0/go.mod h1:8rIFjBGTue3lCU0wplczcUgt9Gxgrkkrw7etMIcn8TM=
3 github.com/astaxie/beego v1.10.0 h1:s0OZ1iUO0rl8+lwWZfPK/0GhQi1tFUcIClTevyz48Pg= 3 github.com/astaxie/beego v1.10.0 h1:s0OZ1iUO0rl8+lwWZfPK/0GhQi1tFUcIClTevyz48Pg=
4 github.com/astaxie/beego v1.10.0/go.mod h1:0R4++1tUqERR0WYFWdfkcrsyoVBCG4DgpDGokT3yb+U= 4 github.com/astaxie/beego v1.10.0/go.mod h1:0R4++1tUqERR0WYFWdfkcrsyoVBCG4DgpDGokT3yb+U=
  5 +github.com/astaxie/beego v1.11.1 h1:6DESefxW5oMcRLFRKi53/6exzup/IR6N4EzzS1n6CnQ=
  6 +github.com/astaxie/beego v1.11.1/go.mod h1:i69hVzgauOPSw5qeyF4GVZhn7Od0yG5bbCGzmhbWxgQ=
5 github.com/astaxie/beego v1.12.0 h1:MRhVoeeye5N+Flul5PoVfD9CslfdoH+xqC/xvSQ5u2Y= 7 github.com/astaxie/beego v1.12.0 h1:MRhVoeeye5N+Flul5PoVfD9CslfdoH+xqC/xvSQ5u2Y=
6 github.com/astaxie/beego v1.12.0/go.mod h1:fysx+LZNZKnvh4GED/xND7jWtjCR6HzydR2Hh2Im57o= 8 github.com/astaxie/beego v1.12.0/go.mod h1:fysx+LZNZKnvh4GED/xND7jWtjCR6HzydR2Hh2Im57o=
7 github.com/beego/goyaml2 v0.0.0-20130207012346-5545475820dd/go.mod h1:1b+Y/CofkYwXMUU0OhQqGvsY2Bvgr4j6jfT699wyZKQ= 9 github.com/beego/goyaml2 v0.0.0-20130207012346-5545475820dd/go.mod h1:1b+Y/CofkYwXMUU0OhQqGvsY2Bvgr4j6jfT699wyZKQ=
8 github.com/beego/x2j v0.0.0-20131220205130-a0352aadc542/go.mod h1:kSeGC/p1AbBiEp5kat81+DSQrZenVBZXklMLaELspWU= 10 github.com/beego/x2j v0.0.0-20131220205130-a0352aadc542/go.mod h1:kSeGC/p1AbBiEp5kat81+DSQrZenVBZXklMLaELspWU=
  11 +github.com/belogik/goes v0.0.0-20151229125003-e54d722c3aff/go.mod h1:PhH1ZhyCzHKt4uAasyx+ljRCgoezetRNf59CUtwUkqY=
9 github.com/bradfitz/gomemcache v0.0.0-20180710155616-bc664df96737/go.mod h1:PmM6Mmwb0LSuEubjR8N7PtNe1KxZLtOUHtbeikc5h60= 12 github.com/bradfitz/gomemcache v0.0.0-20180710155616-bc664df96737/go.mod h1:PmM6Mmwb0LSuEubjR8N7PtNe1KxZLtOUHtbeikc5h60=
10 github.com/casbin/casbin v1.7.0/go.mod h1:c67qKN6Oum3UF5Q1+BByfFxkwKvhwW57ITjqwtzR1KE= 13 github.com/casbin/casbin v1.7.0/go.mod h1:c67qKN6Oum3UF5Q1+BByfFxkwKvhwW57ITjqwtzR1KE=
11 github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58/go.mod h1:EOBUe0h4xcZ5GoxqC5SDxFQ8gwyZPKQoEzownBlhI80= 14 github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58/go.mod h1:EOBUe0h4xcZ5GoxqC5SDxFQ8gwyZPKQoEzownBlhI80=
1 package middleware 1 package middleware
2 2
3 import ( 3 import (
4 - "crypto/sha256"  
5 - "encoding/hex"  
6 "fmt" 4 "fmt"
7 "oppmg/common/log" 5 "oppmg/common/log"
8 "oppmg/protocol" 6 "oppmg/protocol"
9 - "strings" 7 + serveauth "oppmg/services/auth"
  8 + "oppmg/storage/redisdata"
10 9
11 "github.com/astaxie/beego/plugins/cors" 10 "github.com/astaxie/beego/plugins/cors"
12 11
@@ -24,34 +23,86 @@ var LogRequestData = func(ctx *context.Context) { @@ -24,34 +23,86 @@ var LogRequestData = func(ctx *context.Context) {
24 } 23 }
25 24
26 //CheckSign Before Router 25 //CheckSign Before Router
27 -var CheckSign = func(ctx *context.Context) { 26 +// var CheckSign = func(ctx *context.Context) {
  27 +// var (
  28 +// headTimeStamp string
  29 +// headUuid string
  30 +// headAccessToken string
  31 +// headSign string
  32 +// signHex string
  33 +// )
  34 +// headTimeStamp = ctx.Input.Header(protocol.HeaderTimestamp)
  35 +// headUuid = ctx.Input.Header(protocol.HeaderUUID)
  36 +// headSign = ctx.Input.Header(protocol.HeaderSign)
  37 +// setsign := fmt.Sprintf("v!(MmM%v%v%vMmM)i^", headTimeStamp, headUuid, headAccessToken)
  38 +// sha256 := sha256.New()
  39 +// sha256.Write([]byte(setsign))
  40 +// signHex = hex.EncodeToString(sha256.Sum(nil))
  41 +// if strings.Compare(signHex, headSign) != 0 {
  42 +// msg := protocol.BadRequestParam("113")
  43 +// ctx.Output.JSON(msg, false, false)
  44 +// return
  45 +// }
  46 +// return
  47 +// }
  48 +
  49 +//AuthToken Before Router
  50 +var AuthToken = func(ctx *context.Context) {
  51 + log.Debug("执行中间件AuthToken")
28 var ( 52 var (
29 - headTimeStamp string  
30 - headUuid string  
31 - headAccessToken string  
32 - headSign string  
33 - signHex string 53 + storetoken protocol.LoginAuthToken
  54 + msg *protocol.ResponseMessage
  55 + err error
  56 + mtoken *serveauth.MyToken
34 ) 57 )
35 - headTimeStamp = ctx.Input.Header(protocol.HeaderTimestamp)  
36 - headUuid = ctx.Input.Header(protocol.HeaderUUID)  
37 - headSign = ctx.Input.Header(protocol.HeaderSign)  
38 - setsign := fmt.Sprintf("v!(MmM%v%v%vMmM)i^", headTimeStamp, headUuid, headAccessToken)  
39 - sha256 := sha256.New()  
40 - sha256.Write([]byte(setsign))  
41 - signHex = hex.EncodeToString(sha256.Sum(nil))  
42 - if strings.Compare(signHex, headSign) != 0 {  
43 - msg := protocol.BadRequestParam("113") 58 + accesstoken := ctx.Input.Header(protocol.HeaderAccessToken)
  59 + refreshToken := ctx.Input.Header(protocol.HeaderRefreshToken)
  60 + mtoken, err = serveauth.ValidJWTToken(accesstoken)
  61 + if err == nil {
  62 + storetoken, err = redisdata.GetLoginToken(mtoken.UID)
  63 + if err != nil {
  64 + log.Error("err:%s", err)
  65 + msg = protocol.NewMesage("10024")
  66 + ctx.Output.JSON(msg, false, false)
  67 + return
  68 + }
  69 + if storetoken.AccessToken != accesstoken {
  70 + msg = protocol.NewMesage("10025")
  71 + ctx.Output.JSON(msg, false, false)
  72 + return
  73 + }
  74 + ctx.Input.SetData(protocol.HeaderCompanyid, mtoken.CompanyID)
  75 + ctx.Input.SetData(protocol.HeaderUserid, mtoken.UID)
  76 + return
  77 + }
  78 + if ok := serveauth.IsJwtErrorExpired(err); ok {
  79 + //token过期
  80 + mtoken, err = serveauth.ValidJWTToken(refreshToken)
  81 + if err != nil {
  82 + msg = protocol.NewMesage("10024")
  83 + ctx.Output.JSON(msg, false, false)
  84 + return
  85 + }
  86 + storetoken, err = redisdata.GetLoginToken(mtoken.UID)
  87 + if err != nil {
  88 + log.Error("err:%s", err)
  89 + msg = protocol.NewMesage("10024")
  90 + ctx.Output.JSON(msg, false, false)
  91 + return
  92 + }
  93 + if storetoken.AccessToken != accesstoken {
  94 + msg = protocol.NewMesage("10025")
  95 + ctx.Output.JSON(msg, false, false)
  96 + return
  97 + }
  98 + logintoken, _ := serveauth.GenerateAuthToken(mtoken.UID, mtoken.CompanyID)
  99 + serveauth.ResetLoginTokenRedis(logintoken)
  100 + msg = protocol.NewReturnResponse(logintoken, nil)
44 ctx.Output.JSON(msg, false, false) 101 ctx.Output.JSON(msg, false, false)
45 return 102 return
46 } 103 }
47 - return  
48 -}  
49 -  
50 -//AuthToken Before Router  
51 -var AuthToken = func(ctx *context.Context) {  
52 - log.Debug("执行中间件AuthToken")  
53 -  
54 - // ctx.Output.Body([]byte("{}")) 104 + msg = protocol.NewMesage("10024")
  105 + ctx.Output.JSON(msg, false, false)
55 return 106 return
56 } 107 }
57 108
@@ -2,6 +2,7 @@ package models @@ -2,6 +2,7 @@ package models
2 2
3 import ( 3 import (
4 "fmt" 4 "fmt"
  5 + "oppmg/common/log"
5 "time" 6 "time"
6 7
7 "github.com/astaxie/beego/orm" 8 "github.com/astaxie/beego/orm"
@@ -40,13 +41,6 @@ const ( @@ -40,13 +41,6 @@ const (
40 DEVICE_TYPE_WEB 41 DEVICE_TYPE_WEB
41 ) 42 )
42 43
43 -//过期时长设置,单位:秒  
44 -const (  
45 - REFRESH_TIME int64 = 60 * 60 * 4 //4小时  
46 - ACCESS_TIME int64 = 60 * 60 * 2 //2小时  
47 - AUTHCODE_TIME int64 = 60 * 60 * 2 //2小时  
48 -)  
49 -  
50 // AddUserAuth insert a new UserAuth into database and returns 44 // AddUserAuth insert a new UserAuth into database and returns
51 // last inserted Id on success. 45 // last inserted Id on success.
52 func AddUserAuth(m *UserAuth) (id int64, err error) { 46 func AddUserAuth(m *UserAuth) (id int64, err error) {
@@ -68,15 +62,12 @@ func GetUserAuthById(id int64) (v *UserAuth, err error) { @@ -68,15 +62,12 @@ func GetUserAuthById(id int64) (v *UserAuth, err error) {
68 62
69 // UpdateUserAuth updates UserAuth by Id and returns error if 63 // UpdateUserAuth updates UserAuth by Id and returns error if
70 // the record to be updated doesn't exist 64 // the record to be updated doesn't exist
71 -func UpdateUserAuthById(m *UserAuth) (err error) { 65 +func UpdateUserAuthById(m *UserAuth, col []string) (err error) {
72 o := orm.NewOrm() 66 o := orm.NewOrm()
73 - v := UserAuth{Id: m.Id}  
74 // ascertain id exists in the database 67 // ascertain id exists in the database
75 - if err = o.Read(&v); err == nil {  
76 - var num int64  
77 - if num, err = o.Update(m); err == nil {  
78 - fmt.Println("Number of records updated in database:", num)  
79 - } 68 + var num int64
  69 + if num, err = o.Update(m, col...); err == nil {
  70 + log.Info("Number of records updated in database:", num)
80 } 71 }
81 return 72 return
82 } 73 }
@@ -4,12 +4,18 @@ package protocol @@ -4,12 +4,18 @@ package protocol
4 const ( 4 const (
5 HeaderAccessToken string = "x-mmm-accesstoken" 5 HeaderAccessToken string = "x-mmm-accesstoken"
6 HeaderRefreshToken string = "x-mmm-refreshtoken" 6 HeaderRefreshToken string = "x-mmm-refreshtoken"
7 - HeaderUID string = "x-mmm-uid"  
8 - HeaderUUID string = "x-mmm-uuid"  
9 - HeaderTimestamp string = "x-mmm-timestamp"  
10 - HeaderDevicetype string = "x-mmm-devicetype"  
11 - HeaderAppproject string = "x-mmm-appproject"  
12 - HeaderSign string = "x-mmm-sign" 7 + // HeaderUID string = "x-mmm-uid"
  8 + // HeaderUUID string = "x-mmm-uuid"
  9 + // HeaderTimestamp string = "x-mmm-timestamp"
  10 + // HeaderDevicetype string = "x-mmm-devicetype"
  11 + // HeaderAppproject string = "x-mmm-appproject"
  12 + // HeaderSign string = "x-mmm-sign"
  13 +)
  14 +
  15 +//用来存储从token中解析出来的内容对应的键名
  16 +const (
  17 + HeaderCompanyid string = "header_companyid"
  18 + HeaderUserid string = "header_userid"
13 ) 19 )
14 20
15 //BaseHeader 请求的header数据 21 //BaseHeader 请求的header数据
@@ -16,4 +16,8 @@ var errmessge ErrorMap = map[string]string{ @@ -16,4 +16,8 @@ var errmessge ErrorMap = map[string]string{
16 //安全认证相关 16 //安全认证相关
17 "10021": "账号或密码不正确", 17 "10021": "账号或密码不正确",
18 "10022": "账号已被禁用", 18 "10022": "账号已被禁用",
  19 + "10023": "用户无使用权限",
  20 + "10024": "登录凭证失效",
  21 + "10025": "该账号已在其他地方登录",
  22 + "10026": "登录凭证过期",
19 } 23 }
@@ -11,7 +11,7 @@ import ( @@ -11,7 +11,7 @@ import (
11 func init() { 11 func init() {
12 nsV1 := beego.NewNamespace("v1", 12 nsV1 := beego.NewNamespace("v1",
13 beego.NSBefore(middleware.AllowOption), 13 beego.NSBefore(middleware.AllowOption),
14 - beego.NSBefore(middleware.LogRequestData, middleware.AuthToken), 14 + beego.NSBefore(middleware.LogRequestData),
15 beego.NSNamespace("/company", 15 beego.NSNamespace("/company",
16 beego.NSRouter("/:companyid([0-9]+)/department", &controllers.CompanyController{}, "get:DepartmentList"), 16 beego.NSRouter("/:companyid([0-9]+)/department", &controllers.CompanyController{}, "get:DepartmentList"),
17 beego.NSRouter("/department/:id([0-9]+)", &controllers.CompanyController{}, "get:DepartmentOne"), 17 beego.NSRouter("/department/:id([0-9]+)", &controllers.CompanyController{}, "get:DepartmentOne"),
@@ -32,12 +32,17 @@ func init() { @@ -32,12 +32,17 @@ func init() {
32 beego.NSRouter("/role_group", &controllers.RbacController{}, "delete:RoleGroupDelete"), 32 beego.NSRouter("/role_group", &controllers.RbacController{}, "delete:RoleGroupDelete"),
33 beego.NSRouter("/:companyid([0-9]+)/role", &controllers.RbacController{}, "get:RoleList"), 33 beego.NSRouter("/:companyid([0-9]+)/role", &controllers.RbacController{}, "get:RoleList"),
34 ), 34 ),
35 - beego.NSAfter(middleware.LogRouter),  
36 ) 35 )
37 36
38 nsAuth := beego.NewNamespace("/auth", 37 nsAuth := beego.NewNamespace("/auth",
39 - beego.NSBefore(middleware.LogRequestData, middleware.AuthToken),  
40 - beego.NSRouter("/accessToken", &controllers.AuthController{}, "post:AccessToken"), 38 + beego.NSBefore(middleware.AllowOption),
  39 + beego.NSBefore(middleware.LogRequestData),
  40 + beego.NSRouter("/login", &controllers.AuthController{}, "post:Login"),
  41 + beego.NSNamespace("/token",
  42 + beego.NSBefore(middleware.AuthToken),
  43 + beego.NSRouter("/change_company", &controllers.AuthController{}, "post:ChangeCompany"),
  44 + beego.NSRouter("/refresh_token", &controllers.AuthController{}, "post:RefreshToken"),
  45 + ),
41 ) 46 )
42 beego.AddNamespace(nsV1) 47 beego.AddNamespace(nsV1)
43 beego.AddNamespace(nsAuth) 48 beego.AddNamespace(nsAuth)
@@ -9,6 +9,7 @@ import ( @@ -9,6 +9,7 @@ import (
9 "oppmg/common/log" 9 "oppmg/common/log"
10 "oppmg/models" 10 "oppmg/models"
11 "oppmg/protocol" 11 "oppmg/protocol"
  12 + "oppmg/storage/redisdata"
12 "strings" 13 "strings"
13 "time" 14 "time"
14 15
@@ -38,10 +39,9 @@ func validatePassword(from, to string) bool { @@ -38,10 +39,9 @@ func validatePassword(from, to string) bool {
38 } 39 }
39 40
40 //LoginAuth 登录认证 41 //LoginAuth 登录认证
41 -func LoginAuthByPassword(account, password string) error { 42 +func LoginAuthByPassword(account, password string) (protocol.LoginAuthToken, error) {
42 var ( 43 var (
43 user *models.User 44 user *models.User
44 - uAuth *models.UserAuth  
45 companys []models.Company 45 companys []models.Company
46 mcompany models.Company 46 mcompany models.Company
47 loginToken protocol.LoginAuthToken 47 loginToken protocol.LoginAuthToken
@@ -50,82 +50,56 @@ func LoginAuthByPassword(account, password string) error { @@ -50,82 +50,56 @@ func LoginAuthByPassword(account, password string) error {
50 user, err = models.GetUserByPhone(account) 50 user, err = models.GetUserByPhone(account)
51 if err != nil { 51 if err != nil {
52 log.Error(err.Error()) 52 log.Error(err.Error())
53 - return protocol.NewErrWithMessage("10021", err) 53 + return loginToken, protocol.NewErrWithMessage("10021", err)
54 } 54 }
55 if ok := validatePassword(password, user.Passwd); !ok { 55 if ok := validatePassword(password, user.Passwd); !ok {
56 - return protocol.NewErrWithMessage("10021", err) 56 + return loginToken, protocol.NewErrWithMessage("10021", err)
57 } 57 }
58 if ok := user.IsEnable(); !ok { 58 if ok := user.IsEnable(); !ok {
59 - return protocol.NewErrWithMessage("10022") 59 + return loginToken, protocol.NewErrWithMessage("10022")
60 } 60 }
61 companys, err = models.GetCompanyByUser(user.Id) 61 companys, err = models.GetCompanyByUser(user.Id)
62 if err != nil { 62 if err != nil {
63 e := fmt.Errorf("GetCompanyByUser(%d) err:%s", user.Id, err) 63 e := fmt.Errorf("GetCompanyByUser(%d) err:%s", user.Id, err)
64 log.Error(e.Error()) 64 log.Error(e.Error())
65 - return protocol.NewErrWithMessage("1") 65 + return loginToken, protocol.NewErrWithMessage("1")
66 } 66 }
67 if len(companys) <= 0 { 67 if len(companys) <= 0 {
68 log.Error("can not found company") 68 log.Error("can not found company")
69 - return protocol.NewErrWithMessage("1") 69 + return loginToken, protocol.NewErrWithMessage("1")
70 } 70 }
71 mcompany = companys[0] 71 mcompany = companys[0]
72 loginToken, err = GenerateAuthToken(user.Id, mcompany.Id) 72 loginToken, err = GenerateAuthToken(user.Id, mcompany.Id)
73 if err != nil { 73 if err != nil {
74 e := fmt.Errorf("GenerateAuthToken err:%s", err) 74 e := fmt.Errorf("GenerateAuthToken err:%s", err)
75 log.Error(e.Error()) 75 log.Error(e.Error())
76 - return protocol.NewErrWithMessage("1") 76 + return loginToken, protocol.NewErrWithMessage("1")
77 } 77 }
78 - // uAuth, err = models.ReadUserAuthByDevice(user.Id, models.DEVICE_TYPE_WEB)  
79 - // if err != nil && err != orm.ErrNoRows {  
80 - // e := fmt.Errorf("ReadUserAuthByDevice(%d,%d) err:%s", user.Id, models.DEVICE_TYPE_WEB, err)  
81 - // log.Error(e.Error())  
82 - // return protocol.NewErrWithMessage("1", e)  
83 - // }  
84 - // var (  
85 - // authcode string  
86 - // authcodeExp time.Time  
87 - // )  
88 - // authcode = utils.GenerateIDByUUID()  
89 - // authcodeExp = time.Now().Add(time.Duration(models.AUTHCODE_TIME) * time.Second)  
90 - // if err == orm.ErrNoRows {  
91 - // uAuth := &models.UserAuth{  
92 - // UserId: user.Id,  
93 - // AuthCode: authcode,  
94 - // AuthCodeExp: authcodeExp,  
95 - // CreateAt: time.Now(),  
96 - // }  
97 - // _, err = models.AddUserAuth(uAuth)  
98 - // if err != nil {  
99 - // e := fmt.Errorf("AddUserAuth err:%s", err)  
100 - // log.Error(e.Error())  
101 - // return protocol.NewErrWithMessage("1", e)  
102 - // }  
103 - // }  
104 - // if err == nil {  
105 - // uAuth.AuthCode = authcode  
106 - // uAuth.AuthCodeExp = authcodeExp  
107 - // uAuth.UpdateAt = time.Now()  
108 - // err = models.UpdateUserAuthById(uAuth)  
109 - // if err != nil {  
110 - // e := fmt.Errorf("UpdateUserAuthById err:%s", err)  
111 - // log.Error(e.Error())  
112 - // return protocol.NewErrWithMessage("1", e)  
113 - // }  
114 - // }  
115 -  
116 - return nil 78 + return loginToken, nil
117 } 79 }
118 80
119 //ResetLoginToken token存数据库 81 //ResetLoginToken token存数据库
120 -func ResetLoginToken(loginToken protocol.LoginAuthToken, userid int64, companyid int64) error { 82 +func ResetLoginToken(loginToken protocol.LoginAuthToken) error {
121 var ( 83 var (
122 - uAuth *models.UserAuth  
123 - err error  
124 - nowTime = time.Now() 84 + uAuth *models.UserAuth
  85 + err error
  86 + nowTime = time.Now()
  87 + userid int64
  88 + companyid int64
  89 + mtoken *MyToken
125 ) 90 )
  91 + mtoken, err = ValidJWTToken(loginToken.AccessToken)
  92 + if err != nil {
  93 + log.Error("jwt err:%s", err)
  94 + return protocol.NewErrWithMessage("1")
  95 + }
  96 + userid = mtoken.UID
  97 + companyid = mtoken.CompanyID
126 uAuth, err = models.GetUserAuthByUser(userid) 98 uAuth, err = models.GetUserAuthByUser(userid)
127 if err != nil && err != orm.ErrNoRows { 99 if err != nil && err != orm.ErrNoRows {
128 - return err 100 + e := fmt.Errorf("GetUserAuthByUser err:%s", err)
  101 + log.Error(e.Error())
  102 + return protocol.NewErrWithMessage("1", e)
129 } 103 }
130 if err == orm.ErrNoRows { 104 if err == orm.ErrNoRows {
131 //添加用户授权 105 //添加用户授权
@@ -141,11 +115,43 @@ func ResetLoginToken(loginToken protocol.LoginAuthToken, userid int64, companyid @@ -141,11 +115,43 @@ func ResetLoginToken(loginToken protocol.LoginAuthToken, userid int64, companyid
141 DeviceType: models.DEVICE_TYPE_WEB, 115 DeviceType: models.DEVICE_TYPE_WEB,
142 } 116 }
143 _, err = models.AddUserAuth(uAuth) 117 _, err = models.AddUserAuth(uAuth)
144 - return err 118 + if err != nil {
  119 + e := fmt.Errorf("AddUserAuth err:%s", err)
  120 + log.Error(e.Error())
  121 + return protocol.NewErrWithMessage("1", e)
  122 + }
145 } 123 }
146 //更新用户授权 124 //更新用户授权
147 - 125 + uAuth.CurrentCompanyId = companyid
  126 + uAuth.AccessToken = loginToken.AccessToken
  127 + uAuth.AccessTokenExp = time.Unix(loginToken.ExpiresIn, 0)
  128 + uAuth.RefreshToken = loginToken.RefreshToken
  129 + uAuth.RefreshTokenExp = time.Unix(loginToken.RefreshExpires, 0)
  130 + uAuth.UpdateAt = nowTime
  131 + upCol := []string{"CurrentCompanyId", "AccessToken", "RefreshToken", "RefreshTokenExp", "UpdateAt"}
  132 + if err = models.UpdateUserAuthById(uAuth, upCol); err != nil {
  133 + e := fmt.Errorf("UpdateUserAuthById err:%s", err)
  134 + log.Error(e.Error())
  135 + return protocol.NewErrWithMessage("1", e)
  136 + }
148 return nil 137 return nil
149 } 138 }
150 139
151 -//TODO token 存redis 140 +// token 存redis
  141 +func ResetLoginTokenRedis(loginToken protocol.LoginAuthToken) error {
  142 + var (
  143 + mtoken *MyToken
  144 + err error
  145 + )
  146 + mtoken, err = ValidJWTToken(loginToken.AccessToken)
  147 + if err != nil {
  148 + log.Error("jwt err:%s", err)
  149 + return protocol.NewErrWithMessage("1")
  150 + }
  151 + err = redisdata.SetLoginToken(loginToken, mtoken.UID)
  152 + if err != nil {
  153 + log.Error(" redisdata.SetLoginToken err:%s", err)
  154 + }
  155 + return nil
  156 +
  157 +}
@@ -56,6 +56,14 @@ func ValidJWTToken(tokenString string) (*MyToken, error) { @@ -56,6 +56,14 @@ func ValidJWTToken(tokenString string) (*MyToken, error) {
56 return nil, fmt.Errorf("token Valid fail") 56 return nil, fmt.Errorf("token Valid fail")
57 } 57 }
58 58
  59 +func IsJwtErrorExpired(err error) bool {
  60 + ve, ok := err.(*jwt.ValidationError)
  61 + if ok && ve.Errors == jwt.ValidationErrorExpired {
  62 + return true
  63 + }
  64 + return false
  65 +}
  66 +
59 func GenerateAuthToken(uid int64, companyid int64) (protocol.LoginAuthToken, error) { 67 func GenerateAuthToken(uid int64, companyid int64) (protocol.LoginAuthToken, error) {
60 var ( 68 var (
61 authToken protocol.LoginAuthToken 69 authToken protocol.LoginAuthToken
@@ -66,11 +74,11 @@ func GenerateAuthToken(uid int64, companyid int64) (protocol.LoginAuthToken, err @@ -66,11 +74,11 @@ func GenerateAuthToken(uid int64, companyid int64) (protocol.LoginAuthToken, err
66 err error 74 err error
67 nowtime = time.Now() 75 nowtime = time.Now()
68 ) 76 )
69 - accesstoken, err = CreateJWTToken(uid, companyid, expiresIn+10) 77 + accesstoken, err = CreateJWTToken(uid, companyid, expiresIn+2)
70 if err != nil { 78 if err != nil {
71 return authToken, err 79 return authToken, err
72 } 80 }
73 - refreshtoken, err = CreateJWTToken(uid, companyid, refreshExpires+10) 81 + refreshtoken, err = CreateJWTToken(uid, companyid, refreshExpires+2)
74 if err != nil { 82 if err != nil {
75 return authToken, err 83 return authToken, err
76 } 84 }
  1 +package ucenter
  2 +
  3 +import (
  4 + "encoding/json"
  5 + "errors"
  6 +)
  7 +
  8 +type RequesLogin struct {
  9 + Password string `json:"password"`
  10 + Phone string `json:"phone"`
  11 +}
  12 +
  13 +//Format 实现IUCenterParam接口
  14 +func (r RequesLogin) Format() []byte {
  15 + var v []byte
  16 + v, _ = json.Marshal(r)
  17 + return v
  18 +}
  19 +
  20 +//Format 实现IUCenterParam接口
  21 +func (r RequesLogin) GetPath() (string, string) {
  22 + return "/auth/login", "POST"
  23 +}
  24 +
  25 +//Format 实现IUCenterParam接口
  26 +func (r RequesLogin) Valid() error {
  27 + if len(r.Password) == 0 {
  28 + return errors.New("len(r.Password) == 0")
  29 + }
  30 + if len(r.Phone) == 0 {
  31 + return errors.New("len(r.Phone == 0")
  32 + }
  33 + return nil
  34 +}
  35 +
  36 +type RequestAddUser struct {
  37 + Phone string `json:"phone"`
  38 + RegIm bool `json:"regIm"`
  39 + Nickname string `json:"nickname"`
  40 + Avatar string `json:"avatar"`
  41 + Password string `json:"password"`
  42 +}
  43 +
  44 +//Format 实现IUCenterParam接口
  45 +func (r RequestAddUser) Format() []byte {
  46 + var v []byte
  47 + v, _ = json.Marshal(r)
  48 + return v
  49 +}
  50 +
  51 +//Format 实现IUCenterParam接口
  52 +func (r RequestAddUser) GetPath() (string, string) {
  53 + return "/users", "POST"
  54 +}
  55 +
  56 +//Format 实现IUCenterParam接口
  57 +func (r RequestAddUser) Valid() error {
  58 + if len(r.Password) == 0 {
  59 + return errors.New("len(r.Password) == 0")
  60 + }
  61 + if len(r.Phone) == 0 {
  62 + return errors.New("len(r.Phone == 0")
  63 + }
  64 + return nil
  65 +}
  1 +package ucenter
  2 +
  3 +import (
  4 + "bytes"
  5 + "io/ioutil"
  6 + "net/http"
  7 + "time"
  8 +)
  9 +
  10 +type IUCenterParam interface {
  11 + Format() []byte
  12 + GetPath() (string, string) //返回请求路径path,请求方式mathod
  13 + Valid() error
  14 +}
  15 +
  16 +type UCenterClient struct {
  17 + appKey string
  18 + baseUrl string
  19 +}
  20 +
  21 +func (client UCenterClient) buildHeader() http.Header {
  22 + var h = http.Header{}
  23 + h.Set("Content-Type", "application/json")
  24 + h.Set("appKey", client.appKey)
  25 + return h
  26 +}
  27 +
  28 +//httpDo post发送json
  29 +func (client UCenterClient) httpDo(path string, mathod string, posts []byte) ([]byte, error) {
  30 + httpclient := http.Client{
  31 + Timeout: 5 * time.Second, //请求超时时间5秒
  32 + }
  33 + reqURL := client.baseUrl + path
  34 + req, err := http.NewRequest(mathod, reqURL, bytes.NewReader(posts))
  35 + if err != nil {
  36 + return nil, err
  37 + }
  38 + req.Header = client.buildHeader()
  39 + resp, err := httpclient.Do(req)
  40 + if err != nil {
  41 + return nil, err
  42 + }
  43 + defer resp.Body.Close()
  44 + body, err := ioutil.ReadAll(resp.Body)
  45 + if err != nil {
  46 + return nil, err
  47 + }
  48 +
  49 + return body, nil
  50 +}
  51 +
  52 +func (client UCenterClient) Call(param IUCenterParam) ([]byte, error) {
  53 + path, mathod := param.GetPath()
  54 + return client.httpDo(path, mathod, param.Format())
  55 +}
1 package redisdata 1 package redisdata
2 2
3 const ( 3 const (
4 - Key_App_Switch string = "app_switch"  
5 - Value_Switch_On string = "on"  
6 - Value_Switch_Off string = "off" 4 + KEY_PREFIX string = "oppmg_"
  5 + KEY_USER_TOKEN string = "token"
7 ) 6 )
1 package redisdata 1 package redisdata
  2 +
  3 +import (
  4 + "encoding/json"
  5 + "errors"
  6 + "fmt"
  7 + "oppmg/common/log"
  8 + "oppmg/common/redis"
  9 + "oppmg/protocol"
  10 + "time"
  11 +)
  12 +
  13 +func SetLoginToken(param protocol.LoginAuthToken, userid int64) error {
  14 + client := redis.GetRedis()
  15 + var (
  16 + key string
  17 + value []byte
  18 + err error
  19 + exp int64
  20 + nowTime = time.Now().Unix()
  21 + )
  22 + exp = param.RefreshExpires - nowTime
  23 + key = fmt.Sprintf("%s%s:%d", KEY_PREFIX, KEY_USER_TOKEN, userid)
  24 + value, _ = json.Marshal(param)
  25 + err = client.Set(key, string(value), time.Duration(exp)*time.Second).Err()
  26 + return err
  27 +}
  28 +
  29 +func GetLoginToken(userid int64) (protocol.LoginAuthToken, error) {
  30 + client := redis.GetRedis()
  31 + var (
  32 + key string
  33 + value string
  34 + err error
  35 + result protocol.LoginAuthToken
  36 + )
  37 + key = fmt.Sprintf("%s%s:%d", KEY_PREFIX, KEY_USER_TOKEN, userid)
  38 + value = client.Get(key).String()
  39 + if len(value) == 0 {
  40 + return result, errors.New("Token not found")
  41 + }
  42 + err = json.Unmarshal([]byte(value), &result)
  43 + if err != nil {
  44 + log.Error("Unmarshal redis value:%s err:%s", value, err)
  45 + return result, err
  46 + }
  47 + return result, nil
  48 +}
  49 +
  50 +func ExistLoginToken(userid int64) bool {
  51 + client := redis.GetRedis()
  52 + key := fmt.Sprintf("%s%s:%d", KEY_PREFIX, KEY_USER_TOKEN, userid)
  53 + value := client.Exists(key).Val()
  54 + if value > 0 {
  55 + return true
  56 + }
  57 + return false
  58 +}
1 language: go 1 language: go
2 2
3 go: 3 go:
4 - - "1.9.2"  
5 - - "1.10.3" 4 + - "1.10.x"
  5 + - "1.11.x"
6 services: 6 services:
7 - redis-server 7 - redis-server
8 - mysql 8 - mysql
@@ -34,6 +34,7 @@ install: @@ -34,6 +34,7 @@ install:
34 - go get github.com/gogo/protobuf/proto 34 - go get github.com/gogo/protobuf/proto
35 - go get github.com/Knetic/govaluate 35 - go get github.com/Knetic/govaluate
36 - go get github.com/casbin/casbin 36 - go get github.com/casbin/casbin
  37 + - go get github.com/elazarl/go-bindata-assetfs
37 - go get -u honnef.co/go/tools/cmd/gosimple 38 - go get -u honnef.co/go/tools/cmd/gosimple
38 - go get -u github.com/mdempsky/unconvert 39 - go get -u github.com/mdempsky/unconvert
39 - go get -u github.com/gordonklaus/ineffassign 40 - go get -u github.com/gordonklaus/ineffassign
@@ -44,12 +45,12 @@ before_script: @@ -44,12 +45,12 @@ before_script:
44 - sh -c "if [ '$ORM_DRIVER' = 'postgres' ]; then psql -c 'create database orm_test;' -U postgres; fi" 45 - sh -c "if [ '$ORM_DRIVER' = 'postgres' ]; then psql -c 'create database orm_test;' -U postgres; fi"
45 - sh -c "if [ '$ORM_DRIVER' = 'mysql' ]; then mysql -u root -e 'create database orm_test;'; fi" 46 - sh -c "if [ '$ORM_DRIVER' = 'mysql' ]; then mysql -u root -e 'create database orm_test;'; fi"
46 - sh -c "if [ '$ORM_DRIVER' = 'sqlite' ]; then touch $TRAVIS_BUILD_DIR/orm_test.db; fi" 47 - sh -c "if [ '$ORM_DRIVER' = 'sqlite' ]; then touch $TRAVIS_BUILD_DIR/orm_test.db; fi"
47 - - sh -c "if [ $(go version) == *1.[5-9]* ]; then go get github.com/golang/lint/golint; golint ./...; fi"  
48 - - sh -c "if [ $(go version) == *1.[5-9]* ]; then go tool vet .; fi" 48 + - sh -c "go get github.com/golang/lint/golint; golint ./...;"
  49 + - sh -c "go list ./... | grep -v vendor | xargs go vet -v"
49 - mkdir -p res/var 50 - mkdir -p res/var
50 - ./ssdb/ssdb-server ./ssdb/ssdb.conf -d 51 - ./ssdb/ssdb-server ./ssdb/ssdb.conf -d
51 after_script: 52 after_script:
52 - -killall -w ssdb-server 53 + - killall -w ssdb-server
53 - rm -rf ./res/var/* 54 - rm -rf ./res/var/*
54 script: 55 script:
55 - go test -v ./... 56 - go test -v ./...
@@ -59,4 +60,4 @@ script: @@ -59,4 +60,4 @@ script:
59 - find . ! \( -path './vendor' -prune \) -type f -name '*.go' -print0 | xargs -0 gofmt -l -s 60 - find . ! \( -path './vendor' -prune \) -type f -name '*.go' -print0 | xargs -0 gofmt -l -s
60 - golint ./... 61 - golint ./...
61 addons: 62 addons:
62 - postgresql: "9.4" 63 + postgresql: "9.6"
@@ -4,6 +4,8 @@ @@ -4,6 +4,8 @@
4 beego is used for rapid development of RESTful APIs, web apps and backend services in Go. 4 beego is used for rapid development of RESTful APIs, web apps and backend services in Go.
5 It is inspired by Tornado, Sinatra and Flask. beego has some Go-specific features such as interfaces and struct embedding. 5 It is inspired by Tornado, Sinatra and Flask. beego has some Go-specific features such as interfaces and struct embedding.
6 6
  7 + Response time ranking: [web-frameworks](https://github.com/the-benchmarker/web-frameworks).
  8 +
7 ###### More info at [beego.me](http://beego.me). 9 ###### More info at [beego.me](http://beego.me).
8 10
9 ## Quick Start 11 ## Quick Start
@@ -20,11 +20,10 @@ import ( @@ -20,11 +20,10 @@ import (
20 "fmt" 20 "fmt"
21 "net/http" 21 "net/http"
22 "os" 22 "os"
  23 + "reflect"
23 "text/template" 24 "text/template"
24 "time" 25 "time"
25 26
26 - "reflect"  
27 -  
28 "github.com/astaxie/beego/grace" 27 "github.com/astaxie/beego/grace"
29 "github.com/astaxie/beego/logs" 28 "github.com/astaxie/beego/logs"
30 "github.com/astaxie/beego/toolbox" 29 "github.com/astaxie/beego/toolbox"
@@ -35,7 +34,7 @@ import ( @@ -35,7 +34,7 @@ import (
35 var beeAdminApp *adminApp 34 var beeAdminApp *adminApp
36 35
37 // FilterMonitorFunc is default monitor filter when admin module is enable. 36 // FilterMonitorFunc is default monitor filter when admin module is enable.
38 -// if this func returns, admin module records qbs for this request by condition of this function logic. 37 +// if this func returns, admin module records qps for this request by condition of this function logic.
39 // usage: 38 // usage:
40 // func MyFilterMonitor(method, requestPath string, t time.Duration, pattern string, statusCode int) bool { 39 // func MyFilterMonitor(method, requestPath string, t time.Duration, pattern string, statusCode int) bool {
41 // if method == "POST" { 40 // if method == "POST" {
@@ -67,18 +66,18 @@ func init() { @@ -67,18 +66,18 @@ func init() {
67 66
68 // AdminIndex is the default http.Handler for admin module. 67 // AdminIndex is the default http.Handler for admin module.
69 // it matches url pattern "/". 68 // it matches url pattern "/".
70 -func adminIndex(rw http.ResponseWriter, r *http.Request) { 69 +func adminIndex(rw http.ResponseWriter, _ *http.Request) {
71 execTpl(rw, map[interface{}]interface{}{}, indexTpl, defaultScriptsTpl) 70 execTpl(rw, map[interface{}]interface{}{}, indexTpl, defaultScriptsTpl)
72 } 71 }
73 72
74 -// QpsIndex is the http.Handler for writing qbs statistics map result info in http.ResponseWriter.  
75 -// it's registered with url pattern "/qbs" in admin module.  
76 -func qpsIndex(rw http.ResponseWriter, r *http.Request) { 73 +// QpsIndex is the http.Handler for writing qps statistics map result info in http.ResponseWriter.
  74 +// it's registered with url pattern "/qps" in admin module.
  75 +func qpsIndex(rw http.ResponseWriter, _ *http.Request) {
77 data := make(map[interface{}]interface{}) 76 data := make(map[interface{}]interface{})
78 data["Content"] = toolbox.StatisticsMap.GetMap() 77 data["Content"] = toolbox.StatisticsMap.GetMap()
79 78
80 // do html escape before display path, avoid xss 79 // do html escape before display path, avoid xss
81 - if content, ok := (data["Content"]).(map[string]interface{}); ok { 80 + if content, ok := (data["Content"]).(M); ok {
82 if resultLists, ok := (content["Data"]).([][]string); ok { 81 if resultLists, ok := (content["Data"]).([][]string); ok {
83 for i := range resultLists { 82 for i := range resultLists {
84 if len(resultLists[i]) > 0 { 83 if len(resultLists[i]) > 0 {
@@ -104,7 +103,7 @@ func listConf(rw http.ResponseWriter, r *http.Request) { @@ -104,7 +103,7 @@ func listConf(rw http.ResponseWriter, r *http.Request) {
104 data := make(map[interface{}]interface{}) 103 data := make(map[interface{}]interface{})
105 switch command { 104 switch command {
106 case "conf": 105 case "conf":
107 - m := make(map[string]interface{}) 106 + m := make(M)
108 list("BConfig", BConfig, m) 107 list("BConfig", BConfig, m)
109 m["AppConfigPath"] = appConfigPath 108 m["AppConfigPath"] = appConfigPath
110 m["AppConfigProvider"] = appConfigProvider 109 m["AppConfigProvider"] = appConfigProvider
@@ -128,14 +127,14 @@ func listConf(rw http.ResponseWriter, r *http.Request) { @@ -128,14 +127,14 @@ func listConf(rw http.ResponseWriter, r *http.Request) {
128 execTpl(rw, data, routerAndFilterTpl, defaultScriptsTpl) 127 execTpl(rw, data, routerAndFilterTpl, defaultScriptsTpl)
129 case "filter": 128 case "filter":
130 var ( 129 var (
131 - content = map[string]interface{}{ 130 + content = M{
132 "Fields": []string{ 131 "Fields": []string{
133 "Router Pattern", 132 "Router Pattern",
134 "Filter Function", 133 "Filter Function",
135 }, 134 },
136 } 135 }
137 filterTypes = []string{} 136 filterTypes = []string{}
138 - filterTypeData = make(map[string]interface{}) 137 + filterTypeData = make(M)
139 ) 138 )
140 139
141 if BeeApp.Handlers.enableFilter { 140 if BeeApp.Handlers.enableFilter {
@@ -173,7 +172,7 @@ func listConf(rw http.ResponseWriter, r *http.Request) { @@ -173,7 +172,7 @@ func listConf(rw http.ResponseWriter, r *http.Request) {
173 } 172 }
174 } 173 }
175 174
176 -func list(root string, p interface{}, m map[string]interface{}) { 175 +func list(root string, p interface{}, m M) {
177 pt := reflect.TypeOf(p) 176 pt := reflect.TypeOf(p)
178 pv := reflect.ValueOf(p) 177 pv := reflect.ValueOf(p)
179 if pt.Kind() == reflect.Ptr { 178 if pt.Kind() == reflect.Ptr {
@@ -196,11 +195,11 @@ func list(root string, p interface{}, m map[string]interface{}) { @@ -196,11 +195,11 @@ func list(root string, p interface{}, m map[string]interface{}) {
196 } 195 }
197 196
198 // PrintTree prints all registered routers. 197 // PrintTree prints all registered routers.
199 -func PrintTree() map[string]interface{} { 198 +func PrintTree() M {
200 var ( 199 var (
201 - content = map[string]interface{}{} 200 + content = M{}
202 methods = []string{} 201 methods = []string{}
203 - methodsData = make(map[string]interface{}) 202 + methodsData = make(M)
204 ) 203 )
205 for method, t := range BeeApp.Handlers.routers { 204 for method, t := range BeeApp.Handlers.routers {
206 205
@@ -291,12 +290,12 @@ func profIndex(rw http.ResponseWriter, r *http.Request) { @@ -291,12 +290,12 @@ func profIndex(rw http.ResponseWriter, r *http.Request) {
291 290
292 // Healthcheck is a http.Handler calling health checking and showing the result. 291 // Healthcheck is a http.Handler calling health checking and showing the result.
293 // it's in "/healthcheck" pattern in admin module. 292 // it's in "/healthcheck" pattern in admin module.
294 -func healthcheck(rw http.ResponseWriter, req *http.Request) { 293 +func healthcheck(rw http.ResponseWriter, _ *http.Request) {
295 var ( 294 var (
296 result []string 295 result []string
297 data = make(map[interface{}]interface{}) 296 data = make(map[interface{}]interface{})
298 resultList = new([][]string) 297 resultList = new([][]string)
299 - content = map[string]interface{}{ 298 + content = M{
300 "Fields": []string{"Name", "Message", "Status"}, 299 "Fields": []string{"Name", "Message", "Status"},
301 } 300 }
302 ) 301 )
@@ -344,7 +343,7 @@ func taskStatus(rw http.ResponseWriter, req *http.Request) { @@ -344,7 +343,7 @@ func taskStatus(rw http.ResponseWriter, req *http.Request) {
344 } 343 }
345 344
346 // List Tasks 345 // List Tasks
347 - content := make(map[string]interface{}) 346 + content := make(M)
348 resultList := new([][]string) 347 resultList := new([][]string)
349 var fields = []string{ 348 var fields = []string{
350 "Task Name", 349 "Task Name",
@@ -23,7 +23,7 @@ import ( @@ -23,7 +23,7 @@ import (
23 23
24 const ( 24 const (
25 // VERSION represent beego web framework version. 25 // VERSION represent beego web framework version.
26 - VERSION = "1.10.0" 26 + VERSION = "1.11.1"
27 27
28 // DEV is for develop 28 // DEV is for develop
29 DEV = "dev" 29 DEV = "dev"
@@ -31,7 +31,10 @@ const ( @@ -31,7 +31,10 @@ const (
31 PROD = "prod" 31 PROD = "prod"
32 ) 32 )
33 33
34 -//hook function to run 34 +// M is Map shortcut
  35 +type M map[string]interface{}
  36 +
  37 +// Hook function to run
35 type hookfunc func() error 38 type hookfunc func() error
36 39
37 var ( 40 var (
@@ -78,15 +78,37 @@ func (ini *IniConfig) parseData(dir string, data []byte) (*IniConfigContainer, e @@ -78,15 +78,37 @@ func (ini *IniConfig) parseData(dir string, data []byte) (*IniConfigContainer, e
78 } 78 }
79 } 79 }
80 section := defaultSection 80 section := defaultSection
  81 + tmpBuf := bytes.NewBuffer(nil)
81 for { 82 for {
82 - line, _, err := buf.ReadLine()  
83 - if err == io.EOF {  
84 - break 83 + tmpBuf.Reset()
  84 +
  85 + shouldBreak := false
  86 + for {
  87 + tmp, isPrefix, err := buf.ReadLine()
  88 + if err == io.EOF {
  89 + shouldBreak = true
  90 + break
  91 + }
  92 +
  93 + //It might be a good idea to throw a error on all unknonw errors?
  94 + if _, ok := err.(*os.PathError); ok {
  95 + return nil, err
  96 + }
  97 +
  98 + tmpBuf.Write(tmp)
  99 + if isPrefix {
  100 + continue
  101 + }
  102 +
  103 + if !isPrefix {
  104 + break
  105 + }
85 } 106 }
86 - //It might be a good idea to throw a error on all unknonw errors?  
87 - if _, ok := err.(*os.PathError); ok {  
88 - return nil, err 107 + if shouldBreak {
  108 + break
89 } 109 }
  110 +
  111 + line := tmpBuf.Bytes()
90 line = bytes.TrimSpace(line) 112 line = bytes.TrimSpace(line)
91 if bytes.Equal(line, bEmpty) { 113 if bytes.Equal(line, bEmpty) {
92 continue 114 continue
@@ -38,6 +38,14 @@ import ( @@ -38,6 +38,14 @@ import (
38 "github.com/astaxie/beego/utils" 38 "github.com/astaxie/beego/utils"
39 ) 39 )
40 40
  41 +//commonly used mime-types
  42 +const (
  43 + ApplicationJSON = "application/json"
  44 + ApplicationXML = "application/xml"
  45 + ApplicationYAML = "application/x-yaml"
  46 + TextXML = "text/xml"
  47 +)
  48 +
41 // NewContext return the Context with Input and Output 49 // NewContext return the Context with Input and Output
42 func NewContext() *Context { 50 func NewContext() *Context {
43 return &Context{ 51 return &Context{
@@ -193,6 +201,7 @@ type Response struct { @@ -193,6 +201,7 @@ type Response struct {
193 http.ResponseWriter 201 http.ResponseWriter
194 Started bool 202 Started bool
195 Status int 203 Status int
  204 + Elapsed time.Duration
196 } 205 }
197 206
198 func (r *Response) reset(rw http.ResponseWriter) { 207 func (r *Response) reset(rw http.ResponseWriter) {
@@ -244,3 +253,11 @@ func (r *Response) CloseNotify() <-chan bool { @@ -244,3 +253,11 @@ func (r *Response) CloseNotify() <-chan bool {
244 } 253 }
245 return nil 254 return nil
246 } 255 }
  256 +
  257 +// Pusher http.Pusher
  258 +func (r *Response) Pusher() (pusher http.Pusher) {
  259 + if pusher, ok := r.ResponseWriter.(http.Pusher); ok {
  260 + return pusher
  261 + }
  262 + return nil
  263 +}
@@ -206,7 +206,7 @@ func (output *BeegoOutput) JSON(data interface{}, hasIndent bool, encoding bool) @@ -206,7 +206,7 @@ func (output *BeegoOutput) JSON(data interface{}, hasIndent bool, encoding bool)
206 206
207 // YAML writes yaml to response body. 207 // YAML writes yaml to response body.
208 func (output *BeegoOutput) YAML(data interface{}) error { 208 func (output *BeegoOutput) YAML(data interface{}) error {
209 - output.Header("Content-Type", "application/application/x-yaml; charset=utf-8") 209 + output.Header("Content-Type", "application/x-yaml; charset=utf-8")
210 var content []byte 210 var content []byte
211 var err error 211 var err error
212 content, err = yaml.Marshal(data) 212 content, err = yaml.Marshal(data)
@@ -260,6 +260,19 @@ func (output *BeegoOutput) XML(data interface{}, hasIndent bool) error { @@ -260,6 +260,19 @@ func (output *BeegoOutput) XML(data interface{}, hasIndent bool) error {
260 return output.Body(content) 260 return output.Body(content)
261 } 261 }
262 262
  263 +// ServeFormatted serve YAML, XML OR JSON, depending on the value of the Accept header
  264 +func (output *BeegoOutput) ServeFormatted(data interface{}, hasIndent bool, hasEncode ...bool) {
  265 + accept := output.Context.Input.Header("Accept")
  266 + switch accept {
  267 + case ApplicationYAML:
  268 + output.YAML(data)
  269 + case ApplicationXML, TextXML:
  270 + output.XML(data, hasIndent)
  271 + default:
  272 + output.JSON(data, hasIndent, len(hasEncode) > 0 && hasEncode[0])
  273 + }
  274 +}
  275 +
263 // Download forces response for download file. 276 // Download forces response for download file.
264 // it prepares the download response header automatically. 277 // it prepares the download response header automatically.
265 func (output *BeegoOutput) Download(file string, filename ...string) { 278 func (output *BeegoOutput) Download(file string, filename ...string) {
@@ -32,14 +32,6 @@ import ( @@ -32,14 +32,6 @@ import (
32 "github.com/astaxie/beego/session" 32 "github.com/astaxie/beego/session"
33 ) 33 )
34 34
35 -//commonly used mime-types  
36 -const (  
37 - applicationJSON = "application/json"  
38 - applicationXML = "application/xml"  
39 - applicationYAML = "application/x-yaml"  
40 - textXML = "text/xml"  
41 -)  
42 -  
43 var ( 35 var (
44 // ErrAbort custom error when user stop request handler manually. 36 // ErrAbort custom error when user stop request handler manually.
45 ErrAbort = errors.New("User stop run") 37 ErrAbort = errors.New("User stop run")
@@ -47,10 +39,37 @@ var ( @@ -47,10 +39,37 @@ var (
47 GlobalControllerRouter = make(map[string][]ControllerComments) 39 GlobalControllerRouter = make(map[string][]ControllerComments)
48 ) 40 )
49 41
  42 +// ControllerFilter store the filter for controller
  43 +type ControllerFilter struct {
  44 + Pattern string
  45 + Pos int
  46 + Filter FilterFunc
  47 + ReturnOnOutput bool
  48 + ResetParams bool
  49 +}
  50 +
  51 +// ControllerFilterComments store the comment for controller level filter
  52 +type ControllerFilterComments struct {
  53 + Pattern string
  54 + Pos int
  55 + Filter string // NOQA
  56 + ReturnOnOutput bool
  57 + ResetParams bool
  58 +}
  59 +
  60 +// ControllerImportComments store the import comment for controller needed
  61 +type ControllerImportComments struct {
  62 + ImportPath string
  63 + ImportAlias string
  64 +}
  65 +
50 // ControllerComments store the comment for the controller method 66 // ControllerComments store the comment for the controller method
51 type ControllerComments struct { 67 type ControllerComments struct {
52 Method string 68 Method string
53 Router string 69 Router string
  70 + Filters []*ControllerFilter
  71 + ImportComments []*ControllerImportComments
  72 + FilterComments []*ControllerFilterComments
54 AllowHTTPMethods []string 73 AllowHTTPMethods []string
55 Params []map[string]string 74 Params []map[string]string
56 MethodParams []*param.MethodParam 75 MethodParams []*param.MethodParam
@@ -275,16 +294,15 @@ func (c *Controller) viewPath() string { @@ -275,16 +294,15 @@ func (c *Controller) viewPath() string {
275 func (c *Controller) Redirect(url string, code int) { 294 func (c *Controller) Redirect(url string, code int) {
276 logAccess(c.Ctx, nil, code) 295 logAccess(c.Ctx, nil, code)
277 c.Ctx.Redirect(code, url) 296 c.Ctx.Redirect(code, url)
278 - panic(ErrAbort)  
279 } 297 }
280 298
281 -// Set the data depending on the accepted 299 +// SetData set the data depending on the accepted
282 func (c *Controller) SetData(data interface{}) { 300 func (c *Controller) SetData(data interface{}) {
283 accept := c.Ctx.Input.Header("Accept") 301 accept := c.Ctx.Input.Header("Accept")
284 switch accept { 302 switch accept {
285 - case applicationJSON:  
286 - c.Data["json"] = data  
287 - case applicationXML, textXML: 303 + case context.ApplicationYAML:
  304 + c.Data["yaml"] = data
  305 + case context.ApplicationXML, context.TextXML:
288 c.Data["xml"] = data 306 c.Data["xml"] = data
289 default: 307 default:
290 c.Data["json"] = data 308 c.Data["json"] = data
@@ -333,54 +351,35 @@ func (c *Controller) URLFor(endpoint string, values ...interface{}) string { @@ -333,54 +351,35 @@ func (c *Controller) URLFor(endpoint string, values ...interface{}) string {
333 // ServeJSON sends a json response with encoding charset. 351 // ServeJSON sends a json response with encoding charset.
334 func (c *Controller) ServeJSON(encoding ...bool) { 352 func (c *Controller) ServeJSON(encoding ...bool) {
335 var ( 353 var (
336 - hasIndent = true  
337 - hasEncoding = false 354 + hasIndent = BConfig.RunMode != PROD
  355 + hasEncoding = len(encoding) > 0 && encoding[0]
338 ) 356 )
339 - if BConfig.RunMode == PROD {  
340 - hasIndent = false  
341 - }  
342 - if len(encoding) > 0 && encoding[0] {  
343 - hasEncoding = true  
344 - } 357 +
345 c.Ctx.Output.JSON(c.Data["json"], hasIndent, hasEncoding) 358 c.Ctx.Output.JSON(c.Data["json"], hasIndent, hasEncoding)
346 } 359 }
347 360
348 // ServeJSONP sends a jsonp response. 361 // ServeJSONP sends a jsonp response.
349 func (c *Controller) ServeJSONP() { 362 func (c *Controller) ServeJSONP() {
350 - hasIndent := true  
351 - if BConfig.RunMode == PROD {  
352 - hasIndent = false  
353 - } 363 + hasIndent := BConfig.RunMode != PROD
354 c.Ctx.Output.JSONP(c.Data["jsonp"], hasIndent) 364 c.Ctx.Output.JSONP(c.Data["jsonp"], hasIndent)
355 } 365 }
356 366
357 // ServeXML sends xml response. 367 // ServeXML sends xml response.
358 func (c *Controller) ServeXML() { 368 func (c *Controller) ServeXML() {
359 - hasIndent := true  
360 - if BConfig.RunMode == PROD {  
361 - hasIndent = false  
362 - } 369 + hasIndent := BConfig.RunMode != PROD
363 c.Ctx.Output.XML(c.Data["xml"], hasIndent) 370 c.Ctx.Output.XML(c.Data["xml"], hasIndent)
364 } 371 }
365 372
366 -// ServeXML sends xml response. 373 +// ServeYAML sends yaml response.
367 func (c *Controller) ServeYAML() { 374 func (c *Controller) ServeYAML() {
368 c.Ctx.Output.YAML(c.Data["yaml"]) 375 c.Ctx.Output.YAML(c.Data["yaml"])
369 } 376 }
370 377
371 -// ServeFormatted serve Xml OR Json, depending on the value of the Accept header  
372 -func (c *Controller) ServeFormatted() {  
373 - accept := c.Ctx.Input.Header("Accept")  
374 - switch accept {  
375 - case applicationJSON:  
376 - c.ServeJSON()  
377 - case applicationXML, textXML:  
378 - c.ServeXML()  
379 - case applicationYAML:  
380 - c.ServeYAML()  
381 - default:  
382 - c.ServeJSON()  
383 - } 378 +// ServeFormatted serve YAML, XML OR JSON, depending on the value of the Accept header
  379 +func (c *Controller) ServeFormatted(encoding ...bool) {
  380 + hasIndent := BConfig.RunMode != PROD
  381 + hasEncoding := len(encoding) > 0 && encoding[0]
  382 + c.Ctx.Output.ServeFormatted(c.Data, hasIndent, hasEncoding)
384 } 383 }
385 384
386 // Input returns the input data map from POST or PUT request body and query string. 385 // Input returns the input data map from POST or PUT request body and query string.
@@ -361,7 +361,7 @@ func gatewayTimeout(rw http.ResponseWriter, r *http.Request) { @@ -361,7 +361,7 @@ func gatewayTimeout(rw http.ResponseWriter, r *http.Request) {
361 361
362 func responseError(rw http.ResponseWriter, r *http.Request, errCode int, errContent string) { 362 func responseError(rw http.ResponseWriter, r *http.Request, errCode int, errContent string) {
363 t, _ := template.New("beegoerrortemp").Parse(errtpl) 363 t, _ := template.New("beegoerrortemp").Parse(errtpl)
364 - data := map[string]interface{}{ 364 + data := M{
365 "Title": http.StatusText(errCode), 365 "Title": http.StatusText(errCode),
366 "BeegoVersion": VERSION, 366 "BeegoVersion": VERSION,
367 "Content": template.HTML(errContent), 367 "Content": template.HTML(errContent),
  1 +package beego
  2 +
  3 +import (
  4 + "net/http"
  5 + "os"
  6 + "path/filepath"
  7 +)
  8 +
  9 +type FileSystem struct {
  10 +}
  11 +
  12 +func (d FileSystem) Open(name string) (http.File, error) {
  13 + return os.Open(name)
  14 +}
  15 +
  16 +// Walk walks the file tree rooted at root in filesystem, calling walkFn for each file or
  17 +// directory in the tree, including root. All errors that arise visiting files
  18 +// and directories are filtered by walkFn.
  19 +func Walk(fs http.FileSystem, root string, walkFn filepath.WalkFunc) error {
  20 +
  21 + f, err := fs.Open(root)
  22 + if err != nil {
  23 + return err
  24 + }
  25 + info, err := f.Stat()
  26 + if err != nil {
  27 + err = walkFn(root, nil, err)
  28 + } else {
  29 + err = walk(fs, root, info, walkFn)
  30 + }
  31 + if err == filepath.SkipDir {
  32 + return nil
  33 + }
  34 + return err
  35 +}
  36 +
  37 +// walk recursively descends path, calling walkFn.
  38 +func walk(fs http.FileSystem, path string, info os.FileInfo, walkFn filepath.WalkFunc) error {
  39 + var err error
  40 + if !info.IsDir() {
  41 + return walkFn(path, info, nil)
  42 + }
  43 +
  44 + dir, err := fs.Open(path)
  45 + defer dir.Close()
  46 + if err != nil {
  47 + if err1 := walkFn(path, info, err); err1 != nil {
  48 + return err1
  49 + }
  50 + return err
  51 + }
  52 + dirs, err := dir.Readdir(-1)
  53 + err1 := walkFn(path, info, err)
  54 + // If err != nil, walk can't walk into this directory.
  55 + // err1 != nil means walkFn want walk to skip this directory or stop walking.
  56 + // Therefore, if one of err and err1 isn't nil, walk will return.
  57 + if err != nil || err1 != nil {
  58 + // The caller's behavior is controlled by the return value, which is decided
  59 + // by walkFn. walkFn may ignore err and return nil.
  60 + // If walkFn returns SkipDir, it will be handled by the caller.
  61 + // So walk should return whatever walkFn returns.
  62 + return err1
  63 + }
  64 +
  65 + for _, fileInfo := range dirs {
  66 + filename := filepath.Join(path, fileInfo.Name())
  67 + if err = walk(fs, filename, fileInfo, walkFn); err != nil {
  68 + if !fileInfo.IsDir() || err != filepath.SkipDir {
  69 + return err
  70 + }
  71 + }
  72 + }
  73 + return nil
  74 +}
  1 +module github.com/astaxie/beego
  2 +
  3 +require (
  4 + github.com/Knetic/govaluate v3.0.0+incompatible // indirect
  5 + github.com/beego/goyaml2 v0.0.0-20130207012346-5545475820dd
  6 + github.com/beego/x2j v0.0.0-20131220205130-a0352aadc542
  7 + github.com/belogik/goes v0.0.0-20151229125003-e54d722c3aff
  8 + github.com/bradfitz/gomemcache v0.0.0-20180710155616-bc664df96737
  9 + github.com/casbin/casbin v1.7.0
  10 + github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58
  11 + github.com/couchbase/go-couchbase v0.0.0-20181122212707-3e9b6e1258bb
  12 + github.com/couchbase/gomemcached v0.0.0-20181122193126-5125a94a666c // indirect
  13 + github.com/couchbase/goutils v0.0.0-20180530154633-e865a1461c8a // indirect
  14 + github.com/cupcake/rdb v0.0.0-20161107195141-43ba34106c76 // indirect
  15 + github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712 // indirect
  16 + github.com/elazarl/go-bindata-assetfs v1.0.0
  17 + github.com/go-redis/redis v6.14.2+incompatible
  18 + github.com/go-sql-driver/mysql v1.4.1
  19 + github.com/gogo/protobuf v1.1.1
  20 + github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db // indirect
  21 + github.com/gomodule/redigo v2.0.0+incompatible
  22 + github.com/lib/pq v1.0.0
  23 + github.com/mattn/go-sqlite3 v1.10.0
  24 + github.com/pelletier/go-toml v1.2.0 // indirect
  25 + github.com/pkg/errors v0.8.0 // indirect
  26 + github.com/siddontang/go v0.0.0-20180604090527-bdc77568d726 // indirect
  27 + github.com/siddontang/ledisdb v0.0.0-20181029004158-becf5f38d373
  28 + github.com/siddontang/rdb v0.0.0-20150307021120-fc89ed2e418d // indirect
  29 + github.com/ssdb/gossdb v0.0.0-20180723034631-88f6b59b84ec
  30 + github.com/syndtr/goleveldb v0.0.0-20181127023241-353a9fca669c // indirect
  31 + github.com/wendal/errors v0.0.0-20130201093226-f66c77a7882b // indirect
  32 + golang.org/x/crypto v0.0.0-20181127143415-eb0de9b17e85
  33 + golang.org/x/net v0.0.0-20181114220301-adae6a3d119a // indirect
  34 + gopkg.in/yaml.v2 v2.2.1
  35 +)
  36 +
  37 +replace golang.org/x/crypto v0.0.0-20181127143415-eb0de9b17e85 => github.com/golang/crypto v0.0.0-20181127143415-eb0de9b17e85
  38 +
  39 +replace gopkg.in/yaml.v2 v2.2.1 => github.com/go-yaml/yaml v0.0.0-20180328195020-5420a8b6744d
  40 +
  41 +go 1.13
  1 +github.com/Knetic/govaluate v3.0.0+incompatible h1:7o6+MAPhYTCF0+fdvoz1xDedhRb4f6s9Tn1Tt7/WTEg=
  2 +github.com/Knetic/govaluate v3.0.0+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
  3 +github.com/beego/goyaml2 v0.0.0-20130207012346-5545475820dd h1:jZtX5jh5IOMu0fpOTC3ayh6QGSPJ/KWOv1lgPvbRw1M=
  4 +github.com/beego/goyaml2 v0.0.0-20130207012346-5545475820dd/go.mod h1:1b+Y/CofkYwXMUU0OhQqGvsY2Bvgr4j6jfT699wyZKQ=
  5 +github.com/beego/x2j v0.0.0-20131220205130-a0352aadc542 h1:nYXb+3jF6Oq/j8R/y90XrKpreCxIalBWfeyeKymgOPk=
  6 +github.com/beego/x2j v0.0.0-20131220205130-a0352aadc542/go.mod h1:kSeGC/p1AbBiEp5kat81+DSQrZenVBZXklMLaELspWU=
  7 +github.com/belogik/goes v0.0.0-20151229125003-e54d722c3aff h1:/kO0p2RTGLB8R5gub7ps0GmYpB2O8LXEoPq8tzFDCUI=
  8 +github.com/belogik/goes v0.0.0-20151229125003-e54d722c3aff/go.mod h1:PhH1ZhyCzHKt4uAasyx+ljRCgoezetRNf59CUtwUkqY=
  9 +github.com/bradfitz/gomemcache v0.0.0-20180710155616-bc664df96737 h1:rRISKWyXfVxvoa702s91Zl5oREZTrR3yv+tXrrX7G/g=
  10 +github.com/bradfitz/gomemcache v0.0.0-20180710155616-bc664df96737/go.mod h1:PmM6Mmwb0LSuEubjR8N7PtNe1KxZLtOUHtbeikc5h60=
  11 +github.com/casbin/casbin v1.7.0 h1:PuzlE8w0JBg/DhIqnkF1Dewf3z+qmUZMVN07PonvVUQ=
  12 +github.com/casbin/casbin v1.7.0/go.mod h1:c67qKN6Oum3UF5Q1+BByfFxkwKvhwW57ITjqwtzR1KE=
  13 +github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58 h1:F1EaeKL/ta07PY/k9Os/UFtwERei2/XzGemhpGnBKNg=
  14 +github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58/go.mod h1:EOBUe0h4xcZ5GoxqC5SDxFQ8gwyZPKQoEzownBlhI80=
  15 +github.com/couchbase/go-couchbase v0.0.0-20181122212707-3e9b6e1258bb h1:w3RapLhkA5+km9Z8vUkC6VCaskduJXvXwJg5neKnfDU=
  16 +github.com/couchbase/go-couchbase v0.0.0-20181122212707-3e9b6e1258bb/go.mod h1:TWI8EKQMs5u5jLKW/tsb9VwauIrMIxQG1r5fMsswK5U=
  17 +github.com/couchbase/gomemcached v0.0.0-20181122193126-5125a94a666c h1:K4FIibkr4//ziZKOKmt4RL0YImuTjLLBtwElf+F2lSQ=
  18 +github.com/couchbase/gomemcached v0.0.0-20181122193126-5125a94a666c/go.mod h1:srVSlQLB8iXBVXHgnqemxUXqN6FCvClgCMPCsjBDR7c=
  19 +github.com/couchbase/goutils v0.0.0-20180530154633-e865a1461c8a h1:Y5XsLCEhtEI8qbD9RP3Qlv5FXdTDHxZM9UPUnMRgBp8=
  20 +github.com/couchbase/goutils v0.0.0-20180530154633-e865a1461c8a/go.mod h1:BQwMFlJzDjFDG3DJUdU0KORxn88UlsOULuxLExMh3Hs=
  21 +github.com/cupcake/rdb v0.0.0-20161107195141-43ba34106c76 h1:Lgdd/Qp96Qj8jqLpq2cI1I1X7BJnu06efS+XkhRoLUQ=
  22 +github.com/cupcake/rdb v0.0.0-20161107195141-43ba34106c76/go.mod h1:vYwsqCOLxGiisLwp9rITslkFNpZD5rz43tf41QFkTWY=
  23 +github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712 h1:aaQcKT9WumO6JEJcRyTqFVq4XUZiUcKR2/GI31TOcz8=
  24 +github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
  25 +github.com/elazarl/go-bindata-assetfs v1.0.0 h1:G/bYguwHIzWq9ZoyUQqrjTmJbbYn3j3CKKpKinvZLFk=
  26 +github.com/elazarl/go-bindata-assetfs v1.0.0/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4=
  27 +github.com/go-redis/redis v6.14.2+incompatible h1:UE9pLhzmWf+xHNmZsoccjXosPicuiNaInPgym8nzfg0=
  28 +github.com/go-redis/redis v6.14.2+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
  29 +github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA=
  30 +github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
  31 +github.com/go-yaml/yaml v0.0.0-20180328195020-5420a8b6744d h1:xy93KVe+KrIIwWDEAfQBdIfsiHJkepbYsDr+VY3g9/o=
  32 +github.com/go-yaml/yaml v0.0.0-20180328195020-5420a8b6744d/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
  33 +github.com/gogo/protobuf v1.1.1 h1:72R+M5VuhED/KujmZVcIquuo8mBgX4oVda//DQb3PXo=
  34 +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
  35 +github.com/golang/crypto v0.0.0-20181127143415-eb0de9b17e85 h1:B7ZbAFz7NOmvpUE5RGtu3u0WIizy5GdvbNpEf4RPnWs=
  36 +github.com/golang/crypto v0.0.0-20181127143415-eb0de9b17e85/go.mod h1:uZvAcrsnNaCxlh1HorK5dUQHGmEKPh2H/Rl1kehswPo=
  37 +github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pOkfl+p/TAqKOfFu+7KPlMVpok/w=
  38 +github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
  39 +github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0=
  40 +github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
  41 +github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A=
  42 +github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
  43 +github.com/mattn/go-sqlite3 v1.10.0 h1:jbhqpg7tQe4SupckyijYiy0mJJ/pRyHvXf7JdWK860o=
  44 +github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
  45 +github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
  46 +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
  47 +github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw=
  48 +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
  49 +github.com/siddontang/go v0.0.0-20180604090527-bdc77568d726 h1:xT+JlYxNGqyT+XcU8iUrN18JYed2TvG9yN5ULG2jATM=
  50 +github.com/siddontang/go v0.0.0-20180604090527-bdc77568d726/go.mod h1:3yhqj7WBBfRhbBlzyOC3gUxftwsU0u8gqevxwIHQpMw=
  51 +github.com/siddontang/ledisdb v0.0.0-20181029004158-becf5f38d373 h1:p6IxqQMjab30l4lb9mmkIkkcE1yv6o0SKbPhW5pxqHI=
  52 +github.com/siddontang/ledisdb v0.0.0-20181029004158-becf5f38d373/go.mod h1:mF1DpOSOUiJRMR+FDqaqu3EBqrybQtrDDszLUZ6oxPg=
  53 +github.com/siddontang/rdb v0.0.0-20150307021120-fc89ed2e418d h1:NVwnfyR3rENtlz62bcrkXME3INVUa4lcdGt+opvxExs=
  54 +github.com/siddontang/rdb v0.0.0-20150307021120-fc89ed2e418d/go.mod h1:AMEsy7v5z92TR1JKMkLLoaOQk++LVnOKL3ScbJ8GNGA=
  55 +github.com/ssdb/gossdb v0.0.0-20180723034631-88f6b59b84ec h1:q6XVwXmKvCRHRqesF3cSv6lNqqHi0QWOvgDlSohg8UA=
  56 +github.com/ssdb/gossdb v0.0.0-20180723034631-88f6b59b84ec/go.mod h1:QBvMkMya+gXctz3kmljlUCu/yB3GZ6oee+dUozsezQE=
  57 +github.com/syndtr/goleveldb v0.0.0-20181127023241-353a9fca669c h1:3eGShk3EQf5gJCYW+WzA0TEJQd37HLOmlYF7N0YJwv0=
  58 +github.com/syndtr/goleveldb v0.0.0-20181127023241-353a9fca669c/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0=
  59 +github.com/wendal/errors v0.0.0-20130201093226-f66c77a7882b h1:0Ve0/CCjiAiyKddUMUn3RwIGlq2iTW4GuVzyoKBYO/8=
  60 +github.com/wendal/errors v0.0.0-20130201093226-f66c77a7882b/go.mod h1:Q12BUT7DqIlHRmgv3RskH+UCM/4eqVMgI0EMmlSpAXc=
  61 +golang.org/x/crypto v0.0.0-20181127143415-eb0de9b17e85 h1:et7+NAX3lLIk5qUCTA9QelBjGE/NkhzYw/mhnr0s7nI=
  62 +golang.org/x/crypto v0.0.0-20181127143415-eb0de9b17e85/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
  63 +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a h1:gOpx8G595UYyvj8UK4+OFyY4rx037g3fmfhe5SasG3U=
  64 +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
  65 +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
  66 +gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
  67 +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
@@ -11,7 +11,7 @@ import ( @@ -11,7 +11,7 @@ import (
11 "github.com/astaxie/beego/session" 11 "github.com/astaxie/beego/session"
12 ) 12 )
13 13
14 -// 14 +// register MIME type with content type
15 func registerMime() error { 15 func registerMime() error {
16 for k, v := range mimemaps { 16 for k, v := range mimemaps {
17 mime.AddExtensionType(k, v) 17 mime.AddExtensionType(k, v)
@@ -54,6 +54,12 @@ type fileLogWriter struct { @@ -54,6 +54,12 @@ type fileLogWriter struct {
54 dailyOpenDate int 54 dailyOpenDate int
55 dailyOpenTime time.Time 55 dailyOpenTime time.Time
56 56
  57 + // Rotate hourly
  58 + Hourly bool `json:"hourly"`
  59 + MaxHours int64 `json:"maxhours"`
  60 + hourlyOpenDate int
  61 + hourlyOpenTime time.Time
  62 +
57 Rotate bool `json:"rotate"` 63 Rotate bool `json:"rotate"`
58 64
59 Level int `json:"level"` 65 Level int `json:"level"`
@@ -70,6 +76,8 @@ func newFileWriter() Logger { @@ -70,6 +76,8 @@ func newFileWriter() Logger {
70 w := &fileLogWriter{ 76 w := &fileLogWriter{
71 Daily: true, 77 Daily: true,
72 MaxDays: 7, 78 MaxDays: 7,
  79 + Hourly: false,
  80 + MaxHours: 168,
73 Rotate: true, 81 Rotate: true,
74 RotatePerm: "0440", 82 RotatePerm: "0440",
75 Level: LevelTrace, 83 Level: LevelTrace,
@@ -83,15 +91,15 @@ func newFileWriter() Logger { @@ -83,15 +91,15 @@ func newFileWriter() Logger {
83 91
84 // Init file logger with json config. 92 // Init file logger with json config.
85 // jsonConfig like: 93 // jsonConfig like:
86 -// {  
87 -// "filename":"logs/beego.log",  
88 -// "maxLines":10000,  
89 -// "maxsize":1024,  
90 -// "daily":true,  
91 -// "maxDays":15,  
92 -// "rotate":true,  
93 -// "perm":"0600"  
94 -// } 94 +// {
  95 +// "filename":"logs/beego.log",
  96 +// "maxLines":10000,
  97 +// "maxsize":1024,
  98 +// "daily":true,
  99 +// "maxDays":15,
  100 +// "rotate":true,
  101 +// "perm":"0600"
  102 +// }
95 func (w *fileLogWriter) Init(jsonConfig string) error { 103 func (w *fileLogWriter) Init(jsonConfig string) error {
96 err := json.Unmarshal([]byte(jsonConfig), w) 104 err := json.Unmarshal([]byte(jsonConfig), w)
97 if err != nil { 105 if err != nil {
@@ -122,10 +130,16 @@ func (w *fileLogWriter) startLogger() error { @@ -122,10 +130,16 @@ func (w *fileLogWriter) startLogger() error {
122 return w.initFd() 130 return w.initFd()
123 } 131 }
124 132
125 -func (w *fileLogWriter) needRotate(size int, day int) bool { 133 +func (w *fileLogWriter) needRotateDaily(size int, day int) bool {
126 return (w.MaxLines > 0 && w.maxLinesCurLines >= w.MaxLines) || 134 return (w.MaxLines > 0 && w.maxLinesCurLines >= w.MaxLines) ||
127 (w.MaxSize > 0 && w.maxSizeCurSize >= w.MaxSize) || 135 (w.MaxSize > 0 && w.maxSizeCurSize >= w.MaxSize) ||
128 (w.Daily && day != w.dailyOpenDate) 136 (w.Daily && day != w.dailyOpenDate)
  137 +}
  138 +
  139 +func (w *fileLogWriter) needRotateHourly(size int, hour int) bool {
  140 + return (w.MaxLines > 0 && w.maxLinesCurLines >= w.MaxLines) ||
  141 + (w.MaxSize > 0 && w.maxSizeCurSize >= w.MaxSize) ||
  142 + (w.Hourly && hour != w.hourlyOpenDate)
129 143
130 } 144 }
131 145
@@ -134,14 +148,23 @@ func (w *fileLogWriter) WriteMsg(when time.Time, msg string, level int) error { @@ -134,14 +148,23 @@ func (w *fileLogWriter) WriteMsg(when time.Time, msg string, level int) error {
134 if level > w.Level { 148 if level > w.Level {
135 return nil 149 return nil
136 } 150 }
137 - h, d := formatTimeHeader(when)  
138 - msg = string(h) + msg + "\n" 151 + hd, d, h := formatTimeHeader(when)
  152 + msg = string(hd) + msg + "\n"
139 if w.Rotate { 153 if w.Rotate {
140 w.RLock() 154 w.RLock()
141 - if w.needRotate(len(msg), d) { 155 + if w.needRotateHourly(len(msg), h) {
  156 + w.RUnlock()
  157 + w.Lock()
  158 + if w.needRotateHourly(len(msg), h) {
  159 + if err := w.doRotate(when); err != nil {
  160 + fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.Filename, err)
  161 + }
  162 + }
  163 + w.Unlock()
  164 + } else if w.needRotateDaily(len(msg), d) {
142 w.RUnlock() 165 w.RUnlock()
143 w.Lock() 166 w.Lock()
144 - if w.needRotate(len(msg), d) { 167 + if w.needRotateDaily(len(msg), d) {
145 if err := w.doRotate(when); err != nil { 168 if err := w.doRotate(when); err != nil {
146 fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.Filename, err) 169 fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.Filename, err)
147 } 170 }
@@ -189,8 +212,12 @@ func (w *fileLogWriter) initFd() error { @@ -189,8 +212,12 @@ func (w *fileLogWriter) initFd() error {
189 w.maxSizeCurSize = int(fInfo.Size()) 212 w.maxSizeCurSize = int(fInfo.Size())
190 w.dailyOpenTime = time.Now() 213 w.dailyOpenTime = time.Now()
191 w.dailyOpenDate = w.dailyOpenTime.Day() 214 w.dailyOpenDate = w.dailyOpenTime.Day()
  215 + w.hourlyOpenTime = time.Now()
  216 + w.hourlyOpenDate = w.hourlyOpenTime.Hour()
192 w.maxLinesCurLines = 0 217 w.maxLinesCurLines = 0
193 - if w.Daily { 218 + if w.Hourly {
  219 + go w.hourlyRotate(w.hourlyOpenTime)
  220 + } else if w.Daily {
194 go w.dailyRotate(w.dailyOpenTime) 221 go w.dailyRotate(w.dailyOpenTime)
195 } 222 }
196 if fInfo.Size() > 0 && w.MaxLines > 0 { 223 if fInfo.Size() > 0 && w.MaxLines > 0 {
@@ -209,7 +236,22 @@ func (w *fileLogWriter) dailyRotate(openTime time.Time) { @@ -209,7 +236,22 @@ func (w *fileLogWriter) dailyRotate(openTime time.Time) {
209 tm := time.NewTimer(time.Duration(nextDay.UnixNano() - openTime.UnixNano() + 100)) 236 tm := time.NewTimer(time.Duration(nextDay.UnixNano() - openTime.UnixNano() + 100))
210 <-tm.C 237 <-tm.C
211 w.Lock() 238 w.Lock()
212 - if w.needRotate(0, time.Now().Day()) { 239 + if w.needRotateDaily(0, time.Now().Day()) {
  240 + if err := w.doRotate(time.Now()); err != nil {
  241 + fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.Filename, err)
  242 + }
  243 + }
  244 + w.Unlock()
  245 +}
  246 +
  247 +func (w *fileLogWriter) hourlyRotate(openTime time.Time) {
  248 + y, m, d := openTime.Add(1 * time.Hour).Date()
  249 + h, _, _ := openTime.Add(1 * time.Hour).Clock()
  250 + nextHour := time.Date(y, m, d, h, 0, 0, 0, openTime.Location())
  251 + tm := time.NewTimer(time.Duration(nextHour.UnixNano() - openTime.UnixNano() + 100))
  252 + <-tm.C
  253 + w.Lock()
  254 + if w.needRotateHourly(0, time.Now().Hour()) {
213 if err := w.doRotate(time.Now()); err != nil { 255 if err := w.doRotate(time.Now()); err != nil {
214 fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.Filename, err) 256 fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.Filename, err)
215 } 257 }
@@ -251,6 +293,8 @@ func (w *fileLogWriter) doRotate(logTime time.Time) error { @@ -251,6 +293,8 @@ func (w *fileLogWriter) doRotate(logTime time.Time) error {
251 // Find the next available number 293 // Find the next available number
252 num := w.MaxFilesCurFiles + 1 294 num := w.MaxFilesCurFiles + 1
253 fName := "" 295 fName := ""
  296 + format := ""
  297 + var openTime time.Time
254 rotatePerm, err := strconv.ParseInt(w.RotatePerm, 8, 64) 298 rotatePerm, err := strconv.ParseInt(w.RotatePerm, 8, 64)
255 if err != nil { 299 if err != nil {
256 return err 300 return err
@@ -262,17 +306,26 @@ func (w *fileLogWriter) doRotate(logTime time.Time) error { @@ -262,17 +306,26 @@ func (w *fileLogWriter) doRotate(logTime time.Time) error {
262 goto RESTART_LOGGER 306 goto RESTART_LOGGER
263 } 307 }
264 308
  309 + if w.Hourly {
  310 + format = "2006010215"
  311 + openTime = w.hourlyOpenTime
  312 + } else if w.Daily {
  313 + format = "2006-01-02"
  314 + openTime = w.dailyOpenTime
  315 + }
  316 +
265 // only when one of them be setted, then the file would be splited 317 // only when one of them be setted, then the file would be splited
266 if w.MaxLines > 0 || w.MaxSize > 0 { 318 if w.MaxLines > 0 || w.MaxSize > 0 {
267 for ; err == nil && num <= w.MaxFiles; num++ { 319 for ; err == nil && num <= w.MaxFiles; num++ {
268 - fName = w.fileNameOnly + fmt.Sprintf(".%s.%03d%s", logTime.Format("2006-01-02"), num, w.suffix) 320 + fName = w.fileNameOnly + fmt.Sprintf(".%s.%03d%s", logTime.Format(format), num, w.suffix)
269 _, err = os.Lstat(fName) 321 _, err = os.Lstat(fName)
270 } 322 }
271 } else { 323 } else {
272 - fName = w.fileNameOnly + fmt.Sprintf(".%s.%03d%s", w.dailyOpenTime.Format("2006-01-02"), num, w.suffix) 324 + fName = w.fileNameOnly + fmt.Sprintf(".%s.%03d%s", openTime.Format(format), num, w.suffix)
273 _, err = os.Lstat(fName) 325 _, err = os.Lstat(fName)
274 w.MaxFilesCurFiles = num 326 w.MaxFilesCurFiles = num
275 } 327 }
  328 +
276 // return error if the last file checked still existed 329 // return error if the last file checked still existed
277 if err == nil { 330 if err == nil {
278 return fmt.Errorf("Rotate: Cannot find free log number to rename %s", w.Filename) 331 return fmt.Errorf("Rotate: Cannot find free log number to rename %s", w.Filename)
@@ -316,13 +369,21 @@ func (w *fileLogWriter) deleteOldLog() { @@ -316,13 +369,21 @@ func (w *fileLogWriter) deleteOldLog() {
316 if info == nil { 369 if info == nil {
317 return 370 return
318 } 371 }
319 -  
320 - if !info.IsDir() && info.ModTime().Add(24 * time.Hour * time.Duration(w.MaxDays)).Before(time.Now()) {  
321 - if strings.HasPrefix(filepath.Base(path), filepath.Base(w.fileNameOnly)) &&  
322 - strings.HasSuffix(filepath.Base(path), w.suffix) {  
323 - os.Remove(path)  
324 - }  
325 - } 372 + if w.Hourly {
  373 + if !info.IsDir() && info.ModTime().Add(1 * time.Hour * time.Duration(w.MaxHours)).Before(time.Now()) {
  374 + if strings.HasPrefix(filepath.Base(path), filepath.Base(w.fileNameOnly)) &&
  375 + strings.HasSuffix(filepath.Base(path), w.suffix) {
  376 + os.Remove(path)
  377 + }
  378 + }
  379 + } else if w.Daily {
  380 + if !info.IsDir() && info.ModTime().Add(24 * time.Hour * time.Duration(w.MaxDays)).Before(time.Now()) {
  381 + if strings.HasPrefix(filepath.Base(path), filepath.Base(w.fileNameOnly)) &&
  382 + strings.HasSuffix(filepath.Base(path), w.suffix) {
  383 + os.Remove(path)
  384 + }
  385 + }
  386 + }
326 return 387 return
327 }) 388 })
328 } 389 }
@@ -33,7 +33,7 @@ func newLogWriter(wr io.Writer) *logWriter { @@ -33,7 +33,7 @@ func newLogWriter(wr io.Writer) *logWriter {
33 33
34 func (lg *logWriter) println(when time.Time, msg string) { 34 func (lg *logWriter) println(when time.Time, msg string) {
35 lg.Lock() 35 lg.Lock()
36 - h, _ := formatTimeHeader(when) 36 + h, _, _:= formatTimeHeader(when)
37 lg.writer.Write(append(append(h, msg...), '\n')) 37 lg.writer.Write(append(append(h, msg...), '\n'))
38 lg.Unlock() 38 lg.Unlock()
39 } 39 }
@@ -90,7 +90,7 @@ const ( @@ -90,7 +90,7 @@ const (
90 ns1 = `0123456789` 90 ns1 = `0123456789`
91 ) 91 )
92 92
93 -func formatTimeHeader(when time.Time) ([]byte, int) { 93 +func formatTimeHeader(when time.Time) ([]byte, int, int) {
94 y, mo, d := when.Date() 94 y, mo, d := when.Date()
95 h, mi, s := when.Clock() 95 h, mi, s := when.Clock()
96 ns := when.Nanosecond() / 1000000 96 ns := when.Nanosecond() / 1000000
@@ -123,7 +123,7 @@ func formatTimeHeader(when time.Time) ([]byte, int) { @@ -123,7 +123,7 @@ func formatTimeHeader(when time.Time) ([]byte, int) {
123 123
124 buf[23] = ' ' 124 buf[23] = ' '
125 125
126 - return buf[0:], d 126 + return buf[0:], d, h
127 } 127 }
128 128
129 var ( 129 var (
@@ -536,6 +536,8 @@ func (d *dbBase) InsertOrUpdate(q dbQuerier, mi *modelInfo, ind reflect.Value, a @@ -536,6 +536,8 @@ func (d *dbBase) InsertOrUpdate(q dbQuerier, mi *modelInfo, ind reflect.Value, a
536 updates := make([]string, len(names)) 536 updates := make([]string, len(names))
537 var conflitValue interface{} 537 var conflitValue interface{}
538 for i, v := range names { 538 for i, v := range names {
  539 + // identifier in database may not be case-sensitive, so quote it
  540 + v = fmt.Sprintf("%s%s%s", Q, v, Q)
539 marks[i] = "?" 541 marks[i] = "?"
540 valueStr := argsMap[strings.ToLower(v)] 542 valueStr := argsMap[strings.ToLower(v)]
541 if v == args0 { 543 if v == args0 {
@@ -760,7 +762,13 @@ func (d *dbBase) UpdateBatch(q dbQuerier, qs *querySet, mi *modelInfo, cond *Con @@ -760,7 +762,13 @@ func (d *dbBase) UpdateBatch(q dbQuerier, qs *querySet, mi *modelInfo, cond *Con
760 } 762 }
761 763
762 d.ins.ReplaceMarks(&query) 764 d.ins.ReplaceMarks(&query)
763 - res, err := q.Exec(query, values...) 765 + var err error
  766 + var res sql.Result
  767 + if qs != nil && qs.forContext {
  768 + res, err = q.ExecContext(qs.ctx, query, values...)
  769 + } else {
  770 + res, err = q.Exec(query, values...)
  771 + }
764 if err == nil { 772 if err == nil {
765 return res.RowsAffected() 773 return res.RowsAffected()
766 } 774 }
@@ -849,11 +857,16 @@ func (d *dbBase) DeleteBatch(q dbQuerier, qs *querySet, mi *modelInfo, cond *Con @@ -849,11 +857,16 @@ func (d *dbBase) DeleteBatch(q dbQuerier, qs *querySet, mi *modelInfo, cond *Con
849 for i := range marks { 857 for i := range marks {
850 marks[i] = "?" 858 marks[i] = "?"
851 } 859 }
852 - sql := fmt.Sprintf("IN (%s)", strings.Join(marks, ", "))  
853 - query = fmt.Sprintf("DELETE FROM %s%s%s WHERE %s%s%s %s", Q, mi.table, Q, Q, mi.fields.pk.column, Q, sql) 860 + sqlIn := fmt.Sprintf("IN (%s)", strings.Join(marks, ", "))
  861 + query = fmt.Sprintf("DELETE FROM %s%s%s WHERE %s%s%s %s", Q, mi.table, Q, Q, mi.fields.pk.column, Q, sqlIn)
854 862
855 d.ins.ReplaceMarks(&query) 863 d.ins.ReplaceMarks(&query)
856 - res, err := q.Exec(query, args...) 864 + var res sql.Result
  865 + if qs != nil && qs.forContext {
  866 + res, err = q.ExecContext(qs.ctx, query, args...)
  867 + } else {
  868 + res, err = q.Exec(query, args...)
  869 + }
857 if err == nil { 870 if err == nil {
858 num, err := res.RowsAffected() 871 num, err := res.RowsAffected()
859 if err != nil { 872 if err != nil {
@@ -926,7 +939,7 @@ func (d *dbBase) ReadBatch(q dbQuerier, qs *querySet, mi *modelInfo, cond *Condi @@ -926,7 +939,7 @@ func (d *dbBase) ReadBatch(q dbQuerier, qs *querySet, mi *modelInfo, cond *Condi
926 maps[fi.column] = true 939 maps[fi.column] = true
927 } 940 }
928 } else { 941 } else {
929 - panic(fmt.Errorf("wrong field/column name `%s`", col)) 942 + return 0, fmt.Errorf("wrong field/column name `%s`", col)
930 } 943 }
931 } 944 }
932 if hasRel { 945 if hasRel {
@@ -976,11 +989,18 @@ func (d *dbBase) ReadBatch(q dbQuerier, qs *querySet, mi *modelInfo, cond *Condi @@ -976,11 +989,18 @@ func (d *dbBase) ReadBatch(q dbQuerier, qs *querySet, mi *modelInfo, cond *Condi
976 d.ins.ReplaceMarks(&query) 989 d.ins.ReplaceMarks(&query)
977 990
978 var rs *sql.Rows 991 var rs *sql.Rows
979 - r, err := q.Query(query, args...)  
980 - if err != nil {  
981 - return 0, err 992 + var err error
  993 + if qs != nil && qs.forContext {
  994 + rs, err = q.QueryContext(qs.ctx, query, args...)
  995 + if err != nil {
  996 + return 0, err
  997 + }
  998 + } else {
  999 + rs, err = q.Query(query, args...)
  1000 + if err != nil {
  1001 + return 0, err
  1002 + }
982 } 1003 }
983 - rs = r  
984 1004
985 refs := make([]interface{}, colsNum) 1005 refs := make([]interface{}, colsNum)
986 for i := range refs { 1006 for i := range refs {
@@ -1109,8 +1129,12 @@ func (d *dbBase) Count(q dbQuerier, qs *querySet, mi *modelInfo, cond *Condition @@ -1109,8 +1129,12 @@ func (d *dbBase) Count(q dbQuerier, qs *querySet, mi *modelInfo, cond *Condition
1109 1129
1110 d.ins.ReplaceMarks(&query) 1130 d.ins.ReplaceMarks(&query)
1111 1131
1112 - row := q.QueryRow(query, args...)  
1113 - 1132 + var row *sql.Row
  1133 + if qs != nil && qs.forContext {
  1134 + row = q.QueryRowContext(qs.ctx, query, args...)
  1135 + } else {
  1136 + row = q.QueryRow(query, args...)
  1137 + }
1114 err = row.Scan(&cnt) 1138 err = row.Scan(&cnt)
1115 return 1139 return
1116 } 1140 }
@@ -372,7 +372,13 @@ func (t *dbTables) getCondSQL(cond *Condition, sub bool, tz *time.Location) (whe @@ -372,7 +372,13 @@ func (t *dbTables) getCondSQL(cond *Condition, sub bool, tz *time.Location) (whe
372 operator = "exact" 372 operator = "exact"
373 } 373 }
374 374
375 - operSQL, args := t.base.GenerateOperatorSQL(mi, fi, operator, p.args, tz) 375 + var operSQL string
  376 + var args []interface{}
  377 + if p.isRaw {
  378 + operSQL = p.sql
  379 + } else {
  380 + operSQL, args = t.base.GenerateOperatorSQL(mi, fi, operator, p.args, tz)
  381 + }
376 382
377 leftCol := fmt.Sprintf("%s.%s%s%s", index, Q, fi.column, Q) 383 leftCol := fmt.Sprintf("%s.%s%s%s", index, Q, fi.column, Q)
378 t.base.GenerateOperatorLeftCol(fi, operator, &leftCol) 384 t.base.GenerateOperatorLeftCol(fi, operator, &leftCol)
@@ -109,7 +109,7 @@ func getTableUnique(val reflect.Value) [][]string { @@ -109,7 +109,7 @@ func getTableUnique(val reflect.Value) [][]string {
109 func getColumnName(ft int, addrField reflect.Value, sf reflect.StructField, col string) string { 109 func getColumnName(ft int, addrField reflect.Value, sf reflect.StructField, col string) string {
110 column := col 110 column := col
111 if col == "" { 111 if col == "" {
112 - column = snakeString(sf.Name) 112 + column = nameStrategyMap[nameStrategy](sf.Name)
113 } 113 }
114 switch ft { 114 switch ft {
115 case RelForeignKey, RelOneToOne: 115 case RelForeignKey, RelOneToOne:
@@ -12,6 +12,8 @@ @@ -12,6 +12,8 @@
12 // See the License for the specific language governing permissions and 12 // See the License for the specific language governing permissions and
13 // limitations under the License. 13 // limitations under the License.
14 14
  15 +// +build go1.8
  16 +
15 // Package orm provide ORM for MySQL/PostgreSQL/sqlite 17 // Package orm provide ORM for MySQL/PostgreSQL/sqlite
16 // Simple Usage 18 // Simple Usage
17 // 19 //
@@ -52,6 +54,7 @@ @@ -52,6 +54,7 @@
52 package orm 54 package orm
53 55
54 import ( 56 import (
  57 + "context"
55 "database/sql" 58 "database/sql"
56 "errors" 59 "errors"
57 "fmt" 60 "fmt"
@@ -422,7 +425,7 @@ func (o *orm) getRelQs(md interface{}, mi *modelInfo, fi *fieldInfo) *querySet { @@ -422,7 +425,7 @@ func (o *orm) getRelQs(md interface{}, mi *modelInfo, fi *fieldInfo) *querySet {
422 func (o *orm) QueryTable(ptrStructOrTableName interface{}) (qs QuerySeter) { 425 func (o *orm) QueryTable(ptrStructOrTableName interface{}) (qs QuerySeter) {
423 var name string 426 var name string
424 if table, ok := ptrStructOrTableName.(string); ok { 427 if table, ok := ptrStructOrTableName.(string); ok {
425 - name = snakeString(table) 428 + name = nameStrategyMap[defaultNameStrategy](table)
426 if mi, ok := modelCache.get(name); ok { 429 if mi, ok := modelCache.get(name); ok {
427 qs = newQuerySet(o, mi) 430 qs = newQuerySet(o, mi)
428 } 431 }
@@ -458,11 +461,15 @@ func (o *orm) Using(name string) error { @@ -458,11 +461,15 @@ func (o *orm) Using(name string) error {
458 461
459 // begin transaction 462 // begin transaction
460 func (o *orm) Begin() error { 463 func (o *orm) Begin() error {
  464 + return o.BeginTx(context.Background(), nil)
  465 +}
  466 +
  467 +func (o *orm) BeginTx(ctx context.Context, opts *sql.TxOptions) error {
461 if o.isTx { 468 if o.isTx {
462 return ErrTxHasBegan 469 return ErrTxHasBegan
463 } 470 }
464 var tx *sql.Tx 471 var tx *sql.Tx
465 - tx, err := o.db.(txer).Begin() 472 + tx, err := o.db.(txer).BeginTx(ctx, opts)
466 if err != nil { 473 if err != nil {
467 return err 474 return err
468 } 475 }
@@ -541,6 +548,9 @@ func NewOrmWithDB(driverName, aliasName string, db *sql.DB) (Ormer, error) { @@ -541,6 +548,9 @@ func NewOrmWithDB(driverName, aliasName string, db *sql.DB) (Ormer, error) {
541 548
542 al.Name = aliasName 549 al.Name = aliasName
543 al.DriverName = driverName 550 al.DriverName = driverName
  551 + al.DB = db
  552 +
  553 + detectTZ(al)
544 554
545 o := new(orm) 555 o := new(orm)
546 o.alias = al 556 o.alias = al
@@ -31,6 +31,8 @@ type condValue struct { @@ -31,6 +31,8 @@ type condValue struct {
31 isOr bool 31 isOr bool
32 isNot bool 32 isNot bool
33 isCond bool 33 isCond bool
  34 + isRaw bool
  35 + sql string
34 } 36 }
35 37
36 // Condition struct. 38 // Condition struct.
@@ -45,6 +47,15 @@ func NewCondition() *Condition { @@ -45,6 +47,15 @@ func NewCondition() *Condition {
45 return c 47 return c
46 } 48 }
47 49
  50 +// Raw add raw sql to condition
  51 +func (c Condition) Raw(expr string, sql string) *Condition {
  52 + if len(sql) == 0 {
  53 + panic(fmt.Errorf("<Condition.Raw> sql cannot empty"))
  54 + }
  55 + c.params = append(c.params, condValue{exprs: strings.Split(expr, ExprSep), sql: sql, isRaw: true})
  56 + return &c
  57 +}
  58 +
48 // And add expression to condition 59 // And add expression to condition
49 func (c Condition) And(expr string, args ...interface{}) *Condition { 60 func (c Condition) And(expr string, args ...interface{}) *Condition {
50 if expr == "" || len(args) == 0 { 61 if expr == "" || len(args) == 0 {
@@ -15,6 +15,7 @@ @@ -15,6 +15,7 @@
15 package orm 15 package orm
16 16
17 import ( 17 import (
  18 + "context"
18 "database/sql" 19 "database/sql"
19 "fmt" 20 "fmt"
20 "io" 21 "io"
@@ -122,6 +123,13 @@ func (d *dbQueryLog) Prepare(query string) (*sql.Stmt, error) { @@ -122,6 +123,13 @@ func (d *dbQueryLog) Prepare(query string) (*sql.Stmt, error) {
122 return stmt, err 123 return stmt, err
123 } 124 }
124 125
  126 +func (d *dbQueryLog) PrepareContext(ctx context.Context, query string) (*sql.Stmt, error) {
  127 + a := time.Now()
  128 + stmt, err := d.db.PrepareContext(ctx, query)
  129 + debugLogQueies(d.alias, "db.Prepare", query, a, err)
  130 + return stmt, err
  131 +}
  132 +
125 func (d *dbQueryLog) Exec(query string, args ...interface{}) (sql.Result, error) { 133 func (d *dbQueryLog) Exec(query string, args ...interface{}) (sql.Result, error) {
126 a := time.Now() 134 a := time.Now()
127 res, err := d.db.Exec(query, args...) 135 res, err := d.db.Exec(query, args...)
@@ -129,6 +137,13 @@ func (d *dbQueryLog) Exec(query string, args ...interface{}) (sql.Result, error) @@ -129,6 +137,13 @@ func (d *dbQueryLog) Exec(query string, args ...interface{}) (sql.Result, error)
129 return res, err 137 return res, err
130 } 138 }
131 139
  140 +func (d *dbQueryLog) ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error) {
  141 + a := time.Now()
  142 + res, err := d.db.ExecContext(ctx, query, args...)
  143 + debugLogQueies(d.alias, "db.Exec", query, a, err, args...)
  144 + return res, err
  145 +}
  146 +
132 func (d *dbQueryLog) Query(query string, args ...interface{}) (*sql.Rows, error) { 147 func (d *dbQueryLog) Query(query string, args ...interface{}) (*sql.Rows, error) {
133 a := time.Now() 148 a := time.Now()
134 res, err := d.db.Query(query, args...) 149 res, err := d.db.Query(query, args...)
@@ -136,6 +151,13 @@ func (d *dbQueryLog) Query(query string, args ...interface{}) (*sql.Rows, error) @@ -136,6 +151,13 @@ func (d *dbQueryLog) Query(query string, args ...interface{}) (*sql.Rows, error)
136 return res, err 151 return res, err
137 } 152 }
138 153
  154 +func (d *dbQueryLog) QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error) {
  155 + a := time.Now()
  156 + res, err := d.db.QueryContext(ctx, query, args...)
  157 + debugLogQueies(d.alias, "db.Query", query, a, err, args...)
  158 + return res, err
  159 +}
  160 +
139 func (d *dbQueryLog) QueryRow(query string, args ...interface{}) *sql.Row { 161 func (d *dbQueryLog) QueryRow(query string, args ...interface{}) *sql.Row {
140 a := time.Now() 162 a := time.Now()
141 res := d.db.QueryRow(query, args...) 163 res := d.db.QueryRow(query, args...)
@@ -143,6 +165,13 @@ func (d *dbQueryLog) QueryRow(query string, args ...interface{}) *sql.Row { @@ -143,6 +165,13 @@ func (d *dbQueryLog) QueryRow(query string, args ...interface{}) *sql.Row {
143 return res 165 return res
144 } 166 }
145 167
  168 +func (d *dbQueryLog) QueryRowContext(ctx context.Context, query string, args ...interface{}) *sql.Row {
  169 + a := time.Now()
  170 + res := d.db.QueryRowContext(ctx, query, args...)
  171 + debugLogQueies(d.alias, "db.QueryRow", query, a, nil, args...)
  172 + return res
  173 +}
  174 +
146 func (d *dbQueryLog) Begin() (*sql.Tx, error) { 175 func (d *dbQueryLog) Begin() (*sql.Tx, error) {
147 a := time.Now() 176 a := time.Now()
148 tx, err := d.db.(txer).Begin() 177 tx, err := d.db.(txer).Begin()
@@ -150,6 +179,13 @@ func (d *dbQueryLog) Begin() (*sql.Tx, error) { @@ -150,6 +179,13 @@ func (d *dbQueryLog) Begin() (*sql.Tx, error) {
150 return tx, err 179 return tx, err
151 } 180 }
152 181
  182 +func (d *dbQueryLog) BeginTx(ctx context.Context, opts *sql.TxOptions) (*sql.Tx, error) {
  183 + a := time.Now()
  184 + tx, err := d.db.(txer).BeginTx(ctx, opts)
  185 + debugLogQueies(d.alias, "db.BeginTx", "START TRANSACTION", a, err)
  186 + return tx, err
  187 +}
  188 +
153 func (d *dbQueryLog) Commit() error { 189 func (d *dbQueryLog) Commit() error {
154 a := time.Now() 190 a := time.Now()
155 err := d.db.(txEnder).Commit() 191 err := d.db.(txEnder).Commit()
@@ -15,6 +15,7 @@ @@ -15,6 +15,7 @@
15 package orm 15 package orm
16 16
17 import ( 17 import (
  18 + "context"
18 "fmt" 19 "fmt"
19 ) 20 )
20 21
@@ -55,17 +56,19 @@ func ColValue(opt operator, value interface{}) interface{} { @@ -55,17 +56,19 @@ func ColValue(opt operator, value interface{}) interface{} {
55 56
56 // real query struct 57 // real query struct
57 type querySet struct { 58 type querySet struct {
58 - mi *modelInfo  
59 - cond *Condition  
60 - related []string  
61 - relDepth int  
62 - limit int64  
63 - offset int64  
64 - groups []string  
65 - orders []string  
66 - distinct bool  
67 - forupdate bool  
68 - orm *orm 59 + mi *modelInfo
  60 + cond *Condition
  61 + related []string
  62 + relDepth int
  63 + limit int64
  64 + offset int64
  65 + groups []string
  66 + orders []string
  67 + distinct bool
  68 + forupdate bool
  69 + orm *orm
  70 + ctx context.Context
  71 + forContext bool
69 } 72 }
70 73
71 var _ QuerySeter = new(querySet) 74 var _ QuerySeter = new(querySet)
@@ -79,6 +82,15 @@ func (o querySet) Filter(expr string, args ...interface{}) QuerySeter { @@ -79,6 +82,15 @@ func (o querySet) Filter(expr string, args ...interface{}) QuerySeter {
79 return &o 82 return &o
80 } 83 }
81 84
  85 +// add raw sql to querySeter.
  86 +func (o querySet) FilterRaw(expr string, sql string) QuerySeter {
  87 + if o.cond == nil {
  88 + o.cond = NewCondition()
  89 + }
  90 + o.cond = o.cond.Raw(expr, sql)
  91 + return &o
  92 +}
  93 +
82 // add NOT condition to querySeter. 94 // add NOT condition to querySeter.
83 func (o querySet) Exclude(expr string, args ...interface{}) QuerySeter { 95 func (o querySet) Exclude(expr string, args ...interface{}) QuerySeter {
84 if o.cond == nil { 96 if o.cond == nil {
@@ -198,11 +210,7 @@ func (o *querySet) PrepareInsert() (Inserter, error) { @@ -198,11 +210,7 @@ func (o *querySet) PrepareInsert() (Inserter, error) {
198 // query all data and map to containers. 210 // query all data and map to containers.
199 // cols means the columns when querying. 211 // cols means the columns when querying.
200 func (o *querySet) All(container interface{}, cols ...string) (int64, error) { 212 func (o *querySet) All(container interface{}, cols ...string) (int64, error) {
201 - num, err := o.orm.alias.DbBaser.ReadBatch(o.orm.db, o, o.mi, o.cond, container, o.orm.alias.TZ, cols)  
202 - if num == 0 {  
203 - return 0, ErrNoRows  
204 - }  
205 - return num, err 213 + return o.orm.alias.DbBaser.ReadBatch(o.orm.db, o, o.mi, o.cond, container, o.orm.alias.TZ, cols)
206 } 214 }
207 215
208 // query one row data and map to containers. 216 // query one row data and map to containers.
@@ -270,6 +278,13 @@ func (o *querySet) RowsToStruct(ptrStruct interface{}, keyCol, valueCol string) @@ -270,6 +278,13 @@ func (o *querySet) RowsToStruct(ptrStruct interface{}, keyCol, valueCol string)
270 panic(ErrNotImplement) 278 panic(ErrNotImplement)
271 } 279 }
272 280
  281 +// set context to QuerySeter.
  282 +func (o querySet) WithContext(ctx context.Context) QuerySeter {
  283 + o.ctx = ctx
  284 + o.forContext = true
  285 + return &o
  286 +}
  287 +
273 // create new QuerySeter. 288 // create new QuerySeter.
274 func newQuerySet(orm *orm, mi *modelInfo) QuerySeter { 289 func newQuerySet(orm *orm, mi *modelInfo) QuerySeter {
275 o := new(querySet) 290 o := new(querySet)
@@ -358,7 +358,7 @@ func (o *rawSet) QueryRow(containers ...interface{}) error { @@ -358,7 +358,7 @@ func (o *rawSet) QueryRow(containers ...interface{}) error {
358 _, tags := parseStructTag(fe.Tag.Get(defaultStructTagName)) 358 _, tags := parseStructTag(fe.Tag.Get(defaultStructTagName))
359 var col string 359 var col string
360 if col = tags["column"]; col == "" { 360 if col = tags["column"]; col == "" {
361 - col = snakeString(fe.Name) 361 + col = nameStrategyMap[nameStrategy](fe.Name)
362 } 362 }
363 if v, ok := columnsMp[col]; ok { 363 if v, ok := columnsMp[col]; ok {
364 value := reflect.ValueOf(v).Elem().Interface() 364 value := reflect.ValueOf(v).Elem().Interface()
@@ -509,7 +509,7 @@ func (o *rawSet) QueryRows(containers ...interface{}) (int64, error) { @@ -509,7 +509,7 @@ func (o *rawSet) QueryRows(containers ...interface{}) (int64, error) {
509 _, tags := parseStructTag(fe.Tag.Get(defaultStructTagName)) 509 _, tags := parseStructTag(fe.Tag.Get(defaultStructTagName))
510 var col string 510 var col string
511 if col = tags["column"]; col == "" { 511 if col = tags["column"]; col == "" {
512 - col = snakeString(fe.Name) 512 + col = nameStrategyMap[nameStrategy](fe.Name)
513 } 513 }
514 if v, ok := columnsMp[col]; ok { 514 if v, ok := columnsMp[col]; ok {
515 value := reflect.ValueOf(v).Elem().Interface() 515 value := reflect.ValueOf(v).Elem().Interface()
@@ -15,6 +15,7 @@ @@ -15,6 +15,7 @@
15 package orm 15 package orm
16 16
17 import ( 17 import (
  18 + "context"
18 "database/sql" 19 "database/sql"
19 "reflect" 20 "reflect"
20 "time" 21 "time"
@@ -106,6 +107,17 @@ type Ormer interface { @@ -106,6 +107,17 @@ type Ormer interface {
106 // ... 107 // ...
107 // err = o.Rollback() 108 // err = o.Rollback()
108 Begin() error 109 Begin() error
  110 + // begin transaction with provided context and option
  111 + // the provided context is used until the transaction is committed or rolled back.
  112 + // if the context is canceled, the transaction will be rolled back.
  113 + // the provided TxOptions is optional and may be nil if defaults should be used.
  114 + // if a non-default isolation level is used that the driver doesn't support, an error will be returned.
  115 + // for example:
  116 + // o := NewOrm()
  117 + // err := o.BeginTx(context.Background(), &sql.TxOptions{Isolation: sql.LevelRepeatableRead})
  118 + // ...
  119 + // err = o.Rollback()
  120 + BeginTx(ctx context.Context, opts *sql.TxOptions) error
109 // commit transaction 121 // commit transaction
110 Commit() error 122 Commit() error
111 // rollback transaction 123 // rollback transaction
@@ -135,6 +147,11 @@ type QuerySeter interface { @@ -135,6 +147,11 @@ type QuerySeter interface {
135 // // time compare 147 // // time compare
136 // qs.Filter("created", time.Now()) 148 // qs.Filter("created", time.Now())
137 Filter(string, ...interface{}) QuerySeter 149 Filter(string, ...interface{}) QuerySeter
  150 + // add raw sql to querySeter.
  151 + // for example:
  152 + // qs.FilterRaw("user_id IN (SELECT id FROM profile WHERE age>=18)")
  153 + // //sql-> WHERE user_id IN (SELECT id FROM profile WHERE age>=18)
  154 + FilterRaw(string, string) QuerySeter
138 // add NOT condition to querySeter. 155 // add NOT condition to querySeter.
139 // have the same usage as Filter 156 // have the same usage as Filter
140 Exclude(string, ...interface{}) QuerySeter 157 Exclude(string, ...interface{}) QuerySeter
@@ -378,16 +395,23 @@ type RawSeter interface { @@ -378,16 +395,23 @@ type RawSeter interface {
378 type stmtQuerier interface { 395 type stmtQuerier interface {
379 Close() error 396 Close() error
380 Exec(args ...interface{}) (sql.Result, error) 397 Exec(args ...interface{}) (sql.Result, error)
  398 + //ExecContext(ctx context.Context, args ...interface{}) (sql.Result, error)
381 Query(args ...interface{}) (*sql.Rows, error) 399 Query(args ...interface{}) (*sql.Rows, error)
  400 + //QueryContext(args ...interface{}) (*sql.Rows, error)
382 QueryRow(args ...interface{}) *sql.Row 401 QueryRow(args ...interface{}) *sql.Row
  402 + //QueryRowContext(ctx context.Context, args ...interface{}) *sql.Row
383 } 403 }
384 404
385 // db querier 405 // db querier
386 type dbQuerier interface { 406 type dbQuerier interface {
387 Prepare(query string) (*sql.Stmt, error) 407 Prepare(query string) (*sql.Stmt, error)
  408 + PrepareContext(ctx context.Context, query string) (*sql.Stmt, error)
388 Exec(query string, args ...interface{}) (sql.Result, error) 409 Exec(query string, args ...interface{}) (sql.Result, error)
  410 + ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error)
389 Query(query string, args ...interface{}) (*sql.Rows, error) 411 Query(query string, args ...interface{}) (*sql.Rows, error)
  412 + QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error)
390 QueryRow(query string, args ...interface{}) *sql.Row 413 QueryRow(query string, args ...interface{}) *sql.Row
  414 + QueryRowContext(ctx context.Context, query string, args ...interface{}) *sql.Row
391 } 415 }
392 416
393 // type DB interface { 417 // type DB interface {
@@ -401,6 +425,7 @@ type dbQuerier interface { @@ -401,6 +425,7 @@ type dbQuerier interface {
401 // transaction beginner 425 // transaction beginner
402 type txer interface { 426 type txer interface {
403 Begin() (*sql.Tx, error) 427 Begin() (*sql.Tx, error)
  428 + BeginTx(ctx context.Context, opts *sql.TxOptions) (*sql.Tx, error)
404 } 429 }
405 430
406 // transaction ending 431 // transaction ending
@@ -23,6 +23,18 @@ import ( @@ -23,6 +23,18 @@ import (
23 "time" 23 "time"
24 ) 24 )
25 25
  26 +type fn func(string) string
  27 +
  28 +var (
  29 + nameStrategyMap = map[string]fn{
  30 + defaultNameStrategy: snakeString,
  31 + SnakeAcronymNameStrategy: snakeStringWithAcronym,
  32 + }
  33 + defaultNameStrategy = "snakeString"
  34 + SnakeAcronymNameStrategy = "snakeStringWithAcronym"
  35 + nameStrategy = defaultNameStrategy
  36 +)
  37 +
26 // StrTo is the target string 38 // StrTo is the target string
27 type StrTo string 39 type StrTo string
28 40
@@ -198,7 +210,28 @@ func ToInt64(value interface{}) (d int64) { @@ -198,7 +210,28 @@ func ToInt64(value interface{}) (d int64) {
198 return 210 return
199 } 211 }
200 212
201 -// snake string, XxYy to xx_yy , XxYY to xx_yy 213 +func snakeStringWithAcronym(s string) string {
  214 + data := make([]byte, 0, len(s)*2)
  215 + num := len(s)
  216 + for i := 0; i < num; i++ {
  217 + d := s[i]
  218 + before := false
  219 + after := false
  220 + if i > 0 {
  221 + before = s[i-1] >= 'a' && s[i-1] <= 'z'
  222 + }
  223 + if i+1 < num {
  224 + after = s[i+1] >= 'a' && s[i+1] <= 'z'
  225 + }
  226 + if i > 0 && d >= 'A' && d <= 'Z' && (before || after) {
  227 + data = append(data, '_')
  228 + }
  229 + data = append(data, d)
  230 + }
  231 + return strings.ToLower(string(data[:]))
  232 +}
  233 +
  234 +// snake string, XxYy to xx_yy , XxYY to xx_y_y
202 func snakeString(s string) string { 235 func snakeString(s string) string {
203 data := make([]byte, 0, len(s)*2) 236 data := make([]byte, 0, len(s)*2)
204 j := false 237 j := false
@@ -216,6 +249,14 @@ func snakeString(s string) string { @@ -216,6 +249,14 @@ func snakeString(s string) string {
216 return strings.ToLower(string(data[:])) 249 return strings.ToLower(string(data[:]))
217 } 250 }
218 251
  252 +// SetNameStrategy set different name strategy
  253 +func SetNameStrategy(s string) {
  254 + if SnakeAcronymNameStrategy != s {
  255 + nameStrategy = defaultNameStrategy
  256 + }
  257 + nameStrategy = s
  258 +}
  259 +
219 // camel string, xx_yy to XxYy 260 // camel string, xx_yy to XxYy
220 func camelString(s string) string { 261 func camelString(s string) string {
221 data := make([]byte, 0, len(s)) 262 data := make([]byte, 0, len(s))
@@ -39,7 +39,7 @@ var globalRouterTemplate = `package routers @@ -39,7 +39,7 @@ var globalRouterTemplate = `package routers
39 39
40 import ( 40 import (
41 "github.com/astaxie/beego" 41 "github.com/astaxie/beego"
42 - "github.com/astaxie/beego/context/param" 42 + "github.com/astaxie/beego/context/param"{{.globalimport}}
43 ) 43 )
44 44
45 func init() { 45 func init() {
@@ -52,6 +52,22 @@ var ( @@ -52,6 +52,22 @@ var (
52 commentFilename string 52 commentFilename string
53 pkgLastupdate map[string]int64 53 pkgLastupdate map[string]int64
54 genInfoList map[string][]ControllerComments 54 genInfoList map[string][]ControllerComments
  55 +
  56 + routerHooks = map[string]int{
  57 + "beego.BeforeStatic": BeforeStatic,
  58 + "beego.BeforeRouter": BeforeRouter,
  59 + "beego.BeforeExec": BeforeExec,
  60 + "beego.AfterExec": AfterExec,
  61 + "beego.FinishRouter": FinishRouter,
  62 + }
  63 +
  64 + routerHooksMapping = map[int]string{
  65 + BeforeStatic: "beego.BeforeStatic",
  66 + BeforeRouter: "beego.BeforeRouter",
  67 + BeforeExec: "beego.BeforeExec",
  68 + AfterExec: "beego.AfterExec",
  69 + FinishRouter: "beego.FinishRouter",
  70 + }
55 ) 71 )
56 72
57 const commentPrefix = "commentsRouter_" 73 const commentPrefix = "commentsRouter_"
@@ -102,6 +118,20 @@ type parsedComment struct { @@ -102,6 +118,20 @@ type parsedComment struct {
102 routerPath string 118 routerPath string
103 methods []string 119 methods []string
104 params map[string]parsedParam 120 params map[string]parsedParam
  121 + filters []parsedFilter
  122 + imports []parsedImport
  123 +}
  124 +
  125 +type parsedImport struct {
  126 + importPath string
  127 + importAlias string
  128 +}
  129 +
  130 +type parsedFilter struct {
  131 + pattern string
  132 + pos int
  133 + filter string
  134 + params []bool
105 } 135 }
106 136
107 type parsedParam struct { 137 type parsedParam struct {
@@ -126,6 +156,8 @@ func parserComments(f *ast.FuncDecl, controllerName, pkgpath string) error { @@ -126,6 +156,8 @@ func parserComments(f *ast.FuncDecl, controllerName, pkgpath string) error {
126 cc.Router = parsedComment.routerPath 156 cc.Router = parsedComment.routerPath
127 cc.AllowHTTPMethods = parsedComment.methods 157 cc.AllowHTTPMethods = parsedComment.methods
128 cc.MethodParams = buildMethodParams(f.Type.Params.List, parsedComment) 158 cc.MethodParams = buildMethodParams(f.Type.Params.List, parsedComment)
  159 + cc.FilterComments = buildFilters(parsedComment.filters)
  160 + cc.ImportComments = buildImports(parsedComment.imports)
129 genInfoList[key] = append(genInfoList[key], cc) 161 genInfoList[key] = append(genInfoList[key], cc)
130 } 162 }
131 } 163 }
@@ -133,6 +165,48 @@ func parserComments(f *ast.FuncDecl, controllerName, pkgpath string) error { @@ -133,6 +165,48 @@ func parserComments(f *ast.FuncDecl, controllerName, pkgpath string) error {
133 return nil 165 return nil
134 } 166 }
135 167
  168 +func buildImports(pis []parsedImport) []*ControllerImportComments {
  169 + var importComments []*ControllerImportComments
  170 +
  171 + for _, pi := range pis {
  172 + importComments = append(importComments, &ControllerImportComments{
  173 + ImportPath: pi.importPath,
  174 + ImportAlias: pi.importAlias,
  175 + })
  176 + }
  177 +
  178 + return importComments
  179 +}
  180 +
  181 +func buildFilters(pfs []parsedFilter) []*ControllerFilterComments {
  182 + var filterComments []*ControllerFilterComments
  183 +
  184 + for _, pf := range pfs {
  185 + var (
  186 + returnOnOutput bool
  187 + resetParams bool
  188 + )
  189 +
  190 + if len(pf.params) >= 1 {
  191 + returnOnOutput = pf.params[0]
  192 + }
  193 +
  194 + if len(pf.params) >= 2 {
  195 + resetParams = pf.params[1]
  196 + }
  197 +
  198 + filterComments = append(filterComments, &ControllerFilterComments{
  199 + Filter: pf.filter,
  200 + Pattern: pf.pattern,
  201 + Pos: pf.pos,
  202 + ReturnOnOutput: returnOnOutput,
  203 + ResetParams: resetParams,
  204 + })
  205 + }
  206 +
  207 + return filterComments
  208 +}
  209 +
136 func buildMethodParams(funcParams []*ast.Field, pc *parsedComment) []*param.MethodParam { 210 func buildMethodParams(funcParams []*ast.Field, pc *parsedComment) []*param.MethodParam {
137 result := make([]*param.MethodParam, 0, len(funcParams)) 211 result := make([]*param.MethodParam, 0, len(funcParams))
138 for _, fparam := range funcParams { 212 for _, fparam := range funcParams {
@@ -181,6 +255,8 @@ var routeRegex = regexp.MustCompile(`@router\s+(\S+)(?:\s+\[(\S+)\])?`) @@ -181,6 +255,8 @@ var routeRegex = regexp.MustCompile(`@router\s+(\S+)(?:\s+\[(\S+)\])?`)
181 func parseComment(lines []*ast.Comment) (pcs []*parsedComment, err error) { 255 func parseComment(lines []*ast.Comment) (pcs []*parsedComment, err error) {
182 pcs = []*parsedComment{} 256 pcs = []*parsedComment{}
183 params := map[string]parsedParam{} 257 params := map[string]parsedParam{}
  258 + filters := []parsedFilter{}
  259 + imports := []parsedImport{}
184 260
185 for _, c := range lines { 261 for _, c := range lines {
186 t := strings.TrimSpace(strings.TrimLeft(c.Text, "//")) 262 t := strings.TrimSpace(strings.TrimLeft(c.Text, "//"))
@@ -210,8 +286,68 @@ func parseComment(lines []*ast.Comment) (pcs []*parsedComment, err error) { @@ -210,8 +286,68 @@ func parseComment(lines []*ast.Comment) (pcs []*parsedComment, err error) {
210 } 286 }
211 287
212 for _, c := range lines { 288 for _, c := range lines {
  289 + t := strings.TrimSpace(strings.TrimLeft(c.Text, "//"))
  290 + if strings.HasPrefix(t, "@Import") {
  291 + iv := getparams(strings.TrimSpace(strings.TrimLeft(t, "@Import")))
  292 + if len(iv) == 0 || len(iv) > 2 {
  293 + logs.Error("Invalid @Import format. Only accepts 1 or 2 parameters")
  294 + continue
  295 + }
  296 +
  297 + p := parsedImport{}
  298 + p.importPath = iv[0]
  299 +
  300 + if len(iv) == 2 {
  301 + p.importAlias = iv[1]
  302 + }
  303 +
  304 + imports = append(imports, p)
  305 + }
  306 + }
  307 +
  308 +filterLoop:
  309 + for _, c := range lines {
  310 + t := strings.TrimSpace(strings.TrimLeft(c.Text, "//"))
  311 + if strings.HasPrefix(t, "@Filter") {
  312 + fv := getparams(strings.TrimSpace(strings.TrimLeft(t, "@Filter")))
  313 + if len(fv) < 3 {
  314 + logs.Error("Invalid @Filter format. Needs at least 3 parameters")
  315 + continue filterLoop
  316 + }
  317 +
  318 + p := parsedFilter{}
  319 + p.pattern = fv[0]
  320 + posName := fv[1]
  321 + if pos, exists := routerHooks[posName]; exists {
  322 + p.pos = pos
  323 + } else {
  324 + logs.Error("Invalid @Filter pos: ", posName)
  325 + continue filterLoop
  326 + }
  327 +
  328 + p.filter = fv[2]
  329 + fvParams := fv[3:]
  330 + for _, fvParam := range fvParams {
  331 + switch fvParam {
  332 + case "true":
  333 + p.params = append(p.params, true)
  334 + case "false":
  335 + p.params = append(p.params, false)
  336 + default:
  337 + logs.Error("Invalid @Filter param: ", fvParam)
  338 + continue filterLoop
  339 + }
  340 + }
  341 +
  342 + filters = append(filters, p)
  343 + }
  344 + }
  345 +
  346 + for _, c := range lines {
213 var pc = &parsedComment{} 347 var pc = &parsedComment{}
214 pc.params = params 348 pc.params = params
  349 + pc.filters = filters
  350 + pc.imports = imports
215 351
216 t := strings.TrimSpace(strings.TrimLeft(c.Text, "//")) 352 t := strings.TrimSpace(strings.TrimLeft(c.Text, "//"))
217 if strings.HasPrefix(t, "@router") { 353 if strings.HasPrefix(t, "@router") {
@@ -276,8 +412,9 @@ func genRouterCode(pkgRealpath string) { @@ -276,8 +412,9 @@ func genRouterCode(pkgRealpath string) {
276 os.Mkdir(getRouterDir(pkgRealpath), 0755) 412 os.Mkdir(getRouterDir(pkgRealpath), 0755)
277 logs.Info("generate router from comments") 413 logs.Info("generate router from comments")
278 var ( 414 var (
279 - globalinfo string  
280 - sortKey []string 415 + globalinfo string
  416 + globalimport string
  417 + sortKey []string
281 ) 418 )
282 for k := range genInfoList { 419 for k := range genInfoList {
283 sortKey = append(sortKey, k) 420 sortKey = append(sortKey, k)
@@ -295,6 +432,7 @@ func genRouterCode(pkgRealpath string) { @@ -295,6 +432,7 @@ func genRouterCode(pkgRealpath string) {
295 } 432 }
296 allmethod = strings.TrimRight(allmethod, ",") + "}" 433 allmethod = strings.TrimRight(allmethod, ",") + "}"
297 } 434 }
  435 +
298 params := "nil" 436 params := "nil"
299 if len(c.Params) > 0 { 437 if len(c.Params) > 0 {
300 params = "[]map[string]string{" 438 params = "[]map[string]string{"
@@ -305,6 +443,7 @@ func genRouterCode(pkgRealpath string) { @@ -305,6 +443,7 @@ func genRouterCode(pkgRealpath string) {
305 } 443 }
306 params = strings.TrimRight(params, ",") + "}" 444 params = strings.TrimRight(params, ",") + "}"
307 } 445 }
  446 +
308 methodParams := "param.Make(" 447 methodParams := "param.Make("
309 if len(c.MethodParams) > 0 { 448 if len(c.MethodParams) > 0 {
310 lines := make([]string, 0, len(c.MethodParams)) 449 lines := make([]string, 0, len(c.MethodParams))
@@ -316,24 +455,66 @@ func genRouterCode(pkgRealpath string) { @@ -316,24 +455,66 @@ func genRouterCode(pkgRealpath string) {
316 ",\n " 455 ",\n "
317 } 456 }
318 methodParams += ")" 457 methodParams += ")"
  458 +
  459 + imports := ""
  460 + if len(c.ImportComments) > 0 {
  461 + for _, i := range c.ImportComments {
  462 + if i.ImportAlias != "" {
  463 + imports += fmt.Sprintf(`
  464 + %s "%s"`, i.ImportAlias, i.ImportPath)
  465 + } else {
  466 + imports += fmt.Sprintf(`
  467 + "%s"`, i.ImportPath)
  468 + }
  469 + }
  470 + }
  471 +
  472 + filters := ""
  473 + if len(c.FilterComments) > 0 {
  474 + for _, f := range c.FilterComments {
  475 + filters += fmt.Sprintf(` &beego.ControllerFilter{
  476 + Pattern: "%s",
  477 + Pos: %s,
  478 + Filter: %s,
  479 + ReturnOnOutput: %v,
  480 + ResetParams: %v,
  481 + },`, f.Pattern, routerHooksMapping[f.Pos], f.Filter, f.ReturnOnOutput, f.ResetParams)
  482 + }
  483 + }
  484 +
  485 + if filters == "" {
  486 + filters = "nil"
  487 + } else {
  488 + filters = fmt.Sprintf(`[]*beego.ControllerFilter{
  489 +%s
  490 + }`, filters)
  491 + }
  492 +
  493 + globalimport = imports
  494 +
319 globalinfo = globalinfo + ` 495 globalinfo = globalinfo + `
320 - beego.GlobalControllerRouter["` + k + `"] = append(beego.GlobalControllerRouter["` + k + `"],  
321 - beego.ControllerComments{  
322 - Method: "` + strings.TrimSpace(c.Method) + `",  
323 - ` + "Router: `" + c.Router + "`" + `,  
324 - AllowHTTPMethods: ` + allmethod + `,  
325 - MethodParams: ` + methodParams + `,  
326 - Params: ` + params + `}) 496 + beego.GlobalControllerRouter["` + k + `"] = append(beego.GlobalControllerRouter["` + k + `"],
  497 + beego.ControllerComments{
  498 + Method: "` + strings.TrimSpace(c.Method) + `",
  499 + ` + "Router: `" + c.Router + "`" + `,
  500 + AllowHTTPMethods: ` + allmethod + `,
  501 + MethodParams: ` + methodParams + `,
  502 + Filters: ` + filters + `,
  503 + Params: ` + params + `})
327 ` 504 `
328 } 505 }
329 } 506 }
  507 +
330 if globalinfo != "" { 508 if globalinfo != "" {
331 f, err := os.Create(filepath.Join(getRouterDir(pkgRealpath), commentFilename)) 509 f, err := os.Create(filepath.Join(getRouterDir(pkgRealpath), commentFilename))
332 if err != nil { 510 if err != nil {
333 panic(err) 511 panic(err)
334 } 512 }
335 defer f.Close() 513 defer f.Close()
336 - f.WriteString(strings.Replace(globalRouterTemplate, "{{.globalinfo}}", globalinfo, -1)) 514 +
  515 + content := strings.Replace(globalRouterTemplate, "{{.globalinfo}}", globalinfo, -1)
  516 + content = strings.Replace(content, "{{.globalimport}}", globalimport, -1)
  517 + f.WriteString(content)
337 } 518 }
338 } 519 }
339 520
  1 +// Copyright 2014 beego Author. All Rights Reserved.
  2 +//
  3 +// Licensed under the Apache License, Version 2.0 (the "License");
  4 +// you may not use this file except in compliance with the License.
  5 +// You may obtain a copy of the License at
  6 +//
  7 +// http://www.apache.org/licenses/LICENSE-2.0
  8 +//
  9 +// Unless required by applicable law or agreed to in writing, software
  10 +// distributed under the License is distributed on an "AS IS" BASIS,
  11 +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12 +// See the License for the specific language governing permissions and
  13 +// limitations under the License.
  14 +
  15 +// Package cors provides handlers to enable CORS support.
  16 +// Usage
  17 +// import (
  18 +// "github.com/astaxie/beego"
  19 +// "github.com/astaxie/beego/plugins/cors"
  20 +// )
  21 +//
  22 +// func main() {
  23 +// // CORS for https://foo.* origins, allowing:
  24 +// // - PUT and PATCH methods
  25 +// // - Origin header
  26 +// // - Credentials share
  27 +// beego.InsertFilter("*", beego.BeforeRouter, cors.Allow(&cors.Options{
  28 +// AllowOrigins: []string{"https://*.foo.com"},
  29 +// AllowMethods: []string{"PUT", "PATCH"},
  30 +// AllowHeaders: []string{"Origin"},
  31 +// ExposeHeaders: []string{"Content-Length"},
  32 +// AllowCredentials: true,
  33 +// }))
  34 +// beego.Run()
  35 +// }
  36 +package cors
  37 +
  38 +import (
  39 + "net/http"
  40 + "regexp"
  41 + "strconv"
  42 + "strings"
  43 + "time"
  44 +
  45 + "github.com/astaxie/beego"
  46 + "github.com/astaxie/beego/context"
  47 +)
  48 +
  49 +const (
  50 + headerAllowOrigin = "Access-Control-Allow-Origin"
  51 + headerAllowCredentials = "Access-Control-Allow-Credentials"
  52 + headerAllowHeaders = "Access-Control-Allow-Headers"
  53 + headerAllowMethods = "Access-Control-Allow-Methods"
  54 + headerExposeHeaders = "Access-Control-Expose-Headers"
  55 + headerMaxAge = "Access-Control-Max-Age"
  56 +
  57 + headerOrigin = "Origin"
  58 + headerRequestMethod = "Access-Control-Request-Method"
  59 + headerRequestHeaders = "Access-Control-Request-Headers"
  60 +)
  61 +
  62 +var (
  63 + defaultAllowHeaders = []string{"Origin", "Accept", "Content-Type", "Authorization"}
  64 + // Regex patterns are generated from AllowOrigins. These are used and generated internally.
  65 + allowOriginPatterns = []string{}
  66 +)
  67 +
  68 +// Options represents Access Control options.
  69 +type Options struct {
  70 + // If set, all origins are allowed.
  71 + AllowAllOrigins bool
  72 + // A list of allowed origins. Wild cards and FQDNs are supported.
  73 + AllowOrigins []string
  74 + // If set, allows to share auth credentials such as cookies.
  75 + AllowCredentials bool
  76 + // A list of allowed HTTP methods.
  77 + AllowMethods []string
  78 + // A list of allowed HTTP headers.
  79 + AllowHeaders []string
  80 + // A list of exposed HTTP headers.
  81 + ExposeHeaders []string
  82 + // Max age of the CORS headers.
  83 + MaxAge time.Duration
  84 +}
  85 +
  86 +// Header converts options into CORS headers.
  87 +func (o *Options) Header(origin string) (headers map[string]string) {
  88 + headers = make(map[string]string)
  89 + // if origin is not allowed, don't extend the headers
  90 + // with CORS headers.
  91 + if !o.AllowAllOrigins && !o.IsOriginAllowed(origin) {
  92 + return
  93 + }
  94 +
  95 + // add allow origin
  96 + if o.AllowAllOrigins {
  97 + headers[headerAllowOrigin] = "*"
  98 + } else {
  99 + headers[headerAllowOrigin] = origin
  100 + }
  101 +
  102 + // add allow credentials
  103 + headers[headerAllowCredentials] = strconv.FormatBool(o.AllowCredentials)
  104 +
  105 + // add allow methods
  106 + if len(o.AllowMethods) > 0 {
  107 + headers[headerAllowMethods] = strings.Join(o.AllowMethods, ",")
  108 + }
  109 +
  110 + // add allow headers
  111 + if len(o.AllowHeaders) > 0 {
  112 + headers[headerAllowHeaders] = strings.Join(o.AllowHeaders, ",")
  113 + }
  114 +
  115 + // add exposed header
  116 + if len(o.ExposeHeaders) > 0 {
  117 + headers[headerExposeHeaders] = strings.Join(o.ExposeHeaders, ",")
  118 + }
  119 + // add a max age header
  120 + if o.MaxAge > time.Duration(0) {
  121 + headers[headerMaxAge] = strconv.FormatInt(int64(o.MaxAge/time.Second), 10)
  122 + }
  123 + return
  124 +}
  125 +
  126 +// PreflightHeader converts options into CORS headers for a preflight response.
  127 +func (o *Options) PreflightHeader(origin, rMethod, rHeaders string) (headers map[string]string) {
  128 + headers = make(map[string]string)
  129 + if !o.AllowAllOrigins && !o.IsOriginAllowed(origin) {
  130 + return
  131 + }
  132 + // verify if requested method is allowed
  133 + for _, method := range o.AllowMethods {
  134 + if method == rMethod {
  135 + headers[headerAllowMethods] = strings.Join(o.AllowMethods, ",")
  136 + break
  137 + }
  138 + }
  139 +
  140 + // verify if requested headers are allowed
  141 + var allowed []string
  142 + for _, rHeader := range strings.Split(rHeaders, ",") {
  143 + rHeader = strings.TrimSpace(rHeader)
  144 + lookupLoop:
  145 + for _, allowedHeader := range o.AllowHeaders {
  146 + if strings.ToLower(rHeader) == strings.ToLower(allowedHeader) {
  147 + allowed = append(allowed, rHeader)
  148 + break lookupLoop
  149 + }
  150 + }
  151 + }
  152 +
  153 + headers[headerAllowCredentials] = strconv.FormatBool(o.AllowCredentials)
  154 + // add allow origin
  155 + if o.AllowAllOrigins {
  156 + headers[headerAllowOrigin] = "*"
  157 + } else {
  158 + headers[headerAllowOrigin] = origin
  159 + }
  160 +
  161 + // add allowed headers
  162 + if len(allowed) > 0 {
  163 + headers[headerAllowHeaders] = strings.Join(allowed, ",")
  164 + }
  165 +
  166 + // add exposed headers
  167 + if len(o.ExposeHeaders) > 0 {
  168 + headers[headerExposeHeaders] = strings.Join(o.ExposeHeaders, ",")
  169 + }
  170 + // add a max age header
  171 + if o.MaxAge > time.Duration(0) {
  172 + headers[headerMaxAge] = strconv.FormatInt(int64(o.MaxAge/time.Second), 10)
  173 + }
  174 + return
  175 +}
  176 +
  177 +// IsOriginAllowed looks up if the origin matches one of the patterns
  178 +// generated from Options.AllowOrigins patterns.
  179 +func (o *Options) IsOriginAllowed(origin string) (allowed bool) {
  180 + for _, pattern := range allowOriginPatterns {
  181 + allowed, _ = regexp.MatchString(pattern, origin)
  182 + if allowed {
  183 + return
  184 + }
  185 + }
  186 + return
  187 +}
  188 +
  189 +// Allow enables CORS for requests those match the provided options.
  190 +func Allow(opts *Options) beego.FilterFunc {
  191 + // Allow default headers if nothing is specified.
  192 + if len(opts.AllowHeaders) == 0 {
  193 + opts.AllowHeaders = defaultAllowHeaders
  194 + }
  195 +
  196 + for _, origin := range opts.AllowOrigins {
  197 + pattern := regexp.QuoteMeta(origin)
  198 + pattern = strings.Replace(pattern, "\\*", ".*", -1)
  199 + pattern = strings.Replace(pattern, "\\?", ".", -1)
  200 + allowOriginPatterns = append(allowOriginPatterns, "^"+pattern+"$")
  201 + }
  202 +
  203 + return func(ctx *context.Context) {
  204 + var (
  205 + origin = ctx.Input.Header(headerOrigin)
  206 + requestedMethod = ctx.Input.Header(headerRequestMethod)
  207 + requestedHeaders = ctx.Input.Header(headerRequestHeaders)
  208 + // additional headers to be added
  209 + // to the response.
  210 + headers map[string]string
  211 + )
  212 +
  213 + if ctx.Input.Method() == "OPTIONS" &&
  214 + (requestedMethod != "" || requestedHeaders != "") {
  215 + headers = opts.PreflightHeader(origin, requestedMethod, requestedHeaders)
  216 + for key, value := range headers {
  217 + ctx.Output.Header(key, value)
  218 + }
  219 + ctx.ResponseWriter.WriteHeader(http.StatusOK)
  220 + return
  221 + }
  222 + headers = opts.Header(origin)
  223 +
  224 + for key, value := range headers {
  225 + ctx.Output.Header(key, value)
  226 + }
  227 + }
  228 +}
@@ -43,7 +43,7 @@ const ( @@ -43,7 +43,7 @@ const (
43 ) 43 )
44 44
45 const ( 45 const (
46 - routerTypeBeego = iota 46 + routerTypeBeego = iota
47 routerTypeRESTFul 47 routerTypeRESTFul
48 routerTypeHandler 48 routerTypeHandler
49 ) 49 )
@@ -133,14 +133,15 @@ type ControllerRegister struct { @@ -133,14 +133,15 @@ type ControllerRegister struct {
133 133
134 // NewControllerRegister returns a new ControllerRegister. 134 // NewControllerRegister returns a new ControllerRegister.
135 func NewControllerRegister() *ControllerRegister { 135 func NewControllerRegister() *ControllerRegister {
136 - cr := &ControllerRegister{ 136 + return &ControllerRegister{
137 routers: make(map[string]*Tree), 137 routers: make(map[string]*Tree),
138 policies: make(map[string]*Tree), 138 policies: make(map[string]*Tree),
  139 + pool: sync.Pool{
  140 + New: func() interface{} {
  141 + return beecontext.NewContext()
  142 + },
  143 + },
139 } 144 }
140 - cr.pool.New = func() interface{} {  
141 - return beecontext.NewContext()  
142 - }  
143 - return cr  
144 } 145 }
145 146
146 // Add controller handler and pattern rules to ControllerRegister. 147 // Add controller handler and pattern rules to ControllerRegister.
@@ -277,6 +278,10 @@ func (p *ControllerRegister) Include(cList ...ControllerInterface) { @@ -277,6 +278,10 @@ func (p *ControllerRegister) Include(cList ...ControllerInterface) {
277 key := t.PkgPath() + ":" + t.Name() 278 key := t.PkgPath() + ":" + t.Name()
278 if comm, ok := GlobalControllerRouter[key]; ok { 279 if comm, ok := GlobalControllerRouter[key]; ok {
279 for _, a := range comm { 280 for _, a := range comm {
  281 + for _, f := range a.Filters {
  282 + p.InsertFilter(f.Pattern, f.Pos, f.Filter, f.ReturnOnOutput, f.ResetParams)
  283 + }
  284 +
280 p.addWithMethodParams(a.Router, c, a.MethodParams, strings.Join(a.AllowHTTPMethods, ",")+":"+a.Method) 285 p.addWithMethodParams(a.Router, c, a.MethodParams, strings.Join(a.AllowHTTPMethods, ",")+":"+a.Method)
281 } 286 }
282 } 287 }
@@ -794,7 +799,7 @@ func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request) @@ -794,7 +799,7 @@ func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request)
794 if !isRunnable { 799 if !isRunnable {
795 //Invoke the request handler 800 //Invoke the request handler
796 var execController ControllerInterface 801 var execController ControllerInterface
797 - if routerInfo.initialize != nil { 802 + if routerInfo != nil && routerInfo.initialize != nil {
798 execController = routerInfo.initialize() 803 execController = routerInfo.initialize()
799 } else { 804 } else {
800 vc := reflect.New(runRouter) 805 vc := reflect.New(runRouter)
@@ -877,7 +882,7 @@ func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request) @@ -877,7 +882,7 @@ func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request)
877 } 882 }
878 883
879 Admin: 884 Admin:
880 -//admin module record QPS 885 + //admin module record QPS
881 886
882 statusCode := context.ResponseWriter.Status 887 statusCode := context.ResponseWriter.Status
883 if statusCode == 0 { 888 if statusCode == 0 {
@@ -886,8 +891,9 @@ Admin: @@ -886,8 +891,9 @@ Admin:
886 891
887 logAccess(context, &startTime, statusCode) 892 logAccess(context, &startTime, statusCode)
888 893
  894 + timeDur := time.Since(startTime)
  895 + context.ResponseWriter.Elapsed = timeDur
889 if BConfig.Listen.EnableAdmin { 896 if BConfig.Listen.EnableAdmin {
890 - timeDur := time.Since(startTime)  
891 pattern := "" 897 pattern := ""
892 if routerInfo != nil { 898 if routerInfo != nil {
893 pattern = routerInfo.pattern 899 pattern = routerInfo.pattern
@@ -904,7 +910,6 @@ Admin: @@ -904,7 +910,6 @@ Admin:
904 910
905 if BConfig.RunMode == DEV && !BConfig.Log.AccessLogs { 911 if BConfig.RunMode == DEV && !BConfig.Log.AccessLogs {
906 var devInfo string 912 var devInfo string
907 - timeDur := time.Since(startTime)  
908 iswin := (runtime.GOOS == "windows") 913 iswin := (runtime.GOOS == "windows")
909 statusColor := logs.ColorByStatus(iswin, statusCode) 914 statusColor := logs.ColorByStatus(iswin, statusCode)
910 methodColor := logs.ColorByMethod(iswin, r.Method) 915 methodColor := logs.ColorByMethod(iswin, r.Method)
@@ -21,6 +21,7 @@ import ( @@ -21,6 +21,7 @@ import (
21 "os" 21 "os"
22 "path" 22 "path"
23 "path/filepath" 23 "path/filepath"
  24 + "strings"
24 "sync" 25 "sync"
25 "time" 26 "time"
26 ) 27 )
@@ -127,6 +128,9 @@ func (fp *FileProvider) SessionInit(maxlifetime int64, savePath string) error { @@ -127,6 +128,9 @@ func (fp *FileProvider) SessionInit(maxlifetime int64, savePath string) error {
127 // if file is not exist, create it. 128 // if file is not exist, create it.
128 // the file path is generated from sid string. 129 // the file path is generated from sid string.
129 func (fp *FileProvider) SessionRead(sid string) (Store, error) { 130 func (fp *FileProvider) SessionRead(sid string) (Store, error) {
  131 + if strings.ContainsAny(sid, "./") {
  132 + return nil, nil
  133 + }
130 filepder.lock.Lock() 134 filepder.lock.Lock()
131 defer filepder.lock.Unlock() 135 defer filepder.lock.Unlock()
132 136
@@ -96,6 +96,7 @@ type ManagerConfig struct { @@ -96,6 +96,7 @@ type ManagerConfig struct {
96 EnableSidInHTTPHeader bool `json:"EnableSidInHTTPHeader"` 96 EnableSidInHTTPHeader bool `json:"EnableSidInHTTPHeader"`
97 SessionNameInHTTPHeader string `json:"SessionNameInHTTPHeader"` 97 SessionNameInHTTPHeader string `json:"SessionNameInHTTPHeader"`
98 EnableSidInURLQuery bool `json:"EnableSidInURLQuery"` 98 EnableSidInURLQuery bool `json:"EnableSidInURLQuery"`
  99 + SessionIDPrefix string `json:"sessionIDPrefix"`
99 } 100 }
100 101
101 // Manager contains Provider and its configuration. 102 // Manager contains Provider and its configuration.
@@ -153,6 +154,11 @@ func NewManager(provideName string, cf *ManagerConfig) (*Manager, error) { @@ -153,6 +154,11 @@ func NewManager(provideName string, cf *ManagerConfig) (*Manager, error) {
153 }, nil 154 }, nil
154 } 155 }
155 156
  157 +// GetProvider return current manager's provider
  158 +func (manager *Manager) GetProvider() Provider {
  159 + return manager.provider
  160 +}
  161 +
156 // getSid retrieves session identifier from HTTP Request. 162 // getSid retrieves session identifier from HTTP Request.
157 // First try to retrieve id by reading from cookie, session cookie name is configurable, 163 // First try to retrieve id by reading from cookie, session cookie name is configurable,
158 // if not exist, then retrieve id from querying parameters. 164 // if not exist, then retrieve id from querying parameters.
@@ -331,7 +337,7 @@ func (manager *Manager) sessionID() (string, error) { @@ -331,7 +337,7 @@ func (manager *Manager) sessionID() (string, error) {
331 if n != len(b) || err != nil { 337 if n != len(b) || err != nil {
332 return "", fmt.Errorf("Could not successfully read from the system CSPRNG") 338 return "", fmt.Errorf("Could not successfully read from the system CSPRNG")
333 } 339 }
334 - return hex.EncodeToString(b), nil 340 + return manager.config.SessionIDPrefix + hex.EncodeToString(b), nil
335 } 341 }
336 342
337 // Set cookie with https. 343 // Set cookie with https.
@@ -178,7 +178,7 @@ func searchFile(ctx *context.Context) (string, os.FileInfo, error) { @@ -178,7 +178,7 @@ func searchFile(ctx *context.Context) (string, os.FileInfo, error) {
178 if !strings.Contains(requestPath, prefix) { 178 if !strings.Contains(requestPath, prefix) {
179 continue 179 continue
180 } 180 }
181 - if len(requestPath) > len(prefix) && requestPath[len(prefix)] != '/' { 181 + if prefix != "/" && len(requestPath) > len(prefix) && requestPath[len(prefix)] != '/' {
182 continue 182 continue
183 } 183 }
184 filePath := path.Join(staticDir, requestPath[len(prefix):]) 184 filePath := path.Join(staticDir, requestPath[len(prefix):])
@@ -20,6 +20,7 @@ import ( @@ -20,6 +20,7 @@ import (
20 "html/template" 20 "html/template"
21 "io" 21 "io"
22 "io/ioutil" 22 "io/ioutil"
  23 + "net/http"
23 "os" 24 "os"
24 "path/filepath" 25 "path/filepath"
25 "regexp" 26 "regexp"
@@ -40,6 +41,7 @@ var ( @@ -40,6 +41,7 @@ var (
40 beeTemplateExt = []string{"tpl", "html"} 41 beeTemplateExt = []string{"tpl", "html"}
41 // beeTemplatePreprocessors stores associations of extension -> preprocessor handler 42 // beeTemplatePreprocessors stores associations of extension -> preprocessor handler
42 beeTemplateEngines = map[string]templatePreProcessor{} 43 beeTemplateEngines = map[string]templatePreProcessor{}
  44 + beeTemplateFS = defaultFSFunc
43 ) 45 )
44 46
45 // ExecuteTemplate applies the template with name to the specified data object, 47 // ExecuteTemplate applies the template with name to the specified data object,
@@ -181,12 +183,17 @@ func lockViewPaths() { @@ -181,12 +183,17 @@ func lockViewPaths() {
181 // BuildTemplate will build all template files in a directory. 183 // BuildTemplate will build all template files in a directory.
182 // it makes beego can render any template file in view directory. 184 // it makes beego can render any template file in view directory.
183 func BuildTemplate(dir string, files ...string) error { 185 func BuildTemplate(dir string, files ...string) error {
184 - if _, err := os.Stat(dir); err != nil { 186 + var err error
  187 + fs := beeTemplateFS()
  188 + f, err := fs.Open(dir)
  189 + if err != nil {
185 if os.IsNotExist(err) { 190 if os.IsNotExist(err) {
186 return nil 191 return nil
187 } 192 }
188 return errors.New("dir open err") 193 return errors.New("dir open err")
189 } 194 }
  195 + defer f.Close()
  196 +
190 beeTemplates, ok := beeViewPathTemplates[dir] 197 beeTemplates, ok := beeViewPathTemplates[dir]
191 if !ok { 198 if !ok {
192 panic("Unknown view path: " + dir) 199 panic("Unknown view path: " + dir)
@@ -195,11 +202,11 @@ func BuildTemplate(dir string, files ...string) error { @@ -195,11 +202,11 @@ func BuildTemplate(dir string, files ...string) error {
195 root: dir, 202 root: dir,
196 files: make(map[string][]string), 203 files: make(map[string][]string),
197 } 204 }
198 - err := filepath.Walk(dir, func(path string, f os.FileInfo, err error) error { 205 + err = Walk(fs, dir, func(path string, f os.FileInfo, err error) error {
199 return self.visit(path, f, err) 206 return self.visit(path, f, err)
200 }) 207 })
201 if err != nil { 208 if err != nil {
202 - fmt.Printf("filepath.Walk() returned %v\n", err) 209 + fmt.Printf("Walk() returned %v\n", err)
203 return err 210 return err
204 } 211 }
205 buildAllFiles := len(files) == 0 212 buildAllFiles := len(files) == 0
@@ -210,11 +217,11 @@ func BuildTemplate(dir string, files ...string) error { @@ -210,11 +217,11 @@ func BuildTemplate(dir string, files ...string) error {
210 ext := filepath.Ext(file) 217 ext := filepath.Ext(file)
211 var t *template.Template 218 var t *template.Template
212 if len(ext) == 0 { 219 if len(ext) == 0 {
213 - t, err = getTemplate(self.root, file, v...) 220 + t, err = getTemplate(self.root, fs, file, v...)
214 } else if fn, ok := beeTemplateEngines[ext[1:]]; ok { 221 } else if fn, ok := beeTemplateEngines[ext[1:]]; ok {
215 t, err = fn(self.root, file, beegoTplFuncMap) 222 t, err = fn(self.root, file, beegoTplFuncMap)
216 } else { 223 } else {
217 - t, err = getTemplate(self.root, file, v...) 224 + t, err = getTemplate(self.root, fs, file, v...)
218 } 225 }
219 if err != nil { 226 if err != nil {
220 logs.Error("parse template err:", file, err) 227 logs.Error("parse template err:", file, err)
@@ -229,9 +236,10 @@ func BuildTemplate(dir string, files ...string) error { @@ -229,9 +236,10 @@ func BuildTemplate(dir string, files ...string) error {
229 return nil 236 return nil
230 } 237 }
231 238
232 -func getTplDeep(root, file, parent string, t *template.Template) (*template.Template, [][]string, error) { 239 +func getTplDeep(root string, fs http.FileSystem, file string, parent string, t *template.Template) (*template.Template, [][]string, error) {
233 var fileAbsPath string 240 var fileAbsPath string
234 var rParent string 241 var rParent string
  242 + var err error
235 if filepath.HasPrefix(file, "../") { 243 if filepath.HasPrefix(file, "../") {
236 rParent = filepath.Join(filepath.Dir(parent), file) 244 rParent = filepath.Join(filepath.Dir(parent), file)
237 fileAbsPath = filepath.Join(root, filepath.Dir(parent), file) 245 fileAbsPath = filepath.Join(root, filepath.Dir(parent), file)
@@ -239,10 +247,12 @@ func getTplDeep(root, file, parent string, t *template.Template) (*template.Temp @@ -239,10 +247,12 @@ func getTplDeep(root, file, parent string, t *template.Template) (*template.Temp
239 rParent = file 247 rParent = file
240 fileAbsPath = filepath.Join(root, file) 248 fileAbsPath = filepath.Join(root, file)
241 } 249 }
242 - if e := utils.FileExists(fileAbsPath); !e { 250 + f, err := fs.Open(fileAbsPath)
  251 + defer f.Close()
  252 + if err != nil {
243 panic("can't find template file:" + file) 253 panic("can't find template file:" + file)
244 } 254 }
245 - data, err := ioutil.ReadFile(fileAbsPath) 255 + data, err := ioutil.ReadAll(f)
246 if err != nil { 256 if err != nil {
247 return nil, [][]string{}, err 257 return nil, [][]string{}, err
248 } 258 }
@@ -261,7 +271,7 @@ func getTplDeep(root, file, parent string, t *template.Template) (*template.Temp @@ -261,7 +271,7 @@ func getTplDeep(root, file, parent string, t *template.Template) (*template.Temp
261 if !HasTemplateExt(m[1]) { 271 if !HasTemplateExt(m[1]) {
262 continue 272 continue
263 } 273 }
264 - _, _, err = getTplDeep(root, m[1], rParent, t) 274 + _, _, err = getTplDeep(root, fs, m[1], rParent, t)
265 if err != nil { 275 if err != nil {
266 return nil, [][]string{}, err 276 return nil, [][]string{}, err
267 } 277 }
@@ -270,14 +280,14 @@ func getTplDeep(root, file, parent string, t *template.Template) (*template.Temp @@ -270,14 +280,14 @@ func getTplDeep(root, file, parent string, t *template.Template) (*template.Temp
270 return t, allSub, nil 280 return t, allSub, nil
271 } 281 }
272 282
273 -func getTemplate(root, file string, others ...string) (t *template.Template, err error) { 283 +func getTemplate(root string, fs http.FileSystem, file string, others ...string) (t *template.Template, err error) {
274 t = template.New(file).Delims(BConfig.WebConfig.TemplateLeft, BConfig.WebConfig.TemplateRight).Funcs(beegoTplFuncMap) 284 t = template.New(file).Delims(BConfig.WebConfig.TemplateLeft, BConfig.WebConfig.TemplateRight).Funcs(beegoTplFuncMap)
275 var subMods [][]string 285 var subMods [][]string
276 - t, subMods, err = getTplDeep(root, file, "", t) 286 + t, subMods, err = getTplDeep(root, fs, file, "", t)
277 if err != nil { 287 if err != nil {
278 return nil, err 288 return nil, err
279 } 289 }
280 - t, err = _getTemplate(t, root, subMods, others...) 290 + t, err = _getTemplate(t, root, fs, subMods, others...)
281 291
282 if err != nil { 292 if err != nil {
283 return nil, err 293 return nil, err
@@ -285,7 +295,7 @@ func getTemplate(root, file string, others ...string) (t *template.Template, err @@ -285,7 +295,7 @@ func getTemplate(root, file string, others ...string) (t *template.Template, err
285 return 295 return
286 } 296 }
287 297
288 -func _getTemplate(t0 *template.Template, root string, subMods [][]string, others ...string) (t *template.Template, err error) { 298 +func _getTemplate(t0 *template.Template, root string, fs http.FileSystem, subMods [][]string, others ...string) (t *template.Template, err error) {
289 t = t0 299 t = t0
290 for _, m := range subMods { 300 for _, m := range subMods {
291 if len(m) == 2 { 301 if len(m) == 2 {
@@ -297,11 +307,11 @@ func _getTemplate(t0 *template.Template, root string, subMods [][]string, others @@ -297,11 +307,11 @@ func _getTemplate(t0 *template.Template, root string, subMods [][]string, others
297 for _, otherFile := range others { 307 for _, otherFile := range others {
298 if otherFile == m[1] { 308 if otherFile == m[1] {
299 var subMods1 [][]string 309 var subMods1 [][]string
300 - t, subMods1, err = getTplDeep(root, otherFile, "", t) 310 + t, subMods1, err = getTplDeep(root, fs, otherFile, "", t)
301 if err != nil { 311 if err != nil {
302 logs.Trace("template parse file err:", err) 312 logs.Trace("template parse file err:", err)
303 } else if len(subMods1) > 0 { 313 } else if len(subMods1) > 0 {
304 - t, err = _getTemplate(t, root, subMods1, others...) 314 + t, err = _getTemplate(t, root, fs, subMods1, others...)
305 } 315 }
306 break 316 break
307 } 317 }
@@ -310,8 +320,16 @@ func _getTemplate(t0 *template.Template, root string, subMods [][]string, others @@ -310,8 +320,16 @@ func _getTemplate(t0 *template.Template, root string, subMods [][]string, others
310 for _, otherFile := range others { 320 for _, otherFile := range others {
311 var data []byte 321 var data []byte
312 fileAbsPath := filepath.Join(root, otherFile) 322 fileAbsPath := filepath.Join(root, otherFile)
313 - data, err = ioutil.ReadFile(fileAbsPath) 323 + f, err := fs.Open(fileAbsPath)
  324 + if err != nil {
  325 + f.Close()
  326 + logs.Trace("template file parse error, not success open file:", err)
  327 + continue
  328 + }
  329 + data, err = ioutil.ReadAll(f)
  330 + f.Close()
314 if err != nil { 331 if err != nil {
  332 + logs.Trace("template file parse error, not success read file:", err)
315 continue 333 continue
316 } 334 }
317 reg := regexp.MustCompile(BConfig.WebConfig.TemplateLeft + "[ ]*define[ ]+\"([^\"]+)\"") 335 reg := regexp.MustCompile(BConfig.WebConfig.TemplateLeft + "[ ]*define[ ]+\"([^\"]+)\"")
@@ -319,11 +337,14 @@ func _getTemplate(t0 *template.Template, root string, subMods [][]string, others @@ -319,11 +337,14 @@ func _getTemplate(t0 *template.Template, root string, subMods [][]string, others
319 for _, sub := range allSub { 337 for _, sub := range allSub {
320 if len(sub) == 2 && sub[1] == m[1] { 338 if len(sub) == 2 && sub[1] == m[1] {
321 var subMods1 [][]string 339 var subMods1 [][]string
322 - t, subMods1, err = getTplDeep(root, otherFile, "", t) 340 + t, subMods1, err = getTplDeep(root, fs, otherFile, "", t)
323 if err != nil { 341 if err != nil {
324 logs.Trace("template parse file err:", err) 342 logs.Trace("template parse file err:", err)
325 } else if len(subMods1) > 0 { 343 } else if len(subMods1) > 0 {
326 - t, err = _getTemplate(t, root, subMods1, others...) 344 + t, err = _getTemplate(t, root, fs, subMods1, others...)
  345 + if err != nil {
  346 + logs.Trace("template parse file err:", err)
  347 + }
327 } 348 }
328 break 349 break
329 } 350 }
@@ -335,6 +356,17 @@ func _getTemplate(t0 *template.Template, root string, subMods [][]string, others @@ -335,6 +356,17 @@ func _getTemplate(t0 *template.Template, root string, subMods [][]string, others
335 return 356 return
336 } 357 }
337 358
  359 +type templateFSFunc func() http.FileSystem
  360 +
  361 +func defaultFSFunc() http.FileSystem {
  362 + return FileSystem{}
  363 +}
  364 +
  365 +// SetTemplateFSFunc set default filesystem function
  366 +func SetTemplateFSFunc(fnt templateFSFunc) {
  367 + beeTemplateFS = fnt
  368 +}
  369 +
338 // SetViewsPath sets view directory path in beego application. 370 // SetViewsPath sets view directory path in beego application.
339 func SetViewsPath(path string) *App { 371 func SetViewsPath(path string) *App {
340 BConfig.WebConfig.ViewsPath = path 372 BConfig.WebConfig.ViewsPath = path
@@ -692,7 +692,7 @@ func ge(arg1, arg2 interface{}) (bool, error) { @@ -692,7 +692,7 @@ func ge(arg1, arg2 interface{}) (bool, error) {
692 692
693 // MapGet getting value from map by keys 693 // MapGet getting value from map by keys
694 // usage: 694 // usage:
695 -// Data["m"] = map[string]interface{} { 695 +// Data["m"] = M{
696 // "a": 1, 696 // "a": 1,
697 // "1": map[string]float64{ 697 // "1": map[string]float64{
698 // "c": 4, 698 // "c": 4,
@@ -428,6 +428,9 @@ func run() { @@ -428,6 +428,9 @@ func run() {
428 continue 428 continue
429 case <-changed: 429 case <-changed:
430 now = time.Now().Local() 430 now = time.Now().Local()
  431 + for _, t := range AdminTaskList {
  432 + t.SetNext(now)
  433 + }
431 continue 434 continue
432 case <-stop: 435 case <-stop:
433 return 436 return
@@ -446,6 +449,7 @@ func StopTask() { @@ -446,6 +449,7 @@ func StopTask() {
446 449
447 // AddTask add task with name 450 // AddTask add task with name
448 func AddTask(taskname string, t Tasker) { 451 func AddTask(taskname string, t Tasker) {
  452 + t.SetNext(time.Now().Local())
449 AdminTaskList[taskname] = t 453 AdminTaskList[taskname] = t
450 if isstart { 454 if isstart {
451 changed <- true 455 changed <- true
  1 +language: go
  2 +sudo: false
  3 +go:
  4 + - 1.2
  5 + - 1.3
  6 + - 1.4
  7 + - 1.5
  8 + - 1.6
  9 + - 1.7
  10 + - 1.8
  11 + - 1.9
  12 + - tip
  13 +matrix:
  14 + allow_failures:
  15 + - go: tip
  16 + fast_finish: true
  17 +before_install:
  18 + - go get github.com/mattn/goveralls
  19 + - go get golang.org/x/tools/cmd/cover
  20 +script:
  21 + - $HOME/gopath/bin/goveralls -service=travis-ci
  22 +notifications:
  23 + email: false
  1 +Copyright (C) 2013-2018 by Maxim Bublis <b@codemonkey.ru>
  2 +
  3 +Permission is hereby granted, free of charge, to any person obtaining
  4 +a copy of this software and associated documentation files (the
  5 +"Software"), to deal in the Software without restriction, including
  6 +without limitation the rights to use, copy, modify, merge, publish,
  7 +distribute, sublicense, and/or sell copies of the Software, and to
  8 +permit persons to whom the Software is furnished to do so, subject to
  9 +the following conditions:
  10 +
  11 +The above copyright notice and this permission notice shall be
  12 +included in all copies or substantial portions of the Software.
  13 +
  14 +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  15 +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  16 +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  17 +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  18 +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  19 +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  20 +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  1 +# UUID package for Go language
  2 +
  3 +[![Build Status](https://travis-ci.org/satori/go.uuid.png?branch=master)](https://travis-ci.org/satori/go.uuid)
  4 +[![Coverage Status](https://coveralls.io/repos/github/satori/go.uuid/badge.svg?branch=master)](https://coveralls.io/github/satori/go.uuid)
  5 +[![GoDoc](http://godoc.org/github.com/satori/go.uuid?status.png)](http://godoc.org/github.com/satori/go.uuid)
  6 +
  7 +This package provides pure Go implementation of Universally Unique Identifier (UUID). Supported both creation and parsing of UUIDs.
  8 +
  9 +With 100% test coverage and benchmarks out of box.
  10 +
  11 +Supported versions:
  12 +* Version 1, based on timestamp and MAC address (RFC 4122)
  13 +* Version 2, based on timestamp, MAC address and POSIX UID/GID (DCE 1.1)
  14 +* Version 3, based on MD5 hashing (RFC 4122)
  15 +* Version 4, based on random numbers (RFC 4122)
  16 +* Version 5, based on SHA-1 hashing (RFC 4122)
  17 +
  18 +## Installation
  19 +
  20 +Use the `go` command:
  21 +
  22 + $ go get github.com/satori/go.uuid
  23 +
  24 +## Requirements
  25 +
  26 +UUID package requires Go >= 1.2.
  27 +
  28 +## Example
  29 +
  30 +```go
  31 +package main
  32 +
  33 +import (
  34 + "fmt"
  35 + "github.com/satori/go.uuid"
  36 +)
  37 +
  38 +func main() {
  39 + // Creating UUID Version 4
  40 + u1 := uuid.NewV4()
  41 + fmt.Printf("UUIDv4: %s\n", u1)
  42 +
  43 + // Parsing UUID from string input
  44 + u2, err := uuid.FromString("6ba7b810-9dad-11d1-80b4-00c04fd430c8")
  45 + if err != nil {
  46 + fmt.Printf("Something gone wrong: %s", err)
  47 + }
  48 + fmt.Printf("Successfully parsed: %s", u2)
  49 +}
  50 +```
  51 +
  52 +## Documentation
  53 +
  54 +[Documentation](http://godoc.org/github.com/satori/go.uuid) is hosted at GoDoc project.
  55 +
  56 +## Links
  57 +* [RFC 4122](http://tools.ietf.org/html/rfc4122)
  58 +* [DCE 1.1: Authentication and Security Services](http://pubs.opengroup.org/onlinepubs/9696989899/chap5.htm#tagcjh_08_02_01_01)
  59 +
  60 +## Copyright
  61 +
  62 +Copyright (C) 2013-2018 by Maxim Bublis <b@codemonkey.ru>.
  63 +
  64 +UUID package released under MIT License.
  65 +See [LICENSE](https://github.com/satori/go.uuid/blob/master/LICENSE) for details.
  1 +// Copyright (C) 2013-2018 by Maxim Bublis <b@codemonkey.ru>
  2 +//
  3 +// Permission is hereby granted, free of charge, to any person obtaining
  4 +// a copy of this software and associated documentation files (the
  5 +// "Software"), to deal in the Software without restriction, including
  6 +// without limitation the rights to use, copy, modify, merge, publish,
  7 +// distribute, sublicense, and/or sell copies of the Software, and to
  8 +// permit persons to whom the Software is furnished to do so, subject to
  9 +// the following conditions:
  10 +//
  11 +// The above copyright notice and this permission notice shall be
  12 +// included in all copies or substantial portions of the Software.
  13 +//
  14 +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  15 +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  16 +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  17 +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  18 +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  19 +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  20 +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  21 +
  22 +package uuid
  23 +
  24 +import (
  25 + "bytes"
  26 + "encoding/hex"
  27 + "fmt"
  28 +)
  29 +
  30 +// FromBytes returns UUID converted from raw byte slice input.
  31 +// It will return error if the slice isn't 16 bytes long.
  32 +func FromBytes(input []byte) (u UUID, err error) {
  33 + err = u.UnmarshalBinary(input)
  34 + return
  35 +}
  36 +
  37 +// FromBytesOrNil returns UUID converted from raw byte slice input.
  38 +// Same behavior as FromBytes, but returns a Nil UUID on error.
  39 +func FromBytesOrNil(input []byte) UUID {
  40 + uuid, err := FromBytes(input)
  41 + if err != nil {
  42 + return Nil
  43 + }
  44 + return uuid
  45 +}
  46 +
  47 +// FromString returns UUID parsed from string input.
  48 +// Input is expected in a form accepted by UnmarshalText.
  49 +func FromString(input string) (u UUID, err error) {
  50 + err = u.UnmarshalText([]byte(input))
  51 + return
  52 +}
  53 +
  54 +// FromStringOrNil returns UUID parsed from string input.
  55 +// Same behavior as FromString, but returns a Nil UUID on error.
  56 +func FromStringOrNil(input string) UUID {
  57 + uuid, err := FromString(input)
  58 + if err != nil {
  59 + return Nil
  60 + }
  61 + return uuid
  62 +}
  63 +
  64 +// MarshalText implements the encoding.TextMarshaler interface.
  65 +// The encoding is the same as returned by String.
  66 +func (u UUID) MarshalText() (text []byte, err error) {
  67 + text = []byte(u.String())
  68 + return
  69 +}
  70 +
  71 +// UnmarshalText implements the encoding.TextUnmarshaler interface.
  72 +// Following formats are supported:
  73 +// "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
  74 +// "{6ba7b810-9dad-11d1-80b4-00c04fd430c8}",
  75 +// "urn:uuid:6ba7b810-9dad-11d1-80b4-00c04fd430c8"
  76 +// "6ba7b8109dad11d180b400c04fd430c8"
  77 +// ABNF for supported UUID text representation follows:
  78 +// uuid := canonical | hashlike | braced | urn
  79 +// plain := canonical | hashlike
  80 +// canonical := 4hexoct '-' 2hexoct '-' 2hexoct '-' 6hexoct
  81 +// hashlike := 12hexoct
  82 +// braced := '{' plain '}'
  83 +// urn := URN ':' UUID-NID ':' plain
  84 +// URN := 'urn'
  85 +// UUID-NID := 'uuid'
  86 +// 12hexoct := 6hexoct 6hexoct
  87 +// 6hexoct := 4hexoct 2hexoct
  88 +// 4hexoct := 2hexoct 2hexoct
  89 +// 2hexoct := hexoct hexoct
  90 +// hexoct := hexdig hexdig
  91 +// hexdig := '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' |
  92 +// 'a' | 'b' | 'c' | 'd' | 'e' | 'f' |
  93 +// 'A' | 'B' | 'C' | 'D' | 'E' | 'F'
  94 +func (u *UUID) UnmarshalText(text []byte) (err error) {
  95 + switch len(text) {
  96 + case 32:
  97 + return u.decodeHashLike(text)
  98 + case 36:
  99 + return u.decodeCanonical(text)
  100 + case 38:
  101 + return u.decodeBraced(text)
  102 + case 41:
  103 + fallthrough
  104 + case 45:
  105 + return u.decodeURN(text)
  106 + default:
  107 + return fmt.Errorf("uuid: incorrect UUID length: %s", text)
  108 + }
  109 +}
  110 +
  111 +// decodeCanonical decodes UUID string in format
  112 +// "6ba7b810-9dad-11d1-80b4-00c04fd430c8".
  113 +func (u *UUID) decodeCanonical(t []byte) (err error) {
  114 + if t[8] != '-' || t[13] != '-' || t[18] != '-' || t[23] != '-' {
  115 + return fmt.Errorf("uuid: incorrect UUID format %s", t)
  116 + }
  117 +
  118 + src := t[:]
  119 + dst := u[:]
  120 +
  121 + for i, byteGroup := range byteGroups {
  122 + if i > 0 {
  123 + src = src[1:] // skip dash
  124 + }
  125 + _, err = hex.Decode(dst[:byteGroup/2], src[:byteGroup])
  126 + if err != nil {
  127 + return
  128 + }
  129 + src = src[byteGroup:]
  130 + dst = dst[byteGroup/2:]
  131 + }
  132 +
  133 + return
  134 +}
  135 +
  136 +// decodeHashLike decodes UUID string in format
  137 +// "6ba7b8109dad11d180b400c04fd430c8".
  138 +func (u *UUID) decodeHashLike(t []byte) (err error) {
  139 + src := t[:]
  140 + dst := u[:]
  141 +
  142 + if _, err = hex.Decode(dst, src); err != nil {
  143 + return err
  144 + }
  145 + return
  146 +}
  147 +
  148 +// decodeBraced decodes UUID string in format
  149 +// "{6ba7b810-9dad-11d1-80b4-00c04fd430c8}" or in format
  150 +// "{6ba7b8109dad11d180b400c04fd430c8}".
  151 +func (u *UUID) decodeBraced(t []byte) (err error) {
  152 + l := len(t)
  153 +
  154 + if t[0] != '{' || t[l-1] != '}' {
  155 + return fmt.Errorf("uuid: incorrect UUID format %s", t)
  156 + }
  157 +
  158 + return u.decodePlain(t[1 : l-1])
  159 +}
  160 +
  161 +// decodeURN decodes UUID string in format
  162 +// "urn:uuid:6ba7b810-9dad-11d1-80b4-00c04fd430c8" or in format
  163 +// "urn:uuid:6ba7b8109dad11d180b400c04fd430c8".
  164 +func (u *UUID) decodeURN(t []byte) (err error) {
  165 + total := len(t)
  166 +
  167 + urn_uuid_prefix := t[:9]
  168 +
  169 + if !bytes.Equal(urn_uuid_prefix, urnPrefix) {
  170 + return fmt.Errorf("uuid: incorrect UUID format: %s", t)
  171 + }
  172 +
  173 + return u.decodePlain(t[9:total])
  174 +}
  175 +
  176 +// decodePlain decodes UUID string in canonical format
  177 +// "6ba7b810-9dad-11d1-80b4-00c04fd430c8" or in hash-like format
  178 +// "6ba7b8109dad11d180b400c04fd430c8".
  179 +func (u *UUID) decodePlain(t []byte) (err error) {
  180 + switch len(t) {
  181 + case 32:
  182 + return u.decodeHashLike(t)
  183 + case 36:
  184 + return u.decodeCanonical(t)
  185 + default:
  186 + return fmt.Errorf("uuid: incorrrect UUID length: %s", t)
  187 + }
  188 +}
  189 +
  190 +// MarshalBinary implements the encoding.BinaryMarshaler interface.
  191 +func (u UUID) MarshalBinary() (data []byte, err error) {
  192 + data = u.Bytes()
  193 + return
  194 +}
  195 +
  196 +// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface.
  197 +// It will return error if the slice isn't 16 bytes long.
  198 +func (u *UUID) UnmarshalBinary(data []byte) (err error) {
  199 + if len(data) != Size {
  200 + err = fmt.Errorf("uuid: UUID must be exactly 16 bytes long, got %d bytes", len(data))
  201 + return
  202 + }
  203 + copy(u[:], data)
  204 +
  205 + return
  206 +}
  1 +// Copyright (C) 2013-2018 by Maxim Bublis <b@codemonkey.ru>
  2 +//
  3 +// Permission is hereby granted, free of charge, to any person obtaining
  4 +// a copy of this software and associated documentation files (the
  5 +// "Software"), to deal in the Software without restriction, including
  6 +// without limitation the rights to use, copy, modify, merge, publish,
  7 +// distribute, sublicense, and/or sell copies of the Software, and to
  8 +// permit persons to whom the Software is furnished to do so, subject to
  9 +// the following conditions:
  10 +//
  11 +// The above copyright notice and this permission notice shall be
  12 +// included in all copies or substantial portions of the Software.
  13 +//
  14 +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  15 +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  16 +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  17 +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  18 +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  19 +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  20 +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  21 +
  22 +package uuid
  23 +
  24 +import (
  25 + "crypto/md5"
  26 + "crypto/rand"
  27 + "crypto/sha1"
  28 + "encoding/binary"
  29 + "hash"
  30 + "net"
  31 + "os"
  32 + "sync"
  33 + "time"
  34 +)
  35 +
  36 +// Difference in 100-nanosecond intervals between
  37 +// UUID epoch (October 15, 1582) and Unix epoch (January 1, 1970).
  38 +const epochStart = 122192928000000000
  39 +
  40 +var (
  41 + global = newDefaultGenerator()
  42 +
  43 + epochFunc = unixTimeFunc
  44 + posixUID = uint32(os.Getuid())
  45 + posixGID = uint32(os.Getgid())
  46 +)
  47 +
  48 +// NewV1 returns UUID based on current timestamp and MAC address.
  49 +func NewV1() UUID {
  50 + return global.NewV1()
  51 +}
  52 +
  53 +// NewV2 returns DCE Security UUID based on POSIX UID/GID.
  54 +func NewV2(domain byte) UUID {
  55 + return global.NewV2(domain)
  56 +}
  57 +
  58 +// NewV3 returns UUID based on MD5 hash of namespace UUID and name.
  59 +func NewV3(ns UUID, name string) UUID {
  60 + return global.NewV3(ns, name)
  61 +}
  62 +
  63 +// NewV4 returns random generated UUID.
  64 +func NewV4() UUID {
  65 + return global.NewV4()
  66 +}
  67 +
  68 +// NewV5 returns UUID based on SHA-1 hash of namespace UUID and name.
  69 +func NewV5(ns UUID, name string) UUID {
  70 + return global.NewV5(ns, name)
  71 +}
  72 +
  73 +// Generator provides interface for generating UUIDs.
  74 +type Generator interface {
  75 + NewV1() UUID
  76 + NewV2(domain byte) UUID
  77 + NewV3(ns UUID, name string) UUID
  78 + NewV4() UUID
  79 + NewV5(ns UUID, name string) UUID
  80 +}
  81 +
  82 +// Default generator implementation.
  83 +type generator struct {
  84 + storageOnce sync.Once
  85 + storageMutex sync.Mutex
  86 +
  87 + lastTime uint64
  88 + clockSequence uint16
  89 + hardwareAddr [6]byte
  90 +}
  91 +
  92 +func newDefaultGenerator() Generator {
  93 + return &generator{}
  94 +}
  95 +
  96 +// NewV1 returns UUID based on current timestamp and MAC address.
  97 +func (g *generator) NewV1() UUID {
  98 + u := UUID{}
  99 +
  100 + timeNow, clockSeq, hardwareAddr := g.getStorage()
  101 +
  102 + binary.BigEndian.PutUint32(u[0:], uint32(timeNow))
  103 + binary.BigEndian.PutUint16(u[4:], uint16(timeNow>>32))
  104 + binary.BigEndian.PutUint16(u[6:], uint16(timeNow>>48))
  105 + binary.BigEndian.PutUint16(u[8:], clockSeq)
  106 +
  107 + copy(u[10:], hardwareAddr)
  108 +
  109 + u.SetVersion(V1)
  110 + u.SetVariant(VariantRFC4122)
  111 +
  112 + return u
  113 +}
  114 +
  115 +// NewV2 returns DCE Security UUID based on POSIX UID/GID.
  116 +func (g *generator) NewV2(domain byte) UUID {
  117 + u := UUID{}
  118 +
  119 + timeNow, clockSeq, hardwareAddr := g.getStorage()
  120 +
  121 + switch domain {
  122 + case DomainPerson:
  123 + binary.BigEndian.PutUint32(u[0:], posixUID)
  124 + case DomainGroup:
  125 + binary.BigEndian.PutUint32(u[0:], posixGID)
  126 + }
  127 +
  128 + binary.BigEndian.PutUint16(u[4:], uint16(timeNow>>32))
  129 + binary.BigEndian.PutUint16(u[6:], uint16(timeNow>>48))
  130 + binary.BigEndian.PutUint16(u[8:], clockSeq)
  131 + u[9] = domain
  132 +
  133 + copy(u[10:], hardwareAddr)
  134 +
  135 + u.SetVersion(V2)
  136 + u.SetVariant(VariantRFC4122)
  137 +
  138 + return u
  139 +}
  140 +
  141 +// NewV3 returns UUID based on MD5 hash of namespace UUID and name.
  142 +func (g *generator) NewV3(ns UUID, name string) UUID {
  143 + u := newFromHash(md5.New(), ns, name)
  144 + u.SetVersion(V3)
  145 + u.SetVariant(VariantRFC4122)
  146 +
  147 + return u
  148 +}
  149 +
  150 +// NewV4 returns random generated UUID.
  151 +func (g *generator) NewV4() UUID {
  152 + u := UUID{}
  153 + g.safeRandom(u[:])
  154 + u.SetVersion(V4)
  155 + u.SetVariant(VariantRFC4122)
  156 +
  157 + return u
  158 +}
  159 +
  160 +// NewV5 returns UUID based on SHA-1 hash of namespace UUID and name.
  161 +func (g *generator) NewV5(ns UUID, name string) UUID {
  162 + u := newFromHash(sha1.New(), ns, name)
  163 + u.SetVersion(V5)
  164 + u.SetVariant(VariantRFC4122)
  165 +
  166 + return u
  167 +}
  168 +
  169 +func (g *generator) initStorage() {
  170 + g.initClockSequence()
  171 + g.initHardwareAddr()
  172 +}
  173 +
  174 +func (g *generator) initClockSequence() {
  175 + buf := make([]byte, 2)
  176 + g.safeRandom(buf)
  177 + g.clockSequence = binary.BigEndian.Uint16(buf)
  178 +}
  179 +
  180 +func (g *generator) initHardwareAddr() {
  181 + interfaces, err := net.Interfaces()
  182 + if err == nil {
  183 + for _, iface := range interfaces {
  184 + if len(iface.HardwareAddr) >= 6 {
  185 + copy(g.hardwareAddr[:], iface.HardwareAddr)
  186 + return
  187 + }
  188 + }
  189 + }
  190 +
  191 + // Initialize hardwareAddr randomly in case
  192 + // of real network interfaces absence
  193 + g.safeRandom(g.hardwareAddr[:])
  194 +
  195 + // Set multicast bit as recommended in RFC 4122
  196 + g.hardwareAddr[0] |= 0x01
  197 +}
  198 +
  199 +func (g *generator) safeRandom(dest []byte) {
  200 + if _, err := rand.Read(dest); err != nil {
  201 + panic(err)
  202 + }
  203 +}
  204 +
  205 +// Returns UUID v1/v2 storage state.
  206 +// Returns epoch timestamp, clock sequence, and hardware address.
  207 +func (g *generator) getStorage() (uint64, uint16, []byte) {
  208 + g.storageOnce.Do(g.initStorage)
  209 +
  210 + g.storageMutex.Lock()
  211 + defer g.storageMutex.Unlock()
  212 +
  213 + timeNow := epochFunc()
  214 + // Clock changed backwards since last UUID generation.
  215 + // Should increase clock sequence.
  216 + if timeNow <= g.lastTime {
  217 + g.clockSequence++
  218 + }
  219 + g.lastTime = timeNow
  220 +
  221 + return timeNow, g.clockSequence, g.hardwareAddr[:]
  222 +}
  223 +
  224 +// Returns difference in 100-nanosecond intervals between
  225 +// UUID epoch (October 15, 1582) and current time.
  226 +// This is default epoch calculation function.
  227 +func unixTimeFunc() uint64 {
  228 + return epochStart + uint64(time.Now().UnixNano()/100)
  229 +}
  230 +
  231 +// Returns UUID based on hashing of namespace UUID and name.
  232 +func newFromHash(h hash.Hash, ns UUID, name string) UUID {
  233 + u := UUID{}
  234 + h.Write(ns[:])
  235 + h.Write([]byte(name))
  236 + copy(u[:], h.Sum(nil))
  237 +
  238 + return u
  239 +}
  1 +// Copyright (C) 2013-2018 by Maxim Bublis <b@codemonkey.ru>
  2 +//
  3 +// Permission is hereby granted, free of charge, to any person obtaining
  4 +// a copy of this software and associated documentation files (the
  5 +// "Software"), to deal in the Software without restriction, including
  6 +// without limitation the rights to use, copy, modify, merge, publish,
  7 +// distribute, sublicense, and/or sell copies of the Software, and to
  8 +// permit persons to whom the Software is furnished to do so, subject to
  9 +// the following conditions:
  10 +//
  11 +// The above copyright notice and this permission notice shall be
  12 +// included in all copies or substantial portions of the Software.
  13 +//
  14 +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  15 +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  16 +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  17 +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  18 +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  19 +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  20 +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  21 +
  22 +package uuid
  23 +
  24 +import (
  25 + "database/sql/driver"
  26 + "fmt"
  27 +)
  28 +
  29 +// Value implements the driver.Valuer interface.
  30 +func (u UUID) Value() (driver.Value, error) {
  31 + return u.String(), nil
  32 +}
  33 +
  34 +// Scan implements the sql.Scanner interface.
  35 +// A 16-byte slice is handled by UnmarshalBinary, while
  36 +// a longer byte slice or a string is handled by UnmarshalText.
  37 +func (u *UUID) Scan(src interface{}) error {
  38 + switch src := src.(type) {
  39 + case []byte:
  40 + if len(src) == Size {
  41 + return u.UnmarshalBinary(src)
  42 + }
  43 + return u.UnmarshalText(src)
  44 +
  45 + case string:
  46 + return u.UnmarshalText([]byte(src))
  47 + }
  48 +
  49 + return fmt.Errorf("uuid: cannot convert %T to UUID", src)
  50 +}
  51 +
  52 +// NullUUID can be used with the standard sql package to represent a
  53 +// UUID value that can be NULL in the database
  54 +type NullUUID struct {
  55 + UUID UUID
  56 + Valid bool
  57 +}
  58 +
  59 +// Value implements the driver.Valuer interface.
  60 +func (u NullUUID) Value() (driver.Value, error) {
  61 + if !u.Valid {
  62 + return nil, nil
  63 + }
  64 + // Delegate to UUID Value function
  65 + return u.UUID.Value()
  66 +}
  67 +
  68 +// Scan implements the sql.Scanner interface.
  69 +func (u *NullUUID) Scan(src interface{}) error {
  70 + if src == nil {
  71 + u.UUID, u.Valid = Nil, false
  72 + return nil
  73 + }
  74 +
  75 + // Delegate to UUID Scan function
  76 + u.Valid = true
  77 + return u.UUID.Scan(src)
  78 +}
  1 +// Copyright (C) 2013-2018 by Maxim Bublis <b@codemonkey.ru>
  2 +//
  3 +// Permission is hereby granted, free of charge, to any person obtaining
  4 +// a copy of this software and associated documentation files (the
  5 +// "Software"), to deal in the Software without restriction, including
  6 +// without limitation the rights to use, copy, modify, merge, publish,
  7 +// distribute, sublicense, and/or sell copies of the Software, and to
  8 +// permit persons to whom the Software is furnished to do so, subject to
  9 +// the following conditions:
  10 +//
  11 +// The above copyright notice and this permission notice shall be
  12 +// included in all copies or substantial portions of the Software.
  13 +//
  14 +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  15 +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  16 +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  17 +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  18 +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  19 +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  20 +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  21 +
  22 +// Package uuid provides implementation of Universally Unique Identifier (UUID).
  23 +// Supported versions are 1, 3, 4 and 5 (as specified in RFC 4122) and
  24 +// version 2 (as specified in DCE 1.1).
  25 +package uuid
  26 +
  27 +import (
  28 + "bytes"
  29 + "encoding/hex"
  30 +)
  31 +
  32 +// Size of a UUID in bytes.
  33 +const Size = 16
  34 +
  35 +// UUID representation compliant with specification
  36 +// described in RFC 4122.
  37 +type UUID [Size]byte
  38 +
  39 +// UUID versions
  40 +const (
  41 + _ byte = iota
  42 + V1
  43 + V2
  44 + V3
  45 + V4
  46 + V5
  47 +)
  48 +
  49 +// UUID layout variants.
  50 +const (
  51 + VariantNCS byte = iota
  52 + VariantRFC4122
  53 + VariantMicrosoft
  54 + VariantFuture
  55 +)
  56 +
  57 +// UUID DCE domains.
  58 +const (
  59 + DomainPerson = iota
  60 + DomainGroup
  61 + DomainOrg
  62 +)
  63 +
  64 +// String parse helpers.
  65 +var (
  66 + urnPrefix = []byte("urn:uuid:")
  67 + byteGroups = []int{8, 4, 4, 4, 12}
  68 +)
  69 +
  70 +// Nil is special form of UUID that is specified to have all
  71 +// 128 bits set to zero.
  72 +var Nil = UUID{}
  73 +
  74 +// Predefined namespace UUIDs.
  75 +var (
  76 + NamespaceDNS = Must(FromString("6ba7b810-9dad-11d1-80b4-00c04fd430c8"))
  77 + NamespaceURL = Must(FromString("6ba7b811-9dad-11d1-80b4-00c04fd430c8"))
  78 + NamespaceOID = Must(FromString("6ba7b812-9dad-11d1-80b4-00c04fd430c8"))
  79 + NamespaceX500 = Must(FromString("6ba7b814-9dad-11d1-80b4-00c04fd430c8"))
  80 +)
  81 +
  82 +// Equal returns true if u1 and u2 equals, otherwise returns false.
  83 +func Equal(u1 UUID, u2 UUID) bool {
  84 + return bytes.Equal(u1[:], u2[:])
  85 +}
  86 +
  87 +// Version returns algorithm version used to generate UUID.
  88 +func (u UUID) Version() byte {
  89 + return u[6] >> 4
  90 +}
  91 +
  92 +// Variant returns UUID layout variant.
  93 +func (u UUID) Variant() byte {
  94 + switch {
  95 + case (u[8] >> 7) == 0x00:
  96 + return VariantNCS
  97 + case (u[8] >> 6) == 0x02:
  98 + return VariantRFC4122
  99 + case (u[8] >> 5) == 0x06:
  100 + return VariantMicrosoft
  101 + case (u[8] >> 5) == 0x07:
  102 + fallthrough
  103 + default:
  104 + return VariantFuture
  105 + }
  106 +}
  107 +
  108 +// Bytes returns bytes slice representation of UUID.
  109 +func (u UUID) Bytes() []byte {
  110 + return u[:]
  111 +}
  112 +
  113 +// Returns canonical string representation of UUID:
  114 +// xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.
  115 +func (u UUID) String() string {
  116 + buf := make([]byte, 36)
  117 +
  118 + hex.Encode(buf[0:8], u[0:4])
  119 + buf[8] = '-'
  120 + hex.Encode(buf[9:13], u[4:6])
  121 + buf[13] = '-'
  122 + hex.Encode(buf[14:18], u[6:8])
  123 + buf[18] = '-'
  124 + hex.Encode(buf[19:23], u[8:10])
  125 + buf[23] = '-'
  126 + hex.Encode(buf[24:], u[10:])
  127 +
  128 + return string(buf)
  129 +}
  130 +
  131 +// SetVersion sets version bits.
  132 +func (u *UUID) SetVersion(v byte) {
  133 + u[6] = (u[6] & 0x0f) | (v << 4)
  134 +}
  135 +
  136 +// SetVariant sets variant bits.
  137 +func (u *UUID) SetVariant(v byte) {
  138 + switch v {
  139 + case VariantNCS:
  140 + u[8] = (u[8]&(0xff>>1) | (0x00 << 7))
  141 + case VariantRFC4122:
  142 + u[8] = (u[8]&(0xff>>2) | (0x02 << 6))
  143 + case VariantMicrosoft:
  144 + u[8] = (u[8]&(0xff>>3) | (0x06 << 5))
  145 + case VariantFuture:
  146 + fallthrough
  147 + default:
  148 + u[8] = (u[8]&(0xff>>3) | (0x07 << 5))
  149 + }
  150 +}
  151 +
  152 +// Must is a helper that wraps a call to a function returning (UUID, error)
  153 +// and panics if the error is non-nil. It is intended for use in variable
  154 +// initializations such as
  155 +// var packageUUID = uuid.Must(uuid.FromString("123e4567-e89b-12d3-a456-426655440000"));
  156 +func Must(u UUID, err error) UUID {
  157 + if err != nil {
  158 + panic(err)
  159 + }
  160 + return u
  161 +}
  1 +language: go
  2 +go:
  3 + - 1.10.x
  4 + - 1.11.x
  5 +sudo: false
  6 +before_install:
  7 + - go get -u golang.org/x/lint/golint
  8 + - go get github.com/axw/gocov/gocov
  9 + - go get github.com/mattn/goveralls
  10 +script:
  11 + - test -z "`gofmt -l .`"
  12 + - test -z "`golint ./...`"
  13 + - $GOPATH/bin/goveralls -service=travis-ci
  14 + - cd example && ./linux64_build.sh
  1 +The MIT License (MIT)
  2 +
  3 +Copyright 2015 Sony Corporation
  4 +
  5 +Permission is hereby granted, free of charge, to any person obtaining a copy
  6 +of this software and associated documentation files (the "Software"), to deal
  7 +in the Software without restriction, including without limitation the rights
  8 +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9 +copies of the Software, and to permit persons to whom the Software is
  10 +furnished to do so, subject to the following conditions:
  11 +
  12 +The above copyright notice and this permission notice shall be included in
  13 +all copies or substantial portions of the Software.
  14 +
  15 +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16 +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17 +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18 +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19 +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20 +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  21 +THE SOFTWARE.
  1 +Sonyflake
  2 +=========
  3 +
  4 +[![GoDoc](https://godoc.org/github.com/sony/sonyflake?status.svg)](http://godoc.org/github.com/sony/sonyflake)
  5 +[![Build Status](https://travis-ci.org/sony/sonyflake.svg?branch=master)](https://travis-ci.org/sony/sonyflake)
  6 +[![Coverage Status](https://coveralls.io/repos/sony/sonyflake/badge.svg?branch=master&service=github)](https://coveralls.io/github/sony/sonyflake?branch=master)
  7 +[![Go Report Card](https://goreportcard.com/badge/github.com/sony/sonyflake)](https://goreportcard.com/report/github.com/sony/sonyflake)
  8 +
  9 +Sonyflake is a distributed unique ID generator inspired by [Twitter's Snowflake](https://blog.twitter.com/2010/announcing-snowflake).
  10 +A Sonyflake ID is composed of
  11 +
  12 + 39 bits for time in units of 10 msec
  13 + 8 bits for a sequence number
  14 + 16 bits for a machine id
  15 +
  16 +Installation
  17 +------------
  18 +
  19 +```
  20 +go get github.com/sony/sonyflake
  21 +```
  22 +
  23 +Usage
  24 +-----
  25 +
  26 +The function NewSonyflake creates a new Sonyflake instance.
  27 +
  28 +```go
  29 +func NewSonyflake(st Settings) *Sonyflake
  30 +```
  31 +
  32 +You can configure Sonyflake by the struct Settings:
  33 +
  34 +```go
  35 +type Settings struct {
  36 + StartTime time.Time
  37 + MachineID func() (uint16, error)
  38 + CheckMachineID func(uint16) bool
  39 +}
  40 +```
  41 +
  42 +- StartTime is the time since which the Sonyflake time is defined as the elapsed time.
  43 + If StartTime is 0, the start time of the Sonyflake is set to "2014-09-01 00:00:00 +0000 UTC".
  44 + If StartTime is ahead of the current time, Sonyflake is not created.
  45 +
  46 +- MachineID returns the unique ID of the Sonyflake instance.
  47 + If MachineID returns an error, Sonyflake is not created.
  48 + If MachineID is nil, default MachineID is used.
  49 + Default MachineID returns the lower 16 bits of the private IP address.
  50 +
  51 +- CheckMachineID validates the uniqueness of the machine ID.
  52 + If CheckMachineID returns false, Sonyflake is not created.
  53 + If CheckMachineID is nil, no validation is done.
  54 +
  55 +In order to get a new unique ID, you just have to call the method NextID.
  56 +
  57 +```go
  58 +func (sf *Sonyflake) NextID() (uint64, error)
  59 +```
  60 +
  61 +NextID can continue to generate IDs for about 174 years from StartTime.
  62 +But after the Sonyflake time is over the limit, NextID returns an error.
  63 +
  64 +AWS VPC and Docker
  65 +------------------
  66 +
  67 +The [awsutil](https://github.com/sony/sonyflake/blob/master/awsutil) package provides
  68 +the function AmazonEC2MachineID that returns the lower 16-bit private IP address of the Amazon EC2 instance.
  69 +It also works correctly on Docker
  70 +by retrieving [instance metadata](http://docs.aws.amazon.com/en_us/AWSEC2/latest/UserGuide/ec2-instance-metadata.html).
  71 +
  72 +[AWS VPC](http://docs.aws.amazon.com/en_us/AmazonVPC/latest/UserGuide/VPC_Subnets.html)
  73 +is assigned a single CIDR with a netmask between /28 and /16.
  74 +So if each EC2 instance has a unique private IP address in AWS VPC,
  75 +the lower 16 bits of the address is also unique.
  76 +In this common case, you can use AmazonEC2MachineID as Settings.MachineID.
  77 +
  78 +See [example](https://github.com/sony/sonyflake/blob/master/example) that runs Sonyflake on AWS Elastic Beanstalk.
  79 +
  80 +License
  81 +-------
  82 +
  83 +The MIT License (MIT)
  84 +
  85 +See [LICENSE](https://github.com/sony/sonyflake/blob/master/LICENSE) for details.
  1 +module github.com/sony/sonyflake
  2 +
  3 +go 1.12
  4 +
  5 +require github.com/deckarep/golang-set v1.7.1
  1 +github.com/deckarep/golang-set v1.7.1 h1:SCQV0S6gTtp6itiFrTqI+pfmJ4LN85S1YzhDf9rTHJQ=
  2 +github.com/deckarep/golang-set v1.7.1/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ=
  1 +// Package sonyflake implements Sonyflake, a distributed unique ID generator inspired by Twitter's Snowflake.
  2 +//
  3 +// A Sonyflake ID is composed of
  4 +// 39 bits for time in units of 10 msec
  5 +// 8 bits for a sequence number
  6 +// 16 bits for a machine id
  7 +package sonyflake
  8 +
  9 +import (
  10 + "errors"
  11 + "net"
  12 + "sync"
  13 + "time"
  14 +)
  15 +
  16 +// These constants are the bit lengths of Sonyflake ID parts.
  17 +const (
  18 + BitLenTime = 39 // bit length of time
  19 + BitLenSequence = 8 // bit length of sequence number
  20 + BitLenMachineID = 63 - BitLenTime - BitLenSequence // bit length of machine id
  21 +)
  22 +
  23 +// Settings configures Sonyflake:
  24 +//
  25 +// StartTime is the time since which the Sonyflake time is defined as the elapsed time.
  26 +// If StartTime is 0, the start time of the Sonyflake is set to "2014-09-01 00:00:00 +0000 UTC".
  27 +// If StartTime is ahead of the current time, Sonyflake is not created.
  28 +//
  29 +// MachineID returns the unique ID of the Sonyflake instance.
  30 +// If MachineID returns an error, Sonyflake is not created.
  31 +// If MachineID is nil, default MachineID is used.
  32 +// Default MachineID returns the lower 16 bits of the private IP address.
  33 +//
  34 +// CheckMachineID validates the uniqueness of the machine ID.
  35 +// If CheckMachineID returns false, Sonyflake is not created.
  36 +// If CheckMachineID is nil, no validation is done.
  37 +type Settings struct {
  38 + StartTime time.Time
  39 + MachineID func() (uint16, error)
  40 + CheckMachineID func(uint16) bool
  41 +}
  42 +
  43 +// Sonyflake is a distributed unique ID generator.
  44 +type Sonyflake struct {
  45 + mutex *sync.Mutex
  46 + startTime int64
  47 + elapsedTime int64
  48 + sequence uint16
  49 + machineID uint16
  50 +}
  51 +
  52 +// NewSonyflake returns a new Sonyflake configured with the given Settings.
  53 +// NewSonyflake returns nil in the following cases:
  54 +// - Settings.StartTime is ahead of the current time.
  55 +// - Settings.MachineID returns an error.
  56 +// - Settings.CheckMachineID returns false.
  57 +func NewSonyflake(st Settings) *Sonyflake {
  58 + sf := new(Sonyflake)
  59 + sf.mutex = new(sync.Mutex)
  60 + sf.sequence = uint16(1<<BitLenSequence - 1)
  61 +
  62 + if st.StartTime.After(time.Now()) {
  63 + return nil
  64 + }
  65 + if st.StartTime.IsZero() {
  66 + sf.startTime = toSonyflakeTime(time.Date(2014, 9, 1, 0, 0, 0, 0, time.UTC))
  67 + } else {
  68 + sf.startTime = toSonyflakeTime(st.StartTime)
  69 + }
  70 +
  71 + var err error
  72 + if st.MachineID == nil {
  73 + sf.machineID, err = lower16BitPrivateIP()
  74 + } else {
  75 + sf.machineID, err = st.MachineID()
  76 + }
  77 + if err != nil || (st.CheckMachineID != nil && !st.CheckMachineID(sf.machineID)) {
  78 + return nil
  79 + }
  80 +
  81 + return sf
  82 +}
  83 +
  84 +// NextID generates a next unique ID.
  85 +// After the Sonyflake time overflows, NextID returns an error.
  86 +func (sf *Sonyflake) NextID() (uint64, error) {
  87 + const maskSequence = uint16(1<<BitLenSequence - 1)
  88 +
  89 + sf.mutex.Lock()
  90 + defer sf.mutex.Unlock()
  91 +
  92 + current := currentElapsedTime(sf.startTime)
  93 + if sf.elapsedTime < current {
  94 + sf.elapsedTime = current
  95 + sf.sequence = 0
  96 + } else { // sf.elapsedTime >= current
  97 + sf.sequence = (sf.sequence + 1) & maskSequence
  98 + if sf.sequence == 0 {
  99 + sf.elapsedTime++
  100 + overtime := sf.elapsedTime - current
  101 + time.Sleep(sleepTime((overtime)))
  102 + }
  103 + }
  104 +
  105 + return sf.toID()
  106 +}
  107 +
  108 +const sonyflakeTimeUnit = 1e7 // nsec, i.e. 10 msec
  109 +
  110 +func toSonyflakeTime(t time.Time) int64 {
  111 + return t.UTC().UnixNano() / sonyflakeTimeUnit
  112 +}
  113 +
  114 +func currentElapsedTime(startTime int64) int64 {
  115 + return toSonyflakeTime(time.Now()) - startTime
  116 +}
  117 +
  118 +func sleepTime(overtime int64) time.Duration {
  119 + return time.Duration(overtime)*10*time.Millisecond -
  120 + time.Duration(time.Now().UTC().UnixNano()%sonyflakeTimeUnit)*time.Nanosecond
  121 +}
  122 +
  123 +func (sf *Sonyflake) toID() (uint64, error) {
  124 + if sf.elapsedTime >= 1<<BitLenTime {
  125 + return 0, errors.New("over the time limit")
  126 + }
  127 +
  128 + return uint64(sf.elapsedTime)<<(BitLenSequence+BitLenMachineID) |
  129 + uint64(sf.sequence)<<BitLenMachineID |
  130 + uint64(sf.machineID), nil
  131 +}
  132 +
  133 +func privateIPv4() (net.IP, error) {
  134 + as, err := net.InterfaceAddrs()
  135 + if err != nil {
  136 + return nil, err
  137 + }
  138 +
  139 + for _, a := range as {
  140 + ipnet, ok := a.(*net.IPNet)
  141 + if !ok || ipnet.IP.IsLoopback() {
  142 + continue
  143 + }
  144 +
  145 + ip := ipnet.IP.To4()
  146 + if isPrivateIPv4(ip) {
  147 + return ip, nil
  148 + }
  149 + }
  150 + return nil, errors.New("no private ip address")
  151 +}
  152 +
  153 +func isPrivateIPv4(ip net.IP) bool {
  154 + return ip != nil &&
  155 + (ip[0] == 10 || ip[0] == 172 && (ip[1] >= 16 && ip[1] < 32) || ip[0] == 192 && ip[1] == 168)
  156 +}
  157 +
  158 +func lower16BitPrivateIP() (uint16, error) {
  159 + ip, err := privateIPv4()
  160 + if err != nil {
  161 + return 0, err
  162 + }
  163 +
  164 + return uint16(ip[2])<<8 + uint16(ip[3]), nil
  165 +}
  166 +
  167 +// Decompose returns a set of Sonyflake ID parts.
  168 +func Decompose(id uint64) map[string]uint64 {
  169 + const maskSequence = uint64((1<<BitLenSequence - 1) << BitLenMachineID)
  170 + const maskMachineID = uint64(1<<BitLenMachineID - 1)
  171 +
  172 + msb := id >> 63
  173 + time := id >> (BitLenSequence + BitLenMachineID)
  174 + sequence := id & maskSequence >> BitLenMachineID
  175 + machineID := id & maskMachineID
  176 + return map[string]uint64{
  177 + "id": id,
  178 + "msb": msb,
  179 + "time": time,
  180 + "sequence": sequence,
  181 + "machine-id": machineID,
  182 + }
  183 +}
1 -# github.com/astaxie/beego v1.10.0 1 +# github.com/astaxie/beego v1.11.1
2 github.com/astaxie/beego 2 github.com/astaxie/beego
3 github.com/astaxie/beego/config 3 github.com/astaxie/beego/config
4 github.com/astaxie/beego/context 4 github.com/astaxie/beego/context
@@ -6,6 +6,7 @@ github.com/astaxie/beego/context/param @@ -6,6 +6,7 @@ github.com/astaxie/beego/context/param
6 github.com/astaxie/beego/grace 6 github.com/astaxie/beego/grace
7 github.com/astaxie/beego/logs 7 github.com/astaxie/beego/logs
8 github.com/astaxie/beego/orm 8 github.com/astaxie/beego/orm
  9 +github.com/astaxie/beego/plugins/cors
9 github.com/astaxie/beego/session 10 github.com/astaxie/beego/session
10 github.com/astaxie/beego/toolbox 11 github.com/astaxie/beego/toolbox
11 github.com/astaxie/beego/utils 12 github.com/astaxie/beego/utils
@@ -22,6 +23,10 @@ github.com/go-redis/redis/internal/proto @@ -22,6 +23,10 @@ github.com/go-redis/redis/internal/proto
22 github.com/go-redis/redis/internal/util 23 github.com/go-redis/redis/internal/util
23 # github.com/go-sql-driver/mysql v1.4.1 24 # github.com/go-sql-driver/mysql v1.4.1
24 github.com/go-sql-driver/mysql 25 github.com/go-sql-driver/mysql
  26 +# github.com/satori/go.uuid v1.2.0
  27 +github.com/satori/go.uuid
  28 +# github.com/sony/sonyflake v1.0.0
  29 +github.com/sony/sonyflake
25 # golang.org/x/crypto v0.0.0-20191119213627-4f8c1d86b1ba 30 # golang.org/x/crypto v0.0.0-20191119213627-4f8c1d86b1ba
26 golang.org/x/crypto/acme 31 golang.org/x/crypto/acme
27 golang.org/x/crypto/acme/autocert 32 golang.org/x/crypto/acme/autocert