diff --git a/app/start.go b/app/start.go index a20a271..ccb696e 100644 --- a/app/start.go +++ b/app/start.go @@ -74,6 +74,9 @@ func Start(conf common.Conf, dbO *sqlx.DB) *gin.Engine { DB: conf.RedisDB, }) + // token updater + go tokenUpdater(db) + api := r.Group("/api") { p := api.Group("/") diff --git a/app/tokens.go b/app/tokens.go index 25463c9..071532b 100644 --- a/app/tokens.go +++ b/app/tokens.go @@ -4,20 +4,20 @@ import ( "crypto/md5" "database/sql" "fmt" - - "github.com/jmoiron/sqlx" + "time" "git.zxq.co/ripple/rippleapi/common" + "github.com/jmoiron/sqlx" ) // GetTokenFull retrieves an user ID and their token privileges knowing their API token. func GetTokenFull(token string, db *sqlx.DB) (common.Token, bool) { - var t common.Token var ( + t common.Token tokenPrivsRaw uint64 userPrivsRaw uint64 + priv8 bool ) - var priv8 bool err := db.QueryRow(`SELECT t.id, t.user, t.privileges, t.private, u.privileges FROM tokens t @@ -27,6 +27,7 @@ WHERE token = ? LIMIT 1`, Scan( &t.ID, &t.UserID, &tokenPrivsRaw, &priv8, &userPrivsRaw, ) + updateTokens <- t.ID if priv8 { tokenPrivsRaw = common.PrivilegeReadConfidential | common.PrivilegeWrite } @@ -42,3 +43,43 @@ WHERE token = ? LIMIT 1`, return t, true } } + +var updateTokens = make(chan int, 100) + +func tokenUpdater(db *sqlx.DB) { + for { + // prepare a queue of tokens to update. + tokensToUpdate := make([]int, 0, 50) + AwaitLoop: + for { + // if we got ten, move on and update + if len(tokensToUpdate) >= 50 { + break + } + // if we ain't got any, add what we get straight from updateTokens + if len(tokensToUpdate) == 0 { + x := <-updateTokens + tokensToUpdate = append(tokensToUpdate, x) + continue + } + + // otherwise, wait from updateTokens with a timeout of 10 seconds + select { + case x := <-updateTokens: + tokensToUpdate = append(tokensToUpdate, x) + case <-time.After(10 * time.Second): + // wondering what this means? + // https://golang.org/ref/spec#Break_statements + break AwaitLoop + } + } + + q, a, _ := sqlx.In("UPDATE tokens SET last_updated = ? WHERE id IN (?)", time.Now().Unix(), tokensToUpdate) + + q = db.Rebind(q) + _, err := db.Exec(q, a...) + if err != nil { + fmt.Println(err) + } + } +} diff --git a/app/v1/token.go b/app/v1/token.go index bee4431..cef2c69 100644 --- a/app/v1/token.go +++ b/app/v1/token.go @@ -4,6 +4,7 @@ import ( "crypto/md5" "database/sql" "fmt" + "time" "github.com/jmoiron/sqlx" @@ -121,7 +122,8 @@ func TokenNewPOST(md common.MethodData) common.CodeMessager { return Err500 } } - _, err = md.DB.Exec("INSERT INTO tokens(user, privileges, description, token, private) VALUES (?, ?, ?, ?, '0')", r.ID, r.Privileges, data.Description, tokenMD5) + _, err = md.DB.Exec("INSERT INTO tokens(user, privileges, description, token, private, last_updated) VALUES (?, ?, ?, ?, '0', ?)", + r.ID, r.Privileges, data.Description, tokenMD5, time.Now().Unix()) if err != nil { md.Err(err) return Err500 @@ -146,9 +148,10 @@ func TokenSelfDeletePOST(md common.MethodData) common.CodeMessager { } type token struct { - ID int `json:"id"` - Privileges uint64 `json:"privileges"` - Description string `json:"description"` + ID int `json:"id"` + Privileges uint64 `json:"privileges"` + Description string `json:"description"` + LastUpdated common.UnixTimestamp `json:"last_updated"` } type tokenResponse struct { common.ResponseBase @@ -157,14 +160,14 @@ type tokenResponse struct { // TokenGET retrieves a list listing all the user's public tokens. func TokenGET(md common.MethodData) common.CodeMessager { - rows, err := md.DB.Query("SELECT id, privileges, description FROM tokens WHERE user = ? AND private = '0'", md.ID()) + rows, err := md.DB.Query("SELECT id, privileges, description, last_updated FROM tokens WHERE user = ? AND private = '0'", md.ID()) if err != nil { return Err500 } var r tokenResponse for rows.Next() { var t token - err = rows.Scan(&t.ID, &t.Privileges, &t.Description) + err = rows.Scan(&t.ID, &t.Privileges, &t.Description, &t.LastUpdated) if err != nil { md.Err(err) continue @@ -187,9 +190,9 @@ func TokenSelfGET(md common.MethodData) common.CodeMessager { } var r tokenSingleResponse // md.User.ID = token id, userid would have been md.User.UserID. what a clusterfuck - err := md.DB.QueryRow("SELECT id, privileges, description FROM tokens WHERE id = ? "+ + err := md.DB.QueryRow("SELECT id, privileges, description, last_updated FROM tokens WHERE id = ? "+ common.Paginate(md.Query("p"), md.Query("l"), 50), md.User.ID).Scan( - &r.ID, &r.Privileges, &r.Description, + &r.ID, &r.Privileges, &r.Description, &r.LastUpdated, ) if err != nil { md.Err(err) @@ -220,7 +223,7 @@ func fixPrivileges(user int, db *sqlx.DB) { } rows, err := db.Query(` SELECT - tokens.id, tokens.privileges, users.privileges + tokens.id, tokens.privileges, users.privileges FROM tokens LEFT JOIN users ON users.id = tokens.user `+wc, params...)